diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 7516949148..36205292b6 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -686,7 +686,7 @@ character ; functionName - : reservedWord + : reservedWord ('.' reservedWord)* ; reservedWord diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index e705664a7f..3f5aa00df6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -2503,7 +2503,17 @@ public List visitCharacter(HqlParser.CharacterContext ctx) @Override public List visitFunctionName(HqlParser.FunctionNameContext ctx) { - return visit(ctx.reservedWord()); + + List tokens = new ArrayList<>(); + + ctx.reservedWord().forEach(reservedWordContext -> { + tokens.addAll(visit(reservedWordContext)); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + }); + CLIP(tokens); + + return tokens; } @Override 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 48592afbf8..d95edc11e4 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 @@ -1556,4 +1556,32 @@ void typeShouldBeAValidParameter() { assertQuery("select e from Employee e where e.type = :_type"); assertQuery("select te from TestEntity te where te.type = :type"); } + + @Test // GH-3099 + void functionNamesShouldSupportSchemaScoping() { + + assertQuery(""" + SELECT b + FROM MyEntity b + WHERE b.status = :status + AND utl_raw.cast_to_varchar2((nlssort(lower(b.name), 'nls_sort=binary_ai'))) LIKE lower(:name) + ORDER BY utl_raw.cast_to_varchar2((nlssort(lower(b.name), 'nls_sort=binary_ai'))) ASC + """); + + assertQuery(""" + select b + from Bairro b + where b.situacao = :situacao + and utl_raw.cast_to_varchar2((nlssort(lower(b.nome), 'nls_sort=binary_ai'))) like lower(:nome) + order by utl_raw.cast_to_varchar2((nlssort(lower(b.nome), 'nls_sort=binary_ai'))) ASC + """); + + assertQuery(""" + select b + from Bairro b + where b.situacao = :situacao + and CTM_UTLRAW_NLSSORT_LOWER(b.nome) like lower(:nome) + order by CTM_UTLRAW_NLSSORT_LOWER(b.nome) ASC + """); + } }