5555import org .hibernate .query .internal .DelegatingDomainQueryExecutionContext ;
5656import org .hibernate .query .internal .ParameterMetadataImpl ;
5757import org .hibernate .query .internal .QueryOptionsImpl ;
58- import org .hibernate .query .internal .QueryParameterBindingsImpl ;
5958import org .hibernate .query .internal .ResultSetMappingResolutionContext ;
6059import org .hibernate .query .named .NamedResultSetMappingMemento ;
6160import org .hibernate .query .results .Builders ;
9089import org .hibernate .query .sql .spi .ParameterInterpretation ;
9190import org .hibernate .query .sql .spi .ParameterOccurrence ;
9291import org .hibernate .query .sql .spi .SelectInterpretationsKey ;
92+ import org .hibernate .sql .ast .spi .ParameterMarkerStrategy ;
9393import org .hibernate .sql .exec .internal .CallbackImpl ;
9494import org .hibernate .sql .exec .spi .Callback ;
9595import org .hibernate .sql .results .jdbc .spi .JdbcValuesMappingProducer ;
117117
118118/**
119119 * @author Steve Ebersole
120+ * @author Nathan Xu
120121 */
121122public 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
0 commit comments