Add support for missing clauses and functions.

Original Pull Request: #3691
This commit is contained in:
Mark Paluch
2024-11-26 16:49:10 +01:00
committed by Christoph Strobl
parent 34dc005af1
commit c44e5c3953
4 changed files with 1910 additions and 405 deletions

View File

@@ -188,10 +188,6 @@ instantiation
: NEW instantiationTarget '(' instantiationArguments ')'
;
alias
: AS? identifier // spec says IDENTIFIER but clearly does NOT mean a reserved word
;
groupedItem
: identifier
| INTEGER_LITERAL
@@ -354,6 +350,17 @@ dateTimeLiteral
| INSTANT
;
/**
* A field that may be extracted from a date, time, or datetime
*/
extractField
: datetimeField
| dayField
| weekField
| timeZoneField
| dateOrTimeField
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-duration-literals
datetimeField
: YEAR
@@ -368,6 +375,27 @@ datetimeField
| EPOCH
;
dayField
: DAY OF MONTH
| DAY OF WEEK
| DAY OF YEAR
;
weekField
: WEEK OF MONTH
| WEEK OF YEAR
;
timeZoneField
: OFFSET (HOUR | MINUTE)?
| TIMEZONE_HOUR | TIMEZONE_MINUTE
;
dateOrTimeField
: DATE
| TIME
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-binary-literals
binaryLiteral
: BINARY_LITERAL
@@ -416,11 +444,6 @@ primaryExpression
// TBD
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-path-expressions
identificationVariable
: identifier
| simplePath
;
path
: treatedPath pathContinutation?
| generalPathFragment
@@ -467,112 +490,498 @@ caseWhenPredicateClause
;
// Functions
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-exp-functions
/**
* A function invocation that may occur in an arbitrary expression
*/
function
: functionName '(' (functionArguments | ASTERISK)? ')' pathContinutation? filterClause? withinGroup? overClause? # GenericFunction
| functionName '(' subquery ')' # FunctionWithSubquery
| castFunction # CastFunctionInvocation
| extractFunction # ExtractFunctionInvocation
| trimFunction # TrimFunctionInvocation
| everyFunction # EveryFunctionInvocation
| anyFunction # AnyFunctionInvocation
| treatedPath # TreatedPathInvocation
: standardFunction # StandardFunctionInvocation
| aggregateFunction # AggregateFunctionInvocation
| collectionSizeFunction # CollectionSizeFunctionInvocation
| collectionAggregateFunction # CollectionAggregateFunctionInvocation
| collectionFunctionMisuse # CollectionFunctionMisuseInvocation
| jpaNonstandardFunction # JpaNonstandardFunctionInvocation
| columnFunction # ColumnFunctionInvocation
| genericFunction # GenericFunctionInvocation
;
functionArguments
: DISTINCT? expressionOrPredicate (',' expressionOrPredicate)*
/**
* Any function with an irregular syntax for the argument list
*
* These are all inspired by the syntax of ANSI SQL
*/
standardFunction
: castFunction
| treatedPath
| extractFunction
| truncFunction
| formatFunction
| collateFunction
| substringFunction
| overlayFunction
| trimFunction
| padFunction
| positionFunction
| currentDateFunction
| currentTimeFunction
| currentTimestampFunction
| instantFunction
| localDateFunction
| localTimeFunction
| localDateTimeFunction
| offsetDateTimeFunction
| cube
| rollup
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-aggregate-functions-filter
filterClause
: FILTER '(' whereClause ')'
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-aggregate-functions-orderedset
withinGroup
: WITHIN GROUP '(' orderByClause ')'
;
overClause
: OVER '(' partitionClause? orderByClause? frameClause? ')'
;
partitionClause
: PARTITION BY expression (',' expression)*
;
frameClause
: (RANGE|ROWS|GROUPS) frameStart frameExclusion?
| (RANGE|ROWS|GROUPS) BETWEEN frameStart AND frameEnd frameExclusion?
;
frameStart
: UNBOUNDED PRECEDING # UnboundedPrecedingFrameStart
| expression PRECEDING # ExpressionPrecedingFrameStart
| CURRENT ROW # CurrentRowFrameStart
| expression FOLLOWING # ExpressionFollowingFrameStart
;
frameExclusion
: EXCLUDE CURRENT ROW # CurrentRowFrameExclusion
| EXCLUDE GROUP # GroupFrameExclusion
| EXCLUDE TIES # TiesFrameExclusion
| EXCLUDE NO OTHERS # NoOthersFrameExclusion
;
frameEnd
: expression PRECEDING # ExpressionPrecedingFrameEnd
| CURRENT ROW # CurrentRowFrameEnd
| expression FOLLOWING # ExpressionFollowingFrameEnd
| UNBOUNDED FOLLOWING # UnboundedFollowingFrameEnd
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-functions
/**
* The 'cast()' function for typecasting
*/
castFunction
: CAST '(' expression AS castTarget ')'
;
/**
* The target type for a typecast: a typename, together with length or precision/scale
*/
castTarget
: castTargetType ('(' INTEGER_LITERAL (',' INTEGER_LITERAL)? ')')?
;
/**
* The name of the target type in a typecast
*
* Like the 'entityName' rule, we have a specialized dotIdentifierSequence rule
*/
castTargetType
returns [String fullTargetName]
: (i=identifier { $fullTargetName = _localctx.i.getText(); }) ('.' c=identifier { $fullTargetName += ("." + _localctx.c.getText() ); })*
;
extractFunction
: EXTRACT '(' expression FROM expression ')'
| dateTimeFunction '(' expression ')'
/**
* The two formats for the 'substring() function: one defined by JPQL, the other by ANSI SQL
*/
substringFunction
: SUBSTRING '(' expression ',' substringFunctionStartArgument (',' substringFunctionLengthArgument)? ')'
| SUBSTRING '(' expression FROM substringFunctionStartArgument (FOR substringFunctionLengthArgument)? ')'
;
substringFunctionStartArgument
: expression
;
substringFunctionLengthArgument
: expression
;
/**
* The ANSI SQL-style 'trim()' function
*/
trimFunction
: TRIM '(' (LEADING | TRAILING | BOTH)? stringLiteral? FROM? expression ')'
: TRIM '(' trimSpecification? trimCharacter? FROM? expression ')'
;
dateTimeFunction
: d=(YEAR
| MONTH
| DAY
| WEEK
| QUARTER
| HOUR
| MINUTE
| SECOND
| NANOSECOND
| EPOCH)
trimSpecification
: LEADING
| TRAILING
| BOTH
;
trimCharacter
: stringLiteral
| parameter
;
/**
* A 'pad()' function inspired by 'trim()'
*/
padFunction
: PAD '(' expression WITH padLength padSpecification padCharacter? ')'
;
padSpecification
: LEADING
| TRAILING
;
padCharacter
: stringLiteral
;
padLength
: expression
;
/**
* The ANSI SQL-style 'position()' function
*/
positionFunction
: POSITION '(' positionFunctionPatternArgument IN positionFunctionStringArgument ')'
;
positionFunctionPatternArgument
: expression
;
positionFunctionStringArgument
: expression
;
/**
* The ANSI SQL-style 'overlay()' function
*/
overlayFunction
: OVERLAY '(' overlayFunctionStringArgument PLACING overlayFunctionReplacementArgument FROM overlayFunctionStartArgument (FOR overlayFunctionLengthArgument)? ')'
;
overlayFunctionStringArgument
: expression
;
overlayFunctionReplacementArgument
: expression
;
overlayFunctionStartArgument
: expression
;
overlayFunctionLengthArgument
: expression
;
/**
* The deprecated current_date function required by JPQL
*/
currentDateFunction
: CURRENT_DATE ('(' ')')?
| CURRENT DATE
;
/**
* The deprecated current_time function required by JPQL
*/
currentTimeFunction
: CURRENT_TIME ('(' ')')?
| CURRENT TIME
;
/**
* The deprecated current_timestamp function required by JPQL
*/
currentTimestampFunction
: CURRENT_TIMESTAMP ('(' ')')?
| CURRENT TIMESTAMP
;
/**
* The instant function, and deprecated current_instant function
*/
instantFunction
: CURRENT_INSTANT ('(' ')')? //deprecated legacy syntax
| INSTANT
;
/**
* The 'local datetime' function (or literal if you prefer)
*/
localDateTimeFunction
: LOCAL_DATETIME ('(' ')')?
| LOCAL DATETIME
;
/**
* The 'offset datetime' function (or literal if you prefer)
*/
offsetDateTimeFunction
: OFFSET_DATETIME ('(' ')')?
| OFFSET DATETIME
;
/**
* The 'local date' function (or literal if you prefer)
*/
localDateFunction
: LOCAL_DATE ('(' ')')?
| LOCAL DATE
;
/**
* The 'local time' function (or literal if you prefer)
*/
localTimeFunction
: LOCAL_TIME ('(' ')')?
| LOCAL TIME
;
/**
* The 'format()' function for formatting dates and times according to a pattern
*/
formatFunction
: FORMAT '(' expression AS format ')'
;
/**
* The name of a database-defined collation
*
* Certain databases allow a period in a collation name
*/
collation
: simplePath
;
/**
* The special 'collate()' functions
*/
collateFunction
: COLLATE '(' expression AS collation ')'
;
/**
* The 'cube()' function specific to the 'group by' clause
*/
cube
: CUBE '(' expressionOrPredicate (',' expressionOrPredicate)* ')'
;
/**
* The 'rollup()' function specific to the 'group by' clause
*/
rollup
: ROLLUP '(' expressionOrPredicate (',' expressionOrPredicate)* ')'
;
/**
* A format pattern, with a syntax inspired by by java.time.format.DateTimeFormatter
*
* see 'Dialect.appendDatetimeFormat()'
*/
format
: stringLiteral
;
/**
* The 'extract()' function for extracting fields of dates, times, and datetimes
*/
extractFunction
: EXTRACT '(' extractField FROM expression ')'
| datetimeField '(' expression ')'
;
/**
* The 'trunc()' function for truncating both numeric and datetime values
*/
truncFunction
: (TRUNC | TRUNCATE) '(' expression (',' (datetimeField | expression))? ')'
;
/**
* A syntax for calling user-defined or native database functions, required by JPQL
*/
jpaNonstandardFunction
: FUNCTION '(' jpaNonstandardFunctionName (AS castTarget)? (',' genericFunctionArguments)? ')'
;
/**
* The name of a user-defined or native database function, given as a quoted string
*/
jpaNonstandardFunctionName
: stringLiteral
| identifier
;
columnFunction
: COLUMN '(' path '.' jpaNonstandardFunctionName (AS castTarget)? ')'
;
/**
* Any function invocation that follows the regular syntax
*
* The function name, followed by a parenthesized list of ','-separated expressions
*/
genericFunction
: genericFunctionName '(' (genericFunctionArguments | ASTERISK)? ')' pathContinutation?
nthSideClause? nullsClause? withinGroupClause? filterClause? overClause?
;
/**
* The name of a generic function, which may contain periods and quoted identifiers
*
* Names of generic functions are resolved against the SqmFunctionRegistry
*/
genericFunctionName
: simplePath
;
/**
* The arguments of a generic function
*/
genericFunctionArguments
: (DISTINCT | datetimeField ',')? expressionOrPredicate (',' expressionOrPredicate)*
;
/**
* The special 'size()' function defined by JPQL
*/
collectionSizeFunction
: SIZE '(' path ')'
;
/**
* Special rule for 'max(elements())`, 'avg(keys())', 'sum(indices())`, etc., as defined by HQL
* Also the deprecated 'maxindex()', 'maxelement()', 'minindex()', 'minelement()' functions from old HQL
*/
collectionAggregateFunction
: (MAX|MIN|SUM|AVG) '(' elementsValuesQuantifier '(' path ')' ')' # ElementAggregateFunction
| (MAX|MIN|SUM|AVG) '(' indicesKeysQuantifier '(' path ')' ')' # IndexAggregateFunction
| (MAXELEMENT|MINELEMENT) '(' path ')' # ElementAggregateFunction
| (MAXINDEX|MININDEX) '(' path ')' # IndexAggregateFunction
;
/**
* To accommodate the misuse of elements() and indices() in the select clause
*
* (At some stage in the history of HQL, someone mixed them up with value() and index(),
* and so we have tests that insist they're interchangeable. Ugh.)
*/
collectionFunctionMisuse
: elementsValuesQuantifier '(' path ')'
| indicesKeysQuantifier '(' path ')'
;
/**
* The special 'every()', 'all()', 'any()' and 'some()' functions defined by HQL
*
* May be applied to a subquery or collection reference, or may occur as an aggregate function in the 'select' clause
*/
aggregateFunction
: everyFunction
| anyFunction
| listaggFunction
;
/**
* The functions 'every()' and 'all()' are synonyms
*/
everyFunction
: every=(EVERY | ALL) '(' predicate ')'
| every=(EVERY | ALL) '(' subquery ')'
| every=(EVERY | ALL) (ELEMENTS | INDICES) '(' simplePath ')'
: everyAllQuantifier '(' predicate ')' filterClause? overClause?
| everyAllQuantifier '(' subquery ')'
| everyAllQuantifier collectionQuantifier '(' simplePath ')'
;
/**
* The functions 'any()' and 'some()' are synonyms
*/
anyFunction
: any=(ANY | SOME) '(' predicate ')'
| any=(ANY | SOME) '(' subquery ')'
| any=(ANY | SOME) (ELEMENTS | INDICES) '(' simplePath ')'
: anySomeQuantifier '(' predicate ')' filterClause? overClause?
| anySomeQuantifier '(' subquery ')'
| anySomeQuantifier collectionQuantifier '(' simplePath ')'
;
everyAllQuantifier
: EVERY
| ALL
;
anySomeQuantifier
: ANY
| SOME
;
/**
* The 'listagg()' ordered set-aggregate function
*/
listaggFunction
: LISTAGG '(' DISTINCT? expressionOrPredicate ',' expressionOrPredicate onOverflowClause? ')'
withinGroupClause? filterClause? overClause?
;
/**
* A 'on overflow' clause: what to do when the text data type used for 'listagg' overflows
*/
onOverflowClause
: ON OVERFLOW (ERROR | TRUNCATE expression? (WITH|WITHOUT) COUNT)
;
/**
* A 'within group' clause: defines the order in which the ordered set-aggregate function should work
*/
withinGroupClause
: WITHIN GROUP '(' orderByClause ')'
;
/**
* A 'filter' clause: a restriction applied to an aggregate function
*/
filterClause
: FILTER '(' whereClause ')'
;
/**
* A `nulls` clause: what should a value access window function do when encountering a `null`
*/
nullsClause
: RESPECT NULLS
| IGNORE NULLS
;
/**
* A `nulls` clause: what should a value access window function do when encountering a `null`
*/
nthSideClause
: FROM FIRST
| FROM LAST
;
/**
* A 'over' clause: the specification of a window within which the function should act
*/
overClause
: OVER '(' partitionClause? orderByClause? frameClause? ')'
;
/**
* A 'partition' clause: the specification the group within which a function should act in a window
*/
partitionClause
: PARTITION BY expression (',' expression)*
;
/**
* A 'frame' clause: the specification the content of the window
*/
frameClause
: (RANGE|ROWS|GROUPS) frameStart frameExclusion?
| (RANGE|ROWS|GROUPS) BETWEEN frameStart AND frameEnd frameExclusion?
;
/**
* The start of the window content
*/
frameStart
: CURRENT ROW
| UNBOUNDED PRECEDING
| expression PRECEDING
| expression FOLLOWING
;
/**
* The end of the window content
*/
frameEnd
: CURRENT ROW
| UNBOUNDED FOLLOWING
| expression PRECEDING
| expression FOLLOWING
;
/**
* A 'exclusion' clause: the specification what to exclude from the window content
*/
frameExclusion
: EXCLUDE CURRENT ROW
| EXCLUDE GROUP
| EXCLUDE TIES
| EXCLUDE NO OTHERS
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-treat-type
@@ -608,6 +1017,21 @@ expressionOrPredicate
| predicate
;
collectionQuantifier
: elementsValuesQuantifier
| indicesKeysQuantifier
;
elementsValuesQuantifier
: ELEMENTS
| VALUES
;
indicesKeysQuantifier
: INDICES
| KEYS
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-relational-comparisons
// NOTE: The TIP shows that "!=" is also supported. Hibernate's source code shows that "^=" is another NOT_EQUALS option as well.
relationalExpression
@@ -691,10 +1115,6 @@ identifier
: reservedWord
;
character
: CHARACTER
;
functionName
: reservedWord ('.' reservedWord)*
;
@@ -998,6 +1418,7 @@ INTO : I N T O;
IS : I S;
JOIN : J O I N;
KEY : K E Y;
KEYS : K E Y S;
LAST : L A S T;
LATERAL : L A T E R A L;
LEADING : L E A D I N G;
@@ -1114,4 +1535,3 @@ BINARY_LITERAL : [xX] '\'' HEX_DIGIT+ '\''
;
IDENTIFICATION_VARIABLE : ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '$' | '_') ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '0' .. '9' | '$' | '_')* ;

View File

@@ -37,6 +37,7 @@ import org.junit.jupiter.params.provider.ValueSource;
*
* @author Greg Turnquist
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.1
*/
class HqlQueryRendererTests {
@@ -1551,9 +1552,11 @@ class HqlQueryRendererTests {
assertQuery("SELECT o FROM Order o WHERE CAST(:userId AS java.util.UUID) IS NULL OR o.user.id = :userId");
}
@Test // GH-3025
void durationLiteralsShouldWork() {
assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE (ce.endDate - ce.startDate) > 5 MINUTE");
@ParameterizedTest // GH-3025
@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));
}
@Test // GH-3025

View File

@@ -21,6 +21,9 @@ import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
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;
/**
@@ -31,6 +34,7 @@ import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer
* IMPORTANT: Purely verifies the parser without any transformations.
*
* @author Greg Turnquist
* @author Mark Paluch
* @since 3.1
*/
class HqlSpecificationTests {
@@ -331,6 +335,177 @@ class HqlSpecificationTests {
""");
}
@Test // GH-3689
void generic() {
assertQuery("""
SELECT e FROM Employee e
WHERE FOO(x).bar RESPECT NULLS
""");
assertQuery("""
SELECT e FROM Employee e
WHERE FOO(x).bar IGNORE NULLS
""");
}
@Test // GH-3689
void size() {
assertQuery("""
SELECT e FROM Employee e
WHERE SIZE(x) > 1
""");
}
@Test // GH-3689
void collectionAggregate() {
assertQuery("""
SELECT e FROM Employee e
WHERE MAXELEMENT(foo) > MINELEMENT(bar)
""");
assertQuery("""
SELECT e FROM Employee e
WHERE MININDEX(foo) > MAXINDEX(bar)
""");
}
@Test // GH-3689
void trunc() {
assertQuery("""
SELECT e FROM Employee e
WHERE TRUNC(x) = TRUNCATE(y)
""");
assertQuery("""
SELECT e FROM Employee e
WHERE TRUNC(e, 'foo') = TRUNCATE(e, 'bar')
""");
}
@ParameterizedTest // GH-3689
@ValueSource(strings = { "YYYY", "MONTH", "DAY", "WEEK", "QUARTER", "HOUR", "MINUTE", "SECOND", "NANOSECOND",
"NANOSECOND", "EPOCH" })
void trunc(String truncation) {
assertQuery("""
SELECT e FROM Employee e
WHERE TRUNC(e, %1$s) = TRUNCATE(e, %1$s)
""".formatted(truncation));
}
@Test // GH-3689
void format() {
assertQuery("""
SELECT e FROM Employee e
WHERE FORMAT(x AS 'foo') = FORMAT(x AS 'bar')
""");
}
@Test // GH-3689
void collate() {
assertQuery("""
SELECT e FROM Employee e
WHERE COLLATE(x AS foo) = COLLATE(x AS foo.bar)
""");
}
@Test // GH-3689
void substring() {
assertQuery("select substring(c.number, 1, 2) " + //
"from Call c");
assertQuery("select substring(c.number, 1) " + //
"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");
}
@Test // GH-3689
void overlay() {
assertQuery("select OVERLAY(c.number PLACING 1 FROM 2) " + //
"from Call c ");
assertQuery("select OVERLAY(p.number PLACING 1 FROM 2 FOR 3) " + //
"from Call c ");
}
@Test // GH-3689
void pad() {
assertQuery("select PAD(c.number WITH 1 LEADING) " + //
"from Call c ");
assertQuery("select PAD(c.number WITH 1 TRAILING) " + //
"from Call c ");
assertQuery("select PAD(c.number WITH 1 LEADING '0') " + //
"from Call c ");
assertQuery("select PAD(c.number WITH 1 TRAILING '0') " + //
"from Call c ");
}
@Test // GH-3689
void position() {
assertQuery("select POSITION(c.number IN 'foo') " + //
"from Call c ");
}
@Test // GH-3689
void currentDateFunctions() {
assertQuery("select CURRENT DATE, CURRENT_DATE() " + //
"from Call c ");
assertQuery("select CURRENT TIME, CURRENT_TIME() " + //
"from Call c ");
assertQuery("select CURRENT TIMESTAMP, CURRENT_TIMESTAMP() " + //
"from Call c ");
assertQuery("select INSTANT, CURRENT_INSTANT() " + //
"from Call c ");
assertQuery("select LOCAL DATE, LOCAL_DATE() " + //
"from Call c ");
assertQuery("select LOCAL TIME, LOCAL_TIME() " + //
"from Call c ");
assertQuery("select LOCAL DATETIME, LOCAL_DATETIME() " + //
"from Call c ");
assertQuery("select OFFSET DATETIME, OFFSET_DATETIME() " + //
"from Call c ");
}
@Test // GH-3689
void cube() {
assertQuery("select CUBE(foo), CUBE(foo, bar) " + //
"from Call c ");
}
@Test // GH-3689
void rollup() {
assertQuery("select ROLLUP(foo), ROLLUP(foo, bar) " + //
"from Call c ");
}
@Test
void pathExpressionsNamedParametersExample() {
@@ -383,6 +558,80 @@ class HqlSpecificationTests {
""");
}
@Test // GH-3689
void everyAll() {
assertQuery("""
SELECT DISTINCT emp
FROM Employee emp
WHERE EVERY (SELECT spouseEmp
FROM Employee spouseEmp) > 1
""");
assertQuery("""
SELECT DISTINCT emp
FROM Employee emp
WHERE ALL (SELECT spouseEmp
FROM Employee spouseEmp) > 1
""");
assertQuery("""
SELECT DISTINCT emp
FROM Employee emp
WHERE ALL (foo > 1) OVER (PARTITION BY bar)
""");
assertQuery("""
SELECT DISTINCT emp
FROM Employee emp
WHERE ALL VALUES (foo) > 1
""");
assertQuery("""
SELECT DISTINCT emp
FROM Employee emp
WHERE ALL ELEMENTS (foo) > 1
""");
}
@Test // GH-3689
void anySome() {
assertQuery("""
SELECT DISTINCT emp
FROM Employee emp
WHERE ANY (SELECT spouseEmp
FROM Employee spouseEmp) > 1
""");
assertQuery("""
SELECT DISTINCT emp
FROM Employee emp
WHERE SOME (SELECT spouseEmp
FROM Employee spouseEmp) > 1
""");
assertQuery("""
SELECT DISTINCT emp
FROM Employee emp
WHERE ANY (foo > 1) OVER (PARTITION BY bar)
""");
assertQuery("""
SELECT DISTINCT emp
FROM Employee emp
WHERE ANY VALUES (foo) > 1
""");
}
@Test // GH-3689
void listAgg() {
assertQuery("select listagg(p.number, ', ') within group (order by p.type, p.number) " + //
"from Phone p " + //
"group by p.person");
}
@Test
void allExample() {
@@ -1119,9 +1368,6 @@ class HqlSpecificationTests {
assertQuery("select concat(p.number, ' : ', cast(c.duration as string)) " + //
"from Call c " + //
"join c.phone p");
assertQuery("select substring(p.number, 1, 2) " + //
"from Call c " + //
"join c.phone p");
assertQuery("select upper(p.name) " + //
"from Person p ");
assertQuery("select lower(p.name) " + //
@@ -1450,9 +1696,6 @@ class HqlSpecificationTests {
"from Call c " + //
"join c.phone p " + //
"group by p.number");
assertQuery("select listagg(p.number, ', ') within group (order by p.type, p.number) " + //
"from Phone p " + //
"group by p.person");
assertQuery("select sum(c.duration) " + //
"from Call c ");
assertQuery("select p.name, sum(c.duration) " + //