Handle dateTimeField property with HQL.

Hibernate supports varisous duration literals. They also support a couple formats for binary literals.

See #3025
This commit is contained in:
Greg L. Turnquist
2023-06-14 14:20:57 -05:00
parent 0d03612633
commit 572efd28a9
4 changed files with 115 additions and 4 deletions

View File

@@ -299,6 +299,7 @@ literal
| stringLiteral
| numericLiteral
| dateTimeLiteral
| binaryLiteral
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-boolean-literals
@@ -337,10 +338,24 @@ dateTimeLiteral
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-duration-literals
// TBD
datetimeField
: YEAR
| MONTH
| DAY
| WEEK
| QUARTER
| HOUR
| MINUTE
| SECOND
| NANOSECOND
| EPOCH
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-binary-literals
// TBD
binaryLiteral
: BINARY_LITERAL
| '{' HEXLITERAL (',' HEXLITERAL)* '}'
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-enum-literals
// TBD
@@ -362,6 +377,8 @@ expression
| primaryExpression # PlainPrimaryExpression
| op=('+' | '-') numericLiteral # SignedNumericLiteral
| op=('+' | '-') expression # SignedExpression
| expression datetimeField # ToDurationExpression
| expression BY datetimeField # FromDurationExpression
| expression op=('*' | '/') expression # MultiplicationExpression
| expression op=('+' | '-') expression # AdditionExpression
| expression '||' expression # HqlConcatenationExpression
@@ -607,7 +624,6 @@ inList
;
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-exists-predicate
// TBD
existsExpression
: EXISTS (ELEMENTS | INDICES) '(' simplePath ')'
| EXISTS expression
@@ -1058,13 +1074,18 @@ YEAR : Y E A R;
fragment INTEGER_NUMBER : ('0' .. '9')+ ;
fragment FLOAT_NUMBER : INTEGER_NUMBER+ '.'? INTEGER_NUMBER* (E [+-]? INTEGER_NUMBER)? ;
fragment HEX_DIGIT : [0-9a-fA-F];
CHARACTER : '\'' (~ ('\'' | '\\' )) '\'' ;
STRINGLITERAL : '\'' ('\'' '\'' | ~('\'' | '\\'))* '\'' ;
JAVASTRINGLITERAL : '"' ( ('\\' [btnfr"']) | ~('"'))* '"';
INTEGER_LITERAL : INTEGER_NUMBER (L | B I)? ;
FLOAT_LITERAL : FLOAT_NUMBER (D | F | B D)?;
HEXLITERAL : '0' X ('0' .. '9' | A | B | C | D | E)+ ;
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' | '$' | '_')* ;

View File

@@ -1047,6 +1047,8 @@ class HqlQueryRenderer extends HqlBaseVisitor<List<JpaQueryParsingToken>> {
return visit(ctx.numericLiteral());
} else if (ctx.dateTimeLiteral() != null) {
return visit(ctx.dateTimeLiteral());
} else if (ctx.binaryLiteral() != null) {
return visit(ctx.binaryLiteral());
} else {
return List.of();
}
@@ -1135,6 +1137,56 @@ class HqlQueryRenderer extends HqlBaseVisitor<List<JpaQueryParsingToken>> {
return tokens;
}
@Override
public List<JpaQueryParsingToken> visitDatetimeField(HqlParser.DatetimeFieldContext ctx) {
if (ctx.YEAR() != null) {
return List.of(new JpaQueryParsingToken(ctx.YEAR()));
} else if (ctx.MONTH() != null) {
return List.of(new JpaQueryParsingToken(ctx.MONTH()));
} else if (ctx.DAY() != null) {
return List.of(new JpaQueryParsingToken(ctx.DAY()));
} else if (ctx.WEEK() != null) {
return List.of(new JpaQueryParsingToken(ctx.WEEK()));
} else if (ctx.QUARTER() != null) {
return List.of(new JpaQueryParsingToken(ctx.QUARTER()));
} else if (ctx.HOUR() != null) {
return List.of(new JpaQueryParsingToken(ctx.HOUR()));
} else if (ctx.MINUTE() != null) {
return List.of(new JpaQueryParsingToken(ctx.MINUTE()));
} else if (ctx.SECOND() != null) {
return List.of(new JpaQueryParsingToken(ctx.SECOND()));
} else if (ctx.NANOSECOND() != null) {
return List.of(new JpaQueryParsingToken(ctx.NANOSECOND()));
} else if (ctx.EPOCH() != null) {
return List.of(new JpaQueryParsingToken(ctx.EPOCH()));
} else {
return List.of();
}
}
@Override
public List<JpaQueryParsingToken> visitBinaryLiteral(HqlParser.BinaryLiteralContext ctx) {
List<JpaQueryParsingToken> tokens = new ArrayList<>();
if (ctx.BINARY_LITERAL() != null) {
tokens.add(new JpaQueryParsingToken(ctx.BINARY_LITERAL()));
} else if (ctx.HEXLITERAL() != null) {
tokens.add(TOKEN_OPEN_BRACE);
ctx.HEXLITERAL().forEach(terminalNode -> {
tokens.add(new JpaQueryParsingToken(terminalNode));
NOSPACE(tokens);
tokens.add(TOKEN_COMMA);
});
CLIP(tokens);
tokens.add(TOKEN_CLOSE_BRACE);
}
return tokens;
}
@Override
public List<JpaQueryParsingToken> visitPlainPrimaryExpression(HqlParser.PlainPrimaryExpressionContext ctx) {
return visit(ctx.primaryExpression());
@@ -1177,6 +1229,7 @@ class HqlQueryRenderer extends HqlBaseVisitor<List<JpaQueryParsingToken>> {
tokens.add(TOKEN_OPEN_PAREN);
tokens.addAll(visit(ctx.expression()));
NOSPACE(tokens);
tokens.add(TOKEN_CLOSE_PAREN);
return tokens;
@@ -1242,6 +1295,29 @@ class HqlQueryRenderer extends HqlBaseVisitor<List<JpaQueryParsingToken>> {
return tokens;
}
@Override
public List<JpaQueryParsingToken> visitToDurationExpression(HqlParser.ToDurationExpressionContext ctx) {
List<JpaQueryParsingToken> tokens = new ArrayList<>();
tokens.addAll(visit(ctx.expression()));
tokens.addAll(visit(ctx.datetimeField()));
return tokens;
}
@Override
public List<JpaQueryParsingToken> visitFromDurationExpression(HqlParser.FromDurationExpressionContext ctx) {
List<JpaQueryParsingToken> tokens = new ArrayList<>();
tokens.addAll(visit(ctx.expression()));
tokens.add(new JpaQueryParsingToken(ctx.BY()));
tokens.addAll(visit(ctx.datetimeField()));
return tokens;
}
@Override
public List<JpaQueryParsingToken> visitCaseExpression(HqlParser.CaseExpressionContext ctx) {
return visit(ctx.caseList());

View File

@@ -48,6 +48,7 @@ class JpaQueryParsingToken {
public static final JpaQueryParsingToken TOKEN_CLOSE_SQUARE_BRACKET = new JpaQueryParsingToken("]");
public static final JpaQueryParsingToken TOKEN_COLON = new JpaQueryParsingToken(":", false);
public static final JpaQueryParsingToken TOKEN_QUESTION_MARK = new JpaQueryParsingToken("?", false);
public static final JpaQueryParsingToken TOKEN_OPEN_BRACE = new JpaQueryParsingToken("{", false);
public static final JpaQueryParsingToken TOKEN_CLOSE_BRACE = new JpaQueryParsingToken("}");
public static final JpaQueryParsingToken TOKEN_CLOSE_SQUARE_BRACKET_BRACE = new JpaQueryParsingToken("]}");
public static final JpaQueryParsingToken TOKEN_CLOSE_PAREN_BRACE = new JpaQueryParsingToken(")}");

View File

@@ -1526,4 +1526,17 @@ class HqlQueryRendererTests {
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");
}
@Test // GH-3025
void binaryLiteralsShouldWork() {
assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE ce.value = {0xDE, 0xAD, 0xBE, 0xEF}");
assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE ce.value = X'DEADBEEF'");
assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE ce.value = x'deadbeef'");
}
}