Skip to content

Commit 25beda5

Browse files
Fix order by rendering for queries containing UNION
Make sure to append space after order by clause and fix alias detection for wrapped sub select. Also make sure to ignore alias used in subselect so they do not conflict with root ones.
1 parent 61b4030 commit 25beda5

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public QueryTokenStream visitJoinPath(HqlParser.JoinPathContext ctx) {
100100

101101
QueryTokenStream tokens = super.visitJoinPath(ctx);
102102

103-
if (ctx.variable() != null) {
103+
if (ctx.variable() != null && !isSubquery(ctx)) {
104104
transformerSupport.registerAlias(tokens.getLast());
105105
}
106106

@@ -112,7 +112,7 @@ public QueryTokenStream visitJoinSubquery(HqlParser.JoinSubqueryContext ctx) {
112112

113113
QueryTokenStream tokens = super.visitJoinSubquery(ctx);
114114

115-
if (ctx.variable() != null && !tokens.isEmpty()) {
115+
if (ctx.variable() != null && !tokens.isEmpty() && !isSubquery(ctx)) {
116116
transformerSupport.registerAlias(tokens.getLast());
117117
}
118118

@@ -124,7 +124,7 @@ public QueryTokenStream visitVariable(HqlParser.VariableContext ctx) {
124124

125125
QueryTokenStream tokens = super.visitVariable(ctx);
126126

127-
if (ctx.identifier() != null && !tokens.isEmpty()) {
127+
if (ctx.identifier() != null && !tokens.isEmpty() && !isSubquery(ctx)) {
128128
transformerSupport.registerAlias(tokens.getLast());
129129
}
130130

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

+38
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,22 @@
1717

1818
import static org.assertj.core.api.Assertions.*;
1919

20+
import java.util.regex.Matcher;
21+
import java.util.regex.Pattern;
2022
import java.util.stream.Stream;
2123

2224
import org.assertj.core.api.SoftAssertions;
2325
import org.junit.jupiter.api.Test;
2426
import org.junit.jupiter.params.ParameterizedTest;
2527
import org.junit.jupiter.params.provider.Arguments;
2628
import org.junit.jupiter.params.provider.MethodSource;
29+
import org.junit.jupiter.params.provider.ValueSource;
2730
import org.springframework.dao.InvalidDataAccessApiUsageException;
2831
import org.springframework.data.domain.PageRequest;
2932
import org.springframework.data.domain.Sort;
3033
import org.springframework.data.jpa.domain.JpaSort;
3134
import org.springframework.lang.Nullable;
35+
import org.springframework.util.StringUtils;
3236

3337
/**
3438
* Verify that HQL queries are properly transformed through the {@link JpaQueryEnhancer} and the
@@ -1061,6 +1065,40 @@ void createsCountQueryUsingAliasCorrectly() {
10611065
"select count(distinct a, count(b)) from Employee AS __ GROUP BY n");
10621066
}
10631067

1068+
@Test // GH-3427
1069+
void sortShouldBeAppendedWithSpacingInCaseOfSetOperator() {
1070+
1071+
String source = "SELECT tb FROM Test tb WHERE (tb.type='A') UNION SELECT tb FROM Test tb WHERE (tb.type='B')";
1072+
String target = createQueryFor(source, Sort.by("Type").ascending());
1073+
1074+
assertThat(target).contains(" UNION SELECT ").doesNotContainPattern(Pattern.compile(".*\\SUNION"));
1075+
}
1076+
1077+
@ParameterizedTest // GH-3427
1078+
@ValueSource(strings = {"", "res"})
1079+
void sortShouldBeAppendedToSubSelectWithSetOperatorInSubselect(String alias) {
1080+
1081+
String prefix = StringUtils.hasText(alias) ? (alias + ".") : "";
1082+
String source = "SELECT %sname FROM (SELECT c.name as name FROM Category c UNION SELECT t.name as name FROM Tag t)".formatted(prefix);
1083+
if(StringUtils.hasText(alias)) {
1084+
source = source + " %s".formatted(alias);
1085+
}
1086+
1087+
String target = createQueryFor(source, Sort.by("name").ascending());
1088+
1089+
assertThat(target).contains(" UNION SELECT ").doesNotContainPattern(Pattern.compile(".*\\SUNION"));
1090+
assertThat(target).endsWith("order by %sname asc".formatted(prefix)).satisfies(it -> {
1091+
Pattern pattern = Pattern.compile("order by %sname".formatted(prefix));
1092+
Matcher matcher = pattern.matcher(target);
1093+
int count = 0;
1094+
while(matcher.find()) {
1095+
count++;
1096+
}
1097+
assertThat(count).describedAs("Found order by clause more than once in: \n%s", it).isOne();
1098+
});
1099+
1100+
}
1101+
10641102
private void assertCountQuery(String originalQuery, String countQuery) {
10651103
assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery);
10661104
}

0 commit comments

Comments
 (0)