2323import org .junit .jupiter .api .Test ;
2424import org .junit .jupiter .params .ParameterizedTest ;
2525import org .junit .jupiter .params .provider .ValueSource ;
26-
2726import org .springframework .data .jpa .repository .query .QueryRenderer .TokenRenderer ;
2827
2928/**
3534 *
3635 * @author Greg Turnquist
3736 * @author Mark Paluch
37+ * @author Christoph Strobl
3838 * @since 3.1
3939 */
4040class HqlSpecificationTests {
@@ -335,18 +335,15 @@ OR TREAT(e AS Contractor).hours > 100
335335 """ );
336336 }
337337
338- @ Test // GH-3689
339- void generic () {
340-
341- assertQuery ("""
342- SELECT e FROM Employee e
343- WHERE FOO(x).bar RESPECT NULLS
344- """ );
338+ @ ParameterizedTest // GH-3689
339+ @ ValueSource (strings = { "RESPECT NULLS" , "IGNORE NULLS" })
340+ void generic (String nullHandling ) {
345341
342+ // not in the official documentation but supported in the grammar.
346343 assertQuery ("""
347344 SELECT e FROM Employee e
348- WHERE FOO(x).bar IGNORE NULLS
349- """ );
345+ WHERE FOO(x).bar %s
346+ """ . formatted ( nullHandling ) );
350347 }
351348
352349 @ Test // GH-3689
@@ -356,6 +353,11 @@ void size() {
356353 SELECT e FROM Employee e
357354 WHERE SIZE(x) > 1
358355 """ );
356+
357+ assertQuery ("""
358+ SELECT e FROM Employee e
359+ WHERE SIZE(e.skills) > 1
360+ """ );
359361 }
360362
361363 @ Test // GH-3689
@@ -384,10 +386,15 @@ WHERE TRUNC(x) = TRUNCATE(y)
384386 SELECT e FROM Employee e
385387 WHERE TRUNC(e, 'foo') = TRUNCATE(e, 'bar')
386388 """ );
389+
390+ assertQuery ("""
391+ SELECT e FROM Employee e
392+ WHERE TRUNC(e, 'YEAR') = TRUNCATE(LOCAL DATETIME, 'YEAR')
393+ """ );
387394 }
388395
389396 @ ParameterizedTest // GH-3689
390- @ ValueSource (strings = { "YYYY " , "MONTH" , "DAY" , "WEEK" , "QUARTER" , "HOUR" , "MINUTE" , "SECOND" , "NANOSECOND" ,
397+ @ ValueSource (strings = { "YEAR " , "MONTH" , "DAY" , "WEEK" , "QUARTER" , "HOUR" , "MINUTE" , "SECOND" , "NANOSECOND" ,
391398 "NANOSECOND" , "EPOCH" })
392399 void trunc (String truncation ) {
393400
@@ -402,7 +409,17 @@ void format() {
402409
403410 assertQuery ("""
404411 SELECT e FROM Employee e
405- WHERE FORMAT(x AS 'foo') = FORMAT(x AS 'bar')
412+ WHERE FORMAT(x AS 'yyyy') = FORMAT(e.hiringDate AS 'yyyy')
413+ """ );
414+
415+ assertQuery ("""
416+ SELECT e FROM Employee e
417+ WHERE e.hiringDate = format(LOCAL DATETIME as 'yyyy-MM-dd')
418+ """ );
419+
420+ assertQuery ("""
421+ SELECT e FROM Employee e
422+ WHERE e.hiringDate = format(LOCAL_DATE() as 'yyyy-MM-dd')
406423 """ );
407424 }
408425
@@ -411,7 +428,7 @@ void collate() {
411428
412429 assertQuery ("""
413430 SELECT e FROM Employee e
414- WHERE COLLATE(x AS foo ) = COLLATE(x AS foo.bar )
431+ WHERE COLLATE(x AS ucs_basic ) = COLLATE(e.name AS ucs_basic )
415432 """ );
416433 }
417434
@@ -424,11 +441,20 @@ void substring() {
424441 assertQuery ("select substring(c.number, 1) " + //
425442 "from Call c" );
426443
444+ assertQuery ("select substring(c.number, 1, position('/0' in c.number)) " + //
445+ "from Call c" );
446+
427447 assertQuery ("select substring(c.number FROM 1 FOR 2) " + //
428448 "from Call c" );
429449
430450 assertQuery ("select substring(c.number FROM 1) " + //
431451 "from Call c" );
452+
453+ assertQuery ("select substring(c.number FROM 1 FOR position('/0' in c.number)) " + //
454+ "from Call c" );
455+
456+ assertQuery ("select substring(c.number FROM 1) AS shortNumber " + //
457+ "from Call c" );
432458 }
433459
434460 @ Test // GH-3689
@@ -462,6 +488,9 @@ void position() {
462488
463489 assertQuery ("select POSITION(c.number IN 'foo') " + //
464490 "from Call c " );
491+
492+ assertQuery ("select POSITION(c.number IN 'foo') + 1 AS pos " + //
493+ "from Call c " );
465494 }
466495
467496 @ Test // GH-3689
@@ -490,20 +519,27 @@ void currentDateFunctions() {
490519
491520 assertQuery ("select OFFSET DATETIME, OFFSET_DATETIME() " + //
492521 "from Call c " );
522+
523+ assertQuery ("select OFFSET DATETIME AS offsetDatetime, OFFSET_DATETIME() AS offset_datetime " + //
524+ "from Call c " );
493525 }
494526
495527 @ Test // GH-3689
496528 void cube () {
497529
498530 assertQuery ("select CUBE(foo), CUBE(foo, bar) " + //
499531 "from Call c " );
532+
533+ assertQuery ("select c.callerId from Call c GROUP BY CUBE(state, province)" );
500534 }
501535
502536 @ Test // GH-3689
503537 void rollup () {
504538
505539 assertQuery ("select ROLLUP(foo), ROLLUP(foo, bar) " + //
506540 "from Call c " );
541+
542+ assertQuery ("select c.callerId from Call c GROUP BY ROLLUP(state, province)" );
507543 }
508544
509545 @ Test
@@ -710,16 +746,13 @@ WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE
710746 """ );
711747 }
712748
713- @ Test // GH-3628
714- void functionInvocationWithIsBoolean () {
715-
716- assertQuery ("""
717- from RoleTmpl where find_in_set(:appId, appIds) is true
718- """ );
749+ @ ParameterizedTest // GH-3628
750+ @ ValueSource (strings = { "is true" , "is not true" , "is false" , "is not false" })
751+ void functionInvocationWithIsBoolean (String booleanComparison ) {
719752
720753 assertQuery ("""
721- from RoleTmpl where find_in_set(:appId, appIds) is false
722- """ );
754+ from RoleTmpl where find_in_set(:appId, appIds) %s
755+ """ . formatted ( booleanComparison ) );
723756 }
724757
725758 @ Test
@@ -1094,20 +1127,36 @@ void booleanPredicate() {
10941127 """ );
10951128 }
10961129
1097- @ Test // GH-3628
1098- void distinctFromPredicate () {
1130+ @ ParameterizedTest // GH-3628
1131+ @ ValueSource (strings = { "IS DISTINCT FROM" , "IS NOT DISTINCT FROM" })
1132+ void distinctFromPredicate (String distinctFrom ) {
10991133
11001134 assertQuery ("""
11011135 SELECT c
11021136 FROM Customer c
1103- WHERE c.orders IS DISTINCT FROM c.payments
1104- """ );
1137+ WHERE c.orders %s c.payments
1138+ """ . formatted ( distinctFrom ) );
11051139
11061140 assertQuery ("""
11071141 SELECT c
11081142 FROM Customer c
1109- WHERE c.orders IS NOT DISTINCT FROM c.payments
1110- """ );
1143+ WHERE c.orders %s c.payments
1144+ """ .formatted (distinctFrom ));
1145+
1146+ assertQuery ("""
1147+ SELECT c
1148+ FROM Customer c
1149+ GROUP BY c.lastname
1150+ HAVING c.orders %s c.payments
1151+ """ .formatted (distinctFrom ));
1152+
1153+ assertQuery ("""
1154+ SELECT c
1155+ FROM Customer c
1156+ WHERE EXISTS (SELECT c2
1157+ FROM Customer c2
1158+ WHERE c2.orders %s c.orders)
1159+ """ .formatted (distinctFrom ));
11111160 }
11121161
11131162 @ Test
@@ -1576,7 +1625,7 @@ void hqlQueries() {
15761625 assertQuery ("select longest.duration " + //
15771626 "from Phone p " + //
15781627 "left join lateral (" + //
1579- " select c.duration as duration " + //
1628+ "select c.duration as duration " + //
15801629 " from p.calls c" + //
15811630 " order by c.duration desc" + //
15821631 " limit 1 " + //
0 commit comments