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 1ca0c466b..679c6293b 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 @@ -492,9 +492,18 @@ frameEnd // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-functions castFunction - : CAST '(' expression AS identifier ')' + : CAST '(' expression AS castTarget ')' ; +castTarget + : castTargetType ('(' INTEGER_LITERAL (',' INTEGER_LITERAL)? ')')? + ; + +castTargetType + returns [String fullTargetName] + : (i=identifier { $fullTargetName = _localctx.i.getText(); }) ('.' c=identifier { $fullTargetName += ("." + _localctx.c.getText() ); })* + ; + extractFunction : EXTRACT '(' expression FROM expression ')' | dateTimeFunction '(' expression ')' 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 17458b20e..3ad3d4e24 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 @@ -106,7 +106,7 @@ class HqlQueryRenderer extends HqlBaseVisitor> { if (ctx.MATERIALIZED() != null) { tokens.add(TOKEN_MATERIALIZED); } - + tokens.add(TOKEN_OPEN_PAREN); tokens.addAll(visit(ctx.queryExpression())); tokens.add(TOKEN_CLOSE_PAREN); @@ -1790,16 +1790,47 @@ class HqlQueryRenderer extends HqlBaseVisitor> { List tokens = new ArrayList<>(); - tokens.add(new JpaQueryParsingToken(ctx.CAST())); + tokens.add(new JpaQueryParsingToken(ctx.CAST(), false)); tokens.add(TOKEN_OPEN_PAREN); tokens.addAll(visit(ctx.expression())); tokens.add(new JpaQueryParsingToken(ctx.AS())); - tokens.addAll(visit(ctx.identifier())); + tokens.addAll(visit(ctx.castTarget())); + NOSPACE(tokens); tokens.add(TOKEN_CLOSE_PAREN); return tokens; } + @Override + public List visitCastTarget(HqlParser.CastTargetContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.castTargetType())); + + if (ctx.INTEGER_LITERAL() != null && !ctx.INTEGER_LITERAL().isEmpty()) { + + tokens.add(TOKEN_OPEN_PAREN); + + ctx.INTEGER_LITERAL().forEach(terminalNode -> { + + tokens.add(new JpaQueryParsingToken(terminalNode)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + NOSPACE(tokens); + + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitCastTargetType(HqlParser.CastTargetTypeContext ctx) { + return List.of(new JpaQueryParsingToken(ctx.fullTargetName)); + } + @Override public List visitExtractFunction(HqlParser.ExtractFunctionContext ctx) { 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 fb5a5ed92..a1e62a7e7 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 @@ -1521,4 +1521,9 @@ class HqlQueryRendererTests { void queryWithSignShouldWork() { assertQuery("select t.sign from TestEntity t"); } + + @Test // GH-3024 + void castFunctionWithFqdnShouldWork() { + assertQuery("SELECT o FROM Order o WHERE CAST(:userId AS java.util.UUID) IS NULL OR o.user.id = :userId"); + } }