Skip to content

Commit 2b66802

Browse files
committed
Polishing.
Apply select list rewriting to EQL and JPQL parsers as well. See: #3269 Original pull request: #3276
1 parent be9c0b5 commit 2b66802

File tree

7 files changed

+104
-19
lines changed

7 files changed

+104
-19
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryTransformer.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,14 @@ public List<JpaQueryParsingToken> visitSelect_clause(EqlParser.Select_clauseCont
161161

162162
if (ctx.DISTINCT() != null) {
163163

164-
if (selectItemTokens.stream().anyMatch(jpqlToken -> jpqlToken.getToken().contains("new"))) {
164+
List<JpaQueryParsingToken> countSelection = QueryTransformers.filterCountSelection(selectItemTokens);
165+
166+
if (countSelection.stream().anyMatch(jpqlToken -> jpqlToken.getToken().contains("new"))) {
165167
// constructor
166168
tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias));
167169
} else {
168170
// keep all the select items to distinct against
169-
tokens.addAll(selectItemTokens);
171+
tokens.addAll(countSelection);
170172
}
171173
} else {
172174
tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias));
@@ -240,4 +242,5 @@ public List<JpaQueryParsingToken> visitConstructor_expression(EqlParser.Construc
240242
private static <T> ArrayList<T> newArrayList() {
241243
return new ArrayList<>();
242244
}
245+
243246
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java

+1-15
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ public List<JpaQueryParsingToken> visitSelectClause(HqlParser.SelectClauseContex
358358

359359
if (ctx.DISTINCT() != null) {
360360

361-
List<JpaQueryParsingToken> countSelection = getCountSelection(selectionListTokens);
361+
List<JpaQueryParsingToken> countSelection = QueryTransformers.filterCountSelection(selectionListTokens);
362362

363363
if (countSelection.stream().anyMatch(hqlToken -> hqlToken.getToken().contains("new"))) {
364364
// constructor
@@ -398,18 +398,4 @@ static <T> ArrayList<T> newArrayList() {
398398
return new ArrayList<>();
399399
}
400400

401-
private static List<JpaQueryParsingToken> getCountSelection(List<JpaQueryParsingToken> selectionListTokens) {
402-
403-
List<JpaQueryParsingToken> target = new ArrayList<>(selectionListTokens.size());
404-
for (int i = 0; i < selectionListTokens.size(); i++) {
405-
JpaQueryParsingToken token = selectionListTokens.get(i);
406-
if (token.isA(TOKEN_AS)) {
407-
i++;
408-
continue;
409-
}
410-
target.add(token);
411-
}
412-
selectionListTokens = target;
413-
return selectionListTokens;
414-
}
415401
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java

+6
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ boolean getSpace() {
131131
return this.space;
132132
}
133133

134+
/**
135+
* Compare whether the given {@link JpaQueryParsingToken token} is equal to the one held by this instance.
136+
*
137+
* @param token must not be {@literal null}.
138+
* @return {@literal true} if both tokens are equals (using case-insensitive comparison).
139+
*/
134140
boolean isA(JpaQueryParsingToken token) {
135141
return token.getToken().equalsIgnoreCase(this.getToken());
136142
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,14 @@ public List<JpaQueryParsingToken> visitSelect_clause(JpqlParser.Select_clauseCon
161161

162162
if (ctx.DISTINCT() != null) {
163163

164-
if (selectItemTokens.stream().anyMatch(jpqlToken -> jpqlToken.getToken().contains("new"))) {
164+
List<JpaQueryParsingToken> countSelection = QueryTransformers.filterCountSelection(selectItemTokens);
165+
166+
if (countSelection.stream().anyMatch(jpqlToken -> jpqlToken.getToken().contains("new"))) {
165167
// constructor
166168
tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias));
167169
} else {
168170
// keep all the select items to distinct against
169-
tokens.addAll(selectItemTokens);
171+
tokens.addAll(countSelection);
170172
}
171173
} else {
172174
tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.repository.query;
17+
18+
import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
23+
/**
24+
* Utility class encapsulating common query transformations.
25+
*
26+
* @author Mark Paluch
27+
* @since 3.2.5
28+
*/
29+
class QueryTransformers {
30+
31+
/**
32+
* Filter a token list from a {@code SELECT} clause to be used within a count query. That is, filter any {@code AS …}
33+
* aliases.
34+
*
35+
* @param selection the input selection.
36+
* @return filtered selection to be used with count queries.
37+
*/
38+
static List<JpaQueryParsingToken> filterCountSelection(List<JpaQueryParsingToken> selection) {
39+
40+
List<JpaQueryParsingToken> target = new ArrayList<>(selection.size());
41+
boolean skipNext = false;
42+
43+
for (JpaQueryParsingToken token : selection) {
44+
45+
if (skipNext) {
46+
skipNext = false;
47+
continue;
48+
}
49+
50+
if (token.isA(TOKEN_AS)) {
51+
skipNext = true;
52+
continue;
53+
}
54+
target.add(token);
55+
}
56+
57+
return target;
58+
}
59+
60+
}

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryTransformerTests.java

+14
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,20 @@ void countProjectionDistinctQueryIncludesNewLineAfterEntityAndBeforeWhere() {
615615
"SELECT count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key where entity1.id = 1799");
616616
}
617617

618+
@Test // GH-3269
619+
void createsCountQueryUsingAliasCorrectly() {
620+
621+
assertCountQuery("select distinct 1 as x from Employee e", "select count(distinct 1) from Employee e");
622+
assertCountQuery("SELECT DISTINCT abc AS x FROM T t", "SELECT count(DISTINCT abc) FROM T t");
623+
assertCountQuery("select distinct a as x, b as y from Employee e", "select count(distinct a , b) from Employee e");
624+
assertCountQuery("select distinct sum(amount) as x from Employee e GROUP BY n",
625+
"select count(distinct sum(amount)) from Employee e GROUP BY n");
626+
assertCountQuery("select distinct a, b, sum(amount) as c, d from Employee e GROUP BY n",
627+
"select count(distinct a, b, sum(amount) , d) from Employee e GROUP BY n");
628+
assertCountQuery("select distinct a, count(b) as c from Employee e GROUP BY n",
629+
"select count(distinct a, count(b)) from Employee e GROUP BY n");
630+
}
631+
618632
@Test // GH-2393
619633
void createCountQueryStartsWithWhitespace() {
620634

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java

+14
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,20 @@ void countQueryUsesCorrectVariable() {
679679
.isEqualTo("SELECT count(us) FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)");
680680
}
681681

682+
@Test // GH-3269
683+
void createsCountQueryUsingAliasCorrectly() {
684+
685+
assertCountQuery("select distinct 1 as x from Employee e", "select count(distinct 1) from Employee e");
686+
assertCountQuery("SELECT DISTINCT abc AS x FROM T t", "SELECT count(DISTINCT abc) FROM T t");
687+
assertCountQuery("select distinct a as x, b as y from Employee e", "select count(distinct a , b) from Employee e");
688+
assertCountQuery("select distinct sum(amount) as x from Employee e GROUP BY n",
689+
"select count(distinct sum(amount)) from Employee e GROUP BY n");
690+
assertCountQuery("select distinct a, b, sum(amount) as c, d from Employee e GROUP BY n",
691+
"select count(distinct a, b, sum(amount) , d) from Employee e GROUP BY n");
692+
assertCountQuery("select distinct a, count(b) as c from Employee e GROUP BY n",
693+
"select count(distinct a, count(b)) from Employee e GROUP BY n");
694+
}
695+
682696
@Test // GH-2496, GH-2522, GH-2537, GH-2045
683697
void orderByShouldWorkWithSubSelectStatements() {
684698

0 commit comments

Comments
 (0)