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 271e9d168b..140916515f 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 @@ -428,6 +428,7 @@ string_expression | aggregate_expression | case_expression | function_invocation + | string_expression '||' string_expression | '(' subquery ')' ; 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 7ff5a6f393..13b2bd7fd4 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 @@ -1527,6 +1527,11 @@ public List visitString_expression(JpqlParser.String_expre tokens.addAll(visit(ctx.case_expression())); } else if (ctx.function_invocation() != null) { tokens.addAll(visit(ctx.function_invocation())); + } else if (ctx.string_expression() != null) { + + tokens.addAll(visit(ctx.string_expression(0))); + tokens.add(new JpaQueryParsingToken("||")); + tokens.addAll(visit(ctx.string_expression(1))); } else if (ctx.subquery() != null) { tokens.add(TOKEN_OPEN_PAREN); 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 0feaf2cdde..e2cc7e5105 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 @@ -1600,4 +1600,14 @@ void newShouldBeLegalAsPartOfAStateFieldPathExpression() { ORDER BY j.id """); } + + @Test // GH-3136 + void doublePipeShouldBeValidAsAStringConcatOperator() { + + assertQuery(""" + select e.name || ' ' || e.title + from Employee e + """); + } + } 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 d738ccac37..1eedc0b9da 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 @@ -983,4 +983,13 @@ void newShouldBeLegalAsPartOfAStateFieldPathExpression() { ORDER BY j.id """); } + + @Test // GH-3136 + void doublePipeShouldBeValidAsAStringConcatOperator() { + + assertQuery(""" + select e.name || ' ' || e.title + from Employee e + """); + } }