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 .internal .ParameterMarkerStrategyStandard ;
93+ import org .hibernate .sql .ast .spi .ParameterMarkerStrategy ;
9394import org .hibernate .sql .exec .internal .CallbackImpl ;
9495import org .hibernate .sql .exec .spi .Callback ;
9596import org .hibernate .sql .results .jdbc .spi .JdbcValuesMappingProducer ;
117118
118119/**
119120 * @author Steve Ebersole
121+ * @author Nathan Xu
120122 */
121123public class NativeQueryImpl <R >
122124 extends AbstractQuery <R >
@@ -370,7 +372,8 @@ private ParameterInterpretation resolveParameterInterpretation(
370372 return interpretationCache .resolveNativeQueryParameters (
371373 sqlString ,
372374 s -> {
373- final ParameterRecognizerImpl parameterRecognizer = new ParameterRecognizerImpl ();
375+ final ParameterMarkerStrategy parameterMarkerStrategy = sessionFactory .getJdbcServices ().getDialect ().getNativeParameterMarkerStrategy ();
376+ final ParameterRecognizerImpl parameterRecognizer = new ParameterRecognizerImpl (parameterMarkerStrategy );
374377
375378 session .getFactory ().getServiceRegistry ()
376379 .requireService ( NativeQueryInterpreter .class )
@@ -736,23 +739,38 @@ protected String expandParameterLists() {
736739 // Some DBs limit number of IN expressions. For now, warn...
737740 final SessionFactoryImplementor sessionFactory = getSessionFactory ();
738741 final Dialect dialect = sessionFactory .getJdbcServices ().getDialect ();
742+
743+ ParameterMarkerStrategy parameterMarkerStrategy = dialect .getNativeParameterMarkerStrategy ();
744+ if (parameterMarkerStrategy == null ) {
745+ parameterMarkerStrategy = ParameterMarkerStrategyStandard .INSTANCE ;
746+ }
739747 final boolean paddingEnabled = sessionFactory .getSessionFactoryOptions ().inClauseParameterPaddingEnabled ();
740748 final int inExprLimit = dialect .getInExpressionCountLimit ();
741749
742750 StringBuilder sb = null ;
751+ StringBuilder occurrenceExpansionSB = null ;
743752
744753 // Handle parameter lists
745- int offset = 0 ;
746- for ( ParameterOccurrence occurrence : parameterOccurrences ) {
754+ int sourceOffset = 0 ;
755+ int expandedParamPosition = 1 ;
756+ for ( int originalParamPosition = 1 ; originalParamPosition <= parameterOccurrences .size (); originalParamPosition ++ ) {
757+ final ParameterOccurrence occurrence = parameterOccurrences .get ( originalParamPosition - 1 );
747758 final QueryParameterImplementor <?> queryParameter = occurrence .getParameter ();
748759 final QueryParameterBinding <?> binding = parameterBindings .getBinding ( queryParameter );
749760 if ( !binding .isMultiValued () ) {
761+ if ( originalParamPosition != expandedParamPosition ) {
762+ if ( sb == null ) {
763+ sb = new StringBuilder ( sqlString );
764+ }
765+ sourceOffset = getNewSourceOffsetAfterReplacement ( sb , sourceOffset , occurrence , parameterMarkerStrategy .createMarker ( expandedParamPosition , occurrence .getJdbcType () ) );
766+ }
767+ expandedParamPosition ++;
750768 continue ;
751769 }
752770 final Collection <?> bindValues = binding .getBindValues ();
753771
754- int bindValueCount = bindValues .size ();
755- int bindValueMaxCount = determineBindValueMaxCount ( paddingEnabled , inExprLimit , bindValueCount );
772+ final int bindValueCount = bindValues .size ();
773+ final int bindValueMaxCount = determineBindValueMaxCount ( paddingEnabled , inExprLimit , bindValueCount );
756774
757775 if ( inExprLimit > 0 && bindValueCount > inExprLimit ) {
758776 log .tooManyInExpressions (
@@ -767,6 +785,7 @@ protected String expandParameterLists() {
767785
768786 final int sourcePosition = occurrence .getSourcePosition ();
769787 if ( sourcePosition < 0 ) {
788+ expandedParamPosition ++;
770789 continue ;
771790 }
772791
@@ -781,7 +800,7 @@ protected String expandParameterLists() {
781800 }
782801 }
783802 if ( isEnclosedInParens ) {
784- for ( int i = sourcePosition + 1 ; i < sqlString .length (); i ++ ) {
803+ for ( int i = sourcePosition + occurrence . getLength () ; i < sqlString .length (); i ++ ) {
785804 final char ch = sqlString .charAt ( i );
786805 if ( !Character .isWhitespace ( ch ) ) {
787806 isEnclosedInParens = ch == ')' ;
@@ -790,62 +809,53 @@ protected String expandParameterLists() {
790809 }
791810 }
792811
793- if ( bindValueCount == 1 && isEnclosedInParens ) {
794- // short-circuit for performance when only 1 value and the
795- // placeholder is already enclosed in parentheses...
796- continue ;
812+ if ( sb == null ) {
813+ sb = new StringBuilder ( sqlString );
797814 }
798815
799- if ( sb == null ) {
800- sb = new StringBuilder ( sqlString .length () + 20 );
801- sb .append ( sqlString );
816+ if ( occurrenceExpansionSB == null ) {
817+ occurrenceExpansionSB = new StringBuilder ();
818+ } else {
819+ occurrenceExpansionSB .setLength ( 0 );
820+ }
821+
822+ if ( !isEnclosedInParens ) {
823+ occurrenceExpansionSB .append ( '(' );
802824 }
803825
804- final String expansionListAsString ;
805826 // HHH-8901
806827 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 ] = '?' ;
824- }
825- }
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 ] = '?' ;
828+ occurrenceExpansionSB .append ( "null" );
829+ } else {
830+ for ( int i = 0 ; i < bindValueMaxCount ; i ++ ) {
831+ final String marker = parameterMarkerStrategy .createMarker (
832+ expandedParamPosition + i ,
833+ occurrence .getJdbcType ()
834+ );
835+ occurrenceExpansionSB .append ( marker );
836+ if ( i + 1 < bindValueMaxCount ) {
837+ occurrenceExpansionSB .append ( ',' );
834838 }
835- chars [chars .length - 1 ] = ')' ;
836839 }
837-
838- expansionListAsString = new String (chars );
839840 }
841+ if ( !isEnclosedInParens ) {
842+ occurrenceExpansionSB .append ( ')' );
843+ }
844+
845+ sourceOffset = getNewSourceOffsetAfterReplacement ( sb , sourceOffset , occurrence , occurrenceExpansionSB .toString () );
840846
841- final int start = sourcePosition + offset ;
842- final int end = start + 1 ;
843- sb .replace ( start , end , expansionListAsString );
844- offset += expansionListAsString .length () - 1 ;
847+ expandedParamPosition += bindValueMaxCount ;
845848 }
846849 return sb == null ? sqlString : sb .toString ();
847850 }
848851
852+ private int getNewSourceOffsetAfterReplacement (StringBuilder sb , int sourceOffset , ParameterOccurrence occurrence , String replacement ) {
853+ final int start = occurrence .getSourcePosition () + sourceOffset ;
854+ final int end = start + occurrence .getLength ();
855+ sb .replace ( start , end , replacement );
856+ return sourceOffset + ( replacement .length () - occurrence .getLength () );
857+ }
858+
849859 public static int determineBindValueMaxCount (boolean paddingEnabled , int inExprLimit , int bindValueCount ) {
850860 int bindValueMaxCount = bindValueCount ;
851861
0 commit comments