Skip to content

Commit 8e95f2e

Browse files
committed
Fix EQL and JPQL LIKE with ESCAPE clause parsing.
Closes #3873
1 parent 0d5fd5a commit 8e95f2e

File tree

7 files changed

+44
-5
lines changed

7 files changed

+44
-5
lines changed

spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,8 @@ entity_type_literal
677677

678678
escape_character
679679
: CHARACTER
680-
| character_valued_input_parameter //
680+
| string_literal
681+
| character_valued_input_parameter
681682
;
682683

683684
numeric_literal

spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,8 @@ entity_type_literal
653653

654654
escape_character
655655
: CHARACTER
656-
| character_valued_input_parameter //
656+
| string_literal
657+
| character_valued_input_parameter
657658
;
658659

659660
numeric_literal
@@ -949,7 +950,7 @@ NOT_EQUAL : '<>' | '!=' ;
949950

950951
CHARACTER : '\'' (~ ('\'' | '\\')) '\'' ;
951952
IDENTIFICATION_VARIABLE : ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '$' | '_') ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '0' .. '9' | '$' | '_')* ;
952-
STRINGLITERAL : '\'' (~ ('\'' | '\\'))* '\'' ;
953+
STRINGLITERAL : '\'' (~ ('\'' | '\\')|'\\')* '\'' ;
953954
JAVASTRINGLITERAL : '"' ( ('\\' [btnfr"']) | ~('"'))* '"';
954955
FLOATLITERAL : ('0' .. '9')* '.' ('0' .. '9')+ (E ('0' .. '9')+)* (F|D)?;
955956
INTLITERAL : ('0' .. '9')+ ;

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2418,7 +2418,16 @@ public List<JpaQueryParsingToken> visitEntity_type_literal(EqlParser.Entity_type
24182418

24192419
@Override
24202420
public List<JpaQueryParsingToken> visitEscape_character(EqlParser.Escape_characterContext ctx) {
2421-
return List.of(new JpaQueryParsingToken(ctx.CHARACTER()));
2421+
2422+
if (ctx.CHARACTER() != null) {
2423+
return List.of(new JpaQueryParsingToken(ctx.CHARACTER()));
2424+
} else if (ctx.character_valued_input_parameter() != null) {
2425+
return visit(ctx.character_valued_input_parameter());
2426+
} else if (ctx.string_literal() != null) {
2427+
return visit(ctx.string_literal());
2428+
}
2429+
2430+
return List.of();
24222431
}
24232432

24242433
@Override

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2225,7 +2225,16 @@ public List<JpaQueryParsingToken> visitEntity_type_literal(JpqlParser.Entity_typ
22252225

22262226
@Override
22272227
public List<JpaQueryParsingToken> visitEscape_character(JpqlParser.Escape_characterContext ctx) {
2228-
return List.of(new JpaQueryParsingToken(ctx.CHARACTER()));
2228+
2229+
if (ctx.CHARACTER() != null) {
2230+
return List.of(new JpaQueryParsingToken(ctx.CHARACTER()));
2231+
} else if (ctx.character_valued_input_parameter() != null) {
2232+
return visit(ctx.character_valued_input_parameter());
2233+
} else if (ctx.string_literal() != null) {
2234+
return visit(ctx.string_literal());
2235+
}
2236+
2237+
return List.of();
22292238
}
22302239

22312240
@Override

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,14 @@ void signedExpressionsShouldWork(String query) {
10381038
assertQuery(query);
10391039
}
10401040

1041+
@Test // GH-3873
1042+
void escapeClauseShouldWork() {
1043+
assertQuery("select t.name from SomeDbo t where t.name LIKE :name escape '\\\\'");
1044+
assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE '\\\\'");
1045+
assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE ?1");
1046+
assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE :param");
1047+
}
1048+
10411049
@ParameterizedTest // GH-3451
10421050
@MethodSource("reservedWords")
10431051
void entityNameWithPackageContainingReservedWord(String reservedWord) {

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,9 @@ void binaryLiteralsShouldWork() {
15691569
@Test // GH-3040
15701570
void escapeClauseShouldWork() {
15711571
assertQuery("select t.name from SomeDbo t where t.name LIKE :name escape '\\\\'");
1572+
assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE '\\\\'");
1573+
assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE ?1");
1574+
assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE :param");
15721575
}
15731576

15741577
@Test // GH-3062, GH-3056

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,14 @@ void signedExpressionsShouldWork(String query) {
10461046
assertQuery(query);
10471047
}
10481048

1049+
@Test // GH-3873
1050+
void escapeClauseShouldWork() {
1051+
assertQuery("select t.name from SomeDbo t where t.name LIKE :name escape '\\\\'");
1052+
assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE '\\\\'");
1053+
assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE ?1");
1054+
assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE :param");
1055+
}
1056+
10491057
@ParameterizedTest // GH-3451
10501058
@MethodSource("reservedWords")
10511059
void entityNameWithPackageContainingReservedWord(String reservedWord) {

0 commit comments

Comments
 (0)