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
This commit is contained in:
Greg L. Turnquist
2023-05-22 16:34:21 -05:00
parent 107e4c174f
commit a6e33ad1b9
5 changed files with 84 additions and 5 deletions

View File

@@ -282,6 +282,30 @@ class HqlQueryTransformer extends HqlQueryRenderer {
return tokens;
}
@Override
public List<JpaQueryParsingToken> visitJoinPath(HqlParser.JoinPathContext ctx) {
List<JpaQueryParsingToken> tokens = super.visitJoinPath(ctx);
if (ctx.variable() != null) {
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
}
return tokens;
}
@Override
public List<JpaQueryParsingToken> visitJoinSubquery(HqlParser.JoinSubqueryContext ctx) {
List<JpaQueryParsingToken> tokens = super.visitJoinSubquery(ctx);
if (ctx.variable() != null) {
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
}
return tokens;
}
@Override
public List<JpaQueryParsingToken> visitAlias(HqlParser.AliasContext ctx) {

View File

@@ -131,11 +131,16 @@ class JpaQueryTransformerSupport {
return false;
}
// If the Sort references an alias
// If the Sort references an alias directly
if (projectionAliases.contains(order.getProperty())) {
return false;
}
// If the Sort property starts with an alias
if (projectionAliases.stream().anyMatch(alias -> order.getProperty().startsWith(alias))) {
return false;
}
return true;
}
}

View File

@@ -219,6 +219,16 @@ class JpqlQueryTransformer extends JpqlQueryRenderer {
return tokens;
}
@Override
public List<JpaQueryParsingToken> visitJoin(JpqlParser.JoinContext ctx) {
List<JpaQueryParsingToken> tokens = super.visitJoin(ctx);
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
return tokens;
}
@Override
public List<JpaQueryParsingToken> visitConstructor_expression(JpqlParser.Constructor_expressionContext ctx) {

View File

@@ -245,12 +245,12 @@ class HqlQueryTransformerTests {
""")).isEqualTo("o");
}
@Test // DATAJPA-252
@Test // DATAJPA-252, GH-664, GH-1066, GH-2960
void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() {
String query = "select p from Person p left join p.address address";
Sort sort = Sort.by("address.city");
assertThat(createQueryFor(query, sort)).endsWith("order by p.address.city asc");
assertThat(createQueryFor(query, sort)).endsWith("order by address.city asc");
}
@Test // DATAJPA-252
@@ -986,6 +986,26 @@ class HqlQueryTransformerTests {
assertThat(createQueryFor(query, Sort.unsorted())).isEqualToIgnoringWhitespace(query);
}
@Test // GH-664, GH-1066, GH-2960
void sortingRecognizesJoinAliases() {
String query = "select p from Customer c join c.productOrder p where p.delayed = true";
assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("lastName")))).isEqualToIgnoringWhitespace("""
select p from Customer c
join c.productOrder p
where p.delayed = true
order by c.lastName desc
""");
assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("p.lineItems")))).isEqualToIgnoringWhitespace("""
select p from Customer c
join c.productOrder p
where p.delayed = true
order by p.lineItems desc
""");
}
private void assertCountQuery(String originalQuery, String countQuery) {
assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery);
}

View File

@@ -235,12 +235,12 @@ class JpqlQueryTransformerTests {
""")).isEqualTo("o");
}
@Test // DATAJPA-252
@Test // DATAJPA-252, GH-664, GH-1066, GH-2960
void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() {
String query = "select p from Person p left join p.address address";
Sort sort = Sort.by("address.city");
assertThat(createQueryFor(query, sort)).endsWith("order by p.address.city asc");
assertThat(createQueryFor(query, sort)).endsWith("order by address.city asc");
}
@Test // DATAJPA-252
@@ -742,6 +742,26 @@ class JpqlQueryTransformerTests {
""", relationshipName, joinAlias, joinAlias));
}
@Test // GH-664, GH-1066, GH-2960
void sortingRecognizesJoinAliases() {
String query = "select p from Customer c join c.productOrder p where p.delayed = true";
assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("lastName")))).isEqualToIgnoringWhitespace("""
select p from Customer c
join c.productOrder p
where p.delayed = true
order by c.lastName desc
""");
assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("p.lineItems")))).isEqualToIgnoringWhitespace("""
select p from Customer c
join c.productOrder p
where p.delayed = true
order by p.lineItems desc
""");
}
static Stream<Arguments> queriesWithReservedWordsAsIdentifiers() {
return Stream.of( //