Add HQL rendering tests.
Extend tests for parsing/rendering hql functions and expressions. Assert count query creation works fine for when nested function calls are present in select. Fix minor rendering issue that added superfluous leading whitespace to expressions nested within a function. Bring newly added reserved keywords into order. Original Pull Request: #3691
This commit is contained in:
@@ -1349,6 +1349,9 @@ CASE : C A S E;
|
||||
CAST : C A S T;
|
||||
CEILING : C E I L I N G;
|
||||
COLLATE : C O L L A T E;
|
||||
COLUMN : C O L U M N;
|
||||
CONFLICT : C O N F L I C T;
|
||||
CONSTRAINT : C O N S T R A I N T;
|
||||
CONTAINS : C O N T A I N S;
|
||||
COUNT : C O U N T;
|
||||
CROSS : C R O S S;
|
||||
@@ -1358,11 +1361,7 @@ CURRENT_DATE : C U R R E N T '_' D A T E;
|
||||
CURRENT_INSTANT : C U R R E N T '_' I N S T A N T;
|
||||
CURRENT_TIME : C U R R E N T '_' T I M E;
|
||||
CURRENT_TIMESTAMP : C U R R E N T '_' T I M E S T A M P;
|
||||
CONFLICT : C O N F L I C T;
|
||||
CONSTRAINT : C O N S T R A I N T;
|
||||
COLUMN : C O L U M N;
|
||||
CYCLE : C Y C L E;
|
||||
DO : D O;
|
||||
DATE : D A T E;
|
||||
DATETIME : D A T E T I M E ;
|
||||
DAY : D A Y;
|
||||
@@ -1371,6 +1370,7 @@ DELETE : D E L E T E;
|
||||
DEPTH : D E P T H;
|
||||
DESC : D E S C;
|
||||
DISTINCT : D I S T I N C T;
|
||||
DO : D O;
|
||||
ELEMENT : E L E M E N T;
|
||||
ELEMENTS : E L E M E N T S;
|
||||
ELSE : E L S E;
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.util.CompositeIterator;
|
||||
* </ul>
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
abstract class QueryRenderer implements QueryTokenStream {
|
||||
|
||||
@@ -243,7 +244,7 @@ abstract class QueryRenderer implements QueryTokenStream {
|
||||
for (QueryRenderer queryRenderer : nested) {
|
||||
|
||||
if (lastAppended != null && (lastExpression || queryRenderer.isExpression()) && !builder.isEmpty()
|
||||
&& !lastAppended.endsWith(" ")) {
|
||||
&& (!lastAppended.endsWith(" ") && !lastAppended.endsWith("("))) {
|
||||
builder.append(' ');
|
||||
}
|
||||
|
||||
|
||||
@@ -1556,7 +1556,10 @@ class HqlQueryRendererTests {
|
||||
@ValueSource(strings = { "YEAR", "MONTH", "DAY", "WEEK", "QUARTER", "HOUR", "MINUTE", "SECOND", "NANOSECOND",
|
||||
"NANOSECOND", "EPOCH" })
|
||||
void durationLiteralsShouldWork(String dtField) {
|
||||
|
||||
assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE (ce.endDate - ce.startDate) > 5 %s".formatted(dtField));
|
||||
assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE ce.text LIKE :text GROUP BY year(cd.date) HAVING (ce.endDate - ce.startDate) > 5 %s".formatted(dtField));
|
||||
assertQuery("SELECT ce.id as id, cd.startDate + 5 %s AS summedDate FROM CalendarEvent ce".formatted(dtField));
|
||||
}
|
||||
|
||||
@Test // GH-3025
|
||||
|
||||
@@ -1090,7 +1090,7 @@ class HqlQueryTransformerTests {
|
||||
"SELECT t3 FROM Test3 t3 JOIN t3.test2 x WHERE x.id = :test2Id order by t3.testDuplicateColumnName desc");
|
||||
}
|
||||
|
||||
@Test // GH-3269
|
||||
@Test // GH-3269, GH-3689
|
||||
void createsCountQueryUsingAliasCorrectly() {
|
||||
|
||||
assertCountQuery("select distinct 1 as x from Employee", "select count(distinct 1) from Employee AS __");
|
||||
@@ -1102,6 +1102,7 @@ class HqlQueryTransformerTests {
|
||||
"select count(distinct a, b, sum(amount), d) from Employee AS __ 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");
|
||||
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");
|
||||
}
|
||||
|
||||
@Test // GH-3427
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer;
|
||||
|
||||
/**
|
||||
@@ -35,6 +34,7 @@ import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 3.1
|
||||
*/
|
||||
class HqlSpecificationTests {
|
||||
@@ -335,18 +335,15 @@ class HqlSpecificationTests {
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3689
|
||||
void generic() {
|
||||
@ParameterizedTest // GH-3689
|
||||
@ValueSource(strings = { "RESPECT NULLS", "IGNORE NULLS" })
|
||||
void generic(String nullHandling) {
|
||||
|
||||
// not in the official documentation but supported in the grammar.
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e
|
||||
WHERE FOO(x).bar RESPECT NULLS
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e
|
||||
WHERE FOO(x).bar IGNORE NULLS
|
||||
""");
|
||||
WHERE FOO(x).bar %s
|
||||
""".formatted(nullHandling));
|
||||
}
|
||||
|
||||
@Test // GH-3689
|
||||
@@ -356,6 +353,11 @@ class HqlSpecificationTests {
|
||||
SELECT e FROM Employee e
|
||||
WHERE SIZE(x) > 1
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e
|
||||
WHERE SIZE(e.skills) > 1
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3689
|
||||
@@ -384,10 +386,15 @@ class HqlSpecificationTests {
|
||||
SELECT e FROM Employee e
|
||||
WHERE TRUNC(e, 'foo') = TRUNCATE(e, 'bar')
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e
|
||||
WHERE TRUNC(e, 'YEAR') = TRUNCATE(LOCAL DATETIME, 'YEAR')
|
||||
""");
|
||||
}
|
||||
|
||||
@ParameterizedTest // GH-3689
|
||||
@ValueSource(strings = { "YYYY", "MONTH", "DAY", "WEEK", "QUARTER", "HOUR", "MINUTE", "SECOND", "NANOSECOND",
|
||||
@ValueSource(strings = { "YEAR", "MONTH", "DAY", "WEEK", "QUARTER", "HOUR", "MINUTE", "SECOND", "NANOSECOND",
|
||||
"NANOSECOND", "EPOCH" })
|
||||
void trunc(String truncation) {
|
||||
|
||||
@@ -402,7 +409,17 @@ class HqlSpecificationTests {
|
||||
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e
|
||||
WHERE FORMAT(x AS 'foo') = FORMAT(x AS 'bar')
|
||||
WHERE FORMAT(x AS 'yyyy') = FORMAT(e.hiringDate AS 'yyyy')
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e
|
||||
WHERE e.hiringDate = format(LOCAL DATETIME as 'yyyy-MM-dd')
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e
|
||||
WHERE e.hiringDate = format(LOCAL_DATE() as 'yyyy-MM-dd')
|
||||
""");
|
||||
}
|
||||
|
||||
@@ -411,7 +428,7 @@ class HqlSpecificationTests {
|
||||
|
||||
assertQuery("""
|
||||
SELECT e FROM Employee e
|
||||
WHERE COLLATE(x AS foo) = COLLATE(x AS foo.bar)
|
||||
WHERE COLLATE(x AS ucs_basic) = COLLATE(e.name AS ucs_basic)
|
||||
""");
|
||||
}
|
||||
|
||||
@@ -424,11 +441,20 @@ class HqlSpecificationTests {
|
||||
assertQuery("select substring(c.number, 1) " + //
|
||||
"from Call c");
|
||||
|
||||
assertQuery("select substring(c.number, 1, position('/0' in c.number)) " + //
|
||||
"from Call c");
|
||||
|
||||
assertQuery("select substring(c.number FROM 1 FOR 2) " + //
|
||||
"from Call c");
|
||||
|
||||
assertQuery("select substring(c.number FROM 1) " + //
|
||||
"from Call c");
|
||||
|
||||
assertQuery("select substring(c.number FROM 1 FOR position('/0' in c.number)) " + //
|
||||
"from Call c");
|
||||
|
||||
assertQuery("select substring(c.number FROM 1) AS shortNumber " + //
|
||||
"from Call c");
|
||||
}
|
||||
|
||||
@Test // GH-3689
|
||||
@@ -462,6 +488,9 @@ class HqlSpecificationTests {
|
||||
|
||||
assertQuery("select POSITION(c.number IN 'foo') " + //
|
||||
"from Call c ");
|
||||
|
||||
assertQuery("select POSITION(c.number IN 'foo') + 1 AS pos " + //
|
||||
"from Call c ");
|
||||
}
|
||||
|
||||
@Test // GH-3689
|
||||
@@ -490,6 +519,9 @@ class HqlSpecificationTests {
|
||||
|
||||
assertQuery("select OFFSET DATETIME, OFFSET_DATETIME() " + //
|
||||
"from Call c ");
|
||||
|
||||
assertQuery("select OFFSET DATETIME AS offsetDatetime, OFFSET_DATETIME() AS offset_datetime " + //
|
||||
"from Call c ");
|
||||
}
|
||||
|
||||
@Test // GH-3689
|
||||
@@ -497,6 +529,8 @@ class HqlSpecificationTests {
|
||||
|
||||
assertQuery("select CUBE(foo), CUBE(foo, bar) " + //
|
||||
"from Call c ");
|
||||
|
||||
assertQuery("select c.callerId from Call c GROUP BY CUBE(state, province)");
|
||||
}
|
||||
|
||||
@Test // GH-3689
|
||||
@@ -504,6 +538,8 @@ class HqlSpecificationTests {
|
||||
|
||||
assertQuery("select ROLLUP(foo), ROLLUP(foo, bar) " + //
|
||||
"from Call c ");
|
||||
|
||||
assertQuery("select c.callerId from Call c GROUP BY ROLLUP(state, province)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -710,16 +746,13 @@ class HqlSpecificationTests {
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3628
|
||||
void functionInvocationWithIsBoolean() {
|
||||
@ParameterizedTest // GH-3628
|
||||
@ValueSource(strings = { "is true", "is not true", "is false", "is not false" })
|
||||
void functionInvocationWithIsBoolean(String booleanComparison) {
|
||||
|
||||
assertQuery("""
|
||||
from RoleTmpl where find_in_set(:appId, appIds) is true
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
from RoleTmpl where find_in_set(:appId, appIds) is false
|
||||
""");
|
||||
from RoleTmpl where find_in_set(:appId, appIds) %s
|
||||
""".formatted(booleanComparison));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1094,20 +1127,36 @@ class HqlSpecificationTests {
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3628
|
||||
void distinctFromPredicate() {
|
||||
@ParameterizedTest // GH-3628
|
||||
@ValueSource(strings = { "IS DISTINCT FROM", "IS NOT DISTINCT FROM" })
|
||||
void distinctFromPredicate(String distinctFrom) {
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE c.orders IS DISTINCT FROM c.payments
|
||||
""");
|
||||
WHERE c.orders %s c.payments
|
||||
""".formatted(distinctFrom));
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE c.orders IS NOT DISTINCT FROM c.payments
|
||||
""");
|
||||
WHERE c.orders %s c.payments
|
||||
""".formatted(distinctFrom));
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
GROUP BY c.lastname
|
||||
HAVING c.orders %s c.payments
|
||||
""".formatted(distinctFrom));
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE EXISTS (SELECT c2
|
||||
FROM Customer c2
|
||||
WHERE c2.orders %s c.orders)
|
||||
""".formatted(distinctFrom));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1576,7 +1625,7 @@ class HqlSpecificationTests {
|
||||
assertQuery("select longest.duration " + //
|
||||
"from Phone p " + //
|
||||
"left join lateral (" + //
|
||||
" select c.duration as duration " + //
|
||||
"select c.duration as duration " + //
|
||||
" from p.calls c" + //
|
||||
" order by c.duration desc" + //
|
||||
" limit 1 " + //
|
||||
|
||||
Reference in New Issue
Block a user