Polishing.

Align HQL parsing with Hibernate to accept generic function names. Also, align literals, add missing expression grammar for ID, NATURALID, VERSION, TYPE, FK and remove parser rules interfering with undesired parsing results. Add missing literals.

See #3711
This commit is contained in:
Mark Paluch
2024-12-11 16:34:52 +01:00
parent ff6d2af900
commit e0bd821476
5 changed files with 795 additions and 157 deletions

View File

@@ -25,7 +25,9 @@ grammar Hql;
* management of complex rules in the generated Visitor. Finally, there are labels applied to rule elements (op=('+'|'-')
* to simplify the processing.
*
* @author Greg Turnquist, Yannick Brandt
* @author Greg Turnquist
* @author Mark Paluch
* @author Yannick Brandt
* @since 3.1
*/
}
@@ -307,9 +309,10 @@ setOperator
// Literals
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-literals
literal
: NULL
: STRING_LITERAL
| JAVA_STRING_LITERAL
| NULL
| booleanLiteral
| stringLiteral
| numericLiteral
| dateTimeLiteral
| binaryLiteral
@@ -321,19 +324,16 @@ booleanLiteral
| FALSE
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-string-literals
stringLiteral
: STRINGLITERAL
| JAVASTRINGLITERAL
| CHARACTER
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-numeric-literals
numericLiteral
: INTEGER_LITERAL
| FLOAT_LITERAL
| HEXLITERAL
;
: INTEGER_LITERAL
| LONG_LITERAL
| BIG_INTEGER_LITERAL
| FLOAT_LITERAL
| DOUBLE_LITERAL
| BIG_DECIMAL_LITERAL
| HEX_LITERAL
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-datetime-literals
dateTimeLiteral
@@ -399,7 +399,7 @@ dateOrTimeField
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-binary-literals
binaryLiteral
: BINARY_LITERAL
| '{' HEXLITERAL (',' HEXLITERAL)* '}'
| '{' HEX_LITERAL (',' HEX_LITERAL)* '}'
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-enum-literals
@@ -433,19 +433,29 @@ expression
;
primaryExpression
: caseList # CaseExpression
| literal # LiteralExpression
| parameter # ParameterExpression
| function # FunctionExpression
| generalPathFragment # GeneralPathExpression
: caseList # CaseExpression
| literal # LiteralExpression
| parameter # ParameterExpression
| entityTypeReference # EntityTypeExpression
| entityIdReference # EntityIdExpression
| entityVersionReference # EntityVersionExpression
| entityNaturalIdReference # EntityNaturalIdExpression
| syntacticDomainPath pathContinuation? # SyntacticPathExpression
| function # FunctionExpression
| generalPathFragment # GeneralPathExpression
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-Datetime-arithmetic
// TBD
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-path-expressions
/**
* A much more complicated path expression involving operators and functions
*
* A path which needs to be resolved semantically. This recognizes
* any path-like structure. Generally, the path is semantically
* interpreted by the consumer of the parse-tree. However, there
* are certain cases where we can syntactically recognize a navigable
* path; see 'syntacticNavigablePath' rule
*/
path
: treatedPath pathContinutation?
: syntacticDomainPath pathContinuation?
| generalPathFragment
;
@@ -457,14 +467,120 @@ indexedPathAccessFragment
: '[' expression ']' ('.' generalPathFragment)?
;
/**
* A simple path expression
*
* - a reference to an identification variable (not case-sensitive),
* - followed by a list of period-separated identifiers (case-sensitive)
*/
simplePath
: identifier simplePathElement*
;
/**
* An element of a simple path expression: a period, and an identifier (case-sensitive)
*/
simplePathElement
: '.' identifier
;
/**
* A continuation of a path expression "broken" by an operator or function
*/
pathContinuation
: '.' simplePath
;
/**
* The special function 'type()'
*/
entityTypeReference
: TYPE '(' (path | parameter) ')'
;
/**
* The special function 'id()'
*/
entityIdReference
: ID '(' path ')' pathContinuation?
;
/**
* The special function 'version()'
*/
entityVersionReference
: VERSION '(' path ')'
;
/**
* The special function 'naturalid()'
*/
entityNaturalIdReference
: NATURALID '(' path ')' pathContinuation?
;
/**
* An operator or function that may occur within a path expression
*
* Rule for cases where we syntactically know that the path is a
* "domain path" because it is one of these special cases:
*
* * TREAT( path )
* * ELEMENTS( path )
* * INDICES( path )
* * VALUE( path )
* * KEY( path )
* * path[ selector ]
* * ARRAY_GET( embeddableArrayPath, index ).path
* * COALESCE( array1, array2 )[ selector ].path
*/
syntacticDomainPath
: treatedNavigablePath
| collectionValueNavigablePath
| mapKeyNavigablePath
| simplePath indexedPathAccessFragment
| simplePath slicedPathAccessFragment
| toOneFkReference
| function pathContinuation
| function indexedPathAccessFragment pathContinuation?
| function slicedPathAccessFragment
;
/**
* The slice operator to obtain elements between the lower and upper bound.
*/
slicedPathAccessFragment
: '[' expression ':' expression ']'
;
/**
* A 'treat()' function that "breaks" a path expression
*/
treatedNavigablePath
: TREAT '(' path AS simplePath ')' pathContinuation?
;
/**
* A 'value()' function that "breaks" a path expression
*/
collectionValueNavigablePath
: elementValueQuantifier '(' path ')' pathContinuation?
;
/**
* A 'key()' or 'index()' function that "breaks" a path expression
*/
mapKeyNavigablePath
: indexKeyQuantifier '(' path ')' pathContinuation?
;
/**
* The special function 'fk()'
*/
toOneFkReference
: FK '(' path ')'
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-case-expressions
caseList
: simpleCaseExpression
@@ -511,7 +627,7 @@ function
*/
standardFunction
: castFunction
| treatedPath
| treatedNavigablePath
| extractFunction
| truncFunction
| formatFunction
@@ -587,7 +703,7 @@ trimSpecification
;
trimCharacter
: stringLiteral
: STRING_LITERAL
| parameter
;
@@ -604,7 +720,7 @@ padSpecification
;
padCharacter
: stringLiteral
: STRING_LITERAL
;
padLength
@@ -756,7 +872,7 @@ rollup
* see 'Dialect.appendDatetimeFormat()'
*/
format
: stringLiteral
: STRING_LITERAL
;
/**
@@ -785,7 +901,7 @@ jpaNonstandardFunction
* The name of a user-defined or native database function, given as a quoted string
*/
jpaNonstandardFunctionName
: stringLiteral
: STRING_LITERAL
| identifier
;
@@ -799,7 +915,7 @@ columnFunction
* The function name, followed by a parenthesized list of ','-separated expressions
*/
genericFunction
: genericFunctionName '(' (genericFunctionArguments | ASTERISK)? ')' pathContinutation?
: genericFunctionName '(' (genericFunctionArguments | ASTERISK)? ')' pathContinuation?
nthSideClause? nullsClause? withinGroupClause? filterClause? overClause?
;
@@ -984,15 +1100,6 @@ frameExclusion
| EXCLUDE NO OTHERS
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-treat-type
treatedPath
: TREAT '(' path AS simplePath')' pathContinutation?
;
pathContinutation
: '.' simplePath
;
// Predicates
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-conditional-expressions
predicate
@@ -1027,6 +1134,16 @@ elementsValuesQuantifier
| VALUES
;
elementValueQuantifier
: ELEMENT
| VALUE
;
indexKeyQuantifier
: INDEX
| KEY
;
indicesKeysQuantifier
: INDICES
| KEYS
@@ -1045,7 +1162,7 @@ betweenExpression
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-like-predicate
stringPatternMatching
: expression NOT? (LIKE | ILIKE) expression (ESCAPE (stringLiteral|parameter))?
: expression NOT? (LIKE | ILIKE) expression (ESCAPE (STRING_LITERAL | JAVA_STRING_LITERAL |parameter))?
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-elements-indices
@@ -1097,9 +1214,12 @@ parameterOrNumberLiteral
| numericLiteral
;
/**
* An identification variable (an entity alias)
*/
variable
: AS identifier
| reservedWord
| nakedIdentifier
;
parameter
@@ -1111,16 +1231,9 @@ entityName
: identifier ('.' identifier)*
;
identifier
: reservedWord
;
functionName
: reservedWord ('.' reservedWord)*
;
reservedWord
: IDENTIFICATION_VARIABLE
nakedIdentifier
: IDENTIFIER
| QUOTED_IDENTIFIER
| f=(ALL
| AND
| ANY
@@ -1133,8 +1246,10 @@ reservedWord
| BY
| CASE
| CAST
| CEILING
| COLLATE
| COLUMN
| CONFLICT
| CONSTRAINT
| CONTAINS
| COUNT
| CROSS
@@ -1153,6 +1268,7 @@ reservedWord
| DEPTH
| DESC
| DISTINCT
| DO
| ELEMENT
| ELEMENTS
| ELSE
@@ -1166,18 +1282,15 @@ reservedWord
| EXCEPT
| EXCLUDE
| EXISTS
| EXP
| EXTRACT
| FALSE
| FETCH
| FILTER
| FIRST
| FLOOR
| FK
| FOLLOWING
| FOR
| FORMAT
| FROM
| FULL
| FUNCTION
| GROUP
| GROUPS
@@ -1187,10 +1300,9 @@ reservedWord
| IGNORE
| ILIKE
| IN
| INCLUDES
| INDEX
| INCLUDES
| INDICES
| INNER
| INSERT
| INSTANT
| INTERSECT
@@ -1199,15 +1311,14 @@ reservedWord
| IS
| JOIN
| KEY
| KEYS
| LAST
| LATERAL
| LEADING
| LEFT
| LIKE
| LIMIT
| LIST
| LISTAGG
| LN
| LOCAL
| LOCAL_DATE
| LOCAL_DATETIME
@@ -1231,6 +1342,7 @@ reservedWord
| NEXT
| NO
| NOT
| NOTHING
| NULLS
| OBJECT
| OF
@@ -1241,7 +1353,6 @@ reservedWord
| OR
| ORDER
| OTHERS
| OUTER
| OVER
| OVERFLOW
| OVERLAY
@@ -1250,7 +1361,6 @@ reservedWord
| PERCENT
| PLACING
| POSITION
| POWER
| PRECEDING
| QUARTER
| RANGE
@@ -1267,7 +1377,6 @@ reservedWord
| SOME
| SUBSTRING
| SUM
| TRUE
| THEN
| TIES
| TIME
@@ -1295,7 +1404,17 @@ reservedWord
| WITH
| WITHIN
| WITHOUT
| YEAR)
| YEAR
| ZONED)
;
identifier
: nakedIdentifier
| FULL
| INNER
| LEFT
| OUTER
| RIGHT
;
/*
@@ -1334,14 +1453,20 @@ fragment X: 'x' | 'X';
fragment Y: 'y' | 'Y';
fragment Z: 'z' | 'Z';
ASTERISK : '*';
// The following are reserved identifiers:
ID : I D;
VERSION : V E R S I O N;
VERSIONED : V E R S I O N E D;
NATURALID : N A T U R A L I D;
FK : F K;
ALL : A L L;
AND : A N D;
ANY : A N Y;
AS : A S;
ASC : A S C;
ASTERISK : '*';
AVG : A V G;
BETWEEN : B E T W E E N;
BOTH : B O T H;
@@ -1349,7 +1474,6 @@ BREADTH : B R E A D T H;
BY : B Y;
CASE : C A S E;
CAST : C A S T;
CEILING : C E I L I N G;
COLLATE : C O L L A T E;
COLUMN : C O L U M N;
CONFLICT : C O N F L I C T;
@@ -1386,14 +1510,10 @@ EVERY : E V E R Y;
EXCEPT : E X C E P T;
EXCLUDE : E X C L U D E;
EXISTS : E X I S T S;
EXP : E X P;
EXTRACT : E X T R A C T;
FALSE : F A L S E;
FETCH : F E T C H;
FILTER : F I L T E R;
FIRST : F I R S T;
FK : F K;
FLOOR : F L O O R;
FOLLOWING : F O L L O W I N G;
FOR : F O R;
FORMAT : F O R M A T;
@@ -1404,7 +1524,6 @@ GROUP : G R O U P;
GROUPS : G R O U P S;
HAVING : H A V I N G;
HOUR : H O U R;
ID : I D;
IGNORE : I G N O R E;
ILIKE : I L I K E;
IN : I N;
@@ -1429,7 +1548,6 @@ LIKE : L I K E;
LIMIT : L I M I T;
LIST : L I S T;
LISTAGG : L I S T A G G;
LN : L N;
LOCAL : L O C A L;
LOCAL_DATE : L O C A L '_' D A T E ;
LOCAL_DATETIME : L O C A L '_' D A T E T I M E;
@@ -1448,13 +1566,11 @@ MININDEX : M I N I N D E X;
MINUTE : M I N U T E;
MONTH : M O N T H;
NANOSECOND : N A N O S E C O N D;
NATURALID : N A T U R A L I D;
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;
OF : O F;
@@ -1474,7 +1590,6 @@ PARTITION : P A R T I T I O N;
PERCENT : P E R C E N T;
PLACING : P L A C I N G;
POSITION : P O S I T I O N;
POWER : P O W E R;
PRECEDING : P R E C E D I N G;
QUARTER : Q U A R T E R;
RANGE : R A N G E;
@@ -1501,7 +1616,6 @@ TO : T O;
TRAILING : T R A I L I N G;
TREAT : T R E A T;
TRIM : T R I M;
TRUE : T R U E;
TRUNC : T R U N C;
TRUNCATE : T R U N C A T E;
TYPE : T Y P E;
@@ -1511,8 +1625,6 @@ UPDATE : U P D A T E;
USING : U S I N G;
VALUE : V A L U E;
VALUES : V A L U E S;
VERSION : V E R S I O N;
VERSIONED : V E R S I O N E D;
WEEK : W E E K;
WHEN : W H E N;
WHERE : W H E R E;
@@ -1520,20 +1632,102 @@ WITH : W I T H;
WITHIN : W I T H I N;
WITHOUT : W I T H O U T;
YEAR : Y E A R;
ZONED : Z O N E D;
NULL : N U L L;
TRUE : T R U E;
FALSE : F A L S E;
fragment
INTEGER_NUMBER
: DIGIT+
;
fragment
FLOATING_POINT_NUMBER
: DIGIT+ '.' DIGIT* EXPONENT?
| '.' DIGIT+ EXPONENT?
| DIGIT+ EXPONENT
| DIGIT+
;
fragment
EXPONENT : [eE] [+-]? DIGIT+;
fragment INTEGER_NUMBER : ('0' .. '9')+ ;
fragment FLOAT_NUMBER : INTEGER_NUMBER+ '.'? INTEGER_NUMBER* (E [+-]? INTEGER_NUMBER)? ;
fragment HEX_DIGIT : [0-9a-fA-F];
fragment SINGLE_QUOTE : '\'';
fragment DOUBLE_QUOTE : '"';
STRING_LITERAL : SINGLE_QUOTE ( SINGLE_QUOTE SINGLE_QUOTE | ~('\'') )* SINGLE_QUOTE;
JAVA_STRING_LITERAL
: DOUBLE_QUOTE ( ESCAPE_SEQUENCE | ~('"') )* DOUBLE_QUOTE
| [jJ] SINGLE_QUOTE ( ESCAPE_SEQUENCE | ~('\'') )* SINGLE_QUOTE
| [jJ] DOUBLE_QUOTE ( ESCAPE_SEQUENCE | ~('\'') )* DOUBLE_QUOTE
;
INTEGER_LITERAL : INTEGER_NUMBER ('_' INTEGER_NUMBER)*;
LONG_LITERAL : INTEGER_NUMBER ('_' INTEGER_NUMBER)* LONG_SUFFIX;
FLOAT_LITERAL : FLOATING_POINT_NUMBER FLOAT_SUFFIX;
DOUBLE_LITERAL : FLOATING_POINT_NUMBER DOUBLE_SUFFIX?;
BIG_INTEGER_LITERAL : INTEGER_NUMBER BIG_INTEGER_SUFFIX;
BIG_DECIMAL_LITERAL : FLOATING_POINT_NUMBER BIG_DECIMAL_SUFFIX;
HEX_LITERAL : '0' [xX] HEX_DIGIT+ LONG_SUFFIX?;
CHARACTER : '\'' (~ ('\'' | '\\' )) '\'' ;
STRINGLITERAL : '\'' ('\'' '\'' | ~('\''))* '\'' ;
JAVASTRINGLITERAL : '"' ( ('\\' [btnfr"']) | ~('"'))* '"';
INTEGER_LITERAL : INTEGER_NUMBER (L | B I)? ;
FLOAT_LITERAL : FLOAT_NUMBER (D | F | B D)?;
HEXLITERAL : '0' X HEX_DIGIT+ ;
BINARY_LITERAL : [xX] '\'' HEX_DIGIT+ '\''
| [xX] '"' HEX_DIGIT+ '"'
;
IDENTIFICATION_VARIABLE : ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '$' | '_') ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '0' .. '9' | '$' | '_')* ;
fragment
LETTER : [a-zA-Z\u0080-\ufffe_$];
fragment
DIGIT : [0-9];
fragment
LONG_SUFFIX : [lL];
fragment
FLOAT_SUFFIX : [fF];
fragment
DOUBLE_SUFFIX : [dD];
fragment
BIG_DECIMAL_SUFFIX : [bB] [dD];
fragment
BIG_INTEGER_SUFFIX : [bB] [iI];
// Identifiers
IDENTIFIER
: LETTER (LETTER | DIGIT)*
;
fragment
BACKTICK : '`';
fragment BACKSLASH : '\\';
fragment
UNICODE_ESCAPE
: 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
;
fragment
ESCAPE_SEQUENCE
: BACKSLASH [btnfr"']
| BACKSLASH UNICODE_ESCAPE
| BACKSLASH BACKSLASH
;
QUOTED_IDENTIFIER
: BACKTICK ( ESCAPE_SEQUENCE | '\\' BACKTICK | ~([`]) )* BACKTICK
;

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.jpa.repository.query;
import static org.springframework.data.jpa.repository.query.QueryTokens.TOKEN_COMMA;
import static org.springframework.data.jpa.repository.query.QueryTokens.*;
import java.util.ArrayList;
import java.util.Collections;
@@ -84,7 +84,7 @@ class HqlQueryIntrospector extends HqlBaseVisitor<Void> implements ParsedQueryIn
}
private static String capturePrimaryAlias(VariableContext ctx) {
return ((ctx).reservedWord() != null ? ctx.reservedWord() : ctx.identifier().reservedWord()).getText();
return ((ctx).nakedIdentifier() != null ? ctx.nakedIdentifier() : ctx.identifier()).getText();
}
private static List<QueryToken> captureSelectItems(List<HqlParser.SelectionContext> selections,

View File

@@ -1032,8 +1032,10 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
return QueryRendererBuilder.from(QueryTokens.expression(ctx.NULL()));
} else if (ctx.booleanLiteral() != null) {
return visit(ctx.booleanLiteral());
} else if (ctx.stringLiteral() != null) {
return visit(ctx.stringLiteral());
} else if (ctx.JAVA_STRING_LITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.expression(ctx.JAVA_STRING_LITERAL()));
} else if (ctx.STRING_LITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.expression(ctx.STRING_LITERAL()));
} else if (ctx.numericLiteral() != null) {
return visit(ctx.numericLiteral());
} else if (ctx.dateTimeLiteral() != null) {
@@ -1057,27 +1059,23 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
}
}
@Override
public QueryTokenStream visitStringLiteral(HqlParser.StringLiteralContext ctx) {
if (ctx.STRINGLITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.expression(ctx.STRINGLITERAL()));
} else if (ctx.CHARACTER() != null) {
return QueryRendererBuilder.from(QueryTokens.expression(ctx.CHARACTER()));
} else {
return QueryTokenStream.empty();
}
}
@Override
public QueryTokenStream visitNumericLiteral(HqlParser.NumericLiteralContext ctx) {
if (ctx.INTEGER_LITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.INTEGER_LITERAL()));
} else if (ctx.LONG_LITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.LONG_LITERAL()));
} else if (ctx.BIG_INTEGER_LITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.BIG_INTEGER_LITERAL()));
} else if (ctx.FLOAT_LITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.FLOAT_LITERAL()));
} else if (ctx.HEXLITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.HEXLITERAL()));
} else if (ctx.DOUBLE_LITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.DOUBLE_LITERAL()));
} else if (ctx.BIG_DECIMAL_LITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.BIG_DECIMAL_LITERAL()));
} else if (ctx.HEX_LITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.HEX_LITERAL()));
} else {
return QueryTokenStream.empty();
}
@@ -1238,11 +1236,11 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
if (ctx.BINARY_LITERAL() != null) {
builder.append(QueryTokens.expression(ctx.BINARY_LITERAL()));
} else if (ctx.HEXLITERAL() != null) {
} else if (ctx.HEX_LITERAL() != null) {
builder.append(TOKEN_OPEN_BRACE);
builder.append(QueryTokenStream.concat(ctx.HEXLITERAL(), it -> {
builder.append(QueryTokenStream.concat(ctx.HEX_LITERAL(), it -> {
return QueryRendererBuilder.from(QueryTokens.token(it));
}, TOKEN_COMMA));
@@ -1425,6 +1423,195 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
return visit(ctx.parameter());
}
@Override
public QueryTokenStream visitEntityTypeExpression(HqlParser.EntityTypeExpressionContext ctx) {
return visit(ctx.entityTypeReference());
}
@Override
public QueryTokenStream visitEntityIdExpression(HqlParser.EntityIdExpressionContext ctx) {
return visit(ctx.entityIdReference());
}
@Override
public QueryTokenStream visitEntityVersionExpression(HqlParser.EntityVersionExpressionContext ctx) {
return visit(ctx.entityVersionReference());
}
@Override
public QueryTokenStream visitEntityNaturalIdExpression(HqlParser.EntityNaturalIdExpressionContext ctx) {
return visit(ctx.entityNaturalIdReference());
}
@Override
public QueryTokenStream visitSyntacticPathExpression(HqlParser.SyntacticPathExpressionContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.appendInline(visit(ctx.syntacticDomainPath()));
if (ctx.pathContinuation() != null) {
builder.appendInline(visit(ctx.pathContinuation()));
}
return builder;
}
@Override
public QueryTokenStream visitPathContinuation(HqlParser.PathContinuationContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(TOKEN_DOT);
builder.append(visit(ctx.simplePath()));
return builder;
}
@Override
public QueryTokenStream visitEntityTypeReference(HqlParser.EntityTypeReferenceContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.token(ctx.TYPE()));
builder.append(TOKEN_OPEN_PAREN);
if (ctx.path() != null) {
builder.appendInline(visit(ctx.path()));
}
if (ctx.parameter() != null) {
builder.appendInline(visit(ctx.parameter()));
}
builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
@Override
public QueryTokenStream visitEntityIdReference(HqlParser.EntityIdReferenceContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.token(ctx.ID()));
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(visit(ctx.path()));
builder.append(TOKEN_CLOSE_PAREN);
if (ctx.pathContinuation() != null) {
builder.appendInline(visit(ctx.pathContinuation()));
}
return builder;
}
@Override
public QueryTokenStream visitEntityVersionReference(HqlParser.EntityVersionReferenceContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.token(ctx.VERSION()));
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(visit(ctx.path()));
builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
@Override
public QueryTokenStream visitEntityNaturalIdReference(HqlParser.EntityNaturalIdReferenceContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.token(ctx.NATURALID()));
builder.append(TOKEN_OPEN_PAREN);
builder.appendInline(visit(ctx.path()));
builder.append(TOKEN_CLOSE_PAREN);
if (ctx.pathContinuation() != null) {
builder.appendInline(visit(ctx.pathContinuation()));
}
return builder;
}
@Override
public QueryTokenStream visitSyntacticDomainPath(HqlParser.SyntacticDomainPathContext ctx) {
if (ctx.treatedNavigablePath() != null) {
return visit(ctx.treatedNavigablePath());
}
if (ctx.collectionValueNavigablePath() != null) {
return visit(ctx.collectionValueNavigablePath());
}
if (ctx.mapKeyNavigablePath() != null) {
return visit(ctx.mapKeyNavigablePath());
}
if (ctx.toOneFkReference() != null) {
return visit(ctx.toOneFkReference());
}
if (ctx.function() != null) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(visit(ctx.function()));
if (ctx.indexedPathAccessFragment() != null) {
builder.append(visit(ctx.indexedPathAccessFragment()));
}
if (ctx.slicedPathAccessFragment() != null) {
builder.append(visit(ctx.slicedPathAccessFragment()));
}
if (ctx.pathContinuation() != null) {
builder.append(visit(ctx.pathContinuation()));
}
return builder;
}
if (ctx.indexedPathAccessFragment() != null) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(visit(ctx.simplePath()));
builder.append(visit(ctx.indexedPathAccessFragment()));
return builder;
}
if (ctx.slicedPathAccessFragment() != null) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(visit(ctx.simplePath()));
builder.append(visit(ctx.slicedPathAccessFragment()));
return builder;
}
return QueryRenderer.empty();
}
@Override
public QueryTokenStream visitSlicedPathAccessFragment(HqlParser.SlicedPathAccessFragmentContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(TOKEN_OPEN_SQUARE_BRACKET);
builder.appendInline(visit(ctx.expression(0)));
builder.append(TOKEN_COLON);
builder.appendInline(visit(ctx.expression(1)));
builder.append(TOKEN_CLOSE_SQUARE_BRACKET);
return builder;
}
@Override
public QueryTokenStream visitFunctionExpression(HqlParser.FunctionExpressionContext ctx) {
return visit(ctx.function());
@@ -1479,10 +1666,6 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
return visit(ctx.castFunction());
}
if (ctx.treatedPath() != null) {
return visit(ctx.treatedPath());
}
if (ctx.extractFunction() != null) {
return visit(ctx.extractFunction());
}
@@ -1634,7 +1817,7 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
@Override
public QueryTokenStream visitPadCharacter(HqlParser.PadCharacterContext ctx) {
return visit(ctx.stringLiteral());
return QueryRendererBuilder.from(QueryTokens.token(ctx.STRING_LITERAL()));
}
@Override
@@ -1908,7 +2091,7 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
@Override
public QueryTokenStream visitFormat(HqlParser.FormatContext ctx) {
return visit(ctx.stringLiteral());
return QueryRendererBuilder.from(QueryTokens.token(ctx.STRING_LITERAL()));
}
@Override
@@ -1970,7 +2153,7 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
return visit(ctx.identifier());
}
return visit(ctx.stringLiteral());
return QueryRendererBuilder.from(QueryTokens.token(ctx.STRING_LITERAL()));
}
@Override
@@ -2378,12 +2561,12 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
QueryRendererBuilder builder = QueryRenderer.builder();
if (ctx.treatedPath() != null) {
if (ctx.syntacticDomainPath() != null) {
builder.append(visit(ctx.treatedPath()));
builder.append(visit(ctx.syntacticDomainPath()));
if (ctx.pathContinutation() != null) {
builder.append(visit(ctx.pathContinutation()));
if (ctx.pathContinuation() != null) {
builder.append(visit(ctx.pathContinuation()));
}
} else if (ctx.generalPathFragment() != null) {
builder.append(visit(ctx.generalPathFragment()));
@@ -2549,8 +2732,8 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
nested.append(TOKEN_CLOSE_PAREN);
builder.append(nested);
if (ctx.pathContinutation() != null) {
builder.append(visit(ctx.pathContinutation()));
if (ctx.pathContinuation() != null) {
builder.append(visit(ctx.pathContinuation()));
}
if (ctx.nthSideClause() != null) {
@@ -2821,8 +3004,8 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
@Override
public QueryTokenStream visitTrimCharacter(HqlParser.TrimCharacterContext ctx) {
if (ctx.stringLiteral() != null) {
return visit(ctx.stringLiteral());
if (ctx.STRING_LITERAL() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.STRING_LITERAL()));
}
return visit(ctx.parameter());
@@ -2899,7 +3082,7 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
}
@Override
public QueryTokenStream visitTreatedPath(HqlParser.TreatedPathContext ctx) {
public QueryTokenStream visitTreatedNavigablePath(HqlParser.TreatedNavigablePathContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
@@ -2914,24 +3097,88 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
builder.appendInline(nested);
builder.append(TOKEN_CLOSE_PAREN);
if (ctx.pathContinutation() != null) {
builder.append(visit(ctx.pathContinutation()));
if (ctx.pathContinuation() != null) {
builder.append(visit(ctx.pathContinuation()));
}
return builder;
}
@Override
public QueryTokenStream visitPathContinutation(HqlParser.PathContinutationContext ctx) {
public QueryTokenStream visitCollectionValueNavigablePath(HqlParser.CollectionValueNavigablePathContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(TOKEN_DOT);
builder.append(visit(ctx.simplePath()));
builder.append(visit(ctx.elementValueQuantifier()));
builder.append(TOKEN_OPEN_PAREN);
builder.append(visit(ctx.path()));
builder.append(TOKEN_CLOSE_PAREN);
if (ctx.pathContinuation() != null) {
builder.append(visit(ctx.pathContinuation()));
}
return builder;
}
@Override
public QueryTokenStream visitMapKeyNavigablePath(HqlParser.MapKeyNavigablePathContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(visit(ctx.indexKeyQuantifier()));
builder.append(TOKEN_OPEN_PAREN);
builder.append(visit(ctx.path()));
builder.append(TOKEN_CLOSE_PAREN);
if (ctx.pathContinuation() != null) {
builder.append(visit(ctx.pathContinuation()));
}
return builder;
}
@Override
public QueryTokenStream visitToOneFkReference(HqlParser.ToOneFkReferenceContext ctx) {
QueryRendererBuilder builder = QueryRenderer.builder();
builder.append(QueryTokens.token(ctx.FK()));
builder.append(TOKEN_OPEN_PAREN);
builder.append(visit(ctx.path()));
builder.append(TOKEN_CLOSE_PAREN);
return builder;
}
@Override
public QueryTokenStream visitElementValueQuantifier(HqlParser.ElementValueQuantifierContext ctx) {
if (ctx.ELEMENT() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.ELEMENT()));
}
if (ctx.VALUE() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.VALUE()));
}
return QueryTokenStream.empty();
}
@Override
public QueryTokenStream visitIndexKeyQuantifier(HqlParser.IndexKeyQuantifierContext ctx) {
if (ctx.INDEX() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.INDEX()));
}
if (ctx.KEY() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.KEY()));
}
return QueryTokenStream.empty();
}
@Override
public QueryTokenStream visitIsBooleanPredicate(HqlParser.IsBooleanPredicateContext ctx) {
@@ -3176,8 +3423,10 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
builder.append(QueryTokens.expression(ctx.ESCAPE()));
if (ctx.stringLiteral() != null) {
builder.appendExpression(visit(ctx.stringLiteral()));
if (ctx.STRING_LITERAL() != null) {
builder.append(QueryTokens.expression(ctx.STRING_LITERAL()));
} else if (ctx.JAVA_STRING_LITERAL() != null) {
builder.append(QueryTokens.expression(ctx.JAVA_STRING_LITERAL()));
} else if (ctx.parameter() != null) {
builder.appendExpression(visit(ctx.parameter()));
}
@@ -3335,8 +3584,8 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
builder.append(QueryTokens.expression(ctx.AS()));
builder.append(visit(ctx.identifier()));
} else if (ctx.reservedWord() != null) {
builder.append(visit(ctx.reservedWord()));
} else if (ctx.nakedIdentifier() != null) {
builder.append(visit(ctx.nakedIdentifier()));
}
return builder;
@@ -3371,20 +3620,33 @@ class HqlQueryRenderer extends HqlBaseVisitor<QueryTokenStream> {
@Override
public QueryTokenStream visitIdentifier(HqlParser.IdentifierContext ctx) {
if (ctx.reservedWord() != null) {
return visit(ctx.reservedWord());
} else {
return QueryTokenStream.empty();
if (ctx.nakedIdentifier() != null) {
return visit(ctx.nakedIdentifier());
} else if (ctx.FULL() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.FULL()));
} else if (ctx.LEFT() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.LEFT()));
} else if (ctx.INNER() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.INNER()));
} else if (ctx.OUTER() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.OUTER()));
} else if (ctx.RIGHT() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.RIGHT()));
}
return QueryTokenStream.empty();
}
@Override
public QueryTokenStream visitReservedWord(HqlParser.ReservedWordContext ctx) {
public QueryTokenStream visitNakedIdentifier(HqlParser.NakedIdentifierContext ctx) {
if (ctx.IDENTIFICATION_VARIABLE() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.IDENTIFICATION_VARIABLE()));
if (ctx.IDENTIFIER() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.IDENTIFIER()));
} else if (ctx.QUOTED_IDENTIFIER() != null) {
return QueryRendererBuilder.from(QueryTokens.token(ctx.QUOTED_IDENTIFIER()));
} else {
return QueryRendererBuilder.from(QueryTokens.token(ctx.f));
}
}
}

View File

@@ -37,7 +37,8 @@ import org.junit.jupiter.params.provider.ValueSource;
*
* @author Greg Turnquist
* @author Christoph Strobl
* @author Mark Paluch, Yannick Brandt
* @author Mark Paluch
* @author Yannick Brandt
* @since 3.1
*/
class HqlQueryRendererTests {
@@ -179,6 +180,180 @@ class HqlQueryRendererTests {
""");
}
@Test // GH-3711
void entityTypeReference() {
assertQuery("""
SELECT TYPE(e)
FROM Employee e
""");
assertQuery("""
SELECT TYPE(?0)
FROM Employee e
""");
}
@Test // GH-3711
void entityIdReference() {
assertQuery("""
SELECT ID(e)
FROM Employee e
""");
assertQuery("""
SELECT ID(e).foo
FROM Employee e
""");
}
@Test // GH-3711
void entityNaturalIdReference() {
assertQuery("""
SELECT NATURALID(e)
FROM Employee e
""");
assertQuery("""
SELECT NATURALID(e).foo
FROM Employee e
""");
}
@Test // GH-3711
void entityVersionReference() {
assertQuery("""
SELECT VERSION(e)
FROM Employee e
""");
}
@Test // GH-3711
void treatedNavigablePath() {
assertQuery("""
SELECT TREAT(e as Integer).foo
FROM Employee e
""");
}
@Test // GH-3711
void collectionValueNavigablePath() {
assertQuery("""
SELECT ELEMENT(e)
FROM Employee e
""");
assertQuery("""
SELECT ELEMENT(e).foo
FROM Employee e
""");
assertQuery("""
SELECT VALUE(e)
FROM Employee e
""");
assertQuery("""
SELECT VALUE(e).foo
FROM Employee e
""");
}
@Test // GH-3711
void mapKeyNavigablePath() {
assertQuery("""
SELECT KEY(e)
FROM Employee e
""");
assertQuery("""
SELECT KEY(e).foo
FROM Employee e
""");
assertQuery("""
SELECT INDEX(e)
FROM Employee e
""");
}
@Test // GH-3711
void toOneFkReference() {
assertQuery("""
SELECT FK(e)
FROM Employee e
""");
assertQuery("""
SELECT FK(e.foo)
FROM Employee e
""");
}
@Test // GH-3711
void indexedPathAccessFragment() {
assertQuery("""
SELECT e.names[0]
FROM Employee e
""");
assertQuery("""
SELECT e.payments[1].id
FROM Employee e
""");
assertQuery("""
SELECT some_function()[0]
FROM Employee e
""");
assertQuery("""
SELECT some_function()[1].id
FROM Employee e
""");
}
@Test // GH-3711
void slicedPathAccessFragment() {
assertQuery("""
SELECT e.names[0:1]
FROM Employee e
""");
assertQuery("""
SELECT e.payments[1:2].id
FROM Employee e
""");
assertQuery("""
SELECT some_function()[0:1]
FROM Employee e
""");
assertQuery("""
SELECT some_function()[1:2].id
FROM Employee e
""");
}
@Test // GH-3711
void functionPathContinuation() {
assertQuery("""
SELECT some_function().foo
FROM Employee e
""");
}
@Test
void joinsExample1() {
@@ -299,7 +474,7 @@ class HqlQueryRendererTests {
assertQuery("""
SELECT b.name, b.ISBN
FROM Order o JOIN TREAT(o.product AS Book) b
""");
""");
}
@Test
@@ -308,7 +483,7 @@ class HqlQueryRendererTests {
assertQuery("""
SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp
WHERE lp.budget > 1000
""");
""");
}
/**
@@ -323,7 +498,7 @@ class HqlQueryRendererTests {
WHERE TREAT(p AS LargeProject).budget > 1000
OR TREAT(p AS SmallProject).name LIKE 'Persist%'
OR p.description LIKE "cost overrun"
""");
""");
}
@Test
@@ -334,7 +509,7 @@ class HqlQueryRendererTests {
WHERE TREAT(p AS LargeProject).budget > 1000
OR TREAT(p AS SmallProject).name LIKE 'Persist%'
OR p.description LIKE 'cost overrun'
""");
""");
}
@Test
@@ -344,7 +519,7 @@ class HqlQueryRendererTests {
SELECT e FROM Employee e
WHERE TREAT(e AS Exempt).vacationDays > 10
OR TREAT(e AS Contractor).hours > 100
""");
""");
}
@Test
@@ -408,7 +583,7 @@ class HqlQueryRendererTests {
WHERE emp.salary > ALL (SELECT m.salary
FROM Manager m
WHERE m.department = emp.department)
""");
""");
}
@Test
@@ -420,7 +595,7 @@ class HqlQueryRendererTests {
WHERE EXISTS (SELECT spouseEmp
FROM Employee spouseEmp
WHERE spouseEmp = emp.spouse)
""");
""");
}
@Test
@@ -488,7 +663,7 @@ class HqlQueryRendererTests {
WHEN e.rating = 2 THEN e.salary * 1.05
ELSE e.salary * 1.01
END
""");
""");
}
@Test
@@ -501,7 +676,7 @@ class HqlQueryRendererTests {
WHEN 2 THEN e.salary * 1.05
ELSE e.salary * 1.01
END
""");
""");
}
@Test
@@ -541,7 +716,7 @@ class HqlQueryRendererTests {
SELECT e
FROM Employee e
WHERE TYPE(e) IN (Exempt, Contractor)
""");
""");
}
@Test
@@ -1509,13 +1684,13 @@ class HqlQueryRendererTests {
});
}
@Test
@Test // GH-3711
void ceilingFunctionShouldWork() {
assertQuery("select ceiling(1.5) from Element a");
}
@Test
void lnFunctionSouldWork() {
@Test // GH-3711
void lnFunctionShouldWork() {
assertQuery("select ln(7.5) from Element a");
}
@@ -1568,10 +1743,18 @@ class HqlQueryRendererTests {
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 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));
}
@ParameterizedTest // GH-3711
@ValueSource(strings = { "1", "1_000", "1L", "1_000L", "1bi", "1.1f", "2.2d", "2.2bd" })
void numberLiteralsShouldWork(String literal) {
assertQuery(String.format("SELECT %s FROM User u where u.id = %s", literal, literal));
}
@Test // GH-3025
void binaryLiteralsShouldWork() {

View File

@@ -924,7 +924,7 @@ class HqlQueryTransformerTests {
and iu = u
)
and ct.id = :teamId
""", relationshipName, joinAlias, joinAlias));
""", relationshipName, joinAlias, joinAlias));
}
static Stream<Arguments> queriesWithReservedWordsAsIdentifiers() {
@@ -933,7 +933,6 @@ class HqlQueryTransformerTests {
Arguments.of("right", "rt"), //
Arguments.of("left", "lt"), //
Arguments.of("outer", "ou"), //
Arguments.of("full", "full"), //
Arguments.of("inner", "inr"));
}