Skip to content

Commit ec44947

Browse files
committed
Properly handle Sort's that start with a join alias.
JOIN clauses can have aliases as well, despite not using an AS reserved word. The HQL query parser needs to handle this. See #2960, #1066, #664 Original Pull Request: 2967
1 parent a9f1836 commit ec44947

File tree

5 files changed

+84
-5
lines changed

5 files changed

+84
-5
lines changed

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

+24
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,30 @@ public List<JpaQueryParsingToken> visitJoin(HqlParser.JoinContext ctx) {
282282
return tokens;
283283
}
284284

285+
@Override
286+
public List<JpaQueryParsingToken> visitJoinPath(HqlParser.JoinPathContext ctx) {
287+
288+
List<JpaQueryParsingToken> tokens = super.visitJoinPath(ctx);
289+
290+
if (ctx.variable() != null) {
291+
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
292+
}
293+
294+
return tokens;
295+
}
296+
297+
@Override
298+
public List<JpaQueryParsingToken> visitJoinSubquery(HqlParser.JoinSubqueryContext ctx) {
299+
300+
List<JpaQueryParsingToken> tokens = super.visitJoinSubquery(ctx);
301+
302+
if (ctx.variable() != null) {
303+
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
304+
}
305+
306+
return tokens;
307+
}
308+
285309
@Override
286310
public List<JpaQueryParsingToken> visitAlias(HqlParser.AliasContext ctx) {
287311

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,16 @@ private boolean shouldPrefixWithAlias(Sort.Order order, String primaryFromAlias)
131131
return false;
132132
}
133133

134-
// If the Sort references an alias
134+
// If the Sort references an alias directly
135135
if (projectionAliases.contains(order.getProperty())) {
136136
return false;
137137
}
138138

139+
// If the Sort property starts with an alias
140+
if (projectionAliases.stream().anyMatch(alias -> order.getProperty().startsWith(alias))) {
141+
return false;
142+
}
143+
139144
return true;
140145
}
141146
}

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

+10
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,16 @@ public List<JpaQueryParsingToken> visitRange_variable_declaration(JpqlParser.Ran
219219
return tokens;
220220
}
221221

222+
@Override
223+
public List<JpaQueryParsingToken> visitJoin(JpqlParser.JoinContext ctx) {
224+
225+
List<JpaQueryParsingToken> tokens = super.visitJoin(ctx);
226+
227+
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
228+
229+
return tokens;
230+
}
231+
222232
@Override
223233
public List<JpaQueryParsingToken> visitConstructor_expression(JpqlParser.Constructor_expressionContext ctx) {
224234

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

+22-2
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,12 @@ AND LOWER(COALESCE(vehicle.make, '')) LIKE :query)
245245
""")).isEqualTo("o");
246246
}
247247

248-
@Test // DATAJPA-252
248+
@Test // DATAJPA-252, GH-664, GH-1066, GH-2960
249249
void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() {
250250

251251
String query = "select p from Person p left join p.address address";
252252
Sort sort = Sort.by("address.city");
253-
assertThat(createQueryFor(query, sort)).endsWith("order by p.address.city asc");
253+
assertThat(createQueryFor(query, sort)).endsWith("order by address.city asc");
254254
}
255255

256256
@Test // DATAJPA-252
@@ -986,6 +986,26 @@ select max(id), col
986986
assertThat(createQueryFor(query, Sort.unsorted())).isEqualToIgnoringWhitespace(query);
987987
}
988988

989+
@Test // GH-664, GH-1066, GH-2960
990+
void sortingRecognizesJoinAliases() {
991+
992+
String query = "select p from Customer c join c.productOrder p where p.delayed = true";
993+
994+
assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("lastName")))).isEqualToIgnoringWhitespace("""
995+
select p from Customer c
996+
join c.productOrder p
997+
where p.delayed = true
998+
order by c.lastName desc
999+
""");
1000+
1001+
assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("p.lineItems")))).isEqualToIgnoringWhitespace("""
1002+
select p from Customer c
1003+
join c.productOrder p
1004+
where p.delayed = true
1005+
order by p.lineItems desc
1006+
""");
1007+
}
1008+
9891009
private void assertCountQuery(String originalQuery, String countQuery) {
9901010
assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery);
9911011
}

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

+22-2
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,12 @@ AND LOWER(COALESCE(vehicle.make, '')) LIKE :query)
235235
""")).isEqualTo("o");
236236
}
237237

238-
@Test // DATAJPA-252
238+
@Test // DATAJPA-252, GH-664, GH-1066, GH-2960
239239
void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() {
240240

241241
String query = "select p from Person p left join p.address address";
242242
Sort sort = Sort.by("address.city");
243-
assertThat(createQueryFor(query, sort)).endsWith("order by p.address.city asc");
243+
assertThat(createQueryFor(query, sort)).endsWith("order by address.city asc");
244244
}
245245

246246
@Test // DATAJPA-252
@@ -742,6 +742,26 @@ where exists (
742742
""", relationshipName, joinAlias, joinAlias));
743743
}
744744

745+
@Test // GH-664, GH-1066, GH-2960
746+
void sortingRecognizesJoinAliases() {
747+
748+
String query = "select p from Customer c join c.productOrder p where p.delayed = true";
749+
750+
assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("lastName")))).isEqualToIgnoringWhitespace("""
751+
select p from Customer c
752+
join c.productOrder p
753+
where p.delayed = true
754+
order by c.lastName desc
755+
""");
756+
757+
assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("p.lineItems")))).isEqualToIgnoringWhitespace("""
758+
select p from Customer c
759+
join c.productOrder p
760+
where p.delayed = true
761+
order by p.lineItems desc
762+
""");
763+
}
764+
745765
static Stream<Arguments> queriesWithReservedWordsAsIdentifiers() {
746766

747767
return Stream.of( //

0 commit comments

Comments
 (0)