Fix EQL parsing for MOD expression.

The samples used to validate MOD parsing in tests does not comply with the specification and falsely uses '/' instead of ',' as delimiter.

Definition is: expression ::= MOD(arithmetic_expression, arithmetic_expression)

See: https://eclipse.dev/eclipselink/api/4.0/eclipselink/org/eclipse/persistence/jpa/jpql/parser/ModExpression.html

Closes #3277
Original pull request: #3280
This commit is contained in:
Christoph Strobl
2023-12-20 10:32:41 +01:00
committed by Mark Paluch
parent 148beae6f3
commit 6d73fbc5f3
3 changed files with 14 additions and 8 deletions

View File

@@ -23,6 +23,7 @@ grammar Eql;
* * https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL
*
* @author Greg Turnquist
* @author Christoph Strobl
* @since 3.2
*/
}
@@ -509,7 +510,7 @@ functions_returning_numerics
| LN '(' arithmetic_expression ')'
| SIGN '(' arithmetic_expression ')'
| SQRT '(' arithmetic_expression ')'
| MOD '(' arithmetic_expression '/' arithmetic_expression ')'
| MOD '(' arithmetic_expression ',' arithmetic_expression ')'
| POWER '(' arithmetic_expression ',' arithmetic_expression ')'
| ROUND '(' arithmetic_expression ',' arithmetic_expression ')'
| SIZE '(' collection_valued_path_expression ')'
@@ -894,4 +895,4 @@ INTLITERAL : ('0' .. '9')+ ;
LONGLITERAL : ('0' .. '9')+ L;
DATELITERAL : '{' D STRINGLITERAL '}';
TIMELITERAL : '{' T STRINGLITERAL '}';
TIMESTAMPLITERAL : '{' T S STRINGLITERAL '}';
TIMESTAMPLITERAL : '{' T S STRINGLITERAL '}';

View File

@@ -24,6 +24,7 @@ import java.util.List;
* An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that renders an EQL query without making any changes.
*
* @author Greg Turnquist
* @author Christoph Strobl
* @since 3.2
*/
@SuppressWarnings({ "ConstantConditions", "DuplicatedCode" })
@@ -1912,7 +1913,8 @@ class EqlQueryRenderer extends EqlBaseVisitor<List<JpaQueryParsingToken>> {
tokens.add(new JpaQueryParsingToken(ctx.MOD(), false));
tokens.add(TOKEN_OPEN_PAREN);
tokens.addAll(visit(ctx.arithmetic_expression(0)));
tokens.add(new JpaQueryParsingToken("/"));
NOSPACE(tokens);
tokens.add(TOKEN_COMMA);
tokens.addAll(visit(ctx.arithmetic_expression(1)));
NOSPACE(tokens);
tokens.add(TOKEN_CLOSE_PAREN);

View File

@@ -25,10 +25,13 @@ import org.junit.jupiter.api.Test;
/**
* Tests built around examples of EQL found in the EclipseLink's docs at
* https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL<br/>
* With the exception of {@literal MOD} which is defined as {@literal MOD(arithmetic_expression , arithmetic_expression)},
* but shown in tests as {@literal MOD(arithmetic_expression ? arithmetic_expression)}.
* <br/>
* IMPORTANT: Purely verifies the parser without any transformations.
*
* @author Greg Turnquist
* @author Christoph Strobl
*/
class EqlComplianceTests {
@@ -214,7 +217,7 @@ class EqlComplianceTests {
assertQuery("SELECT e.name, CURRENT_TIMESTAMP FROM Employee e");
assertQuery("SELECT LENGTH(e.lastName) FROM Employee e");
assertQuery("SELECT LOWER(e.lastName) FROM Employee e");
assertQuery("SELECT MOD(e.hoursWorked / 8) FROM Employee e");
assertQuery("SELECT MOD(e.hoursWorked, 8) FROM Employee e");
assertQuery("SELECT NULLIF(e.salary, 0) FROM Employee e");
assertQuery("SELECT SQRT(o.RESULT) FROM Output o");
assertQuery("SELECT SUBSTRING(e.lastName, 0, 2) FROM Employee e");
@@ -243,7 +246,7 @@ class EqlComplianceTests {
assertQuery("SELECT e FROM Employee e WHERE CURRENT_TIME > CURRENT_TIMESTAMP");
assertQuery("SELECT e FROM Employee e WHERE LENGTH(e.lastName) > 0");
assertQuery("SELECT e FROM Employee e WHERE LOWER(e.lastName) = 'bilbo'");
assertQuery("SELECT e FROM Employee e WHERE MOD(e.hoursWorked / 8) > 0");
assertQuery("SELECT e FROM Employee e WHERE MOD(e.hoursWorked, 8) > 0");
assertQuery("SELECT e FROM Employee e WHERE NULLIF(e.salary, 0) is null");
assertQuery("SELECT e FROM Employee e WHERE SQRT(o.RESULT) > 0.0");
assertQuery("SELECT e FROM Employee e WHERE SUBSTRING(e.lastName, 0, 2) = 'Bilbo'");
@@ -272,7 +275,7 @@ class EqlComplianceTests {
assertQuery("SELECT e FROM Employee e ORDER BY CURRENT_TIMESTAMP");
assertQuery("SELECT e FROM Employee e ORDER BY LENGTH(e.lastName)");
assertQuery("SELECT e FROM Employee e ORDER BY LOWER(e.lastName)");
assertQuery("SELECT e FROM Employee e ORDER BY MOD(e.hoursWorked / 8)");
assertQuery("SELECT e FROM Employee e ORDER BY MOD(e.hoursWorked, 8)");
assertQuery("SELECT e FROM Employee e ORDER BY NULLIF(e.salary, 0)");
assertQuery("SELECT e FROM Employee e ORDER BY SQRT(o.RESULT)");
assertQuery("SELECT e FROM Employee e ORDER BY SUBSTRING(e.lastName, 0, 2)");
@@ -301,7 +304,7 @@ class EqlComplianceTests {
assertQuery("SELECT e FROM Employee e GROUP BY CURRENT_TIMESTAMP");
assertQuery("SELECT e FROM Employee e GROUP BY LENGTH(e.lastName)");
assertQuery("SELECT e FROM Employee e GROUP BY LOWER(e.lastName)");
assertQuery("SELECT e FROM Employee e GROUP BY MOD(e.hoursWorked / 8)");
assertQuery("SELECT e FROM Employee e GROUP BY MOD(e.hoursWorked, 8)");
assertQuery("SELECT e FROM Employee e GROUP BY NULLIF(e.salary, 0)");
assertQuery("SELECT e FROM Employee e GROUP BY SQRT(o.RESULT)");
assertQuery("SELECT e FROM Employee e GROUP BY SUBSTRING(e.lastName, 0, 2)");
@@ -329,7 +332,7 @@ class EqlComplianceTests {
assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING CURRENT_TIME > CURRENT_TIMESTAMP");
assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING LENGTH(e.lastName) > 0");
assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING LOWER(e.lastName) = 'bilbo'");
assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING MOD(e.hoursWorked / 8) > 0");
assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING MOD(e.hoursWorked, 8) > 0");
assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING NULLIF(e.salary, 0) is null");
assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING SQRT(o.RESULT) > 0.0");
assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING SUBSTRING(e.lastName, 0, 2) = 'Bilbo'");