From f62d38288f71ee84632441c485d1fa645c37c542 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 1 Aug 2024 09:50:01 +0200 Subject: [PATCH] Suppress selection item aliasing for the actual count query. We now no longer apply count selection filtering but rather skip select field aliasing when rendering a count query to drop the field alias within a count query. Previously, we removed field aliasing by filtering the token stream which also removed the AS keyword from cast operators. Closes: #3536 Original Pull Request: #3553 --- .../repository/query/HqlQueryTransformer.java | 19 +++++++++++++---- .../query/HqlQueryTransformerTests.java | 21 +++++++++++++++++-- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java index f1e18f397..7aa2df8cf 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java @@ -23,6 +23,7 @@ import java.util.List; import org.antlr.v4.runtime.ParserRuleContext; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.query.HqlParser.SelectionContext; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -331,6 +332,17 @@ class HqlQueryTransformer extends HqlQueryRenderer { return tokens; } + @Override + public List visitSelection(SelectionContext ctx) { + + if(!countQuery || isSubquery(ctx)) { + return super.visitSelection(ctx); + } + + // do not append variables to skip AS field aliasing + return visit(ctx.selectExpression()); + } + @Override public List visitSelectClause(HqlParser.SelectClauseContext ctx) { @@ -339,6 +351,7 @@ class HqlQueryTransformer extends HqlQueryRenderer { tokens.add(new JpaQueryParsingToken(ctx.SELECT())); if (countQuery && !isSubquery(ctx)) { + tokens.add(TOKEN_COUNT_FUNC); if (countProjection != null) { @@ -358,14 +371,12 @@ class HqlQueryTransformer extends HqlQueryRenderer { if (ctx.DISTINCT() != null) { - List countSelection = QueryTransformers.filterCountSelection(selectionListTokens); - - if (countSelection.stream().anyMatch(hqlToken -> hqlToken.getToken().contains("new"))) { + if (selectionListTokens.stream().anyMatch(hqlToken -> hqlToken.getToken().contains("new"))) { // constructor tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias)); } else { // keep all the select items to distinct against - tokens.addAll(countSelection); + tokens.addAll(selectionListTokens); } } else { tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias)); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index c476f0d5b..549a1ed5f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -85,6 +85,23 @@ class HqlQueryTransformerTests { assertThat(results).isEqualTo("select count(e) FROM Employee e where e.name = :name"); } + @Test // GH-3536 + void shouldCreateCountQueryForDistinctCount() { + + // given + var original = """ + select distinct cast(e.timestampField as date) as foo + from ExampleEntity e + order by cast(e.timestampField as date) desc + """; + + // when + var results = createCountQueryFor(original); + + // then + assertThat(results).isEqualTo("select count(distinct cast(e.timestampField as date)) from ExampleEntity e"); + } + @Test void applyCountToMoreComplexQuery() { @@ -1037,9 +1054,9 @@ class HqlQueryTransformerTests { assertCountQuery("select distinct 1 as x from Employee","select count(distinct 1) from Employee AS __"); assertCountQuery("SELECT DISTINCT abc AS x FROM T","SELECT count(DISTINCT abc) FROM T AS __"); - assertCountQuery("select distinct a as x, b as y from Employee","select count(distinct a , b) from Employee AS __"); + assertCountQuery("select distinct a as x, b as y from Employee","select count(distinct a, b) from Employee AS __"); assertCountQuery("select distinct sum(amount) as x from Employee GROUP BY n","select count(distinct sum(amount)) from Employee AS __ GROUP BY n"); - assertCountQuery("select distinct a, b, sum(amount) as c, d from Employee GROUP BY n","select count(distinct a, b, sum(amount) , d) from Employee AS __ GROUP BY n"); + assertCountQuery("select distinct a, b, sum(amount) as c, d from Employee GROUP BY n","select count(distinct a, b, sum(amount), d) from Employee AS __ GROUP BY n"); assertCountQuery("select distinct a, count(b) as c from Employee GROUP BY n","select count(distinct a, count(b)) from Employee AS __ GROUP BY n"); }