Skip to content

Commit c22d0c7

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

File tree

15 files changed

+349
-112
lines changed

15 files changed

+349
-112
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
@@ -6,7 +6,10 @@
66
*/
77
package org.hibernate.dialect.pagination;
88

9+
import java.util.function.Supplier;
10+
911
import org.hibernate.query.spi.Limit;
12+
import org.hibernate.query.spi.QueryOptions;
1013

1114
/**
1215
* A {@link LimitHandler} for databases which support the
@@ -26,7 +29,7 @@ public OffsetFetchLimitHandler(boolean variableLimit) {
2629
}
2730

2831
@Override
29-
public String processSql(String sql, Limit limit) {
32+
public String processSql(String sql, Limit limit, QueryOptions queryOptions) {
3033

3134
boolean hasFirstRow = hasFirstRow(limit);
3235
boolean hasMaxRows = hasMaxRows(limit);
@@ -39,10 +42,14 @@ public String processSql(String sql, Limit limit) {
3942

4043
begin(sql, offsetFetch, hasFirstRow, hasMaxRows);
4144

45+
Supplier<String> parameterMarkerSupplier = ( queryOptions == null || queryOptions.getLimitHandlerParameterMarkerSupplier() == null )
46+
? () -> "?"
47+
: queryOptions.getLimitHandlerParameterMarkerSupplier();
48+
4249
if ( hasFirstRow ) {
4350
offsetFetch.append( " offset " );
4451
if ( supportsVariableLimit() ) {
45-
offsetFetch.append( "?" );
52+
offsetFetch.append( parameterMarkerSupplier.get() );
4653
}
4754
else {
4855
offsetFetch.append( limit.getFirstRow() );
@@ -60,7 +67,7 @@ public String processSql(String sql, Limit limit) {
6067
offsetFetch.append( " fetch first " );
6168
}
6269
if ( supportsVariableLimit() ) {
63-
offsetFetch.append( "?" );
70+
offsetFetch.append( parameterMarkerSupplier.get() );
6471
}
6572
else {
6673
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
@@ -11,6 +11,7 @@
1111
import java.util.HashSet;
1212
import java.util.List;
1313
import java.util.Set;
14+
import java.util.function.Supplier;
1415

1516
import jakarta.persistence.CacheRetrieveMode;
1617
import jakarta.persistence.CacheStoreMode;
@@ -54,6 +55,17 @@ public class QueryOptionsImpl implements MutableQueryOptions, AppliedGraph {
5455
private Set<String> enabledFetchProfiles;
5556
private Set<String> disabledFetchProfiles;
5657

58+
@Nullable
59+
private final Supplier<String> limitHandlerParameterMarkerSupplier;
60+
61+
public QueryOptionsImpl() {
62+
this( null );
63+
}
64+
65+
public QueryOptionsImpl(Supplier<String> limitHandlerParameterMarkerSupplier) {
66+
this.limitHandlerParameterMarkerSupplier = limitHandlerParameterMarkerSupplier;
67+
}
68+
5769
@Override
5870
public Integer getTimeout() {
5971
return timeout;
@@ -257,4 +269,9 @@ public AppliedGraph getAppliedGraph() {
257269
public @Nullable GraphSemantic getSemantic() {
258270
return graphSemantic;
259271
}
272+
273+
@Override
274+
public @Nullable Supplier<String> getLimitHandlerParameterMarkerSupplier() {
275+
return this.limitHandlerParameterMarkerSupplier;
276+
}
260277
}

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
@@ -9,6 +9,7 @@
99
import java.sql.Statement;
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;
@@ -195,6 +196,10 @@ default ListResultsConsumer.UniqueSemantic getUniqueSemantic(){
195196
return null;
196197
}
197198

199+
default Supplier<String> getLimitHandlerParameterMarkerSupplier() {
200+
return () -> "?";
201+
}
202+
198203
/**
199204
* Provide singleton access for frequently needed options:
200205
*/

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
@@ -16,8 +16,8 @@
1616
import org.hibernate.query.spi.DomainQueryExecutionContext;
1717
import org.hibernate.query.spi.NonSelectQueryPlan;
1818
import org.hibernate.query.spi.QueryParameterBindings;
19+
import org.hibernate.query.sql.spi.NativeQueryPlan;
1920
import org.hibernate.query.sql.spi.ParameterOccurrence;
20-
import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter;
2121
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
2222
import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation;
2323
import org.hibernate.sql.exec.spi.JdbcOperationQueryMutationNative;
@@ -27,7 +27,7 @@
2727
/**
2828
* @author Steve Ebersole
2929
*/
30-
public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan {
30+
public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan, NativeQueryPlan {
3131
private final String sql;
3232
private final Set<String> affectedTableNames;
3333

@@ -81,7 +81,7 @@ public int executeUpdate(DomainQueryExecutionContext executionContext) {
8181
.getStatementPreparer()
8282
.prepareStatement( sql ),
8383
(integer, preparedStatement) -> {},
84-
SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext )
84+
getSqmJdbcExecutionContext( executionContext, jdbcParameterBindings )
8585
);
8686
}
8787
}

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

Lines changed: 59 additions & 43 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,62 @@ 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+
}
823+
else {
824+
occurrenceExpansionSB.setLength( 0 );
825+
}
826+
827+
if ( !isEnclosedInParens ) {
828+
occurrenceExpansionSB.append( '(' );
802829
}
803830

804-
final String expansionListAsString;
805831
// HHH-8901
806832
if ( bindValueMaxCount == 0 ) {
807-
if ( isEnclosedInParens ) {
808-
expansionListAsString = "null";
809-
}
810-
else {
811-
expansionListAsString = "(null)";
812-
}
833+
occurrenceExpansionSB.append( "null" );
813834
}
814835
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] = '?';
836+
for ( int i = 0; i < bindValueMaxCount; i++ ) {
837+
final String marker = parameterMarkerStrategy.createMarker(
838+
expandedParamPosition + i,
839+
null
840+
);
841+
occurrenceExpansionSB.append( marker );
842+
if ( i + 1 < bindValueMaxCount ) {
843+
occurrenceExpansionSB.append( ',' );
824844
}
825845
}
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);
839846
}
847+
if ( !isEnclosedInParens ) {
848+
occurrenceExpansionSB.append( ')' );
849+
}
850+
851+
sourceOffset = getNewSourceOffsetAfterReplacement( sb, sourceOffset, occurrence, occurrenceExpansionSB.toString() );
840852

841-
final int start = sourcePosition + offset;
842-
final int end = start + 1;
843-
sb.replace( start, end, expansionListAsString );
844-
offset += expansionListAsString.length() - 1;
853+
expandedParamPosition += bindValueMaxCount;
845854
}
846855
return sb == null ? sqlString : sb.toString();
847856
}
848857

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

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
@@ -22,7 +22,6 @@
2222
import org.hibernate.query.spi.ScrollableResultsImplementor;
2323
import org.hibernate.query.sql.spi.NativeSelectQueryPlan;
2424
import org.hibernate.query.sql.spi.ParameterOccurrence;
25-
import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter;
2625
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
2726
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
2827
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
@@ -92,7 +91,7 @@ public <T> T executeQuery(DomainQueryExecutionContext executionContext, ResultsC
9291
return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor().executeQuery(
9392
jdbcSelect,
9493
jdbcParameterBindings,
95-
SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ),
94+
getSqmJdbcExecutionContext( executionContext, jdbcParameterBindings ),
9695
null,
9796
null,
9897
-1,
@@ -135,7 +134,7 @@ public List<R> performList(DomainQueryExecutionContext executionContext) {
135134
return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor().list(
136135
jdbcSelect,
137136
jdbcParameterBindings,
138-
SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ),
137+
getSqmJdbcExecutionContext( executionContext, jdbcParameterBindings ),
139138
null,
140139
queryOptions.getUniqueSemantic() == null ?
141140
ListResultsConsumer.UniqueSemantic.NEVER :
@@ -179,7 +178,7 @@ public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, Doma
179178
jdbcSelect,
180179
scrollMode,
181180
jdbcParameterBindings,
182-
SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ),
181+
getSqmJdbcExecutionContext( executionContext, jdbcParameterBindings ),
183182
null,
184183
-1
185184
);

0 commit comments

Comments
 (0)