HHH-15745 Change string literal handling in HQL lexer

This commit is contained in:
Marco Belladelli 2022-12-06 17:39:25 +01:00 committed by Christian Beikov
parent 1109dfbb1c
commit f2576d6b87
5 changed files with 89 additions and 50 deletions

View File

@ -75,9 +75,12 @@ HEX_LITERAL : '0' [xX] HEX_DIGIT+ LONG_SUFFIX?;
fragment SINGLE_QUOTE : '\''; fragment SINGLE_QUOTE : '\'';
fragment DOUBLE_QUOTE : '"'; fragment DOUBLE_QUOTE : '"';
STRING_LITERAL STRING_LITERAL : SINGLE_QUOTE ( SINGLE_QUOTE SINGLE_QUOTE | ~('\'') )* SINGLE_QUOTE;
: DOUBLE_QUOTE ( ESCAPE_SEQUENCE | DOUBLE_QUOTE DOUBLE_QUOTE | ~('"') )* DOUBLE_QUOTE
| SINGLE_QUOTE ( ESCAPE_SEQUENCE | SINGLE_QUOTE SINGLE_QUOTE | ~('\'') )* SINGLE_QUOTE JAVA_STRING_LITERAL
: DOUBLE_QUOTE ( ESCAPE_SEQUENCE | ~('"') )* DOUBLE_QUOTE
| [jJ] SINGLE_QUOTE ( ESCAPE_SEQUENCE | ~('\'') )* SINGLE_QUOTE
| [jJ] DOUBLE_QUOTE ( ESCAPE_SEQUENCE | ~('\'') )* DOUBLE_QUOTE
; ;
fragment BACKSLASH : '\\'; fragment BACKSLASH : '\\';
@ -96,6 +99,7 @@ UNICODE_ESCAPE
BINARY_LITERAL BINARY_LITERAL
: [xX] SINGLE_QUOTE (HEX_DIGIT HEX_DIGIT)* SINGLE_QUOTE : [xX] SINGLE_QUOTE (HEX_DIGIT HEX_DIGIT)* SINGLE_QUOTE
| [xX] DOUBLE_QUOTE (HEX_DIGIT HEX_DIGIT)* DOUBLE_QUOTE
; ;
// ESCAPE start tokens // ESCAPE start tokens

View File

@ -665,7 +665,7 @@ inList
* A single character used to escape the '_' and '%' wildcards in a 'like' pattern * A single character used to escape the '_' and '%' wildcards in a 'like' pattern
*/ */
likeEscape likeEscape
: ESCAPE (STRING_LITERAL | parameter) : ESCAPE (STRING_LITERAL | JAVA_STRING_LITERAL | parameter)
; ;
@ -823,6 +823,7 @@ searchedCaseWhen
*/ */
literal literal
: STRING_LITERAL : STRING_LITERAL
| JAVA_STRING_LITERAL
| NULL | NULL
| booleanLiteral | booleanLiteral
| numericLiteral | numericLiteral

View File

@ -74,7 +74,7 @@ public final class QuotingHelper {
final int end = text.length() - 1; final int end = text.length() - 1;
final char delimiter = text.charAt( 0 ); final char delimiter = text.charAt( 0 );
assert delimiter == text.charAt( end ); assert delimiter == text.charAt( end );
// Unescape the parsed literal and handle escape sequences // Unescape the parsed literal
final StringBuilder sb = new StringBuilder( text.length() - 2 ); final StringBuilder sb = new StringBuilder( text.length() - 2 );
for ( int i = 1; i < end; i++ ) { for ( int i = 1; i < end; i++ ) {
char c = text.charAt( i ); char c = text.charAt( i );
@ -89,8 +89,26 @@ public final class QuotingHelper {
i++; i++;
} }
break; break;
case '\\': default:
if ( ( i + 1 ) < end ) { break;
}
sb.append( c );
}
return sb.toString();
}
public static String unquoteJavaStringLiteral(String text) {
assert text.length() > 1;
final char firstChar = text.charAt( 0 );
final int start = firstChar == 'j' || firstChar == 'J' ? 1 : 0;
final int end = text.length() - 1;
final char delimiter = text.charAt( start );
assert delimiter == text.charAt( end );
// Handle escape sequences
final StringBuilder sb = new StringBuilder( text.length() - ( start + 2 ) );
for ( int i = start + 1; i < end; i++ ) {
char c = text.charAt( i );
if ( c == '\\' && ( i + 1 ) < end ) {
char nextChar = text.charAt( ++i ); char nextChar = text.charAt( ++i );
switch ( nextChar ) { switch ( nextChar ) {
case 'b': case 'b':
@ -130,10 +148,6 @@ public final class QuotingHelper {
break; break;
} }
} }
break;
default:
break;
}
sb.append( c ); sb.append( c );
} }
return sb.toString(); return sb.toString();

View File

@ -3275,6 +3275,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
switch ( node.getSymbol().getType() ) { switch ( node.getSymbol().getType() ) {
case HqlParser.STRING_LITERAL: case HqlParser.STRING_LITERAL:
return stringLiteral( node.getText() ); return stringLiteral( node.getText() );
case HqlParser.JAVA_STRING_LITERAL:
return javaStringLiteral( node.getText() );
case HqlParser.INTEGER_LITERAL: case HqlParser.INTEGER_LITERAL:
return integerOrLongLiteral( node.getText() ); return integerOrLongLiteral( node.getText() );
case HqlParser.LONG_LITERAL: case HqlParser.LONG_LITERAL:
@ -3587,6 +3589,15 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
); );
} }
private SqmLiteral<String> javaStringLiteral(String text) {
String unquoted = QuotingHelper.unquoteJavaStringLiteral( text );
return new SqmLiteral<>(
unquoted,
resolveExpressibleTypeBasic( String.class ),
creationContext.getNodeBuilder()
);
}
private SqmLiteral<byte[]> binaryLiteral(String text) { private SqmLiteral<byte[]> binaryLiteral(String text) {
return new SqmLiteral<>( return new SqmLiteral<>(
PrimitiveByteArrayJavaType.INSTANCE.fromString( PrimitiveByteArrayJavaType.INSTANCE.fromString(

View File

@ -60,6 +60,17 @@ public class LiteralTests {
); );
} }
@Test
public void testJavaString(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
assertThat( session.createQuery( "select \"\\n\"" ).getSingleResult(), is( "\n" ) );
assertThat( session.createQuery( "select J'\\n'" ).getSingleResult(), is( "\n" ) );
assertThat( session.createQuery( "select J'\\''" ).getSingleResult(), is( "'" ) );
}
);
}
@Test @Test
public void testJdbcTimeLiteral(SessionFactoryScope scope) { public void testJdbcTimeLiteral(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
@ -315,6 +326,4 @@ public class LiteralTests {
} }
); );
} }
} }