Polishing.

Revisit DISTINCT count queries when primary alias isn't set. HQL can handle such queries, JPQL and EQL transformers now fail properly.

See #3744
This commit is contained in:
Mark Paluch
2025-01-14 10:36:11 +01:00
parent b4cb5b55ca
commit 036342f329
4 changed files with 61 additions and 7 deletions

View File

@@ -100,6 +100,10 @@ class EqlCountQueryTransformer extends EqlQueryRenderer {
if (countSelection.requiresPrimaryAlias()) {
// constructor
if (primaryFromAlias == null) {
throw new IllegalStateException(
"Primary alias must be set for DISTINCT count selection using constructor expressions");
}
nested.append(QueryTokens.token(primaryFromAlias));
} else {
// keep all the select items to distinct against

View File

@@ -100,6 +100,10 @@ class JpqlCountQueryTransformer extends JpqlQueryRenderer {
if (countSelection.requiresPrimaryAlias()) {
// constructor
if (primaryFromAlias == null) {
throw new IllegalStateException(
"Primary alias must be set for DISTINCT count selection using constructor expressions");
}
nested.append(QueryTokens.token(primaryFromAlias));
} else {
// keep all the select items to distinct against

View File

@@ -71,6 +71,42 @@ class QueryTransformers {
return new CountSelectionTokenStream(target, containsNew);
}
/**
* Filter constructor expression and return the selection list of the constructor.
*
* @return the selection list of the constructor without {@code NEW}, class name, and the first level of
* parentheses.
* @since 3.5.2
*/
public CountSelectionTokenStream withoutConstructorExpression() {
if (!requiresPrimaryAlias()) {
return this;
}
List<QueryToken> target = new ArrayList<>(size());
int nestingLevel = 0;
for (QueryToken token : this) {
if (token.equals(TOKEN_OPEN_PAREN)) {
nestingLevel++;
continue;
}
if (token.equals(TOKEN_CLOSE_PAREN)) {
nestingLevel--;
continue;
}
if (nestingLevel > 0) {
target.add(token);
}
}
return new CountSelectionTokenStream(target, requiresPrimaryAlias());
}
@Override
public Iterator<QueryToken> iterator() {
return tokens.iterator();

View File

@@ -41,6 +41,7 @@ import org.springframework.util.StringUtils;
*
* @author Greg Turnquist
* @author Christoph Strobl
* @author Mark Paluch
*/
class HqlQueryTransformerTests {
@@ -182,6 +183,12 @@ class HqlQueryTransformerTests {
@Test
void createsCountQueryCorrectly() {
assertCountQuery("SELECT id FROM Person", "SELECT count(id) FROM Person");
assertCountQuery("SELECT p.id FROM Person p", "SELECT count(p) FROM Person p");
assertCountQuery("SELECT id FROM Person p", "SELECT count(id) FROM Person p");
assertCountQuery("SELECT id, name FROM Person", "SELECT count(*) FROM Person");
assertCountQuery("SELECT id, name FROM Person p", "SELECT count(p) FROM Person p");
assertCountQuery(QUERY, COUNT_QUERY);
}
@@ -204,6 +211,9 @@ class HqlQueryTransformerTests {
assertCountQuery("select distinct new com.example.User(u.name) from User u where u.foo = ?1",
"select count(distinct u) from User u where u.foo = ?1");
assertCountQuery("select distinct new com.example.User(name, lastname) from User where foo = ?1",
"select count(distinct name, lastname) from User where foo = ?1");
}
@Test
@@ -913,7 +923,7 @@ class HqlQueryTransformerTests {
void countQueryShouldWorkEvenWithoutExplicitAlias() {
assertCountQuery("FROM BookError WHERE portal = :portal",
"select count(__) FROM BookError AS __ WHERE portal = :portal");
"select count(__) FROM BookError WHERE portal = :portal");
assertCountQuery("FROM BookError b WHERE portal = :portal",
"select count(b) FROM BookError b WHERE portal = :portal");
@@ -1107,15 +1117,15 @@ class HqlQueryTransformerTests {
@Test // GH-3269, GH-3689
void createsCountQueryUsingAliasCorrectly() {
assertCountQuery("select distinct 1 as x from Employee", "select count(distinct 1) from Employee AS __");
assertCountQuery("SELECT DISTINCT abc AS x FROM T", "SELECT count(DISTINCT abc) FROM T AS __");
assertCountQuery("select distinct a as x, b as y from Employee", "select count(distinct a, b) from Employee AS __");
assertCountQuery("select distinct 1 as x from Employee", "select count(distinct 1) from Employee");
assertCountQuery("SELECT DISTINCT abc AS x FROM T", "SELECT count(DISTINCT abc) FROM T");
assertCountQuery("select distinct a as x, b as y from Employee", "select count(distinct a, b) from Employee");
assertCountQuery("select distinct sum(amount) as x from Employee GROUP BY n",
"select count(distinct sum(amount)) from Employee AS __ GROUP BY n");
"select count(distinct sum(amount)) from Employee GROUP BY n");
assertCountQuery("select distinct a, b, sum(amount) as c, d from Employee GROUP BY n",
"select count(distinct a, b, sum(amount), d) from Employee AS __ GROUP BY n");
"select count(distinct a, b, sum(amount), d) from Employee GROUP BY n");
assertCountQuery("select distinct a, count(b) as c from Employee GROUP BY n",
"select count(distinct a, count(b)) from Employee AS __ GROUP BY n");
"select count(distinct a, count(b)) from Employee GROUP BY n");
assertCountQuery("select distinct substring(e.firstname, 1, position('a' in e.lastname)) as x from from Employee",
"select count(distinct substring(e.firstname, 1, position('a' in e.lastname))) from from Employee");
}