Add support for IS TRUE|FALSE|EMPTY and CONTAINS|INCLUDES|INTERSECTS HQL predicates.
Closes #3628
This commit is contained in:
@@ -571,13 +571,15 @@ pathContinutation
|
||||
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-conditional-expressions
|
||||
predicate
|
||||
: '(' predicate ')' # GroupedPredicate
|
||||
| dealingWithNullExpression # NullExpressionPredicate
|
||||
| expression IS NOT? (NULL|EMPTY|TRUE|FALSE) # IsBooleanPredicate
|
||||
| expression IS NOT? DISTINCT FROM expression # IsDistinctFromPredicate
|
||||
| expression NOT? MEMBER OF? path # MemberOfPredicate
|
||||
| inExpression # InPredicate
|
||||
| betweenExpression # BetweenPredicate
|
||||
| expression NOT? (CONTAINS|INCLUDES|INTERSECTS) expression # ContainsPredicate
|
||||
| relationalExpression # RelationalPredicate
|
||||
| stringPatternMatching # LikePredicate
|
||||
| existsExpression # ExistsPredicate
|
||||
| collectionExpression # CollectionPredicate
|
||||
| NOT predicate # NotPredicate
|
||||
| predicate AND predicate # AndPredicate
|
||||
| predicate OR predicate # OrPredicate
|
||||
@@ -600,12 +602,6 @@ betweenExpression
|
||||
: expression NOT? BETWEEN expression AND expression
|
||||
;
|
||||
|
||||
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-null-predicate
|
||||
dealingWithNullExpression
|
||||
: expression IS NOT? NULL
|
||||
| expression IS NOT? DISTINCT FROM expression
|
||||
;
|
||||
|
||||
// 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))?
|
||||
@@ -632,12 +628,6 @@ existsExpression
|
||||
| EXISTS expression
|
||||
;
|
||||
|
||||
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-collection-operators
|
||||
collectionExpression
|
||||
: expression IS NOT? EMPTY
|
||||
| expression NOT? MEMBER OF path
|
||||
;
|
||||
|
||||
// Projection
|
||||
// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-select-new
|
||||
instantiationTarget
|
||||
@@ -707,6 +697,7 @@ reservedWord
|
||||
| CASE
|
||||
| CAST
|
||||
| COLLATE
|
||||
| CONTAINS
|
||||
| COUNT
|
||||
| CROSS
|
||||
| CUBE
|
||||
@@ -739,6 +730,7 @@ reservedWord
|
||||
| EXISTS
|
||||
| EXP
|
||||
| EXTRACT
|
||||
| FALSE
|
||||
| FETCH
|
||||
| FILTER
|
||||
| FIRST
|
||||
@@ -757,12 +749,14 @@ reservedWord
|
||||
| IGNORE
|
||||
| ILIKE
|
||||
| IN
|
||||
| INCLUDES
|
||||
| INDEX
|
||||
| INDICES
|
||||
| INNER
|
||||
| INSERT
|
||||
| INSTANT
|
||||
| INTERSECT
|
||||
| INTERSECTS
|
||||
| INTO
|
||||
| IS
|
||||
| JOIN
|
||||
@@ -834,6 +828,7 @@ reservedWord
|
||||
| SOME
|
||||
| SUBSTRING
|
||||
| SUM
|
||||
| TRUE
|
||||
| THEN
|
||||
| TIES
|
||||
| TIME
|
||||
@@ -917,6 +912,7 @@ 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;
|
||||
CONTAINS : C O N T A I N S;
|
||||
COUNT : C O U N T;
|
||||
CROSS : C R O S S;
|
||||
CUBE : C U B E;
|
||||
@@ -969,12 +965,14 @@ ID : I D;
|
||||
IGNORE : I G N O R E;
|
||||
ILIKE : I L I K E;
|
||||
IN : I N;
|
||||
INCLUDES : I N C L U D E S;
|
||||
INDEX : I N D E X;
|
||||
INDICES : I N D I C E S;
|
||||
INNER : I N N E R;
|
||||
INSERT : I N S E R T;
|
||||
INSTANT : I N S T A N T;
|
||||
INTERSECT : I N T E R S E C T;
|
||||
INTERSECTS : I N T E R S E C T S;
|
||||
INTO : I N T O;
|
||||
IS : I S;
|
||||
JOIN : J O I N;
|
||||
|
||||
@@ -2094,8 +2094,77 @@ class HqlQueryRenderer extends HqlBaseVisitor<List<JpaQueryParsingToken>> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitNullExpressionPredicate(HqlParser.NullExpressionPredicateContext ctx) {
|
||||
return visit(ctx.dealingWithNullExpression());
|
||||
public List<JpaQueryParsingToken> visitIsBooleanPredicate(HqlParser.IsBooleanPredicateContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
|
||||
tokens.addAll(visit(ctx.expression()));
|
||||
tokens.add(new JpaQueryParsingToken(ctx.IS()));
|
||||
|
||||
if (ctx.NOT() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.NOT()));
|
||||
}
|
||||
|
||||
if (ctx.NULL() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.NULL()));
|
||||
}
|
||||
|
||||
if (ctx.TRUE() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.TRUE()));
|
||||
}
|
||||
|
||||
if (ctx.FALSE() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.FALSE()));
|
||||
}
|
||||
|
||||
if (ctx.EMPTY() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.EMPTY()));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitMemberOfPredicate(HqlParser.MemberOfPredicateContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
|
||||
tokens.addAll(visit(ctx.expression()));
|
||||
if (ctx.NOT() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.NOT()));
|
||||
}
|
||||
if (ctx.MEMBER() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.MEMBER()));
|
||||
}
|
||||
if (ctx.OF() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.OF()));
|
||||
}
|
||||
|
||||
tokens.addAll(visit(ctx.path()));
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitIsDistinctFromPredicate(HqlParser.IsDistinctFromPredicateContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>(16);
|
||||
|
||||
tokens.addAll(visit(ctx.expression(0)));
|
||||
tokens.add(new JpaQueryParsingToken(ctx.IS()));
|
||||
|
||||
if (ctx.NOT() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.NOT()));
|
||||
}
|
||||
|
||||
if (ctx.DISTINCT() != null) {
|
||||
|
||||
tokens.add(new JpaQueryParsingToken(ctx.DISTINCT()));
|
||||
tokens.add(new JpaQueryParsingToken(ctx.FROM()));
|
||||
tokens.addAll(visit(ctx.expression(1)));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2103,6 +2172,33 @@ class HqlQueryRenderer extends HqlBaseVisitor<List<JpaQueryParsingToken>> {
|
||||
return visit(ctx.betweenExpression());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitContainsPredicate(HqlParser.ContainsPredicateContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
|
||||
tokens.addAll(visit(ctx.expression(0)));
|
||||
|
||||
if (ctx.NOT() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.NOT()));
|
||||
}
|
||||
|
||||
if (ctx.CONTAINS() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.CONTAINS()));
|
||||
}
|
||||
if (ctx.INCLUDES() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.INCLUDES()));
|
||||
}
|
||||
if (ctx.INTERSECTS() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.INTERSECTS()));
|
||||
}
|
||||
|
||||
tokens.addAll(visit(ctx.expression(1)));
|
||||
|
||||
return tokens;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitOrPredicate(HqlParser.OrPredicateContext ctx) {
|
||||
|
||||
@@ -2125,11 +2221,6 @@ class HqlQueryRenderer extends HqlBaseVisitor<List<JpaQueryParsingToken>> {
|
||||
return visit(ctx.existsExpression());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitCollectionPredicate(HqlParser.CollectionPredicateContext ctx) {
|
||||
return visit(ctx.collectionExpression());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitAndPredicate(HqlParser.AndPredicateContext ctx) {
|
||||
|
||||
@@ -2224,30 +2315,6 @@ class HqlQueryRenderer extends HqlBaseVisitor<List<JpaQueryParsingToken>> {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitDealingWithNullExpression(HqlParser.DealingWithNullExpressionContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
|
||||
tokens.addAll(visit(ctx.expression(0)));
|
||||
tokens.add(new JpaQueryParsingToken(ctx.IS()));
|
||||
|
||||
if (ctx.NOT() != null) {
|
||||
tokens.add(TOKEN_NOT);
|
||||
}
|
||||
|
||||
if (ctx.NULL() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.NULL()));
|
||||
} else if (ctx.DISTINCT() != null) {
|
||||
|
||||
tokens.add(new JpaQueryParsingToken(ctx.DISTINCT()));
|
||||
tokens.add(new JpaQueryParsingToken(ctx.FROM()));
|
||||
tokens.addAll(visit(ctx.expression(1)));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitStringPatternMatching(HqlParser.StringPatternMatchingContext ctx) {
|
||||
|
||||
@@ -2368,36 +2435,6 @@ class HqlQueryRenderer extends HqlBaseVisitor<List<JpaQueryParsingToken>> {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitCollectionExpression(HqlParser.CollectionExpressionContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
|
||||
tokens.addAll(visit(ctx.expression()));
|
||||
|
||||
if (ctx.IS() != null) {
|
||||
|
||||
tokens.add(new JpaQueryParsingToken(ctx.IS()));
|
||||
|
||||
if (ctx.NOT() != null) {
|
||||
tokens.add(TOKEN_NOT);
|
||||
}
|
||||
|
||||
tokens.add(new JpaQueryParsingToken(ctx.EMPTY()));
|
||||
} else if (ctx.MEMBER() != null) {
|
||||
|
||||
if (ctx.NOT() != null) {
|
||||
tokens.add(TOKEN_NOT);
|
||||
}
|
||||
|
||||
tokens.add(new JpaQueryParsingToken(ctx.MEMBER()));
|
||||
tokens.add(new JpaQueryParsingToken(ctx.OF()));
|
||||
tokens.addAll(visit(ctx.path()));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitInstantiationTarget(HqlParser.InstantiationTargetContext ctx) {
|
||||
|
||||
|
||||
@@ -416,8 +416,7 @@ class HqlSpecificationTests {
|
||||
* @see #functionInvocationExampleWithCorrection()
|
||||
*/
|
||||
@Test
|
||||
@Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator")
|
||||
void functionInvocationExample_SPEC_BUG() {
|
||||
void functionInvocationExampleAsBooleanExpression() {
|
||||
|
||||
HqlQueryParser.parseQuery("""
|
||||
SELECT c
|
||||
@@ -436,6 +435,18 @@ class HqlSpecificationTests {
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3628
|
||||
void functionInvocationWithIsBoolean() {
|
||||
|
||||
assertQuery("""
|
||||
from RoleTmpl where find_in_set(:appId, appIds) is true
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
from RoleTmpl where find_in_set(:appId, appIds) is false
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateCaseExample1() {
|
||||
|
||||
@@ -751,7 +762,7 @@ class HqlSpecificationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void theRest26() {
|
||||
void collectionIsEmpty() {
|
||||
|
||||
HqlQueryParser.parseQuery("""
|
||||
DELETE
|
||||
@@ -759,6 +770,69 @@ class HqlSpecificationTests {
|
||||
WHERE c.status = 'inactive'
|
||||
AND c.orders IS EMPTY
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
DELETE
|
||||
FROM Customer c
|
||||
WHERE c.status = 'inactive'
|
||||
AND c.orders IS NOT EMPTY
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3628
|
||||
void booleanPredicate() {
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE c.orders IS TRUE
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE c.orders IS NOT TRUE
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE c.orders IS FALSE
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE c.orders IS NOT FALSE
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE c.orders IS NULL
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE c.orders IS NOT NULL
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3628
|
||||
void distinctFromPredicate() {
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE c.orders IS DISTINCT FROM c.payments
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
SELECT c
|
||||
FROM Customer c
|
||||
WHERE c.orders IS NOT DISTINCT FROM c.payments
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user