8888import org .hibernate .query .sql .spi .ParameterInterpretation ;
8989import org .hibernate .query .sql .spi .ParameterOccurrence ;
9090import org .hibernate .query .sql .spi .SelectInterpretationsKey ;
91+ import org .hibernate .sql .ast .spi .ParameterMarkerStrategy ;
9192import org .hibernate .sql .exec .internal .CallbackImpl ;
9293import org .hibernate .sql .exec .spi .Callback ;
9394import org .hibernate .sql .results .jdbc .spi .JdbcValuesMappingProducer ;
@@ -368,7 +369,8 @@ private ParameterInterpretation resolveParameterInterpretation(
368369 return interpretationCache .resolveNativeQueryParameters (
369370 sqlString ,
370371 s -> {
371- final ParameterRecognizerImpl parameterRecognizer = new ParameterRecognizerImpl ();
372+ final ParameterMarkerStrategy parameterMarkerStrategy = sessionFactory .getServiceRegistry ().getService ( ParameterMarkerStrategy .class );
373+ final ParameterRecognizerImpl parameterRecognizer = new ParameterRecognizerImpl ( parameterMarkerStrategy );
372374
373375 session .getFactory ().getServiceRegistry ()
374376 .requireService ( NativeQueryInterpreter .class )
@@ -734,23 +736,36 @@ protected String expandParameterLists() {
734736 // Some DBs limit number of IN expressions. For now, warn...
735737 final SessionFactoryImplementor sessionFactory = getSessionFactory ();
736738 final Dialect dialect = sessionFactory .getJdbcServices ().getDialect ();
739+
740+ final ParameterMarkerStrategy parameterMarkerStrategy = sessionFactory .getServiceRegistry ().getService ( ParameterMarkerStrategy .class );
741+
737742 final boolean paddingEnabled = sessionFactory .getSessionFactoryOptions ().inClauseParameterPaddingEnabled ();
738743 final int inExprLimit = dialect .getInExpressionCountLimit ();
739744
740745 StringBuilder sb = null ;
746+ StringBuilder occurrenceExpansionSB = null ;
741747
742748 // Handle parameter lists
743- int offset = 0 ;
744- for ( ParameterOccurrence occurrence : parameterOccurrences ) {
749+ int sourceOffset = 0 ;
750+ int expandedParamPosition = 1 ;
751+ for ( int originalParamPosition = 1 ; originalParamPosition <= parameterOccurrences .size (); originalParamPosition ++ ) {
752+ final ParameterOccurrence occurrence = parameterOccurrences .get ( originalParamPosition - 1 );
745753 final QueryParameterImplementor <?> queryParameter = occurrence .getParameter ();
746754 final QueryParameterBinding <?> binding = parameterBindings .getBinding ( queryParameter );
747755 if ( !binding .isMultiValued () ) {
756+ if ( originalParamPosition != expandedParamPosition ) {
757+ if ( sb == null ) {
758+ sb = new StringBuilder ( sqlString );
759+ }
760+ sourceOffset = getNewSourceOffsetAfterReplacement ( sb , sourceOffset , occurrence , parameterMarkerStrategy .createMarker ( expandedParamPosition , null ) );
761+ }
762+ expandedParamPosition ++;
748763 continue ;
749764 }
750765 final Collection <?> bindValues = binding .getBindValues ();
751766
752- int bindValueCount = bindValues .size ();
753- int bindValueMaxCount = determineBindValueMaxCount ( paddingEnabled , inExprLimit , bindValueCount );
767+ final int bindValueCount = bindValues .size ();
768+ final int bindValueMaxCount = determineBindValueMaxCount ( paddingEnabled , inExprLimit , bindValueCount );
754769
755770 if ( inExprLimit > 0 && bindValueCount > inExprLimit ) {
756771 log .tooManyInExpressions (
@@ -765,6 +780,7 @@ protected String expandParameterLists() {
765780
766781 final int sourcePosition = occurrence .getSourcePosition ();
767782 if ( sourcePosition < 0 ) {
783+ expandedParamPosition ++;
768784 continue ;
769785 }
770786
@@ -779,7 +795,7 @@ protected String expandParameterLists() {
779795 }
780796 }
781797 if ( isEnclosedInParens ) {
782- for ( int i = sourcePosition + 1 ; i < sqlString .length (); i ++ ) {
798+ for ( int i = sourcePosition + occurrence . getLength () ; i < sqlString .length (); i ++ ) {
783799 final char ch = sqlString .charAt ( i );
784800 if ( !Character .isWhitespace ( ch ) ) {
785801 isEnclosedInParens = ch == ')' ;
@@ -788,62 +804,62 @@ protected String expandParameterLists() {
788804 }
789805 }
790806
791- if ( bindValueCount == 1 && isEnclosedInParens ) {
807+ if ( bindValueCount == 1 && isEnclosedInParens && expandedParamPosition == originalParamPosition ) {
792808 // short-circuit for performance when only 1 value and the
793809 // placeholder is already enclosed in parentheses...
810+ expandedParamPosition ++;
794811 continue ;
795812 }
796813
797814 if ( sb == null ) {
798- sb = new StringBuilder ( sqlString .length () + 20 );
799- sb .append ( sqlString );
815+ sb = new StringBuilder ( sqlString );
816+ }
817+
818+ if ( occurrenceExpansionSB == null ) {
819+ occurrenceExpansionSB = new StringBuilder ();
820+ }
821+ else {
822+ occurrenceExpansionSB .setLength ( 0 );
823+ }
824+
825+ if ( !isEnclosedInParens ) {
826+ occurrenceExpansionSB .append ( '(' );
800827 }
801828
802- final String expansionListAsString ;
803829 // HHH-8901
804830 if ( bindValueMaxCount == 0 ) {
805- if ( isEnclosedInParens ) {
806- expansionListAsString = "null" ;
807- }
808- else {
809- expansionListAsString = "(null)" ;
810- }
831+ occurrenceExpansionSB .append ( "null" );
811832 }
812833 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 ] = '?' ;
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 ( ',' );
822842 }
823843 }
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 );
837844 }
845+ if ( !isEnclosedInParens ) {
846+ occurrenceExpansionSB .append ( ')' );
847+ }
848+
849+ sourceOffset = getNewSourceOffsetAfterReplacement ( sb , sourceOffset , occurrence , occurrenceExpansionSB .toString () );
838850
839- final int start = sourcePosition + offset ;
840- final int end = start + 1 ;
841- sb .replace ( start , end , expansionListAsString );
842- offset += expansionListAsString .length () - 1 ;
851+ expandedParamPosition += bindValueMaxCount ;
843852 }
844853 return sb == null ? sqlString : sb .toString ();
845854 }
846855
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+
847863 public static int determineBindValueMaxCount (boolean paddingEnabled , int inExprLimit , int bindValueCount ) {
848864 int bindValueMaxCount = bindValueCount ;
849865
0 commit comments