Add support for JPA 3.2 additions to JPQL.
See: #3136 Original Pull Request: #3695
This commit is contained in:
committed by
Mark Paluch
parent
a3a9c927ed
commit
ebda35d385
@@ -43,7 +43,13 @@ ql_statement
|
||||
;
|
||||
|
||||
select_statement
|
||||
: select_clause from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)?
|
||||
: select_clause from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)? (setOperator_with_select_statement)*
|
||||
;
|
||||
|
||||
setOperator_with_select_statement
|
||||
: INTERSECT select_statement
|
||||
| UNION select_statement
|
||||
| EXCEPT select_statement
|
||||
;
|
||||
|
||||
update_statement
|
||||
@@ -443,6 +449,7 @@ string_expression
|
||||
| aggregate_expression
|
||||
| case_expression
|
||||
| function_invocation
|
||||
| string_expression op='||' string_expression
|
||||
| string_cast_function
|
||||
| type_cast_function
|
||||
| '(' subquery ')'
|
||||
@@ -908,6 +915,7 @@ ELSE : E L S E;
|
||||
EMPTY : E M P T Y;
|
||||
ENTRY : E N T R Y;
|
||||
ESCAPE : E S C A P E;
|
||||
EXCEPT : E X C E P T;
|
||||
EXISTS : E X I S T S;
|
||||
EXP : E X P;
|
||||
EXTRACT : E X T R A C T;
|
||||
@@ -972,6 +980,7 @@ TREAT : T R E A T;
|
||||
TRIM : T R I M;
|
||||
TRUE : T R U E;
|
||||
TYPE : T Y P E;
|
||||
UNION : U N I O N;
|
||||
UPDATE : U P D A T E;
|
||||
UPPER : U P P E R;
|
||||
VALUE : V A L U E;
|
||||
|
||||
@@ -80,6 +80,29 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<QueryTokenStream> {
|
||||
builder.appendExpression(visit(ctx.orderby_clause()));
|
||||
}
|
||||
|
||||
ctx.setOperator_with_select_statement().forEach(setOperatorWithSelectStatementContext -> {
|
||||
tokens.addAll(visit(setOperatorWithSelectStatementContext));
|
||||
});
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitSetOperator_with_select_statement(
|
||||
JpqlParser.SetOperator_with_select_statementContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
|
||||
if (ctx.INTERSECT() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.INTERSECT()));
|
||||
} else if (ctx.UNION() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.UNION()));
|
||||
} else if (ctx.EXCEPT() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.EXCEPT()));
|
||||
}
|
||||
|
||||
tokens.addAll(visit(ctx.select_statement()));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -800,6 +823,25 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<QueryTokenStream> {
|
||||
if (ctx.nullsPrecedence() != null) {
|
||||
builder.append(visit(ctx.nullsPrecedence()));
|
||||
}
|
||||
if (ctx.nullsPrecedence() != null) {
|
||||
tokens.addAll(visit(ctx.nullsPrecedence()));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaQueryParsingToken> visitNullsPrecedence(JpqlParser.NullsPrecedenceContext ctx) {
|
||||
|
||||
List<JpaQueryParsingToken> tokens = new ArrayList<>();
|
||||
|
||||
tokens.add(new JpaQueryParsingToken(ctx.NULLS()));
|
||||
|
||||
if (ctx.FIRST() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.FIRST()));
|
||||
} else if (ctx.LAST() != null) {
|
||||
tokens.add(new JpaQueryParsingToken(ctx.LAST()));
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
@@ -1518,6 +1560,11 @@ class JpqlQueryRenderer extends JpqlBaseVisitor<QueryTokenStream> {
|
||||
builder.append(visit(ctx.type_cast_function()));
|
||||
} else if (ctx.function_invocation() != null) {
|
||||
builder.append(visit(ctx.function_invocation()));
|
||||
} else if (ctx.op != null) {
|
||||
|
||||
tokens.addAll(visit(ctx.string_expression(0)));
|
||||
tokens.add(new JpaQueryParsingToken(ctx.op));
|
||||
tokens.addAll(visit(ctx.string_expression(1)));
|
||||
} else if (ctx.subquery() != null) {
|
||||
|
||||
builder.append(TOKEN_OPEN_PAREN);
|
||||
|
||||
@@ -1651,29 +1651,25 @@ class HqlQueryRendererTests {
|
||||
@Test // GH-2962
|
||||
void orderByWithNullsFirstOrLastShouldWork() {
|
||||
|
||||
assertThatNoException().isThrownBy(() -> {
|
||||
parseWithoutChanges("""
|
||||
select a,
|
||||
case
|
||||
when a.geaendertAm is null then a.erstelltAm
|
||||
else a.geaendertAm end as mutationAm
|
||||
from Element a
|
||||
where a.erstelltDurch = :variable
|
||||
order by mutationAm desc nulls first
|
||||
""");
|
||||
});
|
||||
assertQuery("""
|
||||
select a,
|
||||
case
|
||||
when a.geaendertAm is null then a.erstelltAm
|
||||
else a.geaendertAm end as mutationAm
|
||||
from Element a
|
||||
where a.erstelltDurch = :variable
|
||||
order by mutationAm desc nulls first
|
||||
""");
|
||||
|
||||
assertThatNoException().isThrownBy(() -> {
|
||||
parseWithoutChanges("""
|
||||
select a,
|
||||
case
|
||||
when a.geaendertAm is null then a.erstelltAm
|
||||
else a.geaendertAm end as mutationAm
|
||||
from Element a
|
||||
where a.erstelltDurch = :variable
|
||||
order by mutationAm desc nulls last
|
||||
assertQuery("""
|
||||
select a,
|
||||
case
|
||||
when a.geaendertAm is null then a.erstelltAm
|
||||
else a.geaendertAm end as mutationAm
|
||||
from Element a
|
||||
where a.erstelltDurch = :variable
|
||||
order by mutationAm desc nulls last
|
||||
""");
|
||||
});
|
||||
}
|
||||
|
||||
@Test // GH-3882
|
||||
@@ -1693,14 +1689,12 @@ class HqlQueryRendererTests {
|
||||
@Test // GH-2964
|
||||
void roundFunctionShouldWorkLikeAnyOtherFunction() {
|
||||
|
||||
assertThatNoException().isThrownBy(() -> {
|
||||
parseWithoutChanges("""
|
||||
select round(count(ri) * 100 / max(ri.receipt.positions), 0) as perc
|
||||
from StockOrderItem oi
|
||||
right join StockReceiptItem ri
|
||||
on ri.article = oi.article
|
||||
""");
|
||||
});
|
||||
assertQuery("""
|
||||
select round(count(ri)*100/max(ri.receipt.positions), 0) as perc
|
||||
from StockOrderItem oi
|
||||
right join StockReceiptItem ri
|
||||
on ri.article = oi.article
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3711
|
||||
@@ -1883,6 +1877,42 @@ class HqlQueryRendererTests {
|
||||
assertQuery("select e.power.id from MyEntity e");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void doublePipeShouldBeValidAsAStringConcatOperator() {
|
||||
|
||||
assertQuery("""
|
||||
select e.name || ' ' || e.title
|
||||
from Employee e
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void additionalStringOperationsShouldWork() {
|
||||
|
||||
assertQuery("""
|
||||
select
|
||||
replace(e.name, 'Baggins', 'Proudfeet'),
|
||||
left(e.role, 4),
|
||||
right(e.home, 5),
|
||||
cast(e.distance_from_home, int)
|
||||
from Employee e
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void combinedSelectStatementsShouldWork() {
|
||||
|
||||
assertQuery("""
|
||||
select e from Employee e where e.last_name = 'Baggins'
|
||||
intersect
|
||||
select e from Employee e where e.first_name = 'Samwise'
|
||||
union
|
||||
select e from Employee e where e.home = 'The Shire'
|
||||
except
|
||||
select e from Employee e where e.home = 'Isengard'
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3219
|
||||
void extractFunctionShouldSupportAdditionalExtensions() {
|
||||
|
||||
|
||||
@@ -1034,6 +1034,59 @@ class JpqlQueryRendererTests {
|
||||
assertQuery("select e.power.id from MyEntity e");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void doublePipeShouldBeValidAsAStringConcatOperator() {
|
||||
|
||||
assertQuery("""
|
||||
select e.name || ' ' || e.title
|
||||
from Employee e
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void combinedSelectStatementsShouldWork() {
|
||||
|
||||
assertQuery("""
|
||||
select e from Employee e where e.last_name = 'Baggins'
|
||||
intersect
|
||||
select e from Employee e where e.first_name = 'Samwise'
|
||||
union
|
||||
select e from Employee e where e.home = 'The Shire'
|
||||
except
|
||||
select e from Employee e where e.home = 'Isengard'
|
||||
""");
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@Test // GH-3136
|
||||
void additionalStringOperationsShouldWork() {
|
||||
|
||||
assertQuery("""
|
||||
select
|
||||
replace(e.name, 'Baggins', 'Proudfeet'),
|
||||
left(e.role, 4),
|
||||
right(e.home, 5),
|
||||
cast(e.distance_from_home, int)
|
||||
from Employee e
|
||||
""");
|
||||
}
|
||||
|
||||
@Test // GH-3136
|
||||
void orderByWithNullsFirstOrLastShouldWork() {
|
||||
|
||||
assertQuery("""
|
||||
select a
|
||||
from Element a
|
||||
order by mutationAm desc nulls first
|
||||
""");
|
||||
|
||||
assertQuery("""
|
||||
select a
|
||||
from Element a
|
||||
order by mutationAm desc nulls last
|
||||
""");
|
||||
}
|
||||
|
||||
@ParameterizedTest // GH-3342
|
||||
@ValueSource(strings = { "select 1 as value from User u", "select -1 as value from User u",
|
||||
"select +1 as value from User u", "select +1 * -100 as value from User u",
|
||||
|
||||
Reference in New Issue
Block a user