-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Fix order by rendering for queries containing UNION #3429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
61b4030
25beda5
82b28a4
c5064ed
132d54b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,18 +17,22 @@ | |
|
||
import static org.assertj.core.api.Assertions.*; | ||
|
||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
import java.util.stream.Stream; | ||
|
||
import org.assertj.core.api.SoftAssertions; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
import org.springframework.dao.InvalidDataAccessApiUsageException; | ||
import org.springframework.data.domain.PageRequest; | ||
import org.springframework.data.domain.Sort; | ||
import org.springframework.data.jpa.domain.JpaSort; | ||
import org.springframework.lang.Nullable; | ||
import org.springframework.util.StringUtils; | ||
|
||
/** | ||
* Verify that HQL queries are properly transformed through the {@link JpaQueryEnhancer} and the | ||
|
@@ -1061,6 +1065,40 @@ void createsCountQueryUsingAliasCorrectly() { | |
"select count(distinct a, count(b)) from Employee AS __ GROUP BY n"); | ||
} | ||
|
||
@Test // GH-3427 | ||
void sortShouldBeAppendedWithSpacingInCaseOfSetOperator() { | ||
|
||
String source = "SELECT tb FROM Test tb WHERE (tb.type='A') UNION SELECT tb FROM Test tb WHERE (tb.type='B')"; | ||
String target = createQueryFor(source, Sort.by("Type").ascending()); | ||
|
||
assertThat(target).isEqualTo("SELECT tb FROM Test tb WHERE (tb.type = 'A') UNION SELECT tb FROM Test tb WHERE (tb.type = 'B') order by tb.Type asc"); | ||
} | ||
|
||
@ParameterizedTest // GH-3427 | ||
@ValueSource(strings = {"", "res"}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A comment would be nice explaining why we need to run this with different prefixes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. deemed to be obvious - apparently isn't |
||
void sortShouldBeAppendedToSubSelectWithSetOperatorInSubselect(String alias) { | ||
|
||
String prefix = StringUtils.hasText(alias) ? (alias + ".") : ""; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again I think we should be able to and prefer a simple equals-assertion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not a fan of duplication. |
||
String source = "SELECT %sname FROM (SELECT c.name as name FROM Category c UNION SELECT t.name as name FROM Tag t)".formatted(prefix); | ||
if(StringUtils.hasText(alias)) { | ||
source = source + " %s".formatted(alias); | ||
} | ||
|
||
String target = createQueryFor(source, Sort.by("name").ascending()); | ||
|
||
assertThat(target).contains(" UNION SELECT ").doesNotContainPattern(Pattern.compile(".*\\SUNION")); | ||
assertThat(target).endsWith("order by %sname asc".formatted(prefix)).satisfies(it -> { | ||
Pattern pattern = Pattern.compile("order by %sname".formatted(prefix)); | ||
Matcher matcher = pattern.matcher(target); | ||
int count = 0; | ||
while(matcher.find()) { | ||
count++; | ||
} | ||
assertThat(count).describedAs("Found order by clause more than once in: \n%s", it).isOne(); | ||
}); | ||
|
||
} | ||
|
||
private void assertCountQuery(String originalQuery, String countQuery) { | ||
assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After this line
target
has the valuei.e. the
order by
is applied twice which is illegal, since according to answers here https://stackoverflow.com/questions/4715820/how-to-order-by-with-union-in-sql the laterorder by
applies to the fullunion
and anorder by
on the individual selects might be even illegal (probably depends on the database)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since we do not know the target db nor how it handles order by we cannot imply ordering the last is the correct behaviour, nor do we have the means to define which of the selects to decorate. an alternative would be to raise an error and demand wrapping the entire thing as it's done in one of the follow up tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably, let's wait and see what kind of reports we get. We can then still react to the feedback we receive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reading through IBM an Oracle SQL reference docs @schauder has a good point in the above mentioned query not being compliant (at least not in the form it is currently rendered)