diff --git a/pom.xml b/pom.xml
index 1847f4d2c4..c2cc0e49e3 100755
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-jpa-parent
- 3.5.0-SNAPSHOT
+ 3.5.0-GH-3689-SNAPSHOT
pom
Spring Data JPA Parent
diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml
index 5dcbbb69fd..2d26e4ffe0 100755
--- a/spring-data-envers/pom.xml
+++ b/spring-data-envers/pom.xml
@@ -5,12 +5,12 @@
org.springframework.data
spring-data-envers
- 3.5.0-SNAPSHOT
+ 3.5.0-GH-3689-SNAPSHOT
org.springframework.data
spring-data-jpa-parent
- 3.5.0-SNAPSHOT
+ 3.5.0-GH-3689-SNAPSHOT
../pom.xml
diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml
index 38a234cb71..6273bff393 100644
--- a/spring-data-jpa-distribution/pom.xml
+++ b/spring-data-jpa-distribution/pom.xml
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-jpa-parent
- 3.5.0-SNAPSHOT
+ 3.5.0-GH-3689-SNAPSHOT
../pom.xml
diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml
index c6d9301c02..825f122291 100644
--- a/spring-data-jpa/pom.xml
+++ b/spring-data-jpa/pom.xml
@@ -6,7 +6,7 @@
org.springframework.data
spring-data-jpa
- 3.5.0-SNAPSHOT
+ 3.5.0-GH-3689-SNAPSHOT
Spring Data JPA
Spring Data module for JPA repositories.
@@ -15,7 +15,7 @@
org.springframework.data
spring-data-jpa-parent
- 3.5.0-SNAPSHOT
+ 3.5.0-GH-3689-SNAPSHOT
../pom.xml
diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4
index 54a93e9ebf..728d3fe7b2 100644
--- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4
+++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4
@@ -147,7 +147,7 @@ deleteStatement
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-insert
insertStatement
- : INSERT INTO? targetEntity targetFields (queryExpression | valuesList)
+ : INSERT INTO? targetEntity targetFields (queryExpression | valuesList) conflictClause?
;
// Already defined underneath updateStatement
@@ -167,12 +167,25 @@ values
: '(' expression (',' expression)* ')'
;
-instantiation
- : NEW instantiationTarget '(' instantiationArguments ')'
+/**
+ * a 'conflict' clause in an 'insert' statement
+ */
+conflictClause
+ : ON CONFLICT conflictTarget? DO conflictAction
+ ;
+
+conflictTarget
+ : ON CONSTRAINT identifier
+ | '(' simplePath (',' simplePath)* ')'
+ ;
+
+conflictAction
+ : NOTHING
+ | UPDATE setClause whereClause?
;
-alias
- : AS? identifier // spec says IDENTIFIER but clearly does NOT mean a reserved word
+instantiation
+ : NEW instantiationTarget '(' instantiationArguments ')'
;
groupedItem
@@ -337,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
@@ -351,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
@@ -399,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
@@ -450,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 ')'
+/**
+ * The 'cast()' function for typecasting
+ */
+castFunction
+ : CAST '(' expression AS castTarget ')'
;
-// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-aggregate-functions-orderedset
-withinGroup
- : WITHIN GROUP '(' orderByClause ')'
+/**
+ * The target type for a typecast: a typename, together with length or precision/scale
+ */
+castTarget
+ : castTargetType ('(' INTEGER_LITERAL (',' INTEGER_LITERAL)? ')')?
;
-overClause
- : OVER '(' partitionClause? orderByClause? frameClause? ')'
+/**
+ * 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() ); })*
;
-partitionClause
- : PARTITION BY expression (',' 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)? ')'
;
-frameClause
- : (RANGE|ROWS|GROUPS) frameStart frameExclusion?
- | (RANGE|ROWS|GROUPS) BETWEEN frameStart AND frameEnd frameExclusion?
+substringFunctionStartArgument
+ : expression
;
-frameStart
- : UNBOUNDED PRECEDING # UnboundedPrecedingFrameStart
- | expression PRECEDING # ExpressionPrecedingFrameStart
- | CURRENT ROW # CurrentRowFrameStart
- | expression FOLLOWING # ExpressionFollowingFrameStart
+substringFunctionLengthArgument
+ : expression
;
-frameExclusion
- : EXCLUDE CURRENT ROW # CurrentRowFrameExclusion
- | EXCLUDE GROUP # GroupFrameExclusion
- | EXCLUDE TIES # TiesFrameExclusion
- | EXCLUDE NO OTHERS # NoOthersFrameExclusion
+/**
+ * The ANSI SQL-style 'trim()' function
+ */
+trimFunction
+ : TRIM '(' trimSpecification? trimCharacter? FROM? expression ')'
;
-frameEnd
- : expression PRECEDING # ExpressionPrecedingFrameEnd
- | CURRENT ROW # CurrentRowFrameEnd
- | expression FOLLOWING # ExpressionFollowingFrameEnd
- | UNBOUNDED FOLLOWING # UnboundedFollowingFrameEnd
+trimSpecification
+ : LEADING
+ | TRAILING
+ | BOTH
;
-// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-functions
-castFunction
- : CAST '(' expression AS castTarget ')'
+trimCharacter
+ : stringLiteral
+ | parameter
;
-castTarget
- : castTargetType ('(' INTEGER_LITERAL (',' INTEGER_LITERAL)? ')')?
+/**
+ * A 'pad()' function inspired by 'trim()'
+ */
+padFunction
+ : PAD '(' expression WITH padLength padSpecification padCharacter? ')'
;
-castTargetType
- returns [String fullTargetName]
- : (i=identifier { $fullTargetName = _localctx.i.getText(); }) ('.' c=identifier { $fullTargetName += ("." + _localctx.c.getText() ); })*
+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 '(' expression FROM expression ')'
- | dateTimeFunction '(' expression ')'
+ : EXTRACT '(' extractField FROM expression ')'
+ | datetimeField '(' expression ')'
;
-trimFunction
- : TRIM '(' (LEADING | TRAILING | BOTH)? stringLiteral? FROM? expression ')'
+/**
+ * The 'trunc()' function for truncating both numeric and datetime values
+ */
+truncFunction
+ : (TRUNC | TRUNCATE) '(' expression (',' (datetimeField | expression))? ')'
;
-dateTimeFunction
- : d=(YEAR
- | MONTH
- | DAY
- | WEEK
- | QUARTER
- | HOUR
- | MINUTE
- | SECOND
- | NANOSECOND
- | EPOCH)
+/**
+ * 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
@@ -591,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
@@ -674,10 +1115,6 @@ identifier
: reservedWord
;
-character
- : CHARACTER
- ;
-
functionName
: reservedWord ('.' reservedWord)*
;
@@ -921,7 +1358,11 @@ 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;
@@ -977,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;
@@ -1009,6 +1451,7 @@ NEW : N E W;
NEXT : N E X T;
NO : N O;
NOT : N O T;
+NOTHING : N O T H I N G;
NULL : N U L L;
NULLS : N U L L S;
OBJECT : O B J E C T;
@@ -1092,4 +1535,3 @@ BINARY_LITERAL : [xX] '\'' HEX_DIGIT+ '\''
;
IDENTIFICATION_VARIABLE : ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '$' | '_') ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '0' .. '9' | '$' | '_')* ;
-
diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java
index 9976347f1d..2ef49b95ff 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java
@@ -24,6 +24,7 @@
import org.antlr.v4.runtime.tree.ParseTree;
import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder;
+import org.springframework.util.ObjectUtils;
/**
* An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that renders an HQL query without making any changes.
@@ -545,6 +546,10 @@ public QueryTokenStream visitInsertStatement(HqlParser.InsertStatementContext ct
builder.appendExpression(visit(ctx.valuesList()));
}
+ if (ctx.conflictClause() != null) {
+ builder.appendExpression(visit(ctx.conflictClause()));
+ }
+
return builder;
}
@@ -584,29 +589,75 @@ public QueryTokenStream visitValues(HqlParser.ValuesContext ctx) {
}
@Override
- public QueryTokenStream visitInstantiation(HqlParser.InstantiationContext ctx) {
+ public QueryTokenStream visitConflictClause(HqlParser.ConflictClauseContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
- builder.append(QueryTokens.expression(ctx.NEW()));
- builder.append(visit(ctx.instantiationTarget()));
- builder.append(TOKEN_OPEN_PAREN);
- builder.appendInline(visit(ctx.instantiationArguments()));
- builder.append(TOKEN_CLOSE_PAREN);
+ builder.append(QueryTokens.expression(ctx.ON()));
+ builder.append(QueryTokens.expression(ctx.CONFLICT()));
+
+ if (ctx.conflictTarget() != null) {
+ builder.appendExpression(visit(ctx.conflictTarget()));
+ }
+
+ builder.append(QueryTokens.expression(ctx.DO()));
+ builder.appendExpression(visit(ctx.conflictAction()));
return builder;
}
@Override
- public QueryTokenStream visitAlias(HqlParser.AliasContext ctx) {
+ public QueryTokenStream visitConflictTarget(HqlParser.ConflictTargetContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
- if (ctx.AS() != null) {
- builder.append(QueryTokens.expression(ctx.AS()));
+ if (ctx.identifier() != null) {
+
+ builder.append(QueryTokens.expression(ctx.ON()));
+ builder.append(QueryTokens.expression(ctx.CONSTRAINT()));
+ builder.appendExpression(visit(ctx.identifier()));
}
- builder.append(visit(ctx.identifier()));
+ if (!ObjectUtils.isEmpty(ctx.simplePath())) {
+
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(QueryTokenStream.concat(ctx.simplePath(), this::visit, TOKEN_COMMA));
+
+ builder.append(TOKEN_CLOSE_PAREN);
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitConflictAction(HqlParser.ConflictActionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.NOTHING() != null) {
+ builder.append(QueryTokens.expression(ctx.NOTHING()));
+ } else {
+ builder.append(QueryTokens.expression(ctx.UPDATE()));
+ builder.appendExpression(visit(ctx.setClause()));
+
+ if (ctx.whereClause() != null) {
+ builder.appendExpression(visit(ctx.whereClause()));
+ }
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitInstantiation(HqlParser.InstantiationContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.expression(ctx.NEW()));
+ builder.append(visit(ctx.instantiationTarget()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.appendInline(visit(ctx.instantiationArguments()));
+ builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
@@ -1081,30 +1132,105 @@ public QueryTokenStream visitDateTimeLiteral(HqlParser.DateTimeLiteralContext ct
public QueryTokenStream visitDatetimeField(HqlParser.DatetimeFieldContext ctx) {
if (ctx.YEAR() != null) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.YEAR()));
+ return QueryRendererBuilder.from(QueryTokens.token(ctx.YEAR()));
} else if (ctx.MONTH() != null) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.MONTH()));
+ return QueryRendererBuilder.from(QueryTokens.token(ctx.MONTH()));
} else if (ctx.DAY() != null) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.DAY()));
+ return QueryRendererBuilder.from(QueryTokens.token(ctx.DAY()));
} else if (ctx.WEEK() != null) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.WEEK()));
+ return QueryRendererBuilder.from(QueryTokens.token(ctx.WEEK()));
} else if (ctx.QUARTER() != null) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.QUARTER()));
+ return QueryRendererBuilder.from(QueryTokens.token(ctx.QUARTER()));
} else if (ctx.HOUR() != null) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.HOUR()));
+ return QueryRendererBuilder.from(QueryTokens.token(ctx.HOUR()));
} else if (ctx.MINUTE() != null) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.MINUTE()));
+ return QueryRendererBuilder.from(QueryTokens.token(ctx.MINUTE()));
} else if (ctx.SECOND() != null) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.SECOND()));
+ return QueryRendererBuilder.from(QueryTokens.token(ctx.SECOND()));
} else if (ctx.NANOSECOND() != null) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.NANOSECOND()));
+ return QueryRendererBuilder.from(QueryTokens.token(ctx.NANOSECOND()));
} else if (ctx.EPOCH() != null) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.EPOCH()));
+ return QueryRendererBuilder.from(QueryTokens.token(ctx.EPOCH()));
} else {
return QueryTokenStream.empty();
}
}
+ @Override
+ public QueryTokenStream visitDayField(HqlParser.DayFieldContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.expression(ctx.DAY()));
+ builder.append(QueryTokens.expression(ctx.OF()));
+
+ if (ctx.MONTH() != null) {
+ builder.append(QueryTokens.expression(ctx.MONTH()));
+ }
+
+ if (ctx.WEEK() != null) {
+ builder.append(QueryTokens.expression(ctx.WEEK()));
+ }
+
+ if (ctx.YEAR() != null) {
+ builder.append(QueryTokens.expression(ctx.YEAR()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitWeekField(HqlParser.WeekFieldContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.expression(ctx.WEEK()));
+ builder.append(QueryTokens.expression(ctx.OF()));
+
+ if (ctx.MONTH() != null) {
+ builder.append(QueryTokens.expression(ctx.MONTH()));
+ }
+
+ if (ctx.YEAR() != null) {
+ builder.append(QueryTokens.expression(ctx.YEAR()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitTimeZoneField(HqlParser.TimeZoneFieldContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.OFFSET() != null) {
+ builder.append(QueryTokens.expression(ctx.OFFSET()));
+
+ if (ctx.HOUR() != null) {
+ builder.append(QueryTokens.expression(ctx.HOUR()));
+ }
+
+ if (ctx.MINUTE() != null) {
+ builder.append(QueryTokens.expression(ctx.MINUTE()));
+ }
+ }
+
+ if (ctx.TIMEZONE_HOUR() != null) {
+ builder.append(QueryTokens.expression(ctx.TIMEZONE_HOUR()));
+ }
+
+ if (ctx.TIMEZONE_HOUR() != null) {
+ builder.append(QueryTokens.expression(ctx.TIMEZONE_MINUTE()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitDateOrTimeField(HqlParser.DateOrTimeFieldContext ctx) {
+ return QueryRendererBuilder.from(QueryTokens.expression(ctx.DATE() != null ? ctx.DATE() : ctx.TIME()));
+ }
+
@Override
public QueryTokenStream visitBinaryLiteral(HqlParser.BinaryLiteralContext ctx) {
@@ -1267,7 +1393,7 @@ public QueryTokenStream visitToDurationExpression(HqlParser.ToDurationExpression
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(visit(ctx.expression()));
- builder.append(visit(ctx.datetimeField()));
+ builder.appendExpression(visit(ctx.datetimeField()));
return builder;
}
@@ -1279,7 +1405,7 @@ public QueryTokenStream visitFromDurationExpression(HqlParser.FromDurationExpres
builder.append(visit(ctx.expression()));
builder.append(QueryTokens.expression(ctx.BY()));
- builder.append(visit(ctx.datetimeField()));
+ builder.appendExpression(visit(ctx.datetimeField()));
return builder;
}
@@ -1305,271 +1431,1147 @@ public QueryTokenStream visitFunctionExpression(HqlParser.FunctionExpressionCont
}
@Override
- public QueryTokenStream visitGeneralPathExpression(HqlParser.GeneralPathExpressionContext ctx) {
- return visit(ctx.generalPathFragment());
+ public QueryTokenStream visitStandardFunctionInvocation(HqlParser.StandardFunctionInvocationContext ctx) {
+ return visit(ctx.standardFunction());
}
@Override
- public QueryTokenStream visitIdentificationVariable(HqlParser.IdentificationVariableContext ctx) {
-
- if (ctx.identifier() != null) {
- return visit(ctx.identifier());
- } else if (ctx.simplePath() != null) {
- return visit(ctx.simplePath());
- } else {
- return QueryTokenStream.empty();
- }
+ public QueryTokenStream visitAggregateFunctionInvocation(HqlParser.AggregateFunctionInvocationContext ctx) {
+ return visit(ctx.aggregateFunction());
}
@Override
- public QueryTokenStream visitPath(HqlParser.PathContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
+ public QueryTokenStream visitCollectionSizeFunctionInvocation(HqlParser.CollectionSizeFunctionInvocationContext ctx) {
+ return visit(ctx.collectionSizeFunction());
+ }
- if (ctx.treatedPath() != null) {
+ @Override
+ public QueryTokenStream visitCollectionAggregateFunctionInvocation(
+ HqlParser.CollectionAggregateFunctionInvocationContext ctx) {
+ return visit(ctx.collectionAggregateFunction());
+ }
- builder.append(visit(ctx.treatedPath()));
+ @Override
+ public QueryTokenStream visitCollectionFunctionMisuseInvocation(
+ HqlParser.CollectionFunctionMisuseInvocationContext ctx) {
+ return visit(ctx.collectionFunctionMisuse());
+ }
- if (ctx.pathContinutation() != null) {
- builder.append(visit(ctx.pathContinutation()));
- }
- } else if (ctx.generalPathFragment() != null) {
- builder.append(visit(ctx.generalPathFragment()));
- }
+ @Override
+ public QueryTokenStream visitJpaNonstandardFunctionInvocation(HqlParser.JpaNonstandardFunctionInvocationContext ctx) {
+ return visit(ctx.jpaNonstandardFunction());
+ }
- return builder;
+ @Override
+ public QueryTokenStream visitColumnFunctionInvocation(HqlParser.ColumnFunctionInvocationContext ctx) {
+ return visit(ctx.columnFunction());
}
@Override
- public QueryTokenStream visitGeneralPathFragment(HqlParser.GeneralPathFragmentContext ctx) {
+ public QueryTokenStream visitGenericFunctionInvocation(HqlParser.GenericFunctionInvocationContext ctx) {
+ return visit(ctx.genericFunction());
+ }
- QueryRendererBuilder builder = QueryRenderer.builder();
+ @Override
+ public QueryTokenStream visitStandardFunction(HqlParser.StandardFunctionContext ctx) {
- builder.append(visit(ctx.simplePath()));
+ if (ctx.castFunction() != null) {
+ return visit(ctx.castFunction());
+ }
- if (ctx.indexedPathAccessFragment() != null) {
- builder.append(visit(ctx.indexedPathAccessFragment()));
+ if (ctx.treatedPath() != null) {
+ return visit(ctx.treatedPath());
}
- return builder;
- }
+ if (ctx.extractFunction() != null) {
+ return visit(ctx.extractFunction());
+ }
- @Override
- public QueryTokenStream visitIndexedPathAccessFragment(HqlParser.IndexedPathAccessFragmentContext ctx) {
+ if (ctx.truncFunction() != null) {
+ return visit(ctx.truncFunction());
+ }
- QueryRendererBuilder builder = QueryRenderer.builder();
+ if (ctx.formatFunction() != null) {
+ return visit(ctx.formatFunction());
+ }
- builder.append(TOKEN_OPEN_SQUARE_BRACKET);
- builder.appendInline(visit(ctx.expression()));
- builder.append(TOKEN_CLOSE_SQUARE_BRACKET);
+ if (ctx.collateFunction() != null) {
+ return visit(ctx.collateFunction());
+ }
- if (ctx.generalPathFragment() != null) {
+ if (ctx.substringFunction() != null) {
+ return visit(ctx.substringFunction());
+ }
- builder.append(TOKEN_DOT);
- builder.append(visit(ctx.generalPathFragment()));
+ if (ctx.overlayFunction() != null) {
+ return visit(ctx.overlayFunction());
}
- return builder;
- }
+ if (ctx.trimFunction() != null) {
+ return visit(ctx.trimFunction());
+ }
- @Override
- public QueryTokenStream visitSimplePath(HqlParser.SimplePathContext ctx) {
+ if (ctx.padFunction() != null) {
+ return visit(ctx.padFunction());
+ }
- QueryRendererBuilder builder = QueryRenderer.builder();
+ if (ctx.positionFunction() != null) {
+ return visit(ctx.positionFunction());
+ }
- builder.append(visit(ctx.identifier()));
+ if (ctx.currentDateFunction() != null) {
+ return visit(ctx.currentDateFunction());
+ }
- if (!ctx.simplePathElement().isEmpty()) {
- builder.append(TOKEN_DOT);
+ if (ctx.currentTimeFunction() != null) {
+ return visit(ctx.currentTimeFunction());
}
- builder.append(QueryTokenStream.concat(ctx.simplePathElement(), this::visit, TOKEN_DOT));
+ if (ctx.currentTimestampFunction() != null) {
+ return visit(ctx.currentTimestampFunction());
+ }
- return builder;
- }
+ if (ctx.instantFunction() != null) {
+ return visit(ctx.instantFunction());
+ }
- @Override
- public QueryTokenStream visitSimplePathElement(HqlParser.SimplePathElementContext ctx) {
+ if (ctx.localDateFunction() != null) {
+ return visit(ctx.localDateFunction());
+ }
- QueryRendererBuilder builder = QueryRenderer.builder();
+ if (ctx.localTimeFunction() != null) {
+ return visit(ctx.localTimeFunction());
+ }
- builder.append(visit(ctx.identifier()));
+ if (ctx.localDateTimeFunction() != null) {
+ return visit(ctx.localDateTimeFunction());
+ }
- return builder;
- }
+ if (ctx.offsetDateTimeFunction() != null) {
+ return visit(ctx.offsetDateTimeFunction());
+ }
- @Override
- public QueryTokenStream visitCaseList(HqlParser.CaseListContext ctx) {
+ if (ctx.cube() != null) {
+ return visit(ctx.cube());
+ }
- if (ctx.simpleCaseExpression() != null) {
- return visit(ctx.simpleCaseExpression());
- } else if (ctx.searchedCaseExpression() != null) {
- return visit(ctx.searchedCaseExpression());
- } else {
- return QueryTokenStream.empty();
+ if (ctx.rollup() != null) {
+ return visit(ctx.rollup());
}
+
+ return QueryTokenStream.empty();
}
@Override
- public QueryTokenStream visitSimpleCaseExpression(HqlParser.SimpleCaseExpressionContext ctx) {
+ public QueryTokenStream visitSubstringFunction(HqlParser.SubstringFunctionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
- builder.append(QueryTokens.expression(ctx.CASE()));
- builder.append(visit(ctx.expressionOrPredicate(0)));
+ builder.append(QueryTokens.token(ctx.SUBSTRING()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(visit(ctx.expression()));
- ctx.caseWhenExpressionClause().forEach(caseWhenExpressionClauseContext -> {
- builder.append(visit(caseWhenExpressionClauseContext));
- });
+ if (ctx.FROM() == null) {
+ builder.append(TOKEN_COMMA);
+ } else {
+ builder.append(QueryTokens.expression(ctx.FROM()));
+ }
- if (ctx.ELSE() != null) {
+ builder.append(visit(ctx.substringFunctionStartArgument()));
- builder.append(QueryTokens.expression(ctx.ELSE()));
- builder.append(visit(ctx.expressionOrPredicate(1)));
+ if (ctx.substringFunctionLengthArgument() != null) {
+ if (ctx.FOR() == null) {
+ builder.append(TOKEN_COMMA);
+ } else {
+ builder.append(QueryTokens.expression(ctx.FOR()));
+ }
+
+ builder.append(visit(ctx.substringFunctionLengthArgument()));
}
- builder.append(QueryTokens.expression(ctx.END()));
+ builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
@Override
- public QueryTokenStream visitSearchedCaseExpression(HqlParser.SearchedCaseExpressionContext ctx) {
+ public QueryTokenStream visitSubstringFunctionStartArgument(HqlParser.SubstringFunctionStartArgumentContext ctx) {
+ return visit(ctx.expression());
+ }
- QueryRendererBuilder builder = QueryRenderer.builder();
+ @Override
+ public QueryTokenStream visitSubstringFunctionLengthArgument(HqlParser.SubstringFunctionLengthArgumentContext ctx) {
+ return visit(ctx.expression());
+ }
- builder.append(QueryTokens.expression(ctx.CASE()));
+ @Override
+ public QueryTokenStream visitPadFunction(HqlParser.PadFunctionContext ctx) {
- builder.append(QueryTokenStream.concat(ctx.caseWhenPredicateClause(), this::visit, TOKEN_SPACE));
+ QueryRendererBuilder builder = QueryRenderer.builder();
- if (ctx.ELSE() != null) {
+ builder.append(QueryTokens.token(ctx.PAD()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(visit(ctx.expression()));
+ builder.append(QueryTokens.expression(ctx.WITH()));
+ builder.appendExpression(visit(ctx.padLength()));
- builder.append(QueryTokens.expression(ctx.ELSE()));
- builder.appendExpression(visit(ctx.expressionOrPredicate()));
+ if (ctx.padCharacter() != null) {
+ builder.appendExpression(visit(ctx.padSpecification()));
+ builder.appendInline(visit(ctx.padCharacter()));
+ } else {
+ builder.append(visit(ctx.padSpecification()));
}
- builder.append(QueryTokens.expression(ctx.END()));
+ builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
@Override
- public QueryTokenStream visitCaseWhenExpressionClause(HqlParser.CaseWhenExpressionClauseContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
+ public QueryTokenStream visitPadSpecification(HqlParser.PadSpecificationContext ctx) {
+ return QueryRendererBuilder.from(QueryTokens.token(ctx.LEADING() != null ? ctx.LEADING() : ctx.TRAILING()));
+ }
- builder.append(QueryTokens.expression(ctx.WHEN()));
- builder.appendExpression(visit(ctx.expression()));
- builder.append(QueryTokens.expression(ctx.THEN()));
- builder.appendExpression(visit(ctx.expressionOrPredicate()));
+ @Override
+ public QueryTokenStream visitPadCharacter(HqlParser.PadCharacterContext ctx) {
+ return visit(ctx.stringLiteral());
+ }
- return builder;
+ @Override
+ public QueryTokenStream visitPadLength(HqlParser.PadLengthContext ctx) {
+ return visit(ctx.expression());
}
@Override
- public QueryTokenStream visitCaseWhenPredicateClause(HqlParser.CaseWhenPredicateClauseContext ctx) {
+ public QueryTokenStream visitPositionFunction(HqlParser.PositionFunctionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
- builder.append(QueryTokens.expression(ctx.WHEN()));
- builder.appendExpression(visit(ctx.predicate()));
- builder.append(QueryTokens.expression(ctx.THEN()));
- builder.appendExpression(visit(ctx.expressionOrPredicate()));
+ builder.append(QueryTokens.token(ctx.POSITION()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(visit(ctx.positionFunctionPatternArgument()));
+ builder.append(QueryTokens.expression(ctx.IN()));
+ builder.appendInline(visit(ctx.positionFunctionStringArgument()));
+ builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
@Override
- public QueryTokenStream visitGenericFunction(HqlParser.GenericFunctionContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
- QueryRendererBuilder nested = QueryRenderer.builder();
+ public QueryTokenStream visitPositionFunctionPatternArgument(HqlParser.PositionFunctionPatternArgumentContext ctx) {
+ return visit(ctx.expression());
+ }
- nested.append(visit(ctx.functionName()));
- nested.append(TOKEN_OPEN_PAREN);
+ @Override
+ public QueryTokenStream visitPositionFunctionStringArgument(HqlParser.PositionFunctionStringArgumentContext ctx) {
+ return visit(ctx.expression());
+ }
+
+ @Override
+ public QueryTokenStream visitOverlayFunction(HqlParser.OverlayFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.token(ctx.OVERLAY()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(visit(ctx.overlayFunctionStringArgument()));
+ builder.append(QueryTokens.expression(ctx.PLACING()));
+ builder.append(visit(ctx.overlayFunctionReplacementArgument()));
+ builder.append(QueryTokens.expression(ctx.FROM()));
+ builder.append(visit(ctx.overlayFunctionStartArgument()));
+
+ if (ctx.overlayFunctionLengthArgument() != null) {
+ builder.append(QueryTokens.expression(ctx.FOR()));
+ builder.append(visit(ctx.overlayFunctionLengthArgument()));
+ }
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitOverlayFunctionStringArgument(HqlParser.OverlayFunctionStringArgumentContext ctx) {
+ return visit(ctx.expression());
+ }
+
+ @Override
+ public QueryTokenStream visitOverlayFunctionReplacementArgument(
+ HqlParser.OverlayFunctionReplacementArgumentContext ctx) {
+ return visit(ctx.expression());
+ }
+
+ @Override
+ public QueryTokenStream visitOverlayFunctionStartArgument(HqlParser.OverlayFunctionStartArgumentContext ctx) {
+ return visit(ctx.expression());
+ }
+
+ @Override
+ public QueryTokenStream visitOverlayFunctionLengthArgument(HqlParser.OverlayFunctionLengthArgumentContext ctx) {
+ return visit(ctx.expression());
+ }
+
+ @Override
+ public QueryTokenStream visitCurrentDateFunction(HqlParser.CurrentDateFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.CURRENT_DATE() != null) {
+ builder.append(QueryTokens.token(ctx.CURRENT_DATE()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
+ builder.append(QueryTokens.expression(ctx.CURRENT()));
+ builder.append(QueryTokens.expression(ctx.DATE()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitCurrentTimeFunction(HqlParser.CurrentTimeFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.CURRENT_TIME() != null) {
+ builder.append(QueryTokens.token(ctx.CURRENT_TIME()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
+ builder.append(QueryTokens.expression(ctx.CURRENT()));
+ builder.append(QueryTokens.expression(ctx.TIME()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitCurrentTimestampFunction(HqlParser.CurrentTimestampFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.CURRENT_TIMESTAMP() != null) {
+ builder.append(QueryTokens.token(ctx.CURRENT_TIMESTAMP()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
+ builder.append(QueryTokens.expression(ctx.CURRENT()));
+ builder.append(QueryTokens.expression(ctx.TIMESTAMP()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitInstantFunction(HqlParser.InstantFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.CURRENT_INSTANT() != null) {
+ builder.append(QueryTokens.token(ctx.CURRENT_INSTANT()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
+ builder.append(QueryTokens.expression(ctx.INSTANT()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitLocalDateTimeFunction(HqlParser.LocalDateTimeFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.LOCAL_DATETIME() != null) {
+ builder.append(QueryTokens.token(ctx.LOCAL_DATETIME()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
+ builder.append(QueryTokens.expression(ctx.LOCAL()));
+ builder.append(QueryTokens.expression(ctx.DATETIME()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitOffsetDateTimeFunction(HqlParser.OffsetDateTimeFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.OFFSET_DATETIME() != null) {
+ builder.append(QueryTokens.token(ctx.OFFSET_DATETIME()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
+ builder.append(QueryTokens.expression(ctx.OFFSET()));
+ builder.append(QueryTokens.expression(ctx.DATETIME()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitLocalDateFunction(HqlParser.LocalDateFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.LOCAL_DATE() != null) {
+ builder.append(QueryTokens.token(ctx.LOCAL_DATE()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
+ builder.append(QueryTokens.expression(ctx.LOCAL()));
+ builder.append(QueryTokens.expression(ctx.DATE()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitLocalTimeFunction(HqlParser.LocalTimeFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.LOCAL_TIME() != null) {
+ builder.append(QueryTokens.token(ctx.LOCAL_TIME()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
+ builder.append(QueryTokens.expression(ctx.LOCAL()));
+ builder.append(QueryTokens.expression(ctx.TIME()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitFormatFunction(HqlParser.FormatFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.token(ctx.FORMAT()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(visit(ctx.expression()));
+ builder.append(QueryTokens.expression(ctx.AS()));
+ builder.appendInline(visit(ctx.format()));
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitCollation(HqlParser.CollationContext ctx) {
+ return visit(ctx.simplePath());
+ }
+
+ @Override
+ public QueryTokenStream visitCollateFunction(HqlParser.CollateFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.token(ctx.COLLATE()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(visit(ctx.expression()));
+ builder.append(QueryTokens.expression(ctx.AS()));
+ builder.appendInline(visit(ctx.collation()));
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitCube(HqlParser.CubeContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.token(ctx.CUBE()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.appendInline(QueryTokenStream.concat(ctx.expressionOrPredicate(), this::visit, TOKEN_COMMA));
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitRollup(HqlParser.RollupContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.token(ctx.ROLLUP()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.appendInline(QueryTokenStream.concat(ctx.expressionOrPredicate(), this::visit, TOKEN_COMMA));
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitFormat(HqlParser.FormatContext ctx) {
+ return visit(ctx.stringLiteral());
+ }
+
+ @Override
+ public QueryTokenStream visitTruncFunction(HqlParser.TruncFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.TRUNC() != null) {
+ builder.append(QueryTokens.token(ctx.TRUNC()));
+ } else {
+ builder.append(QueryTokens.token(ctx.TRUNCATE()));
+ }
+
+ builder.append(TOKEN_OPEN_PAREN);
+
+ if (ctx.datetimeField() != null) {
+ builder.append(visit(ctx.expression(0)));
+ builder.append(TOKEN_COMMA);
+ builder.append(visit(ctx.datetimeField()));
+ } else {
+ builder.append(QueryTokenStream.concat(ctx.expression(), this::visit, TOKEN_COMMA));
+ }
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitJpaNonstandardFunction(HqlParser.JpaNonstandardFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.token(ctx.FUNCTION()));
+ builder.append(TOKEN_OPEN_PAREN);
+
+ QueryRendererBuilder nested = QueryRenderer.builder();
+ nested.appendInline(visit(ctx.jpaNonstandardFunctionName()));
+
+ if (ctx.castTarget() != null) {
+ nested.append(QueryTokens.expression(ctx.AS()));
+ nested.append(visit(ctx.castTarget()));
+ }
+
+ if (ctx.genericFunctionArguments() != null) {
+ nested.append(TOKEN_COMMA);
+ nested.appendInline(visit(ctx.genericFunctionArguments()));
+ }
+
+ builder.appendInline(nested);
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitJpaNonstandardFunctionName(HqlParser.JpaNonstandardFunctionNameContext ctx) {
+
+ if (ctx.identifier() != null) {
+ return visit(ctx.identifier());
+ }
+
+ return visit(ctx.stringLiteral());
+ }
+
+ @Override
+ public QueryTokenStream visitColumnFunction(HqlParser.ColumnFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.token(ctx.COLUMN()));
+ builder.append(TOKEN_OPEN_PAREN);
+
+ QueryRendererBuilder nested = QueryRenderer.builder();
+ nested.appendInline(visit(ctx.path()));
+ nested.append(TOKEN_DOT);
+ nested.appendExpression(visit(ctx.jpaNonstandardFunctionName()));
+
+ if (ctx.castTarget() != null) {
+ nested.append(QueryTokens.expression(ctx.AS()));
+ nested.appendExpression(visit(ctx.jpaNonstandardFunctionName()));
+ }
+
+ builder.appendInline(nested);
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitGenericFunctionName(HqlParser.GenericFunctionNameContext ctx) {
+ return visit(ctx.simplePath());
+ }
+
+ @Override
+ public QueryTokenStream visitGenericFunctionArguments(HqlParser.GenericFunctionArgumentsContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.DISTINCT() != null) {
+ builder.append(QueryTokens.expression(ctx.DISTINCT()));
+ }
+
+ if (ctx.datetimeField() != null) {
+ builder.append(visit(ctx.datetimeField()));
+ builder.append(TOKEN_COMMA);
+ }
+
+ builder.append(QueryTokenStream.concat(ctx.expressionOrPredicate(), this::visit, TOKEN_COMMA));
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitCollectionSizeFunction(HqlParser.CollectionSizeFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.token(ctx.SIZE()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.appendInline(visit(ctx.path()));
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitElementAggregateFunction(HqlParser.ElementAggregateFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.MAXELEMENT() != null || ctx.MINELEMENT() != null) {
+ builder.append(QueryTokens.token(ctx.MAXELEMENT() != null ? ctx.MAXELEMENT() : ctx.MINELEMENT()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(visit(ctx.path()));
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
+
+ if (ctx.MAX() != null) {
+ builder.append(QueryTokens.token(ctx.MAX()));
+ }
+ if (ctx.MIN() != null) {
+ builder.append(QueryTokens.token(ctx.MIN()));
+ }
+ if (ctx.SUM() != null) {
+ builder.append(QueryTokens.token(ctx.SUM()));
+ }
+ if (ctx.AVG() != null) {
+ builder.append(QueryTokens.token(ctx.AVG()));
+ }
+
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(visit(ctx.elementsValuesQuantifier()));
+ builder.append(TOKEN_OPEN_PAREN);
+
+ if (ctx.path() != null) {
+ builder.append(visit(ctx.path()));
+ }
+
+ builder.append(TOKEN_CLOSE_PAREN);
+ builder.append(TOKEN_CLOSE_PAREN);
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitIndexAggregateFunction(HqlParser.IndexAggregateFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.MAXINDEX() != null || ctx.MININDEX() != null) {
+ builder.append(QueryTokens.token(ctx.MAXINDEX() != null ? ctx.MAXINDEX() : ctx.MININDEX()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(visit(ctx.path()));
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
+
+ if (ctx.MAX() != null) {
+ builder.append(QueryTokens.token(ctx.MAX()));
+ }
+ if (ctx.MIN() != null) {
+ builder.append(QueryTokens.token(ctx.MIN()));
+ }
+ if (ctx.SUM() != null) {
+ builder.append(QueryTokens.token(ctx.SUM()));
+ }
+ if (ctx.AVG() != null) {
+ builder.append(QueryTokens.token(ctx.AVG()));
+ }
+
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(visit(ctx.indicesKeysQuantifier()));
+ builder.append(TOKEN_OPEN_PAREN);
+
+ if (ctx.path() != null) {
+ builder.append(visit(ctx.path()));
+ }
+
+ builder.append(TOKEN_CLOSE_PAREN);
+ builder.append(TOKEN_CLOSE_PAREN);
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitCollectionFunctionMisuse(HqlParser.CollectionFunctionMisuseContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(
+ visit(ctx.elementsValuesQuantifier() != null ? ctx.elementsValuesQuantifier() : ctx.indicesKeysQuantifier()));
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.append(visit(ctx.path()));
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitAggregateFunction(HqlParser.AggregateFunctionContext ctx) {
+
+ if (ctx.everyFunction() != null) {
+ return visit(ctx.everyFunction());
+ }
+
+ if (ctx.anyFunction() != null) {
+ return visit(ctx.anyFunction());
+ }
+
+ return visit(ctx.listaggFunction());
+ }
+
+ @Override
+ public QueryTokenStream visitEveryAllQuantifier(HqlParser.EveryAllQuantifierContext ctx) {
+
+ if (ctx.EVERY() != null) {
+ return QueryRenderer.from(QueryTokens.token(ctx.EVERY()));
+ }
+
+ return QueryRenderer.from(QueryTokens.token(ctx.ALL()));
+ }
+
+ @Override
+ public QueryTokenStream visitAnySomeQuantifier(HqlParser.AnySomeQuantifierContext ctx) {
+
+ if (ctx.ANY() != null) {
+ return QueryRenderer.from(QueryTokens.token(ctx.ANY()));
+ }
+
+ return QueryRenderer.from(QueryTokens.token(ctx.SOME()));
+ }
+
+ @Override
+ public QueryTokenStream visitListaggFunction(HqlParser.ListaggFunctionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.token(ctx.LISTAGG()));
+ builder.append(TOKEN_OPEN_PAREN);
+
+ QueryRendererBuilder nested = QueryRenderer.builder();
+
+ if (ctx.DISTINCT() != null) {
+ builder.append(QueryTokens.expression(ctx.DISTINCT()));
+ }
+
+ builder.appendInline(visit(ctx.expressionOrPredicate(0)));
+ builder.append(TOKEN_COMMA);
+ builder.appendInline(visit(ctx.expressionOrPredicate(1)));
+
+ if (ctx.onOverflowClause() != null) {
+ builder.appendExpression(visit(ctx.onOverflowClause()));
+ }
+
+ builder.appendInline(nested);
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ if (ctx.withinGroupClause() != null) {
+ builder.appendExpression(visit(ctx.withinGroupClause()));
+ }
+
+ if (ctx.filterClause() != null) {
+ builder.appendExpression(visit(ctx.filterClause()));
+ }
+
+ if (ctx.overClause() != null) {
+ builder.appendExpression(visit(ctx.overClause()));
+ }
+
+ return builder;
+ }
- if (ctx.functionArguments() != null) {
- nested.appendInline(visit(ctx.functionArguments()));
- } else if (ctx.ASTERISK() != null) {
- nested.append(QueryTokens.token(ctx.ASTERISK()));
+ @Override
+ public QueryTokenStream visitOnOverflowClause(HqlParser.OnOverflowClauseContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.expression(ctx.ON()));
+ builder.append(QueryTokens.expression(ctx.OVERFLOW()));
+
+ if (ctx.ERROR() != null) {
+ builder.append(QueryTokens.expression(ctx.ERROR()));
+ } else {
+
+ builder.append(QueryTokens.expression(ctx.TRUNCATE()));
+
+ if (ctx.expression() != null) {
+ builder.appendExpression(visit(ctx.expression()));
+ }
+
+ if (ctx.WITH() != null) {
+ builder.append(QueryTokens.expression(ctx.WITH()));
+ }
+
+ if (ctx.WITHOUT() != null) {
+ builder.append(QueryTokens.expression(ctx.WITHOUT()));
+ }
+
+ if (ctx.COUNT() != null) {
+ builder.append(QueryTokens.expression(ctx.COUNT()));
+ }
}
- nested.append(TOKEN_CLOSE_PAREN);
+ return builder;
+ }
- builder.append(nested);
+ @Override
+ public QueryTokenStream visitWithinGroupClause(HqlParser.WithinGroupClauseContext ctx) {
- if (ctx.pathContinutation() != null) {
- builder.appendInline(visit(ctx.pathContinutation()));
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.expression(ctx.WITHIN()));
+ builder.append(QueryTokens.expression(ctx.GROUP()));
+
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.appendInline(visit(ctx.orderByClause()));
+ builder.append(TOKEN_CLOSE_PAREN);
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitNullsClause(HqlParser.NullsClauseContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.IGNORE() != null) {
+ builder.append(QueryTokens.expression(ctx.IGNORE()));
+ } else {
+ builder.append(QueryTokens.expression(ctx.RESPECT()));
}
- if (ctx.filterClause() != null) {
- builder.appendExpression(visit(ctx.filterClause()));
+ builder.append(QueryTokens.expression(ctx.NULLS()));
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitNthSideClause(HqlParser.NthSideClauseContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.expression(ctx.FROM()));
+
+ if (ctx.FIRST() != null) {
+ builder.append(QueryTokens.expression(ctx.FIRST()));
+ } else {
+ builder.append(QueryTokens.expression(ctx.LAST()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitFrameStart(HqlParser.FrameStartContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.CURRENT() != null) {
+
+ builder.append(QueryTokens.expression(ctx.CURRENT()));
+ builder.append(QueryTokens.expression(ctx.ROW()));
+ } else if (ctx.UNBOUNDED() != null) {
+ builder.append(QueryTokens.expression(ctx.UNBOUNDED()));
+ builder.append(QueryTokens.expression(ctx.PRECEDING()));
+ } else {
+
+ builder.appendExpression(visit(ctx.expression()));
+ builder.append(QueryTokens.expression(ctx.PRECEDING() != null ? ctx.PRECEDING() : ctx.FOLLOWING()));
}
- if (ctx.withinGroup() != null) {
- builder.appendExpression(visit(ctx.withinGroup()));
+ return builder;
+
+ }
+
+ @Override
+ public QueryTokenStream visitFrameEnd(HqlParser.FrameEndContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.CURRENT() != null) {
+
+ builder.append(QueryTokens.expression(ctx.CURRENT()));
+ builder.append(QueryTokens.expression(ctx.ROW()));
+ } else if (ctx.UNBOUNDED() != null) {
+ builder.append(QueryTokens.expression(ctx.UNBOUNDED()));
+ builder.append(QueryTokens.expression(ctx.FOLLOWING()));
+ } else {
+
+ builder.appendExpression(visit(ctx.expression()));
+ builder.append(QueryTokens.expression(ctx.PRECEDING() != null ? ctx.PRECEDING() : ctx.FOLLOWING()));
}
- if (ctx.overClause() != null) {
- builder.appendExpression(visit(ctx.overClause()));
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitFrameExclusion(HqlParser.FrameExclusionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.expression(ctx.EXCLUDE()));
+
+ if (ctx.CURRENT() != null) {
+ builder.append(QueryTokens.expression(ctx.CURRENT()));
+ builder.append(QueryTokens.expression(ctx.ROW()));
+ } else if (ctx.GROUP() != null) {
+ builder.append(QueryTokens.expression(ctx.GROUP()));
+ } else if (ctx.TIES() != null) {
+ builder.append(QueryTokens.expression(ctx.TIES()));
+ } else {
+ builder.append(QueryTokens.expression(ctx.NO()));
+ builder.append(QueryTokens.expression(ctx.OTHERS()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitCollectionQuantifier(HqlParser.CollectionQuantifierContext ctx) {
+
+ if (ctx.elementsValuesQuantifier() != null) {
+ return visit(ctx.elementsValuesQuantifier());
+ }
+
+ return visit(ctx.indicesKeysQuantifier());
+ }
+
+ @Override
+ public QueryTokenStream visitElementsValuesQuantifier(HqlParser.ElementsValuesQuantifierContext ctx) {
+ return QueryRenderer.from(QueryTokens.token(ctx.ELEMENTS() != null ? ctx.ELEMENTS() : ctx.VALUES()));
+ }
+
+ @Override
+ public QueryTokenStream visitIndicesKeysQuantifier(HqlParser.IndicesKeysQuantifierContext ctx) {
+ return QueryRenderer.from(QueryTokens.token(ctx.INDICES() != null ? ctx.INDICES() : ctx.KEYS()));
+ }
+
+ @Override
+ public QueryTokenStream visitGeneralPathExpression(HqlParser.GeneralPathExpressionContext ctx) {
+ return visit(ctx.generalPathFragment());
+ }
+
+ @Override
+ public QueryTokenStream visitPath(HqlParser.PathContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.treatedPath() != null) {
+
+ builder.append(visit(ctx.treatedPath()));
+
+ if (ctx.pathContinutation() != null) {
+ builder.append(visit(ctx.pathContinutation()));
+ }
+ } else if (ctx.generalPathFragment() != null) {
+ builder.append(visit(ctx.generalPathFragment()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitGeneralPathFragment(HqlParser.GeneralPathFragmentContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(visit(ctx.simplePath()));
+
+ if (ctx.indexedPathAccessFragment() != null) {
+ builder.append(visit(ctx.indexedPathAccessFragment()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitIndexedPathAccessFragment(HqlParser.IndexedPathAccessFragmentContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(TOKEN_OPEN_SQUARE_BRACKET);
+ builder.appendInline(visit(ctx.expression()));
+ builder.append(TOKEN_CLOSE_SQUARE_BRACKET);
+
+ if (ctx.generalPathFragment() != null) {
+
+ builder.append(TOKEN_DOT);
+ builder.append(visit(ctx.generalPathFragment()));
+ }
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitSimplePath(HqlParser.SimplePathContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(visit(ctx.identifier()));
+
+ if (!ctx.simplePathElement().isEmpty()) {
+ builder.append(TOKEN_DOT);
+ }
+
+ builder.append(QueryTokenStream.concat(ctx.simplePathElement(), this::visit, TOKEN_DOT));
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitSimplePathElement(HqlParser.SimplePathElementContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(visit(ctx.identifier()));
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitCaseList(HqlParser.CaseListContext ctx) {
+
+ if (ctx.simpleCaseExpression() != null) {
+ return visit(ctx.simpleCaseExpression());
+ } else if (ctx.searchedCaseExpression() != null) {
+ return visit(ctx.searchedCaseExpression());
+ } else {
+ return QueryTokenStream.empty();
+ }
+ }
+
+ @Override
+ public QueryTokenStream visitSimpleCaseExpression(HqlParser.SimpleCaseExpressionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.expression(ctx.CASE()));
+ builder.append(visit(ctx.expressionOrPredicate(0)));
+
+ ctx.caseWhenExpressionClause().forEach(caseWhenExpressionClauseContext -> {
+ builder.append(visit(caseWhenExpressionClauseContext));
+ });
+
+ if (ctx.ELSE() != null) {
+
+ builder.append(QueryTokens.expression(ctx.ELSE()));
+ builder.append(visit(ctx.expressionOrPredicate(1)));
+ }
+
+ builder.append(QueryTokens.expression(ctx.END()));
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitSearchedCaseExpression(HqlParser.SearchedCaseExpressionContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.expression(ctx.CASE()));
+
+ builder.append(QueryTokenStream.concat(ctx.caseWhenPredicateClause(), this::visit, TOKEN_SPACE));
+
+ if (ctx.ELSE() != null) {
+
+ builder.append(QueryTokens.expression(ctx.ELSE()));
+ builder.appendExpression(visit(ctx.expressionOrPredicate()));
}
+ builder.append(QueryTokens.expression(ctx.END()));
+
+ return builder;
+ }
+
+ @Override
+ public QueryTokenStream visitCaseWhenExpressionClause(HqlParser.CaseWhenExpressionClauseContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ builder.append(QueryTokens.expression(ctx.WHEN()));
+ builder.appendExpression(visit(ctx.expression()));
+ builder.append(QueryTokens.expression(ctx.THEN()));
+ builder.appendExpression(visit(ctx.expressionOrPredicate()));
+
return builder;
}
@Override
- public QueryTokenStream visitFunctionWithSubquery(HqlParser.FunctionWithSubqueryContext ctx) {
+ public QueryTokenStream visitCaseWhenPredicateClause(HqlParser.CaseWhenPredicateClauseContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
- builder.appendExpression(visit(ctx.functionName()));
- builder.append(TOKEN_OPEN_PAREN);
- builder.appendInline(visit(ctx.subquery()));
- builder.append(TOKEN_CLOSE_PAREN);
+ builder.append(QueryTokens.expression(ctx.WHEN()));
+ builder.appendExpression(visit(ctx.predicate()));
+ builder.append(QueryTokens.expression(ctx.THEN()));
+ builder.appendExpression(visit(ctx.expressionOrPredicate()));
return builder;
}
@Override
- public QueryTokenStream visitCastFunctionInvocation(HqlParser.CastFunctionInvocationContext ctx) {
- return visit(ctx.castFunction());
- }
+ public QueryTokenStream visitGenericFunction(HqlParser.GenericFunctionContext ctx) {
- @Override
- public QueryTokenStream visitExtractFunctionInvocation(HqlParser.ExtractFunctionInvocationContext ctx) {
- return visit(ctx.extractFunction());
- }
+ QueryRendererBuilder builder = QueryRenderer.builder();
+ QueryRendererBuilder nested = QueryRenderer.builder();
- @Override
- public QueryTokenStream visitTrimFunctionInvocation(HqlParser.TrimFunctionInvocationContext ctx) {
- return visit(ctx.trimFunction());
- }
+ nested.append(visit(ctx.genericFunctionName()));
+ nested.append(TOKEN_OPEN_PAREN);
- @Override
- public QueryTokenStream visitEveryFunctionInvocation(HqlParser.EveryFunctionInvocationContext ctx) {
- return visit(ctx.everyFunction());
- }
+ if (ctx.genericFunctionArguments() != null) {
+ nested.appendInline(visit(ctx.genericFunctionArguments()));
+ } else if (ctx.ASTERISK() != null) {
+ nested.append(QueryTokens.token(ctx.ASTERISK()));
+ }
- @Override
- public QueryTokenStream visitAnyFunctionInvocation(HqlParser.AnyFunctionInvocationContext ctx) {
- return visit(ctx.anyFunction());
- }
+ nested.append(TOKEN_CLOSE_PAREN);
+ builder.append(nested);
- @Override
- public QueryTokenStream visitTreatedPathInvocation(HqlParser.TreatedPathInvocationContext ctx) {
- return visit(ctx.treatedPath());
- }
+ if (ctx.pathContinutation() != null) {
+ builder.append(visit(ctx.pathContinutation()));
+ }
- @Override
- public QueryTokenStream visitFunctionArguments(HqlParser.FunctionArgumentsContext ctx) {
+ if (ctx.nthSideClause() != null) {
+ builder.appendExpression(visit(ctx.nthSideClause()));
+ }
- QueryRendererBuilder builder = QueryRenderer.builder();
+ if (ctx.nullsClause() != null) {
+ builder.appendExpression(visit(ctx.nullsClause()));
+ }
- if (ctx.DISTINCT() != null) {
- builder.append(QueryTokens.expression(ctx.DISTINCT()));
+ if (ctx.withinGroupClause() != null) {
+ builder.appendExpression(visit(ctx.withinGroupClause()));
}
- builder.append(QueryTokenStream.concat(ctx.expressionOrPredicate(), this::visit, TOKEN_COMMA));
+ if (ctx.filterClause() != null) {
+ builder.appendExpression(visit(ctx.filterClause()));
+ }
+
+ if (ctx.overClause() != null) {
+ builder.appendExpression(visit(ctx.overClause()));
+ }
return builder;
}
@@ -1587,20 +2589,6 @@ public QueryTokenStream visitFilterClause(HqlParser.FilterClauseContext ctx) {
return builder;
}
- @Override
- public QueryTokenStream visitWithinGroup(HqlParser.WithinGroupContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.append(QueryTokens.expression(ctx.WITHIN()));
- builder.append(QueryTokens.expression(ctx.GROUP()));
- builder.append(TOKEN_OPEN_PAREN);
- builder.appendInline(visit(ctx.orderByClause()));
- builder.append(TOKEN_CLOSE_PAREN);
-
- return builder;
- }
-
@Override
public QueryTokenStream visitOverClause(HqlParser.OverClauseContext ctx) {
@@ -1677,140 +2665,6 @@ public QueryTokenStream visitFrameClause(HqlParser.FrameClauseContext ctx) {
return builder;
}
- @Override
- public QueryTokenStream visitUnboundedPrecedingFrameStart(HqlParser.UnboundedPrecedingFrameStartContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.append(QueryTokens.expression(ctx.UNBOUNDED()));
- builder.append(QueryTokens.expression(ctx.PRECEDING()));
-
- return builder;
- }
-
- @Override
- public QueryTokenStream visitExpressionPrecedingFrameStart(HqlParser.ExpressionPrecedingFrameStartContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.appendExpression(visit(ctx.expression()));
- builder.append(QueryTokens.expression(ctx.PRECEDING()));
-
- return builder;
- }
-
- @Override
- public QueryTokenStream visitCurrentRowFrameStart(HqlParser.CurrentRowFrameStartContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.append(QueryTokens.expression(ctx.CURRENT()));
- builder.append(QueryTokens.expression(ctx.ROW()));
-
- return builder;
- }
-
- @Override
- public QueryTokenStream visitExpressionFollowingFrameStart(HqlParser.ExpressionFollowingFrameStartContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.appendExpression(visit(ctx.expression()));
- builder.append(QueryTokens.expression(ctx.FOLLOWING()));
-
- return builder;
- }
-
- @Override
- public QueryTokenStream visitCurrentRowFrameExclusion(HqlParser.CurrentRowFrameExclusionContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.append(QueryTokens.expression(ctx.EXCLUDE()));
- builder.append(QueryTokens.expression(ctx.CURRENT()));
- builder.append(QueryTokens.expression(ctx.ROW()));
-
- return builder;
- }
-
- @Override
- public QueryTokenStream visitGroupFrameExclusion(HqlParser.GroupFrameExclusionContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.append(QueryTokens.expression(ctx.EXCLUDE()));
- builder.append(QueryTokens.expression(ctx.GROUP()));
-
- return builder;
- }
-
- @Override
- public QueryTokenStream visitTiesFrameExclusion(HqlParser.TiesFrameExclusionContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.append(QueryTokens.expression(ctx.EXCLUDE()));
- builder.append(QueryTokens.expression(ctx.TIES()));
-
- return builder;
- }
-
- @Override
- public QueryTokenStream visitNoOthersFrameExclusion(HqlParser.NoOthersFrameExclusionContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.append(QueryTokens.expression(ctx.EXCLUDE()));
- builder.append(QueryTokens.expression(ctx.NO()));
- builder.append(QueryTokens.expression(ctx.OTHERS()));
-
- return builder;
- }
-
- @Override
- public QueryTokenStream visitExpressionPrecedingFrameEnd(HqlParser.ExpressionPrecedingFrameEndContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.appendExpression(visit(ctx.expression()));
- builder.append(QueryTokens.expression(ctx.PRECEDING()));
-
- return builder;
- }
-
- @Override
- public QueryTokenStream visitCurrentRowFrameEnd(HqlParser.CurrentRowFrameEndContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.append(QueryTokens.expression(ctx.CURRENT()));
- builder.append(QueryTokens.expression(ctx.ROW()));
-
- return builder;
- }
-
- @Override
- public QueryTokenStream visitExpressionFollowingFrameEnd(HqlParser.ExpressionFollowingFrameEndContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.appendExpression(visit(ctx.expression()));
- builder.append(QueryTokens.expression(ctx.FOLLOWING()));
-
- return builder;
- }
-
- @Override
- public QueryTokenStream visitUnboundedFollowingFrameEnd(HqlParser.UnboundedFollowingFrameEndContext ctx) {
-
- QueryRendererBuilder builder = QueryRenderer.builder();
-
- builder.append(QueryTokens.expression(ctx.UNBOUNDED()));
- builder.append(QueryTokens.expression(ctx.FOLLOWING()));
-
- return builder;
- }
-
@Override
public QueryTokenStream visitCastFunction(HqlParser.CastFunctionContext ctx) {
@@ -1876,23 +2730,49 @@ public QueryTokenStream visitExtractFunction(HqlParser.ExtractFunctionContext ct
QueryRendererBuilder nested = QueryRenderer.builder();
- nested.appendExpression(visit(ctx.expression(0)));
+ nested.appendExpression(visit(ctx.extractField()));
nested.append(QueryTokens.expression(ctx.FROM()));
- nested.append(visit(ctx.expression(1)));
+ nested.append(visit(ctx.expression()));
builder.appendInline(nested);
builder.append(TOKEN_CLOSE_PAREN);
- } else if (ctx.dateTimeFunction() != null) {
+ } else if (ctx.datetimeField() != null) {
- builder.append(visit(ctx.dateTimeFunction()));
+ builder.append(visit(ctx.datetimeField()));
builder.append(TOKEN_OPEN_PAREN);
- builder.appendInline(visit(ctx.expression(0)));
+ builder.appendInline(visit(ctx.expression()));
builder.append(TOKEN_CLOSE_PAREN);
}
return builder;
}
+ @Override
+ public QueryTokenStream visitExtractField(HqlParser.ExtractFieldContext ctx) {
+
+ if (ctx.datetimeField() != null) {
+ return visit(ctx.datetimeField());
+ }
+
+ if (ctx.dayField() != null) {
+ return visit(ctx.dayField());
+ }
+
+ if (ctx.weekField() != null) {
+ return visit(ctx.weekField());
+ }
+
+ if (ctx.timeZoneField() != null) {
+ return visit(ctx.timeZoneField());
+ }
+
+ if (ctx.dateOrTimeField() != null) {
+ return visit(ctx.dateOrTimeField());
+ }
+
+ return QueryRenderer.builder();
+ }
+
@Override
public QueryTokenStream visitTrimFunction(HqlParser.TrimFunctionContext ctx) {
@@ -1901,31 +2781,51 @@ public QueryTokenStream visitTrimFunction(HqlParser.TrimFunctionContext ctx) {
builder.append(QueryTokens.token(ctx.TRIM()));
builder.append(TOKEN_OPEN_PAREN);
- if (ctx.LEADING() != null) {
- builder.append(QueryTokens.expression(ctx.LEADING()));
- } else if (ctx.TRAILING() != null) {
- builder.append(QueryTokens.expression(ctx.TRAILING()));
- } else if (ctx.BOTH() != null) {
- builder.append(QueryTokens.expression(ctx.BOTH()));
+ if (ctx.trimSpecification() != null) {
+ builder.appendExpression(visit(ctx.trimSpecification()));
}
- if (ctx.stringLiteral() != null) {
- builder.append(visit(ctx.stringLiteral()));
+ if (ctx.trimCharacter() != null) {
+ builder.appendExpression(visit(ctx.trimCharacter()));
}
if (ctx.FROM() != null) {
builder.append(QueryTokens.expression(ctx.FROM()));
}
- builder.appendInline(visit(ctx.expression()));
+ if (ctx.expression() != null) {
+ builder.append(visit(ctx.expression()));
+ }
+
builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
@Override
- public QueryTokenStream visitDateTimeFunction(HqlParser.DateTimeFunctionContext ctx) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.d));
+ public QueryTokenStream visitTrimSpecification(HqlParser.TrimSpecificationContext ctx) {
+
+ QueryRendererBuilder builder = QueryRenderer.builder();
+
+ if (ctx.BOTH() != null) {
+ builder.append(QueryTokens.expression(ctx.BOTH()));
+ } else if (ctx.LEADING() != null) {
+ builder.append(QueryTokens.expression(ctx.LEADING()));
+ } else if (ctx.TRAILING() != null) {
+ builder.append(QueryTokens.expression(ctx.TRAILING()));
+ }
+
+ return builder.build();
+ }
+
+ @Override
+ public QueryTokenStream visitTrimCharacter(HqlParser.TrimCharacterContext ctx) {
+
+ if (ctx.stringLiteral() != null) {
+ return visit(ctx.stringLiteral());
+ }
+
+ return visit(ctx.parameter());
}
@Override
@@ -1933,25 +2833,32 @@ public QueryTokenStream visitEveryFunction(HqlParser.EveryFunctionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
- builder.append(QueryTokens.expression(ctx.every));
+ builder.appendExpression(visit(ctx.everyAllQuantifier()));
- if (ctx.ELEMENTS() != null) {
- builder.append(QueryTokens.expression(ctx.ELEMENTS()));
- } else if (ctx.INDICES() != null) {
- builder.append(QueryTokens.expression(ctx.INDICES()));
- }
+ if (ctx.predicate() != null) {
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.appendInline(visit(ctx.predicate()));
+ builder.append(TOKEN_CLOSE_PAREN);
- builder.append(TOKEN_OPEN_PAREN);
+ if (ctx.filterClause() != null) {
+ builder.appendExpression(visit(ctx.filterClause()));
+ }
- if (ctx.predicate() != null) {
- builder.append(visit(ctx.predicate()));
+ if (ctx.overClause() != null) {
+ builder.appendExpression(visit(ctx.overClause()));
+ }
} else if (ctx.subquery() != null) {
- builder.append(visit(ctx.subquery()));
- } else if (ctx.simplePath() != null) {
- builder.append(visit(ctx.simplePath()));
- }
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.appendInline(visit(ctx.subquery()));
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
- builder.append(TOKEN_CLOSE_PAREN);
+ builder.appendExpression(visit(ctx.collectionQuantifier()));
+
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.appendInline(visit(ctx.simplePath()));
+ builder.append(TOKEN_CLOSE_PAREN);
+ }
return builder;
}
@@ -1961,25 +2868,32 @@ public QueryTokenStream visitAnyFunction(HqlParser.AnyFunctionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
- builder.append(QueryTokens.expression(ctx.any));
+ builder.appendExpression(visit(ctx.anySomeQuantifier()));
- if (ctx.ELEMENTS() != null) {
- builder.append(QueryTokens.expression(ctx.ELEMENTS()));
- } else if (ctx.INDICES() != null) {
- builder.append(QueryTokens.expression(ctx.INDICES()));
- }
+ if (ctx.predicate() != null) {
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.appendInline(visit(ctx.predicate()));
+ builder.append(TOKEN_CLOSE_PAREN);
- builder.append(TOKEN_OPEN_PAREN);
+ if (ctx.filterClause() != null) {
+ builder.appendExpression(visit(ctx.filterClause()));
+ }
- if (ctx.predicate() != null) {
- builder.append(visit(ctx.predicate()));
+ if (ctx.overClause() != null) {
+ builder.appendExpression(visit(ctx.overClause()));
+ }
} else if (ctx.subquery() != null) {
- builder.append(visit(ctx.subquery()));
- } else if (ctx.simplePath() != null) {
- builder.append(visit(ctx.simplePath()));
- }
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.appendInline(visit(ctx.subquery()));
+ builder.append(TOKEN_CLOSE_PAREN);
+ } else {
- builder.append(TOKEN_CLOSE_PAREN);
+ builder.appendExpression(visit(ctx.collectionQuantifier()));
+
+ builder.append(TOKEN_OPEN_PAREN);
+ builder.appendInline(visit(ctx.simplePath()));
+ builder.append(TOKEN_CLOSE_PAREN);
+ }
return builder;
}
@@ -2464,16 +3378,6 @@ public QueryTokenStream visitIdentifier(HqlParser.IdentifierContext ctx) {
}
}
- @Override
- public QueryTokenStream visitCharacter(HqlParser.CharacterContext ctx) {
- return QueryRendererBuilder.from(QueryTokens.expression(ctx.CHARACTER()));
- }
-
- @Override
- public QueryTokenStream visitFunctionName(HqlParser.FunctionNameContext ctx) {
- return QueryTokenStream.concat(ctx.reservedWord(), this::visit, TOKEN_DOT);
- }
-
@Override
public QueryTokenStream visitReservedWord(HqlParser.ReservedWordContext ctx) {
diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRenderer.java
index 1bdde97beb..87fe53e050 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRenderer.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRenderer.java
@@ -38,6 +38,7 @@
*
*
* @author Mark Paluch
+ * @author Christoph Strobl
*/
abstract class QueryRenderer implements QueryTokenStream {
@@ -243,7 +244,7 @@ String render() {
for (QueryRenderer queryRenderer : nested) {
if (lastAppended != null && (lastExpression || queryRenderer.isExpression()) && !builder.isEmpty()
- && !lastAppended.endsWith(" ")) {
+ && (!lastAppended.endsWith(" ") && !lastAppended.endsWith("("))) {
builder.append(' ');
}
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java
index 8a23e279cd..99547994e1 100644
--- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java
@@ -37,6 +37,7 @@
*
* @author Greg Turnquist
* @author Christoph Strobl
+ * @author Mark Paluch
* @since 3.1
*/
class HqlQueryRendererTests {
@@ -1551,9 +1552,14 @@ void castFunctionWithFqdnShouldWork() {
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));
+ 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
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java
index 482b02db47..40aa7d274b 100644
--- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java
@@ -1090,7 +1090,7 @@ void aliasesShouldNotOverlapWithSortProperties() {
"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 @@ void createsCountQueryUsingAliasCorrectly() {
"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
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java
index 62efc2fdc2..80483a05b9 100644
--- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java
@@ -21,6 +21,8 @@
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 +33,8 @@
* IMPORTANT: Purely verifies the parser without any transformations.
*
* @author Greg Turnquist
+ * @author Mark Paluch
+ * @author Christoph Strobl
* @since 3.1
*/
class HqlSpecificationTests {
@@ -331,6 +335,213 @@ OR TREAT(e AS Contractor).hours > 100
""");
}
+ @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 %s
+ """.formatted(nullHandling));
+ }
+
+ @Test // GH-3689
+ void size() {
+
+ assertQuery("""
+ SELECT e FROM Employee e
+ WHERE SIZE(x) > 1
+ """);
+
+ assertQuery("""
+ SELECT e FROM Employee e
+ WHERE SIZE(e.skills) > 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')
+ """);
+
+ assertQuery("""
+ SELECT e FROM Employee e
+ WHERE TRUNC(e, 'YEAR') = TRUNCATE(LOCAL DATETIME, 'YEAR')
+ """);
+ }
+
+ @ParameterizedTest // GH-3689
+ @ValueSource(strings = { "YEAR", "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 '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')
+ """);
+ }
+
+ @Test // GH-3689
+ void collate() {
+
+ assertQuery("""
+ SELECT e FROM Employee e
+ WHERE COLLATE(x AS ucs_basic) = COLLATE(e.name AS ucs_basic)
+ """);
+ }
+
+ @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, 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
+ 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 ");
+
+ assertQuery("select POSITION(c.number IN 'foo') + 1 AS pos " + //
+ "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 ");
+
+ assertQuery("select OFFSET DATETIME AS offsetDatetime, OFFSET_DATETIME() AS offset_datetime " + //
+ "from Call c ");
+ }
+
+ @Test // GH-3689
+ void cube() {
+
+ 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
+ void rollup() {
+
+ assertQuery("select ROLLUP(foo), ROLLUP(foo, bar) " + //
+ "from Call c ");
+
+ assertQuery("select c.callerId from Call c GROUP BY ROLLUP(state, province)");
+ }
+
@Test
void pathExpressionsNamedParametersExample() {
@@ -383,6 +594,80 @@ WHERE EXISTS (SELECT spouseEmp
""");
}
+ @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() {
@@ -461,16 +746,13 @@ WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE
""");
}
- @Test // GH-3628
- void functionInvocationWithIsBoolean() {
-
- assertQuery("""
- from RoleTmpl where find_in_set(:appId, appIds) is true
- """);
+ @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 false
- """);
+ from RoleTmpl where find_in_set(:appId, appIds) %s
+ """.formatted(booleanComparison));
}
@Test
@@ -845,20 +1127,36 @@ void booleanPredicate() {
""");
}
- @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
@@ -983,6 +1281,29 @@ void theRest38() {
""");
}
+ @Test // GH-3689
+ void insertQueries() {
+
+ assertQuery("insert Person (id, name) values (100L, 'Jane Doe')");
+
+ assertQuery("insert Person (id, name) values " + //
+ "(101L, 'J A Doe III'), " + //
+ "(102L, 'J X Doe'), " + //
+ "(103L, 'John Doe, Jr')");
+
+ assertQuery("insert into Partner (id, name) " + //
+ "select p.id, p.name from Person p ");
+
+ assertQuery("INSERT INTO AggregationPrice (range, price, type) " + "VALUES (:range, :price, :priceType) "
+ + "ON CONFLICT (range) DO UPDATE SET price = :price, type = :priceType");
+
+ assertQuery("INSERT INTO AggregationPrice (range, price, type) " + "VALUES (:range, :price, :priceType) "
+ + "ON CONFLICT ON CONSTRAINT foo DO UPDATE SET price = :price, type = :priceType");
+
+ assertQuery("INSERT INTO AggregationPrice (range, price, type) " + "VALUES (:range, :price, :priceType) "
+ + "ON CONFLICT ON CONSTRAINT foo DO NOTHING");
+ }
+
@Test
void hqlQueries() {
@@ -1000,15 +1321,7 @@ void hqlQueries() {
assertQuery("update versioned Person " + //
"set name = :newName " + //
"where name = :oldName");
- assertQuery("insert Person (id, name) " + //
- "values (100L, 'Jane Doe')");
- assertQuery("insert Person (id, name) " + //
- "values (101L, 'J A Doe III'), " + //
- "(102L, 'J X Doe'), " + //
- "(103L, 'John Doe, Jr')");
- assertQuery("insert into Partner (id, name) " + //
- "select p.id, p.name " + //
- "from Person p ");
+
assertQuery("select p " + //
"from Person p " + //
"where p.name like 'Joe'");
@@ -1104,9 +1417,6 @@ void hqlQueries() {
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) " + //
@@ -1315,7 +1625,7 @@ void hqlQueries() {
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 " + //
@@ -1435,9 +1745,6 @@ void hqlQueries() {
"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) " + //