diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 index 17fcc1ece..0c5468091 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 @@ -688,7 +688,8 @@ entity_type_literal escape_character : CHARACTER - | character_valued_input_parameter // + | string_literal + | character_valued_input_parameter ; numeric_literal diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index 140abdbbd..e0e2a0a35 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -674,7 +674,8 @@ entity_type_literal escape_character : CHARACTER - | character_valued_input_parameter // + | string_literal + | character_valued_input_parameter ; numeric_literal @@ -982,7 +983,7 @@ NOT_EQUAL : '<>' | '!=' ; CHARACTER : '\'' (~ ('\'' | '\\')) '\'' ; IDENTIFICATION_VARIABLE : ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '$' | '_') ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '0' .. '9' | '$' | '_')* ; -STRINGLITERAL : '\'' (~ ('\'' | '\\'))* '\'' ; +STRINGLITERAL : '\'' (~ ('\'' | '\\')|'\\')* '\'' ; JAVASTRINGLITERAL : '"' ( ('\\' [btnfr"']) | ~('"'))* '"'; FLOATLITERAL : ('0' .. '9')* '.' ('0' .. '9')+ (E ('0' .. '9')+)* (F|D)?; INTLITERAL : ('0' .. '9')+ ; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java index c5f5236a7..22b264cc1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java @@ -2309,7 +2309,16 @@ class EqlQueryRenderer extends EqlBaseVisitor { @Override public QueryTokenStream visitEscape_character(EqlParser.Escape_characterContext ctx) { - return QueryRendererBuilder.from(QueryTokens.token(ctx.CHARACTER())); + + if (ctx.CHARACTER() != null) { + return QueryRendererBuilder.from(QueryTokens.token(ctx.CHARACTER())); + } else if (ctx.character_valued_input_parameter() != null) { + return visit(ctx.character_valued_input_parameter()); + } else if (ctx.string_literal() != null) { + return visit(ctx.string_literal()); + } + + return QueryTokenStream.empty(); } @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java index a68932cbe..41ca18396 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java @@ -2182,7 +2182,16 @@ class JpqlQueryRenderer extends JpqlBaseVisitor { @Override public QueryTokenStream visitEscape_character(JpqlParser.Escape_characterContext ctx) { - return QueryRenderer.from(QueryTokens.expression(ctx.CHARACTER())); + + if (ctx.CHARACTER() != null) { + return QueryRenderer.from(QueryTokens.expression(ctx.CHARACTER())); + } else if (ctx.character_valued_input_parameter() != null) { + return visit(ctx.character_valued_input_parameter()); + } else if (ctx.string_literal() != null) { + return visit(ctx.string_literal()); + } + + return QueryTokenStream.empty(); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java index 51b5bd419..9ad73bae5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java @@ -1056,6 +1056,14 @@ class EqlQueryRendererTests { assertQuery(query); } + @Test // GH-3873 + void escapeClauseShouldWork() { + assertQuery("select t.name from SomeDbo t where t.name LIKE :name escape '\\\\'"); + assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE '\\\\'"); + assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE ?1"); + assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE :param"); + } + @ParameterizedTest // GH-3451 @MethodSource("reservedWords") void entityNameWithPackageContainingReservedWord(String reservedWord) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index 349b06467..78693498c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1801,6 +1801,9 @@ class HqlQueryRendererTests { @Test // GH-3040 void escapeClauseShouldWork() { assertQuery("select t.name from SomeDbo t where t.name LIKE :name escape '\\\\'"); + assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE '\\\\'"); + assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE ?1"); + assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE :param"); } @Test // GH-3062, GH-3056 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index a7a6b2449..d3daa9e72 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -1049,6 +1049,14 @@ class JpqlQueryRendererTests { assertQuery(query); } + @Test // GH-3873 + void escapeClauseShouldWork() { + assertQuery("select t.name from SomeDbo t where t.name LIKE :name escape '\\\\'"); + assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE '\\\\'"); + assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE ?1"); + assertQuery("SELECT e FROM SampleEntity e WHERE LOWER(e.label) LIKE LOWER(?1) ESCAPE :param"); + } + @ParameterizedTest // GH-3451 @MethodSource("reservedWords") void entityNameWithPackageContainingReservedWord(String reservedWord) {