Skip to content

Commit

Permalink
Add support for JPA 3.2 additions to JPQL.
Browse files Browse the repository at this point in the history
See: #3136
Original Pull Request: #3695
  • Loading branch information
gregturn authored and christophstrobl committed Dec 20, 2024
1 parent 1c79fa8 commit b44271d
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -434,6 +440,7 @@ string_expression
| aggregate_expression
| case_expression
| function_invocation
| string_expression op='||' string_expression
| '(' subquery ')'
;

Expand Down Expand Up @@ -878,6 +885,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;
Expand All @@ -892,6 +900,7 @@ HAVING : H A V I N G;
IN : I N;
INDEX : I N D E X;
INNER : I N N E R;
INTERSECT : I N T E R S E C T;
IS : I S;
JOIN : J O I N;
KEY : K E Y;
Expand Down Expand Up @@ -936,6 +945,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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,29 @@ public QueryTokenStream visitSelect_statement(JpqlParser.Select_statementContext
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;
}

Expand Down Expand Up @@ -799,6 +822,25 @@ public QueryTokenStream visitOrderby_item(JpqlParser.Orderby_itemContext ctx) {
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;
}
Expand Down Expand Up @@ -1441,6 +1483,11 @@ public QueryTokenStream visitString_expression(JpqlParser.String_expressionConte
builder.append(visit(ctx.case_expression()));
} 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1646,42 +1646,36 @@ void hqlQueries() {
@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
""");
});

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 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 last
""");
});
}

@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
Expand Down Expand Up @@ -1831,6 +1825,42 @@ void powerShouldBeLegalInAQuery() {
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() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,59 @@ void powerShouldBeLegalInAQuery() {
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",
Expand Down

0 comments on commit b44271d

Please sign in to comment.