HHH-17759 Avoid costly string search and replace
This commit is contained in:
parent
b9aa76625b
commit
1d1278e58d
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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,20 +129,52 @@ public class ParameterParser {
|
|||
}
|
||||
// otherwise
|
||||
else {
|
||||
if ( c == ':' && indx < stringLength - 1 && Character.isJavaIdentifierStart( sqlString.charAt( indx + 1 ) )
|
||||
&& !(0 < indx && 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;
|
||||
final String param = sqlString.substring( indx + 1, chopLocation );
|
||||
if ( param.isEmpty() ) {
|
||||
throw new QueryParameterException(
|
||||
"Space is not allowed after parameter prefix ':'",
|
||||
sqlString
|
||||
);
|
||||
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;
|
||||
final String param = sqlString.substring( indx + 1, chopLocation );
|
||||
if ( param.isEmpty() ) {
|
||||
throw new QueryParameterException(
|
||||
"Space is not allowed after parameter prefix ':'",
|
||||
sqlString
|
||||
);
|
||||
}
|
||||
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( ':' );
|
||||
}
|
||||
}
|
||||
}
|
||||
recognizer.namedParameter( param, indx );
|
||||
indx = chopLocation - 1;
|
||||
}
|
||||
else if ( c == '?' ) {
|
||||
// could be either a positional or JPA-style ordinal parameter
|
||||
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -73,7 +74,7 @@ public class ParameterParserTest {
|
|||
|
||||
recognizer.validate();
|
||||
|
||||
assertTrue(recognizer.getNamedParameters().contains("param"));
|
||||
assertTrue( recognizer.getNamedParameters().contains( "param" ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -88,7 +89,7 @@ public class ParameterParserTest {
|
|||
recognizer.complete();
|
||||
recognizer.validate();
|
||||
|
||||
assertTrue( recognizer.getNamedParameters().contains("param"));
|
||||
assertTrue( recognizer.getNamedParameters().contains( "param" ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -133,94 +134,97 @@ public class ParameterParserTest {
|
|||
recognizer.complete();
|
||||
recognizer.validate();
|
||||
|
||||
assertTrue(recognizer.getNamedParameters().contains("param"));
|
||||
assertTrue( recognizer.getNamedParameters().contains( "param" ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( value = "HHH-1237")
|
||||
public void testParseColonCharacterEscaped() {
|
||||
final StringBuilder captured = new StringBuilder();
|
||||
ParameterRecognizer recognizer = new ParameterRecognizer() {
|
||||
@Override
|
||||
public void ordinalParameter(int position) {
|
||||
fail();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void namedParameter(String name, int position) {
|
||||
fail();
|
||||
}
|
||||
@Test
|
||||
@JiraKey(value = "HHH-1237")
|
||||
public void testParseColonCharacterEscaped() {
|
||||
final StringBuilder captured = new StringBuilder();
|
||||
ParameterRecognizer recognizer = new ParameterRecognizer() {
|
||||
@Override
|
||||
public void ordinalParameter(int position) {
|
||||
fail();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jpaPositionalParameter(int name, int position) {
|
||||
fail();
|
||||
}
|
||||
@Override
|
||||
public void namedParameter(String name, int position) {
|
||||
fail();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void other(char character) {
|
||||
captured.append(character);
|
||||
}
|
||||
@Override
|
||||
public void jpaPositionalParameter(int name, int position) {
|
||||
fail();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void other(char character) {
|
||||
captured.append( character );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete() {
|
||||
}
|
||||
};
|
||||
ParameterParser.parse("SELECT @a,(@a::=20) FROM tbl_name", recognizer);
|
||||
ParameterParser.parse( "SELECT @a,(@a::=20) FROM tbl_name", recognizer );
|
||||
recognizer.complete();
|
||||
assertEquals("SELECT @a,(@a:=20) FROM tbl_name", captured.toString());
|
||||
}
|
||||
assertEquals( "SELECT @a,(@a:=20) FROM tbl_name", captured.toString() );
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey( value = "HHH-17759")
|
||||
public void testParseColonCharacterTypeCasting() {
|
||||
final StringBuilder captured = new StringBuilder();
|
||||
ParameterRecognizer recognizer = new ParameterRecognizer() {
|
||||
@Override
|
||||
public void ordinalParameter(int position) {
|
||||
// don't care
|
||||
}
|
||||
@Test
|
||||
@JiraKey(value = "HHH-17759")
|
||||
public void testParseColonCharacterTypeCasting() {
|
||||
final StringBuilder captured = new StringBuilder();
|
||||
ParameterRecognizer recognizer = new ParameterRecognizer() {
|
||||
@Override
|
||||
public void ordinalParameter(int position) {
|
||||
// don't care
|
||||
}
|
||||
|
||||
@Override
|
||||
public void namedParameter(String name, int position) {
|
||||
// don't care
|
||||
}
|
||||
@Override
|
||||
public void namedParameter(String name, int position) {
|
||||
// don't care
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jpaPositionalParameter(int name, int position) {
|
||||
// don't care
|
||||
}
|
||||
@Override
|
||||
public void jpaPositionalParameter(int name, int position) {
|
||||
// don't care
|
||||
}
|
||||
|
||||
@Override
|
||||
public void other(char character) {
|
||||
captured.append(character);
|
||||
}
|
||||
@Override
|
||||
public void other(char character) {
|
||||
captured.append( character );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete() {
|
||||
}
|
||||
@Override
|
||||
public void complete() {
|
||||
}
|
||||
|
||||
};
|
||||
String expectedQuery = "SELECT column_name::text FROM table_name";
|
||||
};
|
||||
String expectedQuery = "SELECT column_name::text FROM table_name";
|
||||
|
||||
ParameterParser.parse("SELECT column_name::text FROM table_name", recognizer);
|
||||
recognizer.complete();
|
||||
assertEquals(expectedQuery, captured.toString());
|
||||
|
||||
captured.setLength(0); // clear for new test
|
||||
|
||||
ParameterParser.parse("SELECT column_name::::text FROM table_name", recognizer);
|
||||
ParameterParser.parse( "SELECT column_name::text FROM table_name", recognizer );
|
||||
recognizer.complete();
|
||||
assertEquals(expectedQuery, captured.toString());
|
||||
}
|
||||
assertEquals( expectedQuery, captured.toString() );
|
||||
|
||||
@Test
|
||||
public void testParseNamedParameter() {
|
||||
ExtendedParameterRecognizer recognizer = createRecognizer();
|
||||
NATIVE_QUERY_INTERPRETER.recognizeParameters("from Stock s where s.stockCode = :stockCode and s.xyz = :pxyz", recognizer);
|
||||
captured.setLength( 0 ); // clear for new test
|
||||
|
||||
ParameterParser.parse( "SELECT column_name::::text FROM table_name", recognizer );
|
||||
recognizer.complete();
|
||||
assertEquals( expectedQuery, captured.toString() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseNamedParameter() {
|
||||
ExtendedParameterRecognizer recognizer = createRecognizer();
|
||||
NATIVE_QUERY_INTERPRETER.recognizeParameters(
|
||||
"from Stock s where s.stockCode = :stockCode and s.xyz = :pxyz",
|
||||
recognizer
|
||||
);
|
||||
recognizer.complete();
|
||||
recognizer.validate();
|
||||
|
||||
assertTrue(recognizer.getNamedParameters().contains("stockCode"));
|
||||
assertTrue(recognizer.getNamedParameters().contains("stockCode"));
|
||||
assertTrue(recognizer.getNamedParameters().contains("pxyz"));
|
||||
assertEquals( 2, recognizer.getNamedParameters().size() );
|
||||
}
|
||||
|
@ -232,15 +236,15 @@ public class ParameterParserTest {
|
|||
recognizer.complete();
|
||||
recognizer.validate();
|
||||
|
||||
assertEquals( 1, recognizer.getJpaPositionalParameterCount() );
|
||||
assertEquals( 1, recognizer.getJpaPositionalParameterCount() );
|
||||
|
||||
recognizer = createRecognizer();
|
||||
ParameterParser.parse("from Stock s where s.stockCode = ?1 and s.xyz = ?2", recognizer);
|
||||
ParameterParser.parse( "from Stock s where s.stockCode = ?1 and s.xyz = ?2", recognizer );
|
||||
recognizer.complete();
|
||||
recognizer.validate();
|
||||
|
||||
assertEquals( 2, recognizer.getJpaPositionalParameterCount() );
|
||||
}
|
||||
assertEquals( 2, recognizer.getJpaPositionalParameterCount() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJdbcParameterScanningEnabled() {
|
||||
|
@ -278,7 +282,7 @@ public class ParameterParserTest {
|
|||
recognizer
|
||||
);
|
||||
recognizer.validate();
|
||||
assertTrue(recognizer.getNamedParameters().contains("id"));
|
||||
assertTrue( recognizer.getNamedParameters().contains( "id" ) );
|
||||
assertEquals( 0, recognizer.getOrdinalParameterCount() );
|
||||
|
||||
}
|
||||
|
@ -289,15 +293,18 @@ public class ParameterParserTest {
|
|||
|
||||
private interface ExtendedParameterRecognizer extends org.hibernate.query.sql.spi.ParameterRecognizer {
|
||||
void validate();
|
||||
|
||||
int getOrdinalParameterCount();
|
||||
|
||||
int getJpaPositionalParameterCount();
|
||||
|
||||
Set<String> getNamedParameters();
|
||||
}
|
||||
|
||||
private final static class TestParameterRecognizer implements ExtendedParameterRecognizer {
|
||||
private int ordinalParameterCount = 0;
|
||||
private final Set<Integer> jpaPositionalParameters = new HashSet<>(2);
|
||||
private final Set<String> namedParameters = new HashSet<>(2);
|
||||
private final Set<Integer> jpaPositionalParameters = new HashSet<>( 2 );
|
||||
private final Set<String> namedParameters = new HashSet<>( 2 );
|
||||
|
||||
@Override
|
||||
public void ordinalParameter(int sourcePosition) {
|
||||
|
@ -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" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue