From 70b19e3ee7ca4e02fabd501346a437ee38468b5e Mon Sep 17 00:00:00 2001 From: Radek Felcman Date: Tue, 20 Jun 2023 14:17:20 +0200 Subject: [PATCH 1/2] JPQL 3.2 `||` as a String concat operator Signed-off-by: Radek Felcman --- .../config/ParserValidationType.java | 5 +- .../expressions/ExpressionOperator.java | 17 +- .../expressions/ExpressionString.java | 42 ++++ .../databaseaccess/DatasourcePlatform.java | 3 + .../jpa/jpql/ExpressionBuilderVisitor.java | 30 ++- .../internal/jpa/jpql/HermesParser.java | 7 +- .../internal/jpa/jpql/ReportItemBuilder.java | 17 +- .../internal/jpa/jpql/TypeResolver.java | 9 +- .../query/TestQuerySyntaxStringTests.java | 214 ++++++++++++++++++ .../jpa/test/query/model/StringEntity.java | 76 +++++++ .../jpa/jpql/AbstractGrammarValidator.java | 27 ++- .../jpa/jpql/EclipseLinkVersion.java | 18 +- .../persistence/jpa/jpql/JPAVersion.java | 13 +- .../jpa/jpql/JPQLQueryProblemMessages.java | 11 +- .../persistence/jpa/jpql/WordParser.java | 17 +- .../parser/AbstractExpressionVisitor.java | 8 +- .../parser/AnonymousExpressionVisitor.java | 9 +- .../jpql/parser/ConcatPipesExpression.java | 44 ++++ .../parser/DefaultEclipseLinkJPQLGrammar.java | 6 +- .../jpa/jpql/parser/DefaultJPQLGrammar.java | 6 +- .../parser/EclipseLinkJPQLGrammar4_1.java | 119 ++++++++++ .../jpa/jpql/parser/Expression.java | 9 +- .../jpa/jpql/parser/ExpressionVisitor.java | 11 +- .../jpa/jpql/parser/JPQLGrammar.java | 12 +- .../jpa/jpql/parser/JPQLGrammar3_2.java | 121 ++++++++++ .../parser/SimpleStringExpressionBNF.java | 50 ++++ .../jpa/jpql/parser/StringExpression.java | 88 +++++++ .../jpql/parser/StringExpressionFactory.java | 117 ++++++++++ .../jpa/jpql/parser/StringFactorBNF.java | 49 ++++ .../jpa/jpql/parser/StringTermBNF.java | 51 +++++ .../tools/AbstractContentAssistVisitor.java | 139 +++++++++++- .../AbstractActualJPQLQueryFormatter.java | 12 +- .../model/AbstractJPQLQueryFormatter.java | 11 +- .../tools/model/BasicStateObjectBuilder.java | 26 ++- .../query/AbstractStateObjectVisitor.java | 9 +- .../query/AnonymousStateObjectVisitor.java | 10 +- .../ConcatPipesExpressionStateObject.java | 103 +++++++++ .../tools/model/query/StateObjectVisitor.java | 12 +- .../query/StringExpressionStateObject.java | 95 ++++++++ .../jpql/tools/resolver/ResolverBuilder.java | 10 +- .../jpa/jpql/jpa_jpql_validation.properties | 15 +- .../jpa/tests/jpql/JPQLQueries3_2.java | 42 ++++ .../tests/jpql/parser/AllJPQLParserTests.java | 3 +- .../jpql/parser/AllJPQLParserTests3_2.java | 43 ++++ .../tests/jpql/parser/JPQLGrammarTools.java | 17 +- .../jpa/tests/jpql/parser/JPQLParserTest.java | 24 +- .../tests/jpql/parser/JPQLParserTester.java | 8 +- .../tests/jpql/parser/JPQLParserTests3_2.java | 36 +++ .../tests/jpql/parser/JPQLQueriesTest3_2.java | 119 ++++++++++ .../jpql/parser/JPQLQueryBNFAccessor.java | 11 +- .../jpql/tools/AbstractContentAssistTest.java | 19 +- 51 files changed, 1914 insertions(+), 56 deletions(-) create mode 100644 foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionString.java create mode 100644 jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestQuerySyntaxStringTests.java create mode 100644 jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/model/StringEntity.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/ConcatPipesExpression.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkJPQLGrammar4_1.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/JPQLGrammar3_2.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/SimpleStringExpressionBNF.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringExpression.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringExpressionFactory.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringFactorBNF.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringTermBNF.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/ConcatPipesExpressionStateObject.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/StringExpressionStateObject.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/JPQLQueries3_2.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/AllJPQLParserTests3_2.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTests3_2.java create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLQueriesTest3_2.java diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/config/ParserValidationType.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/config/ParserValidationType.java index 7a63661daa2..c67b363335a 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/config/ParserValidationType.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/config/ParserValidationType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,6 +14,8 @@ // Oracle - initial API and implementation from Oracle TopLink // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.config; /** @@ -34,6 +36,7 @@ public class ParserValidationType { public static final String JPA22 = "JPA 2.2"; public static final String JPA30 = "JPA 3.0"; public static final String JPA31 = "JPA 3.1"; + public static final String JPA32 = "JPA 3.2"; public static final String None = "None"; public static final String DEFAULT = EclipseLink; diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java index 43d060ca902..cc679f632d7 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2022 IBM Corporation. All rights reserved. * * This program and the accompanying materials are made available under the @@ -20,6 +20,8 @@ // - 530214: trim operation should not bind parameters // 02/01/2022: Tomas Kraus // - Issue 1442: Implement New Jakarta Persistence 3.1 Features +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.expressions; import java.io.Serializable; @@ -260,6 +262,9 @@ public class ExpressionOperator implements Serializable { public static final int Cot = 95; public static final int Negate = 135; + // String + public static final int ConcatPipes = 152; + // Object-relational public static final int Deref = 82; public static final int Ref = 83; @@ -736,6 +741,16 @@ public static ExpressionOperator concat() { return operator; } + /** + * INTERNAL: + * Build operator. + */ + public static ExpressionOperator concatPipes() { + ExpressionOperator operator = simpleMath(ConcatPipes, "||"); + operator.setIsBindingSupported(false); + return operator; + } + /** * INTERNAL: * Compare between in memory. diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionString.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionString.java new file mode 100644 index 00000000000..4e68bf311a0 --- /dev/null +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionString.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.expressions; + +/** + *

+ * Purpose: This class contains String operators.

+ *

Example: + *

+ *  ExpressionBuilder builder = new ExpressionBuilder();
+ *  Expression stringResult = ExpressionString.concatPipes("abcd", "xyz");
+ *  session.readAllObjects(Company.class, stringResult);
+ * 
+ */ +public final class ExpressionString { + + private ExpressionString() { + } + + /** + * PUBLIC: + * Return a new expression that applies the function to the given expression. + */ + public static Expression concatPipes(Expression right, Object left) { + ExpressionOperator anOperator = Expression.getOperator(ExpressionOperator.ConcatPipes); + return anOperator.expressionFor(right, left); + } +} diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java index 8215fd154e6..11f4e7c5f16 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java @@ -21,6 +21,8 @@ // - 529602: Added support for CLOBs in DELETE statements for Oracle // 02/01/2022: Tomas Kraus // - Issue 1442: Implement New Jakarta Persistence 3.1 Features +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.internal.databaseaccess; import java.io.IOException; @@ -452,6 +454,7 @@ protected void initializePlatformOperators() { addOperator(ExpressionOperator.toLowerCase()); addOperator(ExpressionOperator.chr()); addOperator(ExpressionOperator.concat()); + addOperator(ExpressionOperator.concatPipes()); addOperator(ExpressionOperator.hexToRaw()); addOperator(ExpressionOperator.initcap()); addOperator(ExpressionOperator.instring()); diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/ExpressionBuilderVisitor.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/ExpressionBuilderVisitor.java index c8b9e9fca47..f1b48917099 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/ExpressionBuilderVisitor.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/ExpressionBuilderVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2006, 2021 IBM Corporation. All rights reserved. * * This program and the accompanying materials are made available under the @@ -24,6 +24,8 @@ // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 // - Issue 317: Implement LOCAL DATE, LOCAL TIME and LOCAL DATETIME. +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.internal.jpa.jpql; import java.sql.Date; @@ -78,6 +80,7 @@ import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression; import org.eclipse.persistence.jpa.jpql.parser.ComparisonExpression; import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression; +import org.eclipse.persistence.jpa.jpql.parser.ConcatPipesExpression; import org.eclipse.persistence.jpa.jpql.parser.ConnectByClause; import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.CountFunction; @@ -797,6 +800,31 @@ public void visit(ConcatExpression expression) { type[0] = String.class; } + @Override + public void visit(ConcatPipesExpression expression) { + //Convert || string operator into function CONCAT() as not every DB supports it + //but DB platform should translate it into platform valid SQL + List expressions = new ArrayList<>(2); + expressions.add(expression.getLeftExpression()); + expressions.add(expression.getRightExpression()); + Expression newExpression = null; + + for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expressions) { + child.accept(this); + if (newExpression == null) { + newExpression = queryExpression; + } + else { + newExpression = newExpression.concat(queryExpression); + } + } + + queryExpression = newExpression; + + // Set the expression type + type[0] = String.class; + } + @Override public void visit(ConnectByClause expression) { expression.getExpression().accept(this); diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/HermesParser.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/HermesParser.java index ab4af91908e..40d980317de 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/HermesParser.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/HermesParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,6 +14,8 @@ // Oracle - initial API and implementation // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.internal.jpa.jpql; import java.text.MessageFormat; @@ -43,6 +45,7 @@ import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar2_2; import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar3_0; import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar3_1; +import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar3_2; import org.eclipse.persistence.jpa.jpql.parser.SelectStatement; import org.eclipse.persistence.jpa.jpql.parser.UpdateStatement; import org.eclipse.persistence.queries.DatabaseQuery; @@ -240,6 +243,8 @@ private JPQLGrammar jpqlGrammar() { return JPQLGrammar3_0.instance(); case ParserValidationType.JPA31: return JPQLGrammar3_1.instance(); + case ParserValidationType.JPA32: + return JPQLGrammar3_2.instance(); default: return DefaultEclipseLinkJPQLGrammar.instance(); } diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/ReportItemBuilder.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/ReportItemBuilder.java index eb891c8a88f..ce77ef49331 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/ReportItemBuilder.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/ReportItemBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019 IBM Corporation. All rights reserved. * * This program and the accompanying materials are made available under the @@ -15,6 +15,8 @@ // Oracle - initial API and implementation // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.internal.jpa.jpql; import java.util.ArrayList; @@ -34,6 +36,7 @@ import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression; import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression; +import org.eclipse.persistence.jpa.jpql.parser.ConcatPipesExpression; import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.CountFunction; import org.eclipse.persistence.jpa.jpql.parser.DateTime; @@ -231,6 +234,18 @@ public void visit(ConcatExpression expression) { addAttribute(ExpressionTools.EMPTY_STRING, queryExpression, type[0]); } + @Override + public void visit(ConcatPipesExpression expression) { + + Expression queryExpression = queryContext.buildExpression(expression, type); + + if (type[0] == Object.class) { + type[0] = null; + } + + addAttribute("ConcatPipes", queryExpression, type[0]); + } + @Override public void visit(ConstructorExpression expression) { diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/TypeResolver.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/TypeResolver.java index 73c7f82e7c8..654fb1c22f1 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/TypeResolver.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/TypeResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,8 @@ // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 // - Issue 317: Implement LOCAL DATE, LOCAL TIME and LOCAL DATETIME. +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.internal.jpa.jpql; import java.lang.reflect.Field; @@ -547,6 +549,11 @@ public void visit(ConcatExpression expression) { type = String.class; } + @Override + public void visit(ConcatPipesExpression expression) { + type = String.class; + } + @Override public void visit(ConnectByClause expression) { type = Object.class; diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestQuerySyntaxStringTests.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestQuerySyntaxStringTests.java new file mode 100644 index 00000000000..f2069f0c70a --- /dev/null +++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestQuerySyntaxStringTests.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// 02/01/2022: Tomas Kraus +// - Issue 1442: Implement New Jakarta Persistence 3.1 Features +package org.eclipse.persistence.jpa.test.query; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.TypedQuery; +import org.eclipse.persistence.jpa.test.query.model.StringEntity; +import org.eclipse.persistence.jpa.test.framework.DDLGen; +import org.eclipse.persistence.jpa.test.framework.Emf; +import org.eclipse.persistence.jpa.test.framework.EmfRunner; +import org.eclipse.persistence.jpa.test.framework.Property; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test math functions in CriteriaBuilder. + * Added to Jakarta Persistence API as PR #351 + */ +@RunWith(EmfRunner.class) +public class TestQuerySyntaxStringTests { + + @Emf(createTables = DDLGen.DROP_CREATE, + classes = { + StringEntity.class + }, + properties = { + @Property(name = "eclipselink.cache.shared.default", value = "false"), + // This property remaps String from VARCHAR->NVARCHAR(or db equivalent) + @Property(name = "eclipselink.target-database-properties", + value = "UseNationalCharacterVaryingTypeForString=true"), + }) + private EntityManagerFactory emf; + + private final StringEntity[] INITIAL_DATA = { + new StringEntity(1, "John", "Smith"), + }; + + @Before + public void setup() { + final EntityManager em = emf.createEntityManager(); + try { + em.getTransaction().begin(); + for (StringEntity e : INITIAL_DATA) { + em.persist(e); + } + em.flush(); + em.getTransaction().commit(); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + em.close(); + } + } + + @After + public void cleanup() { + final EntityManager em = emf.createEntityManager(); + try { + em.getTransaction().begin(); + em.createQuery("DELETE FROM StringEntity e").executeUpdate(); + em.flush(); + em.getTransaction().commit(); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + em.close(); + } + } + + @Test + public void testString_concatOperatorSelect01() { + if (emf == null) + return; + + EntityManager em = emf.createEntityManager(); + + try { + TypedQuery query = em.createQuery("SELECT s.firstName || s.lastName FROM StringEntity s WHERE s.id = 1", String.class); + String result = query.getSingleResult(); + Assert.assertEquals("JohnSmith", result); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + if(em.isOpen()) { + em.close(); + } + } + } + + @Test + public void testString_concatOperatorSelect02() { + if (emf == null) + return; + + EntityManager em = emf.createEntityManager(); + + try { + TypedQuery query = em.createQuery("SELECT s.firstName || s.lastName || s.firstName || s.lastName FROM StringEntity s WHERE s.id = 1", String.class); + String result = query.getSingleResult(); + Assert.assertEquals("JohnSmithJohnSmith", result); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + if(em.isOpen()) { + em.close(); + } + } + } + + @Test + public void testString_concatOperatorSelect03() { + if (emf == null) + return; + + EntityManager em = emf.createEntityManager(); + + try { + TypedQuery query = em.createQuery("SELECT s.firstName || (s.lastName || s.firstName) || s.lastName FROM StringEntity s WHERE s.id = 1", String.class); + String result = query.getSingleResult(); + Assert.assertEquals("JohnSmithJohnSmith", result); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + if(em.isOpen()) { + em.close(); + } + } + } + + @Test + public void testString_concatOperatorSelect04() { + if (emf == null) + return; + + EntityManager em = emf.createEntityManager(); + + try { + TypedQuery query = em.createQuery("SELECT s.firstName || CAST(s.id AS CHAR(1)) FROM StringEntity s WHERE s.id = 1", String.class); + String result = query.getSingleResult(); + Assert.assertEquals("John1", result); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + if(em.isOpen()) { + em.close(); + } + } + } + + @Test + public void testString_concatOperatorWhere01() { + if (emf == null) + return; + + EntityManager em = emf.createEntityManager(); + + try { + TypedQuery query = em.createQuery("SELECT s.firstName || s.lastName FROM StringEntity s WHERE s.firstName || s.lastName = 'JohnSmith'", String.class); + String result = query.getSingleResult(); + Assert.assertEquals("JohnSmith", result); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + if(em.isOpen()) { + em.close(); + } + } + } + + @Test + public void testString_concatOperatorWhere02() { + if (emf == null) + return; + + EntityManager em = emf.createEntityManager(); + + try { + TypedQuery query = em.createQuery("SELECT s.firstName || s.lastName FROM StringEntity s WHERE s.firstName || s.lastName || s.firstName = 'JohnSmithJohn'", String.class); + String result = query.getSingleResult(); + Assert.assertEquals("JohnSmith", result); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + if(em.isOpen()) { + em.close(); + } + } + } +} diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/model/StringEntity.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/model/StringEntity.java new file mode 100644 index 00000000000..b229263ecb1 --- /dev/null +++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/model/StringEntity.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.test.query.model; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +/** + * JPA Entity used in {@code CriteriaBuilder} string operators tests. + */ +@Entity +public class StringEntity { + + @Id + private Integer id; + + private String firstName; + + private String lastName; + + public StringEntity() { + } + + public StringEntity(Integer id, String firstName, String lastName) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + @Override + public String toString() { + return "StringEntity{" + + "id=" + id + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + '}'; + } +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractGrammarValidator.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractGrammarValidator.java index f407eed9ebb..28330bc7a22 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractGrammarValidator.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractGrammarValidator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2022 IBM Corporation. All rights reserved. * * This program and the accompanying materials are made available under the @@ -15,6 +15,8 @@ // Oracle - initial API and implementation // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql; import java.util.ArrayList; @@ -57,6 +59,7 @@ import org.eclipse.persistence.jpa.jpql.parser.ComparisonExpression; import org.eclipse.persistence.jpa.jpql.parser.CompoundExpression; import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression; +import org.eclipse.persistence.jpa.jpql.parser.ConcatPipesExpression; import org.eclipse.persistence.jpa.jpql.parser.ConditionalExpressionBNF; import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.CountFunction; @@ -118,7 +121,10 @@ import org.eclipse.persistence.jpa.jpql.parser.SizeExpression; import org.eclipse.persistence.jpa.jpql.parser.SqrtExpression; import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression; +import org.eclipse.persistence.jpa.jpql.parser.StringExpression; +import org.eclipse.persistence.jpa.jpql.parser.StringExpressionBNF; import org.eclipse.persistence.jpa.jpql.parser.StringLiteral; +import org.eclipse.persistence.jpa.jpql.parser.StringTermBNF; import org.eclipse.persistence.jpa.jpql.parser.SubExpression; import org.eclipse.persistence.jpa.jpql.parser.SubstringExpression; import org.eclipse.persistence.jpa.jpql.parser.SubtractionExpression; @@ -136,6 +142,7 @@ import org.eclipse.persistence.jpa.jpql.parser.WhereClause; import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.*; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.StringExpression_InvalidLeftExpression; import static org.eclipse.persistence.jpa.jpql.parser.Expression.*; /** @@ -2171,6 +2178,19 @@ protected void validateArithmeticExpression(ArithmeticExpression expression) { ); } + protected void validateStringExpression(StringExpression expression) { + validateCompoundExpression( + expression, + expression.getStringSign(), + StringExpression_MissingLeftExpression, + StringExpression_InvalidLeftExpression, + StringExpression_MissingRightExpression, + StringExpression_InvalidRightExpression, + StringExpressionBNF.ID, + StringTermBNF.ID + ); + } + /** * Validates the given {@link Expression} by making sure each child is separated by a comma. * @@ -2947,6 +2967,11 @@ public void visit(ConcatExpression expression) { } } + @Override + public void visit(ConcatPipesExpression expression) { + validateStringExpression(expression); + } + @Override public void visit(ConstructorExpression expression) { diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkVersion.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkVersion.java index 39b391d3b35..75a227c8761 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkVersion.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkVersion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,6 +14,8 @@ // Oracle - initial API and implementation // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql; /** @@ -24,9 +26,9 @@ public enum EclipseLinkVersion { /** - * A constant that points to the current release of EclipseLink, which is 4.0. + * A constant that points to the current release of EclipseLink, which is 4.1. */ - DEFAULT_VERSION(4.0), + DEFAULT_VERSION(4.1), /** * The constant for the EclipseLink 1.x release. @@ -76,7 +78,12 @@ public enum EclipseLinkVersion { /** * The constant for the EclipseLink 4.0 release. */ - VERSION_4_0(4.0); + VERSION_4_0(4.0), + + /** + * The constant for the EclipseLink 4.1 release. + */ + VERSION_4_1(4.1); /** * The real version number. @@ -116,7 +123,7 @@ public static EclipseLinkVersion value(String value) { * @return The list of unique constants */ public static EclipseLinkVersion[] versions() { - EclipseLinkVersion[] values = new EclipseLinkVersion[9]; + EclipseLinkVersion[] values = new EclipseLinkVersion[10]; values[0] = VERSION_1_x; values[1] = VERSION_2_0; values[2] = VERSION_2_1; @@ -126,6 +133,7 @@ public static EclipseLinkVersion[] versions() { values[6] = VERSION_2_5; values[7] = VERSION_3_0; values[8] = VERSION_4_0; + values[9] = VERSION_4_1; return values; } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/JPAVersion.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/JPAVersion.java index a9e2aac4105..93705722e9d 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/JPAVersion.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/JPAVersion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,7 +12,8 @@ // Contributors: // Oracle - initial API and implementation -// +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql; /** @@ -61,7 +62,12 @@ public enum JPAVersion { /** * The constant for the Jakarta Persistence specification 3.1. */ - VERSION_3_1(3.1); + VERSION_3_1(3.1), + + /** + * The constant for the Jakarta Persistence specification 3.2. + */ + VERSION_3_2(3.2); /** * The real version number. @@ -106,6 +112,7 @@ public static JPAVersion[] versions() { values[3] = VERSION_2_2; values[4] = VERSION_3_0; values[5] = VERSION_3_1; + values[6] = VERSION_3_2; return values; } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/JPQLQueryProblemMessages.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/JPQLQueryProblemMessages.java index 2bcc2c4d8a0..ff8d42ef7bc 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/JPQLQueryProblemMessages.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/JPQLQueryProblemMessages.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,7 +12,8 @@ // Contributors: // Oracle - initial API and implementation -// +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql; /** @@ -333,6 +334,12 @@ public interface JPQLQueryProblemMessages { String StringLiteral_MissingClosingQuote = "STRING_LITERAL_MISSING_CLOSING_QUOTE"; String SubExpression_MissingExpression = "SUB_EXPRESSION_MISSING_EXPRESSION"; String SubExpression_MissingRightParenthesis = "SUB_EXPRESSION_MISSING_RIGHT_PARENTHESIS"; + String StringExpression_InvalidLeftExpression = "STRING_EXPRESSION_INVALID_LEFT_EXPRESSION"; + String StringExpression_InvalidRightExpression = "STRING_EXPRESSION_INVALID_RIGHT_EXPRESSION"; + String StringExpression_MissingLeftExpression = "STRING_EXPRESSION_MISSING_LEFT_EXPRESSION"; + String StringExpression_MissingRightExpression = "STRING_EXPRESSION_MISSING_RIGHT_EXPRESSION"; + String StringFactor_InvalidExpression = "STRING_FACTOR_INVALID_EXPRESSION"; + String StringFactor_MissingExpression = "STRING_FACTOR_MISSING_EXPRESSION"; String SubstringExpression_FirstExpression_WrongType = "SUBSTRING_EXPRESSION_FIRST_EXPRESSION_WRONG_TYPE"; String SubstringExpression_InvalidFirstExpression = "SUBSTRING_EXPRESSION_INVALID_FIRST_EXPRESSION"; String SubstringExpression_InvalidSecondExpression = "SUBSTRING_EXPRESSION_INVALID_SECOND_EXPRESSION"; diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/WordParser.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/WordParser.java index 7c6d832b4e0..768a212ea01 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/WordParser.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/WordParser.java @@ -178,7 +178,6 @@ public WordType getWordType() { */ public boolean isArithmeticSymbol(char character) { return character == '>' || - character == '!' || character == '<' || character == '/' || character == '*' || @@ -1124,6 +1123,22 @@ else if (character == '>' || } } + // || + else if (character == '|') { + + // End of the text + if (endIndex == length) { + return endIndex; + } + + // Scan the next character + char nextCharacter = text.charAt(endIndex); + + if (nextCharacter == '|') { + return ++endIndex; + } + } + // Done scanning else if (isWordSeparator(character)) { return --endIndex; diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractExpressionVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractExpressionVisitor.java index 0925d355298..e6d9f29f4a8 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractExpressionVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractExpressionVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,8 @@ // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 // - Issue 317: Implement LOCAL DATE, LOCAL TIME and LOCAL DATETIME. +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.parser; /** @@ -104,6 +106,10 @@ public void visit(ComparisonExpression expression) { public void visit(ConcatExpression expression) { } + @Override + public void visit(ConcatPipesExpression expression) { + } + @Override public void visit(ConstructorExpression expression) { } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AnonymousExpressionVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AnonymousExpressionVisitor.java index aa93579d181..ef73e554d46 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AnonymousExpressionVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AnonymousExpressionVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,8 @@ // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 // - Issue 317: Implement LOCAL DATE, LOCAL TIME and LOCAL DATETIME. +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.parser; /** @@ -121,6 +123,11 @@ public void visit(ConcatExpression expression) { visit((Expression) expression); } + @Override + public void visit(ConcatPipesExpression expression) { + visit((Expression) expression); + } + @Override public void visit(ConstructorExpression expression) { visit((Expression) expression); diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/ConcatPipesExpression.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/ConcatPipesExpression.java new file mode 100644 index 00000000000..a7b63154c29 --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/ConcatPipesExpression.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.jpql.parser; + +/** + * String operators. A concat is a string operation representing the + * combination of two operands together. + * + *
BNF: string_expression ::= string_expression || string_term
+ * + * @version 4.1 + * @since 4.1 + * @author Radek Felcman + */ +public final class ConcatPipesExpression extends StringExpression { + + /** + * Creates a new ConcatExpressionPipes. + * + * @param parent The parent of this expression + */ + public ConcatPipesExpression(AbstractExpression parent) { + super(parent, CONCAT_PIPES); + } + + @Override + public void accept(ExpressionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/DefaultEclipseLinkJPQLGrammar.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/DefaultEclipseLinkJPQLGrammar.java index f0ac88d7537..4e3e738234c 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/DefaultEclipseLinkJPQLGrammar.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/DefaultEclipseLinkJPQLGrammar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,6 +14,8 @@ // Oracle - initial API and implementation // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.parser; import org.eclipse.persistence.jpa.jpql.JPAVersion; @@ -53,7 +55,7 @@ private DefaultEclipseLinkJPQLGrammar() { * @return The latest {@link JPQLGrammar} that supports EclipseLink */ public static JPQLGrammar instance() { - return EclipseLinkJPQLGrammar4_0.instance(); + return EclipseLinkJPQLGrammar4_1.instance(); } @Override diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/DefaultJPQLGrammar.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/DefaultJPQLGrammar.java index 84bf9cea2c7..40a0fbf41eb 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/DefaultJPQLGrammar.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/DefaultJPQLGrammar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,6 +14,8 @@ // Oracle - initial API and implementation // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.parser; import org.eclipse.persistence.jpa.jpql.ExpressionTools; @@ -54,7 +56,7 @@ private DefaultJPQLGrammar() { * @return The singleton instance of this {@link DefaultJPQLGrammar} */ public static JPQLGrammar instance() { - return JPQLGrammar3_1.instance(); + return JPQLGrammar3_2.instance(); } @Override diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkJPQLGrammar4_1.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkJPQLGrammar4_1.java new file mode 100644 index 00000000000..51a92a4f0ac --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkJPQLGrammar4_1.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.jpql.parser; + +import org.eclipse.persistence.jpa.jpql.EclipseLinkVersion; +import org.eclipse.persistence.jpa.jpql.JPAVersion; + +/** + *

This {@link JPQLGrammar} provides support for parsing JPQL queries defined + * in Jakarta Persistence 3.2 and the additional support provided by EclipseLink 4.1.

+ */ +public class EclipseLinkJPQLGrammar4_1 extends AbstractJPQLGrammar { + + /** + * The singleton instance of this {@link EclipseLinkJPQLGrammar4_1}. + */ + private static final JPQLGrammar INSTANCE = new EclipseLinkJPQLGrammar4_1(); + + /** + * The EclipseLink version, which is 4.1. + */ + public static final EclipseLinkVersion VERSION = EclipseLinkVersion.VERSION_4_1; + + /** + * Creates a new EclipseLinkJPQLGrammar4_0. + */ + public EclipseLinkJPQLGrammar4_1() { + super(); + } + + /** + * Creates a new EclipseLinkJPQLGrammar4_0. + * + * @param jpqlGrammar The {@link JPQLGrammar} to extend with the content of this one without + * instantiating the base {@link JPQLGrammar} + */ + public EclipseLinkJPQLGrammar4_1(AbstractJPQLGrammar jpqlGrammar) { + super(jpqlGrammar); + } + + /** + * Extends the given {@link JPQLGrammar} with the information of this one without instantiating + * the base {@link JPQLGrammar}. + * + * @param jpqlGrammar The {@link JPQLGrammar} to extend with the content of this one without + * instantiating the base {@link JPQLGrammar} + */ + public static void extend(AbstractJPQLGrammar jpqlGrammar) { + new EclipseLinkJPQLGrammar4_1(jpqlGrammar); + } + + /** + * Returns the singleton instance of this class. + * + * @return The singleton instance of {@link EclipseLinkJPQLGrammar4_1} + */ + public static JPQLGrammar instance() { + return INSTANCE; + } + + @Override + protected JPQLGrammar buildBaseGrammar() { + // First build the JPQL 3.2 grammar + JPQLGrammar3_2 jpqlGrammar = new JPQLGrammar3_2(); + // Extend it by adding the EclipseLink 2.0 - 4.0 additional support + EclipseLinkJPQLGrammar2_0.extend(jpqlGrammar); + EclipseLinkJPQLGrammar2_1.extend(jpqlGrammar); + EclipseLinkJPQLGrammar2_4.extend(jpqlGrammar); + EclipseLinkJPQLGrammar2_5.extend(jpqlGrammar); + EclipseLinkJPQLGrammar4_0.extend(jpqlGrammar); + return jpqlGrammar; + } + + @Override + public JPAVersion getJPAVersion() { + return JPAVersion.VERSION_3_2; + } + + @Override + public String getProvider() { + return DefaultEclipseLinkJPQLGrammar.PROVIDER_NAME; + } + + @Override + public String getProviderVersion() { + return VERSION.getVersion(); + } + + @Override + protected void initializeBNFs() { + } + + @Override + protected void initializeExpressionFactories() { + } + + @Override + protected void initializeIdentifiers() { + } + + @Override + public String toString() { + return "EclipseLink 4.1"; + } + +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/Expression.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/Expression.java index ae6bd40d73e..fea393b5587 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/Expression.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/Expression.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,8 @@ // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 // - Issue 317: Implement LOCAL DATE, LOCAL TIME and LOCAL DATETIME. +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.parser; import org.eclipse.persistence.jpa.jpql.utility.iterable.ListIterable; @@ -141,6 +143,11 @@ public interface Expression { */ String CONCAT = "CONCAT"; + /** + * The constant for String concat operation like SQL '||'. + */ + String CONCAT_PIPES = "||"; + /** * The constant for 'CONNECT BY'. * diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/ExpressionVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/ExpressionVisitor.java index 5a5ee4f7888..5b7c4dd720f 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/ExpressionVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/ExpressionVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,8 @@ // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 // - Issue 317: Implement LOCAL DATE, LOCAL TIME and LOCAL DATETIME. +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.parser; /** @@ -149,6 +151,13 @@ public interface ExpressionVisitor { */ void visit(ConcatExpression expression); + /** + * Visits the {@link ConcatPipesExpression} expression. + * + * @param expression The {@link Expression} to visit + */ + void visit(ConcatPipesExpression expression); + /** * Visits the {@link ConstructorExpression} expression. * diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/JPQLGrammar.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/JPQLGrammar.java index 7ead87988bf..3fcb73ac8c5 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/JPQLGrammar.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/JPQLGrammar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,7 +12,8 @@ // Contributors: // Oracle - initial API and implementation -// +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.parser; import org.eclipse.persistence.jpa.jpql.JPAVersion; @@ -28,6 +29,10 @@ *
  • {@link JPQLGrammar1_0} defines the JPQL grammar based on JPA 1.0;
  • *
  • {@link JPQLGrammar2_0} defines the JPQL grammar based on JPA 2.0;
  • *
  • {@link JPQLGrammar2_1} defines the JPQL grammar based on JPA 2.1;
  • + *
  • {@link JPQLGrammar2_2} defines the JPQL grammar based on JPA 2.2;
  • + *
  • {@link JPQLGrammar3_0} defines the JPQL grammar based on JPA 3.0;
  • + *
  • {@link JPQLGrammar3_1} defines the JPQL grammar based on JPA 3.1;
  • + *
  • {@link JPQLGrammar3_2} defines the JPQL grammar based on JPA 3.2;
  • *
  • {@link EclipseLinkJPQLGrammar1} defines the JPQL grammar based on JPA 1.0 and EclipseLink 1.x;
  • *
  • {@link EclipseLinkJPQLGrammar2_0} defines the JPQL grammar based on JPA 2.0 and the additional EclipseLink 2.0 support;
  • *
  • {@link EclipseLinkJPQLGrammar2_1} defines the JPQL grammar based on JPA 2.0 and the additional EclipseLink 2.1 support.
  • @@ -35,6 +40,9 @@ *
  • {@link EclipseLinkJPQLGrammar2_3} defines the JPQL grammar based on JPA 2.0 and the additional EclipseLink 2.3 support.
  • *
  • {@link EclipseLinkJPQLGrammar2_4} defines the JPQL grammar based on JPA 2.1 and the additional EclipseLink 2.4 support.
  • *
  • {@link EclipseLinkJPQLGrammar2_5} defines the JPQL grammar based on JPA 2.1 and the additional EclipseLink 2.5 support.
  • + *
  • {@link EclipseLinkJPQLGrammar2_6} defines the JPQL grammar based on JPA 2.1 and the additional EclipseLink 2.6 support.
  • + *
  • {@link EclipseLinkJPQLGrammar4_0} defines the JPQL grammar based on JPA 2.1 and the additional EclipseLink 4.0 support.
  • + *
  • {@link EclipseLinkJPQLGrammar4_1} defines the JPQL grammar based on JPA 2.1 and the additional EclipseLink 4.1 support.
  • *
  • {@link DefaultJPQLGrammar} defines the JPQL grammar based on the latest JPA version;
  • *
  • {@link DefaultEclipseLinkJPQLGrammar} defines the JPQL grammar based on the latest JPA and * the latest EclipseLink;
  • diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/JPQLGrammar3_2.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/JPQLGrammar3_2.java new file mode 100644 index 00000000000..b016feecfea --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/JPQLGrammar3_2.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.jpql.parser; + +import org.eclipse.persistence.jpa.jpql.ExpressionTools; +import org.eclipse.persistence.jpa.jpql.JPAVersion; + +import static org.eclipse.persistence.jpa.jpql.parser.Expression.CONCAT_PIPES; + +/** + * This {@link JPQLGrammar} provides support for parsing JPQL queries defined in Jakarta Persistence 3.2. + *
    
    + *
    + * string_expression ::= string_expression || string_term
    + *
    + * 
    + */ +public class JPQLGrammar3_2 extends AbstractJPQLGrammar { + + /** + * The singleton instance of this {@link JPQLGrammar3_2}. + */ + private static final JPQLGrammar INSTANCE = new JPQLGrammar3_2(); + + /** + * Creates an insance of Jakarta Persistence 3.1 JPQL grammar. + */ + public JPQLGrammar3_2() { + super(); + } + + /** + * Creates an instance of Jakarta Persistence 3.2 JPQL grammar. + * + * @param jpqlGrammar The {@link JPQLGrammar} to extend with the content of this one without + * instantiating the base {@link JPQLGrammar} + */ + private JPQLGrammar3_2(AbstractJPQLGrammar jpqlGrammar) { + super(jpqlGrammar); + } + + /** + * Extends the given {@link JPQLGrammar} with the information of this one without instantiating + * the base {@link JPQLGrammar}. + * + * @param jpqlGrammar The {@link JPQLGrammar} to extend with the content of this one without + * instantiating the base {@link JPQLGrammar} + */ + public static void extend(AbstractJPQLGrammar jpqlGrammar) { + new JPQLGrammar3_2(jpqlGrammar); + } + + /** + * Returns the singleton instance of the default implementation of {@link JPQLGrammar} which + * provides support for the JPQL grammar defined in the Jakarta Persistence 3.2 functional specification. + * + * @return The {@link JPQLGrammar} that only has support for Jakarta Persistence 3.2 + */ + public static JPQLGrammar instance() { + return INSTANCE; + } + + @Override + protected JPQLGrammar buildBaseGrammar() { + return new JPQLGrammar3_1(); + } + + @Override + public JPAVersion getJPAVersion() { + return JPAVersion.VERSION_3_2; + } + + @Override + public String getProvider() { + return DefaultJPQLGrammar.PROVIDER_NAME; + } + + @Override + public String getProviderVersion() { + return ExpressionTools.EMPTY_STRING; + } + + @Override + protected void initializeBNFs() { + registerBNF(new StringFactorBNF()); + registerBNF(new StringTermBNF()); + registerBNF(new SimpleStringExpressionBNF()); + + // Extend some query BNFs + addChildBNF(StringPrimaryBNF.ID, SimpleStringExpressionBNF.ID); + } + + @Override + protected void initializeExpressionFactories() { + registerFactory(new StringExpressionFactory()); + } + + @Override + protected void initializeIdentifiers() { + registerIdentifierRole(CONCAT_PIPES, IdentifierRole.AGGREGATE); // x || y + registerIdentifierVersion(CONCAT_PIPES, JPAVersion.VERSION_3_2); + } + + @Override + public String toString() { + return "JPQLGrammar 3.2"; + } +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/SimpleStringExpressionBNF.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/SimpleStringExpressionBNF.java new file mode 100644 index 00000000000..8f0a88aa5f9 --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/SimpleStringExpressionBNF.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// +package org.eclipse.persistence.jpa.jpql.parser; + +/** + * The query BNF for a simple string expression. + * + *
    BNF: simple_string_expression ::= string_term | simple_string_expression { || } string_term
    + * + * @version 4.1 + * @since 4.1 + * @author Radek Felcman + */ +@SuppressWarnings("nls") +public final class SimpleStringExpressionBNF extends JPQLQueryBNF { + + /** + * The unique identifier of this BNF rule. + */ + public static final String ID = "simple_string_expression"; + + /** + * Creates a new SimpleStringExpressionBNF. + */ + public SimpleStringExpressionBNF() { + super(ID); + } + + @Override + protected void initialize() { + super.initialize(); + setHandleAggregate(true); + setFallbackBNFId(StringTermBNF.ID); + registerExpressionFactory(StringExpressionFactory.ID); + registerChild(StringTermBNF.ID); + } +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringExpression.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringExpression.java new file mode 100644 index 00000000000..1cf8d19c28e --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringExpression.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.jpql.parser; + +import org.eclipse.persistence.jpa.jpql.WordParser; + +/** + * This expression represents a String expression, which means the first and second expressions + * are aggregated with a String sign. + * + * @see ConcatPipesExpression + * + * @version 2.5 + * @since 2.3 + * @author Pascal Filion + */ +public abstract class StringExpression extends CompoundExpression { + + /** + * Creates a new StringExpression. + * + * @param parent The parent of this expression + */ + protected StringExpression(AbstractExpression parent, String identifier) { + super(parent, identifier); + } + + @Override + public JPQLQueryBNF findQueryBNF(Expression expression) { + return getParent().findQueryBNF(expression); + } + + /** + * Returns the string sign this expression is actually representing. + * + * @return The single character value of the string sign + */ + public final String getStringSign() { + return getText(); + } + + @Override + public String getLeftExpressionQueryBNFId() { + return StringExpressionBNF.ID; + } + + @Override + public final JPQLQueryBNF getQueryBNF() { + return getQueryBNF(StringTermBNF.ID); + } + + @Override + public final String getRightExpressionQueryBNFId() { + return StringTermBNF.ID; + } + + @Override + protected boolean isParsingComplete(WordParser wordParser, String word, Expression expression) { + + String character = word.substring(0,1); + + // Concat will create a chain of operations + if ("||".equals(character)) { + return false; + } + + return (expression != null); + } + + @Override + protected final String parseIdentifier(WordParser wordParser) { + return getText(); + } +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringExpressionFactory.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringExpressionFactory.java new file mode 100644 index 00000000000..9666d5cf0f1 --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringExpressionFactory.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.jpql.parser; + +import org.eclipse.persistence.jpa.jpql.WordParser; + +/** + * This {@link ExpressionFactory} creates a new expression when the portion of the query to parse + * starts with a string identifier. It is possible the expression to parse is also a {@link StringLiteral}. + * + * @version 4.1 + * @since 4.1 + * @author Pascal Filion + */ +@SuppressWarnings("nls") +public final class StringExpressionFactory extends ExpressionFactory { + + /** + * This {@link ExpressionVisitor} is used to check if the {@link Expression} passed to this + * factory is an {@link ConcatPipesExpression}. + */ + private StringExpressionVisitor visitor; + + /** + * The unique identifier of this {@link StringExpressionFactory}. + */ + public static final String ID = "||"; + + /** + * Creates a new AbstractStringExpressionFactory. + */ + public StringExpressionFactory() { + super(ID, Expression.CONCAT_PIPES); + } + + /** + * Creates the {@link Expression} this factory for which it is responsible. + * + * @param parent The parent of the new {@link Expression} + * @param character The arithmetic character + * @return A new {@link CompoundExpression} + */ + private CompoundExpression buildExpression(AbstractExpression parent, String character) { + if ("||".equals(character)) { + return new ConcatPipesExpression(parent); + } + return null; + } + + @Override + protected final AbstractExpression buildExpression(AbstractExpression parent, + WordParser wordParser, + String word, + JPQLQueryBNF queryBNF, + AbstractExpression expression, + boolean tolerant) { + + String character = word.substring(0,1); + //"|" implies concat operator which contains two characters + if ("|".equals(character)) { + character = word.substring(0,2); + } + + // Concat operator '||' + if ("||".equals(character)) { + ConcatPipesExpression concatPipesExpression = new ConcatPipesExpression(parent); + concatPipesExpression.setLeftExpression(expression); + concatPipesExpression.parse(wordParser, tolerant); + return concatPipesExpression; + } + + // Create the StringExpression + CompoundExpression compoundExpression = buildExpression(parent, character); + compoundExpression.setLeftExpression(expression); + compoundExpression.parse(wordParser, tolerant); + return compoundExpression; + } + + private StringExpressionVisitor visitor() { + if (visitor == null) { + visitor = new StringExpressionVisitor(); + } + return visitor; + } + + // Made static final for performance reasons. + /** + * This {@link ExpressionVisitor} is used to check if the {@link Expression} passed to this + * factory is a {@link ConcatPipesExpression}. + */ + private static final class StringExpressionVisitor extends AbstractExpressionVisitor { + + /** + * This flag is turned on if the {@link Expression} visited is {@link OrExpression}. + */ + boolean found; + + @Override + public void visit(ConcatPipesExpression expression) { + found = true; + } + } +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringFactorBNF.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringFactorBNF.java new file mode 100644 index 00000000000..efd54d6e5cb --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringFactorBNF.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.jpql.parser; + +/** + * The query BNF for an string factor expression. + * + *
    BNF: string_factor ::= [{ || }] string_primary
    + * + * @version 4.1 + * @since 4.1 + * @author Radek Felcman + */ +@SuppressWarnings("nls") +public final class StringFactorBNF extends JPQLQueryBNF { + + /** + * The unique identifier of this BNF rule. + */ + public static final String ID = "string_factor"; + + /** + * Creates a new StringFactorBNF. + */ + public StringFactorBNF() { + super(ID); + } + + @Override + protected void initialize() { + super.initialize(); + setFallbackBNFId(StringPrimaryBNF.ID); + registerChild(StringPrimaryBNF.ID); + } +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringTermBNF.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringTermBNF.java new file mode 100644 index 00000000000..55f488bf90c --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/StringTermBNF.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.jpql.parser; + +/** + * The query BNF for a string term expression. + * + *
    BNF: string_term ::= string_factor | string_term { || } string_factor
    + * + * @version 4.1 + * @since 4.1 + * @author Radek Felcman + */ +@SuppressWarnings("nls") +public final class StringTermBNF extends JPQLQueryBNF { + + /** + * The unique identifier of this BNF rule. + */ + public static final String ID = "string_term"; + + /** + * Creates a new StringTermBNF. + */ + public StringTermBNF() { + super(ID); + } + + @Override + protected void initialize() { + super.initialize(); + setHandleAggregate(true); + setFallbackBNFId(StringFactorBNF.ID); + registerExpressionFactory(StringExpressionFactory.ID); + registerChild(StringFactorBNF.ID); + } +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/AbstractContentAssistVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/AbstractContentAssistVisitor.java index ebaed5e6455..6041b2018ba 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/AbstractContentAssistVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/AbstractContentAssistVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2022 IBM Corporation. All rights reserved. * * This program and the accompanying materials are made available under the @@ -18,6 +18,8 @@ // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 // - Issue 317: Implement LOCAL DATE, LOCAL TIME and LOCAL DATETIME. +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.tools; import java.util.ArrayList; @@ -74,6 +76,7 @@ import org.eclipse.persistence.jpa.jpql.parser.ComparisonExpressionFactory; import org.eclipse.persistence.jpa.jpql.parser.CompoundExpression; import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression; +import org.eclipse.persistence.jpa.jpql.parser.ConcatPipesExpression; import org.eclipse.persistence.jpa.jpql.parser.ConditionalExpressionBNF; import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.ConstructorItemBNF; @@ -105,6 +108,7 @@ import org.eclipse.persistence.jpa.jpql.parser.InternalJoinBNF; import org.eclipse.persistence.jpa.jpql.parser.JPQLExpression; import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar; +import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar3_2; import org.eclipse.persistence.jpa.jpql.parser.JPQLQueryBNF; import org.eclipse.persistence.jpa.jpql.parser.Join; import org.eclipse.persistence.jpa.jpql.parser.KeyExpression; @@ -148,6 +152,8 @@ import org.eclipse.persistence.jpa.jpql.parser.SizeExpression; import org.eclipse.persistence.jpa.jpql.parser.SqrtExpression; import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression; +import org.eclipse.persistence.jpa.jpql.parser.StringExpression; +import org.eclipse.persistence.jpa.jpql.parser.StringExpressionFactory; import org.eclipse.persistence.jpa.jpql.parser.StringLiteral; import org.eclipse.persistence.jpa.jpql.parser.StringPrimaryBNF; import org.eclipse.persistence.jpa.jpql.parser.SubExpression; @@ -337,6 +343,18 @@ protected void addArithmeticIdentifiers() { } } + /** + * Adds the JPQL identifiers which correspond to the String operators as valid proposals. The + * word has to be an empty string. + */ + protected void addStringIdentifiers() { + if (this.queryContext.getGrammar() instanceof JPQLGrammar3_2) { + if (word.length() == 0) { + addExpressionFactoryIdentifiers(StringExpressionFactory.ID); + } + } + } + /** * Adds the given JPQL identifier as a valid proposal if its role is {@link IdentifierRole#CLAUSE} * and the beginning starts with the current word. @@ -846,6 +864,17 @@ protected boolean areLogicalSymbolsAppendable(Expression expression) { return isAppendable(expression, AppendableType.LOGICAL); } + /** + * Determines whether the given {@link Expression} can be followed by an String operator. + * + * @param expression The {@link Expression} that found left of the cursor, which determines if + * the String operators are appendable or not + * @return true if the operators are appendable; false otherwise + */ + protected boolean areStringSymbolsAppendable(Expression expression) { + return isAppendable(expression, AppendableType.STRING); + } + protected abstract AcceptableTypeVisitor buildAcceptableTypeVisitor(); protected AppendableExpressionVisitor buildAppendableExpressionVisitor() { @@ -2802,6 +2831,12 @@ public void visit(ConcatExpression expression) { visitCollectionExpression(expression, CONCAT, getConcatExpressionCollectionHelper()); } + @Override + public void visit(ConcatPipesExpression expression) { + super.visit(expression); + visitStringExpression(expression); + } + @Override public void visit(ConstructorExpression expression) { super.visit(expression); @@ -4699,6 +4734,36 @@ else if (helper.hasSpaceAfterClause(expression)) { } } + /** + * Visits the given {@link StringExpression} and attempts to find valid proposals. + * + * @param expression The {@link StringExpression} to inspect + */ + protected void visitStringExpression(StringExpression expression) { + + int position = queryPosition.getPosition(expression) - corrections.peek(); + int length = 0; + + if (expression.hasLeftExpression()) { + length += expression.getLeftExpression().getLength() + SPACE_LENGTH; + } + + // Within the string operator + if (isPositionWithin(position, length, CONCAT_PIPES)) { + addAggregateIdentifiers(expression.getQueryBNF()); + } + // After the string operator, with or without the space + else if (expression.hasSpaceAfterIdentifier()) { + length += 2; + + // Right after the space + if (position == length) { + addIdentificationVariables(); + addFunctionIdentifiers(expression.getRightExpressionQueryBNFId()); + } + } + } + /** * Visits the given {@link AbstractPathExpression} and attempts to find valid proposals that is * not provided by the default implementation. Subclasses can add additional proposals that is @@ -4778,7 +4843,7 @@ public void addAtTheEndOfChild(Expression expression, index = (-index / 10) - 1; } - // The only thing that is appendable is an arithmetic operator + // The only thing that is appendable is an arithmetic or String operator // Example: "SELECT e FROM Employee e WHERE e.name|" // Example: "SELECT e FROM Employee e WHERE I|" if ((index == 0) && !virtualSpace) { @@ -4788,6 +4853,10 @@ public void addAtTheEndOfChild(Expression expression, if (visitor.areArithmeticSymbolsAppendable(child)) { visitor.addArithmeticIdentifiers(); } + + if (visitor.areStringSymbolsAppendable(child)) { + visitor.addStringIdentifiers(); + } } else { @@ -4812,6 +4881,10 @@ public void addAtTheEndOfChild(Expression expression, visitor.addArithmeticIdentifiers(); } + if (visitor.areStringSymbolsAppendable(child)) { + visitor.addStringIdentifiers(); + } + if (visitor.areComparisonSymbolsAppendable(child)) { visitor.addComparisonIdentifiers(child); } @@ -5099,7 +5172,7 @@ public void addAtTheEndOfChild(T expression, boolean hasComma, boolean virtualSpace) { - // The only thing that is appendable is an arithmetic operator + // The only thing that is appendable is arithmetic or String operator // Example: "SELECT e.name|" // Example: "SELECT e|" if (queryBNF(expression, index).handleAggregate()) { @@ -5109,6 +5182,10 @@ public void addAtTheEndOfChild(T expression, if (visitor.areArithmeticSymbolsAppendable(child)) { visitor.addArithmeticIdentifiers(); } + + if (visitor.areStringSymbolsAppendable(child)) { + visitor.addStringIdentifiers(); + } } } @@ -5536,6 +5613,13 @@ public void visit(ConcatExpression expression) { expression.hasRightParenthesis(); } + @Override + public void visit(ConcatPipesExpression expression) { + if (expression.hasRightExpression()) { + expression.getRightExpression().accept(this); + } + } + @Override public void visit(ConstructorExpression expression) { appendable = !conditionalExpression && @@ -6124,6 +6208,11 @@ else if (appendableType == AppendableType.CLAUSE || appendable = visitor.queryContext.getTypeHelper().isBooleanType(type); break; } + case STRING: { + // e.name (String) can be followed by || + appendable = visitor.queryContext.getTypeHelper().isStringType(type); + break; + } } } } @@ -6290,6 +6379,11 @@ protected static enum AppendableType { */ LOGICAL, + /** + * Determines whether the String operators (||) can be appended as valid proposals. + */ + STRING, + /** * Determines whether the JPQL identifiers identifying a subquery (eg: SELECT) * can be appended as valid proposals. @@ -6458,7 +6552,7 @@ public void addAtTheEndOfChild(ConcatExpression expression, boolean hasComma, boolean virtualSpace) { - // The only thing that is appendable is an arithmetic operator + // The only thing that is appendable is arithmetic or String operator // Example: "SELECT e.name|" // Example: "SELECT e|" if (queryBNF(expression, index).handleAggregate()) { @@ -6470,6 +6564,10 @@ public void addAtTheEndOfChild(ConcatExpression expression, if (visitor.areArithmeticSymbolsAppendable(child)) { visitor.addArithmeticIdentifiers(); } + + if (visitor.areStringSymbolsAppendable(child)) { + visitor.addStringIdentifiers(); + } } else { @@ -6477,6 +6575,10 @@ public void addAtTheEndOfChild(ConcatExpression expression, visitor.addArithmeticIdentifiers(); } + if (visitor.areStringSymbolsAppendable(child)) { + visitor.addStringIdentifiers(); + } + if (visitor.areComparisonSymbolsAppendable(child)) { visitor.addComparisonIdentifiers(child); } @@ -6593,6 +6695,10 @@ public void addAtTheEndOfChild(ConstructorExpression expression, if (visitor.areArithmeticSymbolsAppendable(child)) { visitor.addArithmeticIdentifiers(); } + + if (visitor.areStringSymbolsAppendable(child)) { + visitor.addStringIdentifiers(); + } } else { @@ -6600,6 +6706,10 @@ public void addAtTheEndOfChild(ConstructorExpression expression, visitor.addArithmeticIdentifiers(); } + if (visitor.areStringSymbolsAppendable(child)) { + visitor.addStringIdentifiers(); + } + if (visitor.areComparisonSymbolsAppendable(child)) { visitor.addComparisonIdentifiers(child); } @@ -6890,6 +7000,10 @@ public void addAtTheEndOfChild(AbstractDoubleEncapsulatedExpression expression, if (visitor.areArithmeticSymbolsAppendable(child)) { visitor.addArithmeticIdentifiers(); } + + if (visitor.areStringSymbolsAppendable(child)) { + visitor.addStringIdentifiers(); + } } else { @@ -6897,6 +7011,10 @@ public void addAtTheEndOfChild(AbstractDoubleEncapsulatedExpression expression, visitor.addArithmeticIdentifiers(); } + if (visitor.areStringSymbolsAppendable(child)) { + visitor.addStringIdentifiers(); + } + if (visitor.areComparisonSymbolsAppendable(child)) { visitor.addComparisonIdentifiers(child); } @@ -7372,6 +7490,11 @@ public void visit(ConcatExpression expression) { visitAbstractSingleEncapsulatedExpression(expression); } + @Override + public void visit(ConcatPipesExpression expression) { + visitCompoundExpression(expression); + } + @Override public void visit(ConstructorExpression expression) { @@ -10482,6 +10605,10 @@ public void addAtTheEndOfChild(AbstractTripleEncapsulatedExpression expression, if (visitor.areArithmeticSymbolsAppendable(child)) { visitor.addArithmeticIdentifiers(); } + + if (visitor.areStringSymbolsAppendable(child)) { + visitor.addStringIdentifiers(); + } } else { @@ -10489,6 +10616,10 @@ public void addAtTheEndOfChild(AbstractTripleEncapsulatedExpression expression, visitor.addArithmeticIdentifiers(); } + if (visitor.areStringSymbolsAppendable(child)) { + visitor.addStringIdentifiers(); + } + if (visitor.areComparisonSymbolsAppendable(child)) { visitor.addComparisonIdentifiers(child); } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/AbstractActualJPQLQueryFormatter.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/AbstractActualJPQLQueryFormatter.java index c806900910a..5a91784522a 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/AbstractActualJPQLQueryFormatter.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/AbstractActualJPQLQueryFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,7 +12,8 @@ // Contributors: // Oracle - initial API and implementation -// +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.tools.model; import static org.eclipse.persistence.jpa.jpql.parser.Expression.AND; @@ -21,6 +22,7 @@ import static org.eclipse.persistence.jpa.jpql.parser.Expression.CASE; import static org.eclipse.persistence.jpa.jpql.parser.Expression.COALESCE; import static org.eclipse.persistence.jpa.jpql.parser.Expression.CONCAT; +import static org.eclipse.persistence.jpa.jpql.parser.Expression.CONCAT_PIPES; import static org.eclipse.persistence.jpa.jpql.parser.Expression.DELETE; import static org.eclipse.persistence.jpa.jpql.parser.Expression.DISTINCT; import static org.eclipse.persistence.jpa.jpql.parser.Expression.DIVISION; @@ -128,6 +130,7 @@ import org.eclipse.persistence.jpa.jpql.tools.model.query.ComparisonExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.CompoundExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.ConcatExpressionStateObject; +import org.eclipse.persistence.jpa.jpql.tools.model.query.ConcatPipesExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.ConstructorExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.CountFunctionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.DateTimeStateObject; @@ -1041,6 +1044,11 @@ public void visit(ConcatExpressionStateObject stateObject) { } } + @Override + public void visit(ConcatPipesExpressionStateObject stateObject) { + toStringCompound(stateObject, CONCAT_PIPES); + } + @Override public void visit(ConstructorExpressionStateObject stateObject) { diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/AbstractJPQLQueryFormatter.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/AbstractJPQLQueryFormatter.java index ff523a59eb7..e8cbc64def8 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/AbstractJPQLQueryFormatter.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/AbstractJPQLQueryFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,7 +12,8 @@ // Contributors: // Oracle - initial API and implementation -// +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.tools.model; import java.util.ListIterator; @@ -45,6 +46,7 @@ import org.eclipse.persistence.jpa.jpql.tools.model.query.ComparisonExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.CompoundExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.ConcatExpressionStateObject; +import org.eclipse.persistence.jpa.jpql.tools.model.query.ConcatPipesExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.ConstructorExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.CountFunctionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.DateTimeStateObject; @@ -652,6 +654,11 @@ public void visit(ConcatExpressionStateObject stateObject) { } } + @Override + public void visit(ConcatPipesExpressionStateObject stateObject) { + toStringCompound(stateObject); + } + @Override public void visit(ConstructorExpressionStateObject stateObject) { diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/BasicStateObjectBuilder.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/BasicStateObjectBuilder.java index 0f072e4ac9c..0e1be4726cd 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/BasicStateObjectBuilder.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/BasicStateObjectBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,7 +12,8 @@ // Contributors: // Oracle - initial API and implementation -// +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.tools.model; import java.util.ArrayList; @@ -42,6 +43,7 @@ import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression; import org.eclipse.persistence.jpa.jpql.parser.ComparisonExpression; import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression; +import org.eclipse.persistence.jpa.jpql.parser.ConcatPipesExpression; import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.CountFunction; import org.eclipse.persistence.jpa.jpql.parser.DateTime; @@ -132,6 +134,7 @@ import org.eclipse.persistence.jpa.jpql.tools.model.query.CollectionValuedPathExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.ComparisonExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.ConcatExpressionStateObject; +import org.eclipse.persistence.jpa.jpql.tools.model.query.ConcatPipesExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.ConstructorExpressionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.CountFunctionStateObject; import org.eclipse.persistence.jpa.jpql.tools.model.query.DateTimeStateObject; @@ -774,6 +777,25 @@ public void visit(ConcatExpression expression) { this.stateObject = stateObject; } + @Override + public void visit(ConcatPipesExpression expression) { + + expression.getLeftExpression().accept(this); + StateObject leftStateObject = stateObject; + + expression.getRightExpression().accept(this); + StateObject rightStateObject = stateObject; + + ConcatPipesExpressionStateObject stateObject = new ConcatPipesExpressionStateObject( + parent, + leftStateObject, + rightStateObject + ); + + stateObject.setExpression(expression); + this.stateObject = stateObject; + } + @Override public void visit(ConstructorExpression expression) { diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/AbstractStateObjectVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/AbstractStateObjectVisitor.java index 38e89d60aa3..d09a7b82057 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/AbstractStateObjectVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/AbstractStateObjectVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,7 +12,8 @@ // Contributors: // Oracle - initial API and implementation -// +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.tools.model.query; /** @@ -89,6 +90,10 @@ public void visit(ComparisonExpressionStateObject stateObject) { public void visit(ConcatExpressionStateObject stateObject) { } + @Override + public void visit(ConcatPipesExpressionStateObject stateObject) { + } + @Override public void visit(ConstructorExpressionStateObject stateObject) { } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/AnonymousStateObjectVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/AnonymousStateObjectVisitor.java index ed57bd7d6ce..5dbb34c1650 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/AnonymousStateObjectVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/AnonymousStateObjectVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,7 +12,8 @@ // Contributors: // Oracle - initial API and implementation -// +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.tools.model.query; /** @@ -105,6 +106,11 @@ public void visit(ConcatExpressionStateObject stateObject) { visit((StateObject) stateObject); } + @Override + public void visit(ConcatPipesExpressionStateObject stateObject) { + visit((StateObject) stateObject); + } + @Override public void visit(ConstructorExpressionStateObject stateObject) { visit((StateObject) stateObject); diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/ConcatPipesExpressionStateObject.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/ConcatPipesExpressionStateObject.java new file mode 100644 index 00000000000..22e470063a2 --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/ConcatPipesExpressionStateObject.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.jpql.tools.model.query; + +import org.eclipse.persistence.jpa.jpql.parser.ConcatPipesExpression; + +import static org.eclipse.persistence.jpa.jpql.parser.Expression.CONCAT_PIPES; + +/** + * + *

    BNF: string_expression ::= string_expression || string_term

    + * + * @see ConcatPipesExpression + * + * @version 2.4 + * @since 2.4 + * @author Pascal Filion + */ +public class ConcatPipesExpressionStateObject extends StringExpressionStateObject { + + /** + * Creates a new ConcatPipesExpressionStateObject. + * + * @param parent The parent of this state object, which cannot be null + * @exception NullPointerException The given parent cannot be null + */ + public ConcatPipesExpressionStateObject(StateObject parent) { + super(parent); + } + + /** + * Creates a new ConcatPipesExpressionStateObject. + * + * @param parent The parent of this state object, which cannot be null + * @param leftStateObject The {@link StateObject} representing the left expression + * @param rightStateObject The {@link StateObject} representing the right expression + * @exception NullPointerException The given parent cannot be null + */ + public ConcatPipesExpressionStateObject(StateObject parent, + StateObject leftStateObject, + StateObject rightStateObject) { + + super(parent, leftStateObject, rightStateObject); + } + + /** + * Creates a new ConcatPipesExpressionStateObject. + * + * @param parent The parent of this state object, which cannot be null + * @param leftJpqlFragment The string representation of the left expression to parse and to + * convert into a {@link StateObject} + * @param rightJpqlFragment The string representation of the right expression to parse and to + * convert into a {@link StateObject} + * @exception NullPointerException The given parent cannot be null + */ + public ConcatPipesExpressionStateObject(StateObject parent, + String leftJpqlFragment, + String rightJpqlFragment) { + + super(parent, leftJpqlFragment, rightJpqlFragment); + } + + @Override + public void accept(StateObjectVisitor visitor) { + visitor.visit(this); + } + + @Override + public ConcatPipesExpression getExpression() { + return (ConcatPipesExpression) super.getExpression(); + } + + @Override + public String getIdentifier() { + return CONCAT_PIPES; + } + + /** + * Keeps a reference of the {@link ConcatPipesExpression parsed object} object, which should only be + * done when this object is instantiated during the conversion of a parsed JPQL query into + * {@link StateObject StateObjects}. + * + * @param expression The {@link ConcatPipesExpression parsed object} representing an addition + * expression + */ + public void setExpression(ConcatPipesExpression expression) { + super.setExpression(expression); + } +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/StateObjectVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/StateObjectVisitor.java index c7257038366..75fdd84d861 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/StateObjectVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/StateObjectVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,7 +12,8 @@ // Contributors: // Oracle - initial API and implementation -// +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.tools.model.query; /** @@ -141,6 +142,13 @@ public interface StateObjectVisitor { */ void visit(ConcatExpressionStateObject stateObject); + /** + * Visits the given {@link ConcatPipesExpressionStateObject}. + * + * @param stateObject The {@link ConcatPipesExpressionStateObject} to visit + */ + void visit(ConcatPipesExpressionStateObject stateObject); + /** * Visits the given {@link ConstructorExpressionStateObject}. * diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/StringExpressionStateObject.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/StringExpressionStateObject.java new file mode 100644 index 00000000000..e04b0cc98b0 --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/query/StringExpressionStateObject.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.jpql.tools.model.query; + +import org.eclipse.persistence.jpa.jpql.parser.StringExpression; +import org.eclipse.persistence.jpa.jpql.parser.StringExpressionBNF; +import org.eclipse.persistence.jpa.jpql.parser.StringTermBNF; + +/** + * This expression represents an arithmetic expression, which means the first and second expressions + * are aggregated with an arithmetic sign. + * + *

    BNF: arithmetic_expression ::= arithmetic_expression <identifier> arithmetic_term

    + * + * @see ConcatPipesExpressionStateObject + * + * @see StringExpression + * + * @version 4.1 + * @since 4.1 + * @author Radek Felcman + */ +public abstract class StringExpressionStateObject extends CompoundExpressionStateObject { + + /** + * Creates a new StringExpressionStateObject. + * + * @param parent The parent of this state object, which cannot be null + * @exception NullPointerException The given parent cannot be null + */ + protected StringExpressionStateObject(StateObject parent) { + super(parent); + } + + /** + * Creates a new StringExpressionStateObject. + * + * @param parent The parent of this state object, which cannot be null + * @param leftStateObject The {@link StateObject} representing the left expression + * @param rightStateObject The {@link StateObject} representing the right expression + * @exception NullPointerException The given parent cannot be null + */ + protected StringExpressionStateObject(StateObject parent, + StateObject leftStateObject, + StateObject rightStateObject) { + + super(parent, leftStateObject, rightStateObject); + } + + /** + * Creates a new StringExpressionStateObject. + * + * @param parent The parent of this state object, which cannot be null + * @param leftJpqlFragment The string representation of the left expression to parse and to + * convert into a {@link StateObject} + * @param rightJpqlFragment The string representation of the right expression to parse and to + * convert into a {@link StateObject} + * @exception NullPointerException The given parent cannot be null + */ + protected StringExpressionStateObject(StateObject parent, + String leftJpqlFragment, + String rightJpqlFragment) { + + super(parent, leftJpqlFragment, rightJpqlFragment); + } + + @Override + public StringExpression getExpression() { + return (StringExpression) super.getExpression(); + } + + @Override + protected String getLeftQueryBNFId() { + return StringExpressionBNF.ID; + } + + @Override + protected String getRightQueryBNFId() { + return StringTermBNF.ID; + } +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/resolver/ResolverBuilder.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/resolver/ResolverBuilder.java index 4f378fed81c..57084c71c48 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/resolver/ResolverBuilder.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/resolver/ResolverBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,6 +15,8 @@ // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 // - Issue 317: Implement LOCAL DATE, LOCAL TIME and LOCAL DATETIME. +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.jpql.tools.resolver; import java.sql.Date; @@ -49,6 +51,7 @@ import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression; import org.eclipse.persistence.jpa.jpql.parser.ComparisonExpression; import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression; +import org.eclipse.persistence.jpa.jpql.parser.ConcatPipesExpression; import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.CountFunction; import org.eclipse.persistence.jpa.jpql.parser.DateTime; @@ -534,6 +537,11 @@ public void visit(ConcatExpression expression) { resolver = buildClassResolver(String.class); } + @Override + public void visit(ConcatPipesExpression expression) { + resolver = buildClassResolver(String.class); + } + @Override public void visit(ConstructorExpression expression) { diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/resources/org/eclipse/persistence/jpa/jpql/jpa_jpql_validation.properties b/jpa/org.eclipse.persistence.jpa.jpql/src/main/resources/org/eclipse/persistence/jpa/jpql/jpa_jpql_validation.properties index 7056d03d587..fe1df3e4039 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/resources/org/eclipse/persistence/jpa/jpql/jpa_jpql_validation.properties +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/resources/org/eclipse/persistence/jpa/jpql/jpa_jpql_validation.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2006, 2020 Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License v. 2.0 which is available at @@ -13,6 +13,8 @@ # Contributors: # Oracle - initial API and implementation +# 06/02/2023: Radek Felcman +# - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 # AbsExpression - Grammar ABS_EXPRESSION_INVALID_EXPRESSION = The encapsulated expression is not a valid expression. @@ -487,6 +489,17 @@ SUB_EXPRESSION_MISSING_RIGHT_PARENTHESIS = The right parenthesis is missing from SUBTRACTION_EXPRESSION_LEFT_EXPRESSION_WRONG_TYPE = The left side of the subtraction is not a valid arithmetic expression. SUBTRACTION_EXPRESSION_RIGHT_EXPRESSION_WRONG_TYPE = The right side of the subtraction is not a valid arithmetic expression. +# StringFactor - Grammar +STRING_FACTOR_MISSING_EXPRESSION = An string factor must be followed by an expression. +# StringFactor - Semantic +STRING_FACTOR_INVALID_EXPRESSION = The expression must be an string expression. + +# StringExpression - Grammar +STRING_EXPRESSION_INVALID_LEFT_EXPRESSION = The left expression is not an string expression. +STRING_EXPRESSION_INVALID_RIGHT_EXPRESSION = The right expression is not an string expression. +STRING_EXPRESSION_MISSING_LEFT_EXPRESSION = The left expression is missing from the string expression. +STRING_EXPRESSION_MISSING_RIGHT_EXPRESSION = The right expression is missing from the string expression. + # SubstringExpression - Grammar SUBSTRING_EXPRESSION_INVALID_FIRST_EXPRESSION = The first argument is not a valid expression. SUBSTRING_EXPRESSION_INVALID_SECOND_EXPRESSION = The second argument is not a valid expression. diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/JPQLQueries3_2.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/JPQLQueries3_2.java new file mode 100644 index 00000000000..901eeac9885 --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/JPQLQueries3_2.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.tests.jpql; + +/** + * This class provides a list of queries that are written against the JPQL 3.2 grammar. + */ +public class JPQLQueries3_2 { + + private JPQLQueries3_2() { + super(); + } + + public static String query_ConcatPipes_Select01() { + return "SELECT c.firstName || 'Smith' FROM Customer c"; + } + + public static String query_ConcatPipes_Select02() { + return "SELECT c.firstName || c.lastName FROM Customer c"; + } + + public static String query_ConcatPipes_Select_Chained() { + return "SELECT c.firstName || 'Francis' || 'Smith' FROM Customer c"; + } + + public static String query_ConcatPipes_Where() { + return "SELECT c FROM Customer c WHERE c.firstName || 'Smith' = 'JohnSmith'"; + } +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/AllJPQLParserTests.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/AllJPQLParserTests.java index 8397cadd267..d0901e5bbf2 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/AllJPQLParserTests.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/AllJPQLParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -30,6 +30,7 @@ AllJPQLParserTests2_0.class, AllJPQLParserTests2_1.class, AllJPQLParserTests3_1.class, + AllJPQLParserTests3_2.class, AllEclipseLinkJPQLParserTests.class, AllEclipseLinkJPQLParserTests2_1.class, AllEclipseLinkJPQLParserTests2_4.class, diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/AllJPQLParserTests3_2.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/AllJPQLParserTests3_2.java new file mode 100644 index 00000000000..429b9f62f58 --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/AllJPQLParserTests3_2.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.tests.jpql.parser; + +import org.eclipse.persistence.jpa.jpql.JPAVersion; +import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar; +import org.eclipse.persistence.jpa.tests.jpql.JPQLTestRunner; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/** + * This test suite runs {@link JPQLParserTests3_2} using JPQL grammar written for JPA 3.2. + */ +@Suite.SuiteClasses({ + JPQLParserTests3_2.class +}) +@RunWith(JPQLTestRunner.class) +public class AllJPQLParserTests3_2 { + + private AllJPQLParserTests3_2() { + super(); + } + + @JPQLGrammarTestHelper + static JPQLGrammar[] buildJPQLGrammars() { + return JPQLGrammarTools.allJPQLGrammars(JPAVersion.VERSION_3_2); + } + +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLGrammarTools.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLGrammarTools.java index 66b5644af6f..14f7d3392bb 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLGrammarTools.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLGrammarTools.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,6 +14,8 @@ // Oracle - initial API and implementation // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.tests.jpql.parser; import org.eclipse.persistence.jpa.jpql.EclipseLinkVersion; @@ -26,11 +28,13 @@ import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkJPQLGrammar2_4; import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkJPQLGrammar2_5; import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkJPQLGrammar4_0; +import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkJPQLGrammar4_1; import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar; import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar1_0; import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar2_0; import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar2_1; import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar3_1; +import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar3_2; /** * This utility class provides an easy way of creating a list of {@link JPQLGrammar}, which is @@ -76,6 +80,11 @@ public static JPQLGrammar[] allDefaultJPQLGrammars(JPAVersion minVersion) { JPQLGrammar3_1.instance() }; } + case VERSION_3_2: { + return new JPQLGrammar[] { + JPQLGrammar3_2.instance() + }; + } default: { return new JPQLGrammar[0]; } @@ -199,6 +208,12 @@ public static JPQLGrammar[] allJPQLGrammars(JPAVersion minVersion) { EclipseLinkJPQLGrammar4_0.instance() }; } + case VERSION_3_2: { + return new JPQLGrammar[] { + JPQLGrammar3_2.instance(), + EclipseLinkJPQLGrammar4_1.instance() + }; + } default: { return new JPQLGrammar[0]; } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTest.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTest.java index f698c4b5b73..6fc319b198e 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTest.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,6 +14,8 @@ // Oracle - initial API and implementation // 04/21/2022: Tomas Kraus // - Issue 1474: Update JPQL Grammar for Jakarta Persistence 2.2, 3.0 and 3.1 +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.tests.jpql.parser; import org.eclipse.persistence.jpa.jpql.ExpressionTools; @@ -48,6 +50,7 @@ import org.eclipse.persistence.jpa.jpql.parser.ComparisonExpression; import org.eclipse.persistence.jpa.jpql.parser.CompoundExpression; import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression; +import org.eclipse.persistence.jpa.jpql.parser.ConcatPipesExpression; import org.eclipse.persistence.jpa.jpql.parser.ConnectByClause; import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.CountFunction; @@ -2129,6 +2132,25 @@ protected String identifier() { } } + public static final class ConcatPipesExpressionTester extends CompoundExpressionTester { + + protected ConcatPipesExpressionTester(ExpressionTester leftExpression, + ExpressionTester rightExpression) { + + super(leftExpression, rightExpression); + } + + @Override + protected Class expressionType() { + return ConcatPipesExpression.class; + } + + @Override + protected String identifier() { + return CONCAT_PIPES; + } + } + public static final class ConnectByClauseTester extends AbstractExpressionTester { private ExpressionTester expression; diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTester.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTester.java index 360bcd92c9f..7b6bb8c4e7e 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTester.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTester.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -42,6 +42,7 @@ import org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTest.CollectionValuedPathExpressionTester; import org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTest.ComparisonExpressionTester; import org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTest.ConcatExpressionTester; +import org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTest.ConcatPipesExpressionTester; import org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTest.ConnectByClauseTester; import org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTest.ConstructorExpressionTester; import org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTest.CountFunctionTester; @@ -440,6 +441,11 @@ public static ConcatExpressionTester concat(ExpressionTester... expressions) { return new ConcatExpressionTester(expressions[0]); } + public static ConcatPipesExpressionTester concatPipes(ExpressionTester leftExpression, + ExpressionTester rightExpression) { + return new ConcatPipesExpressionTester(leftExpression, rightExpression); + } + public static ConnectByClauseTester connectBy(ExpressionTester expression) { return new ConnectByClauseTester(expression); } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTests3_2.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTests3_2.java new file mode 100644 index 00000000000..cbe7b889e56 --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLParserTests3_2.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.tests.jpql.parser; + +import org.junit.runners.Suite; + +/** + * This test suite contains a series of unit-tests that test parsing JPQL queries that follows the + * JPQL grammar defined in Jakarta Persistence 3.2. + */ +@Suite.SuiteClasses({ + // Test the parser with JPQL queries + JPQLQueriesTest3_2.class, + +}) +public class JPQLParserTests3_2 { + + private JPQLParserTests3_2() { + super(); + } + +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLQueriesTest3_2.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLQueriesTest3_2.java new file mode 100644 index 00000000000..0e770eae0dd --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLQueriesTest3_2.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 +package org.eclipse.persistence.jpa.tests.jpql.parser; + +import org.junit.Test; + +import static org.eclipse.persistence.jpa.tests.jpql.JPQLQueries2_0.query_013; +import static org.eclipse.persistence.jpa.tests.jpql.JPQLQueries3_2.query_ConcatPipes_Select01; +import static org.eclipse.persistence.jpa.tests.jpql.JPQLQueries3_2.query_ConcatPipes_Select02; +import static org.eclipse.persistence.jpa.tests.jpql.JPQLQueries3_2.query_ConcatPipes_Select_Chained; +import static org.eclipse.persistence.jpa.tests.jpql.JPQLQueries3_2.query_ConcatPipes_Where; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.concatPipes; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.from; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.numeric; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.path; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.resultVariable; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.select; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.selectStatement; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.string; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.variable; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.where; + +public class JPQLQueriesTest3_2 extends JPQLParserTest { + + @Test + public void test_Query_ConcatPipes_Select01() { + + // SELECT c.firstName || 'Smith' FROM Customer c + ExpressionTester selectStatement = selectStatement( + select(concatPipes(path("c.firstName"), string("'Smith'"))), + from("Customer", "c") + ); + try { + testQuery(query_ConcatPipes_Select01(), selectStatement); + } catch (Throwable t) { + t.printStackTrace(); + throw t; + } + } + + @Test + public void test_Query_ConcatPipes_Select02() { + + // SELECT c.firstName || c.lastName FROM Customer c + ExpressionTester selectStatement = selectStatement( + select(concatPipes(path("c.firstName"), path("c.lastName"))), + from("Customer", "c") + ); + try { + testQuery(query_ConcatPipes_Select02(), selectStatement); + } catch (Throwable t) { + t.printStackTrace(); + throw t; + } + } + + @Test + public void test_Query_ConcatPipes_Select_Chained() { + + // SELECT c.firstName || c.lastName FROM Customer c + ExpressionTester selectStatement = selectStatement( + select(concatPipes(concatPipes(path("c.firstName"), string("'Francis'")), string("'Smith'"))), + from("Customer", "c") + ); + try { + testQuery(query_ConcatPipes_Select_Chained(), selectStatement); + } catch (Throwable t) { + t.printStackTrace(); + throw t; + } + } + + @Test + public void test_Query_ConcatPipes_Where() { + + // SELECT c FROM Customer c WHERE c.firstName || 'Smith' = 'JohnSmith' + ExpressionTester selectStatement = selectStatement( + select(variable("c")), + from("Customer", "c"), + where(concatPipes(path("c.firstName"), string("'Smith'")).equal(string("'JohnSmith'"))) + ); + try { + testQuery(query_ConcatPipes_Where(), selectStatement); + } catch (Throwable t) { + t.printStackTrace(); + throw t; + } + } + + +// @Test + public void test_Query_013() { + + // SELECT e.salary / 1000D n + // From Employee e + + ExpressionTester selectStatement = selectStatement( + select(resultVariable(path("e.salary").divide(numeric("1000D")), "n")), + from("Employee", "e") + ); + + testQuery(query_013(), selectStatement); + } + +} diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLQueryBNFAccessor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLQueryBNFAccessor.java index 322792abfdf..0359f8c6bdc 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLQueryBNFAccessor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLQueryBNFAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -12,7 +12,8 @@ // Contributors: // Oracle - initial API and implementation -// +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.tests.jpql.parser; import java.util.ArrayList; @@ -44,6 +45,7 @@ import org.eclipse.persistence.jpa.jpql.parser.SelectExpressionBNF; import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectExpressionBNF; import org.eclipse.persistence.jpa.jpql.parser.StringExpressionBNF; +import org.eclipse.persistence.jpa.jpql.parser.StringExpressionFactory; import org.eclipse.persistence.jpa.jpql.utility.CollectionTools; import org.eclipse.persistence.jpa.jpql.utility.iterable.ArrayIterable; @@ -295,6 +297,11 @@ private Iterable stringExpressionIdentifiers() { return getIdentifiers(StringExpressionBNF.ID); } + public Iterable strings() { + ExpressionFactory factory = getExpressionFactory(StringExpressionFactory.ID); + return new ArrayIterable<>(factory.identifiers()); + } + public Iterable subSelectFunctions() { return functions(subSelectIdentifiers()); } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/AbstractContentAssistTest.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/AbstractContentAssistTest.java index b6433dfae58..d7b723fc40c 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/AbstractContentAssistTest.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/AbstractContentAssistTest.java @@ -12,7 +12,8 @@ // Contributors: // Oracle - initial API and implementation -// +// 06/02/2023: Radek Felcman +// - Issue 1885: Implement new JPQLGrammar for upcoming Jakarta Persistence 3.2 package org.eclipse.persistence.jpa.tests.jpql.tools; import java.util.ArrayList; @@ -1081,6 +1082,7 @@ protected final void test_CompoundFunction(String identifier) { proposals.remove(OR); proposals.remove(IS_EMPTY); proposals.remove(IS_NOT_EMPTY); + proposals.remove(CONCAT_PIPES); testHasOnlyTheseProposals(jpqlQuery, position, proposals); } @@ -5224,7 +5226,7 @@ public final void test_ResultVariable_09() { List proposals = new ArrayList<>(); proposals.add(AS); proposals.add(FROM); - CollectionTools.addAll(proposals, bnfAccessor.selectItemAggregates()); + CollectionTools.addAll(proposals, bnfAccessor.arithmetics()); testHasOnlyTheseProposals(jpqlQuery, position, proposals); } @@ -5238,7 +5240,7 @@ public final void test_ResultVariable_10() { List proposals = new ArrayList<>(); proposals.add(AS); proposals.add(FROM); - CollectionTools.addAll(proposals, bnfAccessor.selectItemAggregates()); + CollectionTools.addAll(proposals, bnfAccessor.arithmetics()); testHasOnlyTheseProposals(jpqlQuery, position, proposals); } @@ -5266,7 +5268,7 @@ public final void test_ResultVariable_12() { List proposals = new ArrayList<>(); proposals.add(AS); proposals.add(FROM); - CollectionTools.addAll(proposals, bnfAccessor.selectItemAggregates()); + CollectionTools.addAll(proposals, bnfAccessor.arithmetics()); testHasOnlyTheseProposals(jpqlQuery, position, proposals); } @@ -5342,6 +5344,9 @@ public final void test_Select_07() { proposals.add(FROM); CollectionTools.addAll(proposals, bnfAccessor.selectItemAggregates()); + // These are filtered out + proposals.remove(CONCAT_PIPES); + testHasOnlyTheseProposals(jpqlQuery, position, proposals); } @@ -5588,6 +5593,9 @@ public final void test_Subquery_10() { proposals.add(FROM); CollectionTools.addAll(proposals, bnfAccessor.selectItemAggregates()); + // These are filtered out + proposals.remove(CONCAT_PIPES); + testHasOnlyTheseProposals(jpqlQuery, position, proposals); } @@ -6426,6 +6434,7 @@ public final void test_When_09() { proposals.remove(IS_NOT_EMPTY); proposals.remove(AND); proposals.remove(OR); + proposals.remove(CONCAT_PIPES); testHasOnlyTheseProposals(jpqlQuery, position, proposals); } @@ -6463,6 +6472,7 @@ public final void test_When_11() { // Filtered out proposals.remove(IS_EMPTY); proposals.remove(IS_NOT_EMPTY); + proposals.remove(CONCAT_PIPES); testHasOnlyTheseProposals(jpqlQuery, position, proposals); } @@ -6480,6 +6490,7 @@ public final void test_When_12() { // Filtered out proposals.remove(IS_EMPTY); proposals.remove(IS_NOT_EMPTY); + proposals.remove(CONCAT_PIPES); testHasOnlyTheseProposals(jpqlQuery, position, proposals); } From 0dbed116067c789f87b73b706d2858353fe5f930 Mon Sep 17 00:00:00 2001 From: Radek Felcman Date: Mon, 26 Jun 2023 14:16:12 +0200 Subject: [PATCH 2/2] WordParser typo fix Signed-off-by: Radek Felcman --- .../main/java/org/eclipse/persistence/jpa/jpql/WordParser.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/WordParser.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/WordParser.java index 768a212ea01..57265e47bde 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/WordParser.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/WordParser.java @@ -178,6 +178,7 @@ public WordType getWordType() { */ public boolean isArithmeticSymbol(char character) { return character == '>' || + character == '!' || character == '<' || character == '/' || character == '*' ||