HHH-17759 Avoid costly string search and replace

This commit is contained in:
Christian Beikov 2024-03-20 18:25:56 +01:00
parent b9aa76625b
commit 1d1278e58d
3 changed files with 136 additions and 103 deletions

View File

@ -283,4 +283,11 @@ public interface DeprecationLogger extends BasicLogger {
)
void deprecatedSettingNoReplacement(String settingName);
@LogMessage(level = WARN)
@Message(
id = 90000031,
value = "The native query colon escaping used for the [%s] operator is deprecated and will be removed. Use [%s] instead."
)
void deprecatedNativeQueryColonEscaping(String oldOperator, String newOperator);
}

View File

@ -53,7 +53,6 @@ public class ParameterParser {
*/
public static void parse(String sqlString, ParameterRecognizer recognizer, boolean nativeJdbcParametersIgnored) throws QueryException {
checkIsNotAFunctionCall( sqlString );
sqlString = preprocessing(sqlString);
final int stringLength = sqlString.length();
boolean inSingleQuotes = false;
@ -130,8 +129,8 @@ public class ParameterParser {
}
// otherwise
else {
if ( c == ':' && indx < stringLength - 1 && Character.isJavaIdentifierStart( sqlString.charAt( indx + 1 ) )
&& !(0 < indx && sqlString.charAt( indx - 1 ) == ':')) {
if ( c == ':' ) {
if ( indx < stringLength - 1 && Character.isJavaIdentifierStart( sqlString.charAt( indx + 1 ) ) ) {
// named parameter
final int right = StringHelper.firstIndexOfChar( sqlString, HQL_SEPARATORS_BITSET, indx + 1 );
final int chopLocation = right < 0 ? sqlString.length() : right;
@ -145,6 +144,38 @@ public class ParameterParser {
recognizer.namedParameter( param, indx );
indx = chopLocation - 1;
}
else {
// For backwards compatibility, allow some known operators in the escaped form
if ( indx < stringLength - 3
&& sqlString.charAt( indx + 1 ) == ':'
&& sqlString.charAt( indx + 2 ) == ':'
&& sqlString.charAt( indx + 3 ) == ':' ) {
// Detect the :: operator, escaped as ::::
DeprecationLogger.DEPRECATION_LOGGER.deprecatedNativeQueryColonEscaping( "::::", "::" );
recognizer.other( ':' );
recognizer.other( ':' );
indx += 3;
}
else if ( indx < stringLength - 2
&& sqlString.charAt( indx + 1 ) == ':'
&& sqlString.charAt( indx + 2 ) == '=' ) {
// Detect the := operator, escaped as ::=
DeprecationLogger.DEPRECATION_LOGGER.deprecatedNativeQueryColonEscaping( "::=", ":=" );
recognizer.other( ':' );
recognizer.other( '=' );
indx += 2;
}
else {
recognizer.other( ':' );
// Consume all following colons as they are eagerly to not confuse named parameter detection
while ( indx < stringLength - 1
&& sqlString.charAt( indx + 1 ) == ':' ) {
indx++;
recognizer.other( ':' );
}
}
}
}
else if ( c == '?' ) {
// could be either a positional or JPA-style ordinal parameter
if ( indx < stringLength - 1 && Character.isDigit( sqlString.charAt( indx + 1 ) ) ) {
@ -176,19 +207,6 @@ public class ParameterParser {
recognizer.complete();
}
private static String preprocessing(String sqlString) {
final Map<String, String> preprocessingExchangeMap = Map.of("::=", ":=", "::::", "::");
for (Map.Entry<String, String> entry : preprocessingExchangeMap.entrySet()) {
final String preprocessedSqlString = sqlString.replace(entry.getKey(), entry.getValue());
if (!sqlString.equals(preprocessedSqlString)) {
DeprecationLogger.DEPRECATION_LOGGER.warn(
String.format("An unconventional syntax has been used in the SQL statement. It is recommended to use '%s' instead of '%s'.", entry.getValue(), entry.getKey()));
sqlString = preprocessedSqlString;
}
}
return sqlString;
}
public static void parse(String sqlString, ParameterRecognizer recognizer) throws QueryException {
parse( sqlString, recognizer, false );
}

View File

@ -13,6 +13,7 @@ import org.hibernate.engine.query.ParameterRecognitionException;
import org.hibernate.engine.query.internal.NativeQueryInterpreterStandardImpl;
import org.hibernate.query.sql.internal.ParameterParser;
import org.hibernate.query.sql.spi.ParameterRecognizer;
import org.hibernate.testing.orm.junit.JiraKey;
import org.junit.jupiter.api.Test;
@ -216,7 +217,10 @@ public class ParameterParserTest {
@Test
public void testParseNamedParameter() {
ExtendedParameterRecognizer recognizer = createRecognizer();
NATIVE_QUERY_INTERPRETER.recognizeParameters("from Stock s where s.stockCode = :stockCode and s.xyz = :pxyz", recognizer);
NATIVE_QUERY_INTERPRETER.recognizeParameters(
"from Stock s where s.stockCode = :stockCode and s.xyz = :pxyz",
recognizer
);
recognizer.complete();
recognizer.validate();
@ -289,8 +293,11 @@ public class ParameterParserTest {
private interface ExtendedParameterRecognizer extends org.hibernate.query.sql.spi.ParameterRecognizer {
void validate();
int getOrdinalParameterCount();
int getJpaPositionalParameterCount();
Set<String> getNamedParameters();
}
@ -346,7 +353,8 @@ public class ParameterParserTest {
}
private ParameterRecognitionException mixedParamStrategy() {
throw new ParameterRecognitionException( "Mixed parameter strategies - use just one of named, positional or JPA-ordinal strategy" );
throw new ParameterRecognitionException(
"Mixed parameter strategies - use just one of named, positional or JPA-ordinal strategy" );
}
}
}