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 ;
115116
116117/**
117118 * @author Steve Ebersole
119+ * @author Nathan Xu
118120 */
119121public 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
0 commit comments