From 9a922f6711ce85274ac80e6dc1ba5b229c222a48 Mon Sep 17 00:00:00 2001 From: Radek Felcman Date: Mon, 11 Sep 2023 15:28:32 +0200 Subject: [PATCH] Jakarta Persistence 3.2 new feature - JPQL UNION, INTERSECT, EXCEPT, CAST Signed-off-by: Radek Felcman --- .../jpa1/entitymanager/TestGetReference.java | 4 +- ...stractEclipseLinkParameterTypeVisitor.java | 20 +- .../AbstractEclipseLinkSemanticValidator.java | 47 +- .../jpa/jpql/AbstractGrammarValidator.java | 221 +++++ .../jpa/jpql/AbstractSemanticValidator.java | 21 + .../jpa/jpql/AbstractValidator.java | 13 +- .../jpa/jpql/EclipseLinkGrammarValidator.java | 135 +-- .../jpa/jpql/EclipseLinkLiteralVisitor.java | 20 +- .../persistence/jpa/jpql/LiteralVisitor.java | 10 +- .../jpa/jpql/ParameterTypeVisitor.java | 6 + .../AbstractEclipseLinkExpressionVisitor.java | 14 +- ...actEclipseLinkTraverseChildrenVisitor.java | 17 +- ...tractEclipseLinkTraverseParentVisitor.java | 17 +- .../parser/AbstractExpressionVisitor.java | 12 + .../parser/AnonymousExpressionVisitor.java | 15 + ...EclipseLinkAnonymousExpressionVisitor.java | 17 +- .../parser/EclipseLinkExpressionVisitor.java | 23 +- .../jpa/jpql/parser/ExpressionVisitor.java | 21 + .../jpa/jpql/parser/JPQLGrammar3_2.java | 33 +- .../tools/AbstractContentAssistVisitor.java | 162 ++++ .../jpql/tools/DefaultGrammarValidator.java | 7 +- .../EclipseLinkContentAssistVisitor.java | 171 +--- .../tools/EclipseLinkResolverBuilder.java | 20 +- .../tools/model/BasicStateObjectBuilder.java | 6 + .../model/EclipseLinkStateObjectBuilder.java | 20 +- .../jpql/tools/resolver/ResolverBuilder.java | 18 + .../tests/jpql/AllGrammarValidatorTests.java | 24 +- .../jpa/tests/jpql/JPQLQueries3_2.java | 8 + .../jpql/parser/AllJPQLParserTests3_2.java | 4 +- .../tests/jpql/parser/JPQLQueriesTest3_2.java | 52 +- .../tools/DefaultGrammarValidatorTest3_2.java | 820 ++++++++++++++++++ 31 files changed, 1443 insertions(+), 535 deletions(-) create mode 100644 jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/DefaultGrammarValidatorTest3_2.java diff --git a/jpa/eclipselink.jpa.wdf.test/src/it/java/org/eclipse/persistence/testing/tests/wdf/jpa1/entitymanager/TestGetReference.java b/jpa/eclipselink.jpa.wdf.test/src/it/java/org/eclipse/persistence/testing/tests/wdf/jpa1/entitymanager/TestGetReference.java index 310567200e5..50cfd4e9e14 100644 --- a/jpa/eclipselink.jpa.wdf.test/src/it/java/org/eclipse/persistence/testing/tests/wdf/jpa1/entitymanager/TestGetReference.java +++ b/jpa/eclipselink.jpa.wdf.test/src/it/java/org/eclipse/persistence/testing/tests/wdf/jpa1/entitymanager/TestGetReference.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2015 SAP. All rights reserved. * * This program and the accompanying materials are made available under the @@ -187,7 +187,7 @@ public void testNegativ() { boolean operationFailed = false; env.beginTransaction(em); try { - employee = em.getReference(Employee.class, 17 + 4); + employee = em.getReference(Employee.class, 741); } catch (EntityNotFoundException e) { // $JL-EXC$ expected behavior operationFailed = true; diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractEclipseLinkParameterTypeVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractEclipseLinkParameterTypeVisitor.java index b083c39152b..cba6fc77207 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractEclipseLinkParameterTypeVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractEclipseLinkParameterTypeVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 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 @@ -16,9 +16,7 @@ package org.eclipse.persistence.jpa.jpql; import org.eclipse.persistence.jpa.jpql.parser.AsOfClause; -import org.eclipse.persistence.jpa.jpql.parser.CastExpression; import org.eclipse.persistence.jpa.jpql.parser.ConnectByClause; -import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkExpressionVisitor; import org.eclipse.persistence.jpa.jpql.parser.Expression; import org.eclipse.persistence.jpa.jpql.parser.ExtractExpression; @@ -28,7 +26,6 @@ import org.eclipse.persistence.jpa.jpql.parser.StartWithClause; import org.eclipse.persistence.jpa.jpql.parser.TableExpression; import org.eclipse.persistence.jpa.jpql.parser.TableVariableDeclaration; -import org.eclipse.persistence.jpa.jpql.parser.UnionClause; /** * This visitor calculates the type of an input parameter. @@ -57,21 +54,11 @@ public void visit(AsOfClause expression) { type = Object.class; } - @Override - public void visit(CastExpression expression) { - type = Object.class; - } - @Override public void visit(ConnectByClause expression) { type = Object.class; } - @Override - public void visit(DatabaseType expression) { - type = Object.class; - } - @Override public void visit(ExtractExpression expression) { type = Object.class; @@ -115,9 +102,4 @@ public void visit(TableExpression expression) { public void visit(TableVariableDeclaration expression) { type = Object.class; } - - @Override - public void visit(UnionClause expression) { - type = Object.class; - } } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractEclipseLinkSemanticValidator.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractEclipseLinkSemanticValidator.java index 166a6ff7a7b..3014c1d7525 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractEclipseLinkSemanticValidator.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractEclipseLinkSemanticValidator.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 @@ -19,7 +19,6 @@ import org.eclipse.persistence.jpa.jpql.parser.AnonymousExpressionVisitor; import org.eclipse.persistence.jpa.jpql.parser.AsOfClause; import org.eclipse.persistence.jpa.jpql.parser.BadExpression; -import org.eclipse.persistence.jpa.jpql.parser.CastExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression; import org.eclipse.persistence.jpa.jpql.parser.ConnectByClause; @@ -40,7 +39,6 @@ import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression; import org.eclipse.persistence.jpa.jpql.parser.TableExpression; import org.eclipse.persistence.jpa.jpql.parser.TableVariableDeclaration; -import org.eclipse.persistence.jpa.jpql.parser.UnionClause; import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.*; @@ -109,11 +107,6 @@ protected LiteralVisitor buildLiteralVisitor() { return new EclipseLinkLiteralVisitor(); } - @Override - protected OwningClauseVisitor buildOwningClauseVisitor() { - return new EclipseLinkOwningClauseVisitor(); - } - protected SubquerySelectItemCalculator buildSubquerySelectItemCalculator() { return new SubquerySelectItemCalculator(); } @@ -391,12 +384,6 @@ public void visit(AsOfClause expression) { // Nothing to validate semantically } - @Override - public void visit(CastExpression expression) { - super.visit(expression); - // Nothing to validate semantically - } - @Override public void visit(ConnectByClause expression) { super.visit(expression); @@ -464,38 +451,6 @@ public void visit(TableVariableDeclaration expression) { // Nothing to validate semantically } - @Override - public void visit(UnionClause expression) { - super.visit(expression); - // Nothing to validate semantically - } - - // Made static final for performance reasons. - /** - * This visitor retrieves the clause owning the visited {@link Expression}. - */ - public static final class EclipseLinkOwningClauseVisitor extends OwningClauseVisitor { - - public UnionClause unionClause; - - /** - * Creates a new EclipseLinkOwningClauseVisitor. - */ - public EclipseLinkOwningClauseVisitor() { - super(); - } - - @Override - public void dispose() { - super.dispose(); - unionClause = null; - } - - public void visit(UnionClause expression) { - this.unionClause = expression; - } - } - // Made static final for performance reasons. protected static final class SubquerySelectItemCalculator extends AnonymousExpressionVisitor { 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 d893df9e9d3..685c7364ad7 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 @@ -49,6 +49,7 @@ import org.eclipse.persistence.jpa.jpql.parser.AvgFunction; import org.eclipse.persistence.jpa.jpql.parser.BadExpression; import org.eclipse.persistence.jpa.jpql.parser.BetweenExpression; +import org.eclipse.persistence.jpa.jpql.parser.CastExpression; import org.eclipse.persistence.jpa.jpql.parser.CaseExpression; import org.eclipse.persistence.jpa.jpql.parser.CoalesceExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression; @@ -63,6 +64,8 @@ import org.eclipse.persistence.jpa.jpql.parser.ConditionalExpressionBNF; import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.CountFunction; +import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; +import org.eclipse.persistence.jpa.jpql.parser.DatabaseTypeFactory; import org.eclipse.persistence.jpa.jpql.parser.DateTime; import org.eclipse.persistence.jpa.jpql.parser.DeleteClause; import org.eclipse.persistence.jpa.jpql.parser.DeleteStatement; @@ -135,6 +138,7 @@ import org.eclipse.persistence.jpa.jpql.parser.TreatExpression; import org.eclipse.persistence.jpa.jpql.parser.TrimExpression; import org.eclipse.persistence.jpa.jpql.parser.TypeExpression; +import org.eclipse.persistence.jpa.jpql.parser.UnionClause; import org.eclipse.persistence.jpa.jpql.parser.UnknownExpression; import org.eclipse.persistence.jpa.jpql.parser.UpdateClause; import org.eclipse.persistence.jpa.jpql.parser.UpdateItem; @@ -400,6 +404,79 @@ public String rightParenthesisMissingKey(CountFunction expression) { }; } + protected AbstractDoubleEncapsulatedExpressionHelper buildDatabaseTypeHelper() { + return new AbstractDoubleEncapsulatedExpressionHelper(this) { + @Override + protected String firstExpressionInvalidKey() { + return DatabaseType_InvalidFirstExpression; + } + @Override + protected String firstExpressionMissingKey() { + return DatabaseType_MissingFirstExpression; + } + @Override + protected boolean hasComma(DatabaseType expression) { + // If the second expression is not specified, then the comma is not needed + return expression.hasComma() || + !expression.hasSecondExpression(); + } + @Override + protected boolean hasFirstExpression(DatabaseType expression) { + return !expression.hasLeftParenthesis() || + expression.hasFirstExpression(); + } + @Override + public boolean hasLeftParenthesis(DatabaseType expression) { + if (expression.hasLeftParenthesis()) { + return true; + } + // The parenthesis are optional unless one the following + // items is specified, then '(' is required + return !(expression.hasFirstExpression() || + expression.hasComma() || + expression.hasSecondExpression() || + expression.hasRightParenthesis()); + } + @Override + public boolean hasRightParenthesis(DatabaseType expression) { + if (expression.hasRightParenthesis()) { + return true; + } + // The parenthesis are optional unless one the following + // items is specified, then ')' is required + return !(expression.hasLeftParenthesis() || + expression.hasFirstExpression() || + expression.hasComma() || + expression.hasSecondExpression()); + } + @Override + protected boolean hasSecondExpression(DatabaseType expression) { + return !expression.hasComma() || + expression.hasSecondExpression(); + } + @Override + public String leftParenthesisMissingKey(DatabaseType expression) { + return DatabaseType_MissingLeftParenthesis; + } + @Override + protected String missingCommaKey() { + return DatabaseType_MissingComma; + } + @Override + public String rightParenthesisMissingKey(DatabaseType expression) { + return DatabaseType_MissingRightParenthesis; + } + @Override + protected String secondExpressionInvalidKey() { + return DatabaseType_InvalidSecondExpression; + } + @Override + protected String secondExpressionMissingKey() { + return DatabaseType_MissingSecondExpression; + } + }; + } + protected DateTimeVisitor buildDateTimeVisitor() { return new DateTimeVisitor(); } @@ -590,6 +667,15 @@ public String rightParenthesisMissingKey(KeyExpression expression) { }; } + protected AbstractDoubleEncapsulatedExpressionHelper databaseTypeHelper() { + AbstractDoubleEncapsulatedExpressionHelper helper = getHelper(DatabaseTypeFactory.ID); + if (helper == null) { + helper = buildDatabaseTypeHelper(); + registerHelper(DatabaseTypeFactory.ID, helper); + } + return helper; + } + protected AbstractDoubleEncapsulatedExpressionHelper buildLeftExpressionHelper() { return new AbstractDoubleEncapsulatedExpressionHelper(this) { @Override @@ -870,6 +956,32 @@ public String rightParenthesisMissingKey(ObjectExpression expression) { }; } + @Override + protected OwningClauseVisitor buildOwningClauseVisitor() { + return new OwningClauseVisitor(); + } + + protected AbstractSingleEncapsulatedExpressionHelper buildCastExpressionHelper() { + return new AbstractSingleEncapsulatedExpressionHelper(this) { + @Override + protected String encapsulatedExpressionInvalidKey(CastExpression expression) { + return CastExpression_InvalidExpression; + } + @Override + protected String encapsulatedExpressionMissingKey(CastExpression expression) { + return CastExpression_MissingExpression; + } + @Override + public String leftParenthesisMissingKey(CastExpression expression) { + return CastExpression_MissingLeftParenthesis; + } + @Override + public String rightParenthesisMissingKey(CastExpression expression) { + return CastExpression_MissingRightParenthesis; + } + }; + } + protected AbstractTripleEncapsulatedExpressionHelper buildReplaceExpressionHelper() { return new AbstractTripleEncapsulatedExpressionHelper(this) { @Override @@ -1421,6 +1533,16 @@ protected boolean isJPA2_1() { return getJPAVersion().isNewerThanOrEqual(JPAVersion.VERSION_2_1); } + /** + * Determines whether the JPA version defined by the JPQL grammar is 3.2. + * + * @return true if the JPQL grammar was defined for JPA 3.2; false if + * it was defined for a more recent version + */ + protected boolean isJPA3_2() { + return getJPAVersion().isNewerThanOrEqual(JPAVersion.VERSION_3_2); + } + /** * Determines whether the given subquery SELECT clause can return more than * one item or just a single. By default, only one item can be returned. @@ -1720,6 +1842,15 @@ protected void registerHelper(String id, Object helper) { helpers.put(id, helper); } + protected AbstractSingleEncapsulatedExpressionHelper castExpressionHelper() { + AbstractSingleEncapsulatedExpressionHelper helper = getHelper(CAST); + if (helper == null) { + helper = buildCastExpressionHelper(); + registerHelper(CAST, helper); + } + return helper; + } + protected AbstractTripleEncapsulatedExpressionHelper replaceExpressionHelper() { AbstractTripleEncapsulatedExpressionHelper helper = getHelper(REPLACE); if (helper == null) { @@ -1973,6 +2104,24 @@ protected void validateAbstractFromClause(AbstractFromClause expression) { } } + /** + * Determines whether the given {@link Expression} is a child of the UNION clause. + * + * @param expression The {@link Expression} to visit its parent hierarchy up to the clause + * @return true if the first parent being a clause is the UNION clause; + * false otherwise + */ + protected boolean isOwnedByUnionClause(Expression expression) { + OwningClauseVisitor visitor = getOwningClauseVisitor(); + try { + expression.accept(visitor); + return visitor.unionClause != null; + } + finally { + visitor.dispose(); + } + } + /** * Validates the select expression of the given SELECT clause. The select expression * will only be visited if its JPQL query BNF is part of the select item BNF. @@ -1984,6 +2133,13 @@ protected void validateAbstractFromClause(AbstractFromClause expression) { protected void validateAbstractSelectClause(AbstractSelectClause expression, boolean multipleSelectItemsAllowed) { + // Check id a subquery is defined in a UNION clause + // If the flag is false, then the SELECT clause is from a subquery + if (!multipleSelectItemsAllowed) { + Expression parent = expression.getParent(); + multipleSelectItemsAllowed = isOwnedByUnionClause (parent); + } + // Missing select expression if (!expression.hasSelectExpression()) { @@ -2896,6 +3052,42 @@ else if (expression.hasElseExpression() && } } + //TODO RFELCMAN there or DefaultGrammarValidator? + @Override + public void visit(CastExpression expression) { + + // Wrong JPA version + if (!isJPA3_2()) { + addProblem(expression, CastExpression_InvalidJPAVersion); + } + else { + + validateAbstractSingleEncapsulatedExpression(expression, castExpressionHelper()); + + // Database type + if (expression.hasExpression() || expression.hasAs()) { + + // Missing database type + if (!expression.hasDatabaseType()) { + + int startPosition = position(expression) + + 4 /* CAST */ + + (expression.hasLeftParenthesis() ? 1 : 0) + + length(expression.getExpression()) + + (expression.hasSpaceAfterExpression() ? 1 : 0) + + (expression.hasAs() ? 2 : 0) + + (expression.hasSpaceAfterAs() ? 1 : 0); + + addProblem(expression, startPosition, CastExpression_MissingDatabaseType); + } + // Validate database type + else { + expression.getDatabaseType().accept(this); + } + } + } + } + @Override public void visit(CoalesceExpression expression) { @@ -3195,6 +3387,11 @@ public void visit(CountFunction expression) { validateAggregateFunctionLocation(expression, countFunctionHelper()); } + @Override + public void visit(DatabaseType expression) { + validateAbstractDoubleEncapsulatedExpression(expression, databaseTypeHelper()); + } + @Override public void visit(DateTime expression) { @@ -4361,6 +4558,30 @@ public void visit(TypeExpression expression) { } } + @Override + public void visit(UnionClause expression) { + + // Wrong JPA version + if (!isJPA3_2()) { + addProblem(expression, UnionClause_InvalidJPAVersion); + } + // Missing subquery + else if (!expression.hasQuery()) { + + int startPosition = position(expression) + + expression.getIdentifier().length() + + (expression.hasSpaceAfterIdentifier() ? 1 : 0) + + (expression.hasAll() ? 3 : 0) + + (expression.hasSpaceAfterAll() ? 1 : 0); + + addProblem(expression, startPosition, UnionClause_MissingExpression); + } + // Validate the subquery + else { + expression.getQuery().accept(this); + } + } + @Override public void visit(UnknownExpression expression) { // Nothing to validate and we don't want to validate its encapsulated expression diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractSemanticValidator.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractSemanticValidator.java index 3ea07979104..f4f3258015e 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractSemanticValidator.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractSemanticValidator.java @@ -39,6 +39,7 @@ import org.eclipse.persistence.jpa.jpql.parser.BadExpression; import org.eclipse.persistence.jpa.jpql.parser.BetweenExpression; import org.eclipse.persistence.jpa.jpql.parser.CaseExpression; +import org.eclipse.persistence.jpa.jpql.parser.CastExpression; import org.eclipse.persistence.jpa.jpql.parser.CoalesceExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionMemberDeclaration; @@ -49,6 +50,7 @@ import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression; import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.CountFunction; +import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; import org.eclipse.persistence.jpa.jpql.parser.DateTime; import org.eclipse.persistence.jpa.jpql.parser.DeleteClause; import org.eclipse.persistence.jpa.jpql.parser.DeleteStatement; @@ -111,6 +113,7 @@ import org.eclipse.persistence.jpa.jpql.parser.TreatExpression; import org.eclipse.persistence.jpa.jpql.parser.TrimExpression; import org.eclipse.persistence.jpa.jpql.parser.TypeExpression; +import org.eclipse.persistence.jpa.jpql.parser.UnionClause; import org.eclipse.persistence.jpa.jpql.parser.UnknownExpression; import org.eclipse.persistence.jpa.jpql.parser.UpdateClause; import org.eclipse.persistence.jpa.jpql.parser.UpdateItem; @@ -2829,6 +2832,12 @@ public final void visit(CaseExpression expression) { validateCaseExpression(expression); } + @Override + public void visit(CastExpression expression) { + super.visit(expression); + // Nothing to validate semantically + } + @Override public final void visit(CoalesceExpression expression) { validateCoalesceExpression(expression); @@ -2880,6 +2889,12 @@ public final void visit(DateTime expression) { validateDateTime(expression); } + @Override + public void visit(DatabaseType expression) { + super.visit(expression); + // Nothing to validate semantically + } + @Override public final void visit(DeleteClause expression) { validateDeleteClause(expression); @@ -3171,6 +3186,12 @@ public final void visit(TypeExpression expression) { validateTypeExpression(expression); } + @Override + public void visit(UnionClause expression) { + super.visit(expression); + // Nothing to validate semantically + } + @Override public final void visit(UnknownExpression expression) { // Nothing semantically to validate diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractValidator.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractValidator.java index 892548a138b..266a8aa098f 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractValidator.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/AbstractValidator.java @@ -46,6 +46,7 @@ import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectClause; import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement; import org.eclipse.persistence.jpa.jpql.parser.SubExpression; +import org.eclipse.persistence.jpa.jpql.parser.UnionClause; import org.eclipse.persistence.jpa.jpql.parser.UnknownExpression; import org.eclipse.persistence.jpa.jpql.parser.UpdateClause; import org.eclipse.persistence.jpa.jpql.parser.UpdateStatement; @@ -237,7 +238,9 @@ protected NestedArrayVisitor buildNestedArrayVisitor() { * * @return A new {@link OwningClauseVisitor} */ - protected abstract OwningClauseVisitor buildOwningClauseVisitor(); + protected OwningClauseVisitor buildOwningClauseVisitor() { + return new OwningClauseVisitor(); + } /** * Creates the visitor that traverses the parent hierarchy of any {@link Expression} and stops at @@ -1025,6 +1028,7 @@ public static class OwningClauseVisitor extends AbstractTraverseParentVisitor { public SimpleSelectClause simpleSelectClause; public UpdateClause updateClause; public WhereClause whereClause; + public UnionClause unionClause; /** * Creates a new OwningClauseVisitor. @@ -1047,6 +1051,7 @@ public void dispose() { simpleSelectClause = null; updateClause = null; whereClause = null; + unionClause = null; } @Override @@ -1098,6 +1103,12 @@ public void visit(UpdateClause expression) { public void visit(WhereClause expression) { whereClause = expression; } + + @Override + public void visit(UnionClause expression) { + this.unionClause = expression; + } + } /** diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkGrammarValidator.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkGrammarValidator.java index 664b969c383..09679b2fc12 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkGrammarValidator.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkGrammarValidator.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 @@ -15,14 +15,12 @@ // package org.eclipse.persistence.jpa.jpql; -import org.eclipse.persistence.jpa.jpql.AbstractEclipseLinkSemanticValidator.EclipseLinkOwningClauseVisitor; import org.eclipse.persistence.jpa.jpql.parser.AbstractEclipseLinkExpressionVisitor; import org.eclipse.persistence.jpa.jpql.parser.AbstractSelectClause; import org.eclipse.persistence.jpa.jpql.parser.AsOfClause; import org.eclipse.persistence.jpa.jpql.parser.CastExpression; import org.eclipse.persistence.jpa.jpql.parser.ConnectByClause; import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; -import org.eclipse.persistence.jpa.jpql.parser.DatabaseTypeFactory; import org.eclipse.persistence.jpa.jpql.parser.DefaultEclipseLinkJPQLGrammar; import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkExpressionVisitor; import org.eclipse.persistence.jpa.jpql.parser.Expression; @@ -71,100 +69,6 @@ public EclipseLinkGrammarValidator(JPQLGrammar jpqlGrammar) { super(jpqlGrammar); } - protected AbstractSingleEncapsulatedExpressionHelper buildCastExpressionHelper() { - return new AbstractSingleEncapsulatedExpressionHelper(this) { - @Override - protected String encapsulatedExpressionInvalidKey(CastExpression expression) { - return CastExpression_InvalidExpression; - } - @Override - protected String encapsulatedExpressionMissingKey(CastExpression expression) { - return CastExpression_MissingExpression; - } - @Override - public String leftParenthesisMissingKey(CastExpression expression) { - return CastExpression_MissingLeftParenthesis; - } - @Override - public String rightParenthesisMissingKey(CastExpression expression) { - return CastExpression_MissingRightParenthesis; - } - }; - } - - protected AbstractDoubleEncapsulatedExpressionHelper buildDatabaseTypeHelper() { - return new AbstractDoubleEncapsulatedExpressionHelper(this) { - @Override - protected String firstExpressionInvalidKey() { - return DatabaseType_InvalidFirstExpression; - } - @Override - protected String firstExpressionMissingKey() { - return DatabaseType_MissingFirstExpression; - } - @Override - protected boolean hasComma(DatabaseType expression) { - // If the second expression is not specified, then the comma is not needed - return expression.hasComma() || - !expression.hasSecondExpression(); - } - @Override - protected boolean hasFirstExpression(DatabaseType expression) { - return !expression.hasLeftParenthesis() || - expression.hasFirstExpression(); - } - @Override - public boolean hasLeftParenthesis(DatabaseType expression) { - if (expression.hasLeftParenthesis()) { - return true; - } - // The parenthesis are optional unless one the following - // items is specified, then '(' is required - return !(expression.hasFirstExpression() || - expression.hasComma() || - expression.hasSecondExpression() || - expression.hasRightParenthesis()); - } - @Override - public boolean hasRightParenthesis(DatabaseType expression) { - if (expression.hasRightParenthesis()) { - return true; - } - // The parenthesis are optional unless one the following - // items is specified, then ')' is required - return !(expression.hasLeftParenthesis() || - expression.hasFirstExpression() || - expression.hasComma() || - expression.hasSecondExpression()); - } - @Override - protected boolean hasSecondExpression(DatabaseType expression) { - return !expression.hasComma() || - expression.hasSecondExpression(); - } - @Override - public String leftParenthesisMissingKey(DatabaseType expression) { - return DatabaseType_MissingLeftParenthesis; - } - @Override - protected String missingCommaKey() { - return DatabaseType_MissingComma; - } - @Override - public String rightParenthesisMissingKey(DatabaseType expression) { - return DatabaseType_MissingRightParenthesis; - } - @Override - protected String secondExpressionInvalidKey() { - return DatabaseType_InvalidSecondExpression; - } - @Override - protected String secondExpressionMissingKey() { - return DatabaseType_MissingSecondExpression; - } - }; - } - protected AbstractSingleEncapsulatedExpressionHelper buildExtractExpressionHelper() { return new AbstractSingleEncapsulatedExpressionHelper(this) { @Override @@ -206,11 +110,6 @@ protected LiteralVisitor buildLiteralVisitor() { return new EclipseLinkLiteralVisitor(); } - @Override - protected OwningClauseVisitor buildOwningClauseVisitor() { - return new EclipseLinkOwningClauseVisitor(); - } - protected AbstractSingleEncapsulatedExpressionHelper buildTableExpressionHelper() { return new AbstractSingleEncapsulatedExpressionHelper(this) { @Override @@ -241,15 +140,6 @@ protected AbstractSingleEncapsulatedExpressionHelper castExpress return helper; } - protected AbstractDoubleEncapsulatedExpressionHelper databaseTypeHelper() { - AbstractDoubleEncapsulatedExpressionHelper helper = getHelper(DatabaseTypeFactory.ID); - if (helper == null) { - helper = buildDatabaseTypeHelper(); - registerHelper(DatabaseTypeFactory.ID, helper); - } - return helper; - } - protected AbstractSingleEncapsulatedExpressionHelper extractExpressionHelper() { AbstractSingleEncapsulatedExpressionHelper helper = getHelper(EXTRACT); if (helper == null) { @@ -273,11 +163,6 @@ protected InExpressionWithNestedArrayVisitor getInExpressionWithNestedArray() { return inExpressionWithNestedArrayVisitor; } - @Override - protected EclipseLinkOwningClauseVisitor getOwningClauseVisitor() { - return (EclipseLinkOwningClauseVisitor) super.getOwningClauseVisitor(); - } - /** * Determines whether the persistence provider is EclipseLink or not. * @@ -328,24 +213,6 @@ protected boolean isOwnedByInExpression(Expression expression) { return visitor.expression != null; } - /** - * Determines whether the given {@link Expression} is a child of the UNION clause. - * - * @param expression The {@link Expression} to visit its parent hierarchy up to the clause - * @return true if the first parent being a clause is the UNION clause; - * false otherwise - */ - protected boolean isOwnedByUnionClause(Expression expression) { - EclipseLinkOwningClauseVisitor visitor = getOwningClauseVisitor(); - try { - expression.accept(visitor); - return visitor.unionClause != null; - } - finally { - visitor.dispose(); - } - } - @Override protected boolean isSubqueryAllowedAnywhere() { EclipseLinkVersion version = EclipseLinkVersion.value(getProviderVersion()); diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkLiteralVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkLiteralVisitor.java index 34ddb60680c..03aa33f6fb5 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkLiteralVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/EclipseLinkLiteralVisitor.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 @@ -16,9 +16,7 @@ package org.eclipse.persistence.jpa.jpql; import org.eclipse.persistence.jpa.jpql.parser.AsOfClause; -import org.eclipse.persistence.jpa.jpql.parser.CastExpression; import org.eclipse.persistence.jpa.jpql.parser.ConnectByClause; -import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkExpressionVisitor; import org.eclipse.persistence.jpa.jpql.parser.ExtractExpression; import org.eclipse.persistence.jpa.jpql.parser.HierarchicalQueryClause; @@ -27,7 +25,6 @@ import org.eclipse.persistence.jpa.jpql.parser.StartWithClause; import org.eclipse.persistence.jpa.jpql.parser.TableExpression; import org.eclipse.persistence.jpa.jpql.parser.TableVariableDeclaration; -import org.eclipse.persistence.jpa.jpql.parser.UnionClause; /** * This visitor traverses an {@link org.eclipse.persistence.jpa.jpql.parser.Expression Expression} @@ -58,21 +55,10 @@ public EclipseLinkLiteralVisitor() { public void visit(AsOfClause expression) { } - @Override - public void visit(CastExpression expression) { - } - @Override public void visit(ConnectByClause expression) { } - @Override - public void visit(DatabaseType expression) { - if (type == LiteralType.STRING_LITERAL) { - literal = expression.getActualIdentifier(); - } - } - @Override public void visit(ExtractExpression expression) { } @@ -103,8 +89,4 @@ public void visit(TableExpression expression) { @Override public void visit(TableVariableDeclaration expression) { } - - @Override - public void visit(UnionClause expression) { - } } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/LiteralVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/LiteralVisitor.java index 6b52c83ecf9..83b6a66509b 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/LiteralVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/LiteralVisitor.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 @@ -20,6 +20,7 @@ import org.eclipse.persistence.jpa.jpql.parser.AnonymousExpressionVisitor; import org.eclipse.persistence.jpa.jpql.parser.CollectionMemberDeclaration; import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression; +import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; import org.eclipse.persistence.jpa.jpql.parser.EntityTypeLiteral; import org.eclipse.persistence.jpa.jpql.parser.FunctionExpression; import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariable; @@ -112,6 +113,13 @@ public void visit(CollectionValuedPathExpression expression) { visitAbstractPathExpression(expression); } + @Override + public void visit(DatabaseType expression) { + if (type == LiteralType.STRING_LITERAL) { + literal = expression.getActualIdentifier(); + } + } + @Override public void visit(EntityTypeLiteral expression) { if (type == LiteralType.ENTITY_TYPE) { diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/ParameterTypeVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/ParameterTypeVisitor.java index 88fdf955508..3a3000b8286 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/ParameterTypeVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/ParameterTypeVisitor.java @@ -37,6 +37,7 @@ import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression; import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.CountFunction; +import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; import org.eclipse.persistence.jpa.jpql.parser.DateTime; import org.eclipse.persistence.jpa.jpql.parser.DivisionExpression; import org.eclipse.persistence.jpa.jpql.parser.EmptyCollectionComparisonExpression; @@ -308,6 +309,11 @@ public void visit(CountFunction expression) { this.expression = expression; } + @Override + public void visit(DatabaseType expression) { + type = Object.class; + } + @Override public void visit(DateTime expression) { // A date/time always have a type diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkExpressionVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkExpressionVisitor.java index 124eca85548..60c54dfae1c 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkExpressionVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkExpressionVisitor.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 @@ -40,18 +40,10 @@ protected AbstractEclipseLinkExpressionVisitor() { public void visit(AsOfClause expression) { } - @Override - public void visit(CastExpression expression) { - } - @Override public void visit(ConnectByClause expression) { } - @Override - public void visit(DatabaseType expression) { - } - @Override public void visit(ExtractExpression expression) { } @@ -79,8 +71,4 @@ public void visit(TableExpression expression) { @Override public void visit(TableVariableDeclaration expression) { } - - @Override - public void visit(UnionClause expression) { - } } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkTraverseChildrenVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkTraverseChildrenVisitor.java index 17c8569177a..bb2e49b7891 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkTraverseChildrenVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkTraverseChildrenVisitor.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 @@ -43,21 +43,11 @@ public void visit(AsOfClause expression) { visit((Expression) expression); } - @Override - public void visit(CastExpression expression) { - visit((Expression) expression); - } - @Override public void visit(ConnectByClause expression) { visit((Expression) expression); } - @Override - public void visit(DatabaseType expression) { - visit((Expression) expression); - } - @Override public void visit(ExtractExpression expression) { visit((Expression) expression); @@ -92,9 +82,4 @@ public void visit(TableExpression expression) { public void visit(TableVariableDeclaration expression) { visit((Expression) expression); } - - @Override - public void visit(UnionClause expression) { - visit((Expression) expression); - } } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkTraverseParentVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkTraverseParentVisitor.java index d2fb78b89b4..26e527952a3 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkTraverseParentVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractEclipseLinkTraverseParentVisitor.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 @@ -42,21 +42,11 @@ public void visit(AsOfClause expression) { visit((Expression) expression); } - @Override - public void visit(CastExpression expression) { - visit((Expression) expression); - } - @Override public void visit(ConnectByClause expression) { visit((Expression) expression); } - @Override - public void visit(DatabaseType expression) { - visit((Expression) expression); - } - @Override public void visit(ExtractExpression expression) { visit((Expression) expression); @@ -91,9 +81,4 @@ public void visit(TableExpression expression) { public void visit(TableVariableDeclaration expression) { visit((Expression) expression); } - - @Override - public void visit(UnionClause expression) { - visit((Expression) expression); - } } 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 09b27587d90..01340a625a5 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 @@ -78,6 +78,10 @@ public void visit(BetweenExpression expression) { public void visit(CaseExpression expression) { } + @Override + public void visit(CastExpression expression) { + } + @Override public void visit(CoalesceExpression expression) { } @@ -118,6 +122,10 @@ public void visit(ConstructorExpression expression) { public void visit(CountFunction expression) { } + @Override + public void visit(DatabaseType expression) { + } + @Override public void visit(DateTime expression) { } @@ -418,6 +426,10 @@ public void visit(UpperExpression expression) { public void visit(ValueExpression expression) { } + @Override + public void visit(UnionClause expression) { + } + @Override public void visit(WhenClause 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 dcefa23456d..debf38622fd 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 @@ -138,6 +138,11 @@ public void visit(CountFunction expression) { visit((Expression) expression); } + @Override + public void visit(DatabaseType expression) { + visit((Expression) expression); + } + @Override public void visit(DateTime expression) { visit((Expression) expression); @@ -186,6 +191,11 @@ public void visit(ExistsExpression expression) { protected void visit(Expression expression) { } + @Override + public void visit(CastExpression expression) { + visit((Expression) expression); + } + @Override public void visit(FromClause expression) { visit((Expression) expression); @@ -530,4 +540,9 @@ public void visit(WhenClause expression) { public void visit(WhereClause expression) { visit((Expression) expression); } + + @Override + public void visit(UnionClause expression) { + visit((Expression) expression); + } } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkAnonymousExpressionVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkAnonymousExpressionVisitor.java index 55f410fd85b..d22f7e3bf2e 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkAnonymousExpressionVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkAnonymousExpressionVisitor.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 @@ -42,21 +42,11 @@ public void visit(AsOfClause expression) { visit((Expression) expression); } - @Override - public void visit(CastExpression expression) { - visit((Expression) expression); - } - @Override public void visit(ConnectByClause expression) { visit((Expression) expression); } - @Override - public void visit(DatabaseType expression) { - visit((Expression) expression); - } - @Override public void visit(ExtractExpression expression) { visit((Expression) expression); @@ -91,9 +81,4 @@ public void visit(TableExpression expression) { public void visit(TableVariableDeclaration expression) { visit((Expression) expression); } - - @Override - public void visit(UnionClause expression) { - visit((Expression) expression); - } } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkExpressionVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkExpressionVisitor.java index 663ee9c747f..8291bd88db9 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkExpressionVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/EclipseLinkExpressionVisitor.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 @@ -45,13 +45,6 @@ public interface EclipseLinkExpressionVisitor extends ExpressionVisitor { */ void visit(AsOfClause expression); - /** - * Visits the {@link CastExpression} expression. - * - * @param expression The {@link Expression} to visit - */ - void visit(CastExpression expression); - /** * Visits the {@link ConnectByClause} expression. * @@ -59,13 +52,6 @@ public interface EclipseLinkExpressionVisitor extends ExpressionVisitor { */ void visit(ConnectByClause expression); - /** - * Visits the {@link DatabaseType} expression. - * - * @param expression The {@link DatabaseType} to visit - */ - void visit(DatabaseType expression); - /** * Visits the {@link ExtractExpression} expression. * @@ -114,11 +100,4 @@ public interface EclipseLinkExpressionVisitor extends ExpressionVisitor { * @param expression The {@link Expression} to visit */ void visit(TableVariableDeclaration expression); - - /** - * Visits the {@link UnionClause} expression. - * - * @param expression The {@link Expression} to visit - */ - void visit(UnionClause expression); } 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 3ce9dd54785..e2034cee522 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 @@ -102,6 +102,13 @@ public interface ExpressionVisitor { */ void visit(CaseExpression expression); + /** + * Visits the {@link CastExpression} expression. + * + * @param expression The {@link Expression} to visit + */ + void visit(CastExpression expression); + /** * Visits the {@link CoalesceExpression} expression. * @@ -172,6 +179,13 @@ public interface ExpressionVisitor { */ void visit(CountFunction expression); + /** + * Visits the {@link DatabaseType} expression. + * + * @param expression The {@link DatabaseType} to visit + */ + void visit(DatabaseType expression); + /** * Visits the {@link DateTime} expression. * @@ -710,4 +724,11 @@ public interface ExpressionVisitor { * @param expression The {@link WhereClause} to visit */ void visit(WhereClause expression); + + /** + * Visits the {@link UnionClause} expression. + * + * @param expression The {@link UnionClause} to visit + */ + void visit(UnionClause expression); } 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 index f848a2e393b..97d63d58b12 100644 --- 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 @@ -18,19 +18,31 @@ import org.eclipse.persistence.jpa.jpql.ExpressionTools; import org.eclipse.persistence.jpa.jpql.JPAVersion; +import static org.eclipse.persistence.jpa.jpql.parser.Expression.CAST; import static org.eclipse.persistence.jpa.jpql.parser.Expression.CONCAT_PIPES; +import static org.eclipse.persistence.jpa.jpql.parser.Expression.EXCEPT; +import static org.eclipse.persistence.jpa.jpql.parser.Expression.INTERSECT; import static org.eclipse.persistence.jpa.jpql.parser.Expression.LEFT; import static org.eclipse.persistence.jpa.jpql.parser.Expression.REPLACE; import static org.eclipse.persistence.jpa.jpql.parser.Expression.RIGHT; +import static org.eclipse.persistence.jpa.jpql.parser.Expression.UNION; /** * This {@link JPQLGrammar} provides support for parsing JPQL queries defined in Jakarta Persistence 3.2. - *

+ * 
 select_statement ::= select_clause from_clause [where_clause] [groupby_clause] [having_clause] [orderby_clause] {union_clause}*
+ *
+ * union_clause ::= { UNION | INTERSECT | EXCEPT} [ALL] subquery
  *
  * string_expression ::= string_expression || string_term
  *
  * functions_returning_strings ::= REPLACE(string_primary, string_primary, string_primary)
  *
+ * functions_returning_string ::= LEFT(string_primary, simple_arithmetic_expression})
+ *
+ * functions_returning_string ::= RIGHT(string_primary, simple_arithmetic_expression})
+ *
+ * cast_expression ::= CAST(scalar_expression [AS] database_type)
+ *
  * 
*/ public class JPQLGrammar3_2 extends AbstractJPQLGrammar { @@ -109,12 +121,20 @@ protected void initializeBNFs() { registerBNF(new InternalLeftStringExpressionBNF()); registerBNF(new InternalRightPositionExpressionBNF()); registerBNF(new InternalRightStringExpressionBNF()); + registerBNF(new UnionClauseBNF()); + registerBNF(new CastExpressionBNF()); + registerBNF(new DatabaseTypeQueryBNF()); // Extend some query BNFs addChildBNF(StringPrimaryBNF.ID, SimpleStringExpressionBNF.ID); addChildFactory(FunctionsReturningStringsBNF.ID, ReplaceExpressionFactory.ID); addChildFactory(FunctionsReturningStringsBNF.ID, LeftExpressionFactory.ID); addChildFactory(FunctionsReturningStringsBNF.ID, RightExpressionFactory.ID); + + // CAST + addChildBNF(FunctionsReturningDatetimeBNF.ID, CastExpressionBNF.ID); + addChildBNF(FunctionsReturningNumericsBNF.ID, CastExpressionBNF.ID); + addChildBNF(FunctionsReturningStringsBNF.ID, CastExpressionBNF.ID); } @Override @@ -123,6 +143,9 @@ protected void initializeExpressionFactories() { registerFactory(new ReplaceExpressionFactory()); registerFactory(new LeftExpressionFactory()); registerFactory(new RightExpressionFactory()); + registerFactory(new UnionClauseFactory()); + registerFactory(new DatabaseTypeFactory()); + registerFactory(new CastExpressionFactory()); } @Override @@ -135,6 +158,14 @@ protected void initializeIdentifiers() { registerIdentifierVersion(LEFT, JPAVersion.VERSION_3_2); registerIdentifierRole(RIGHT, IdentifierRole.FUNCTION); // REPLACE(x, y) registerIdentifierVersion(RIGHT, JPAVersion.VERSION_3_2); + registerIdentifierRole(UNION, IdentifierRole.CLAUSE); + registerIdentifierVersion(UNION, JPAVersion.VERSION_3_2); + registerIdentifierRole(INTERSECT, IdentifierRole.CLAUSE); + registerIdentifierVersion(INTERSECT, JPAVersion.VERSION_3_2); + registerIdentifierRole(EXCEPT, IdentifierRole.CLAUSE); + registerIdentifierVersion(EXCEPT, JPAVersion.VERSION_3_2); + registerIdentifierRole(CAST, IdentifierRole.FUNCTION); // FUNCTION(n, x1, ..., x2) + registerIdentifierVersion(CAST, JPAVersion.VERSION_3_2); } @Override 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 6c528d8bfcb..9bd4568e197 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 @@ -66,6 +66,7 @@ import org.eclipse.persistence.jpa.jpql.parser.BetweenExpression; import org.eclipse.persistence.jpa.jpql.parser.CaseExpression; import org.eclipse.persistence.jpa.jpql.parser.CaseOperandBNF; +import org.eclipse.persistence.jpa.jpql.parser.CastExpression; import org.eclipse.persistence.jpa.jpql.parser.CoalesceExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionMemberDeclaration; @@ -81,6 +82,7 @@ import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; import org.eclipse.persistence.jpa.jpql.parser.ConstructorItemBNF; import org.eclipse.persistence.jpa.jpql.parser.CountFunction; +import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; import org.eclipse.persistence.jpa.jpql.parser.DateTime; import org.eclipse.persistence.jpa.jpql.parser.DeleteClause; import org.eclipse.persistence.jpa.jpql.parser.DeleteStatement; @@ -168,6 +170,7 @@ import org.eclipse.persistence.jpa.jpql.parser.TrimExpression; import org.eclipse.persistence.jpa.jpql.parser.TypeExpression; import org.eclipse.persistence.jpa.jpql.parser.UnknownExpression; +import org.eclipse.persistence.jpa.jpql.parser.UnionClause; import org.eclipse.persistence.jpa.jpql.parser.UpdateClause; import org.eclipse.persistence.jpa.jpql.parser.UpdateItem; import org.eclipse.persistence.jpa.jpql.parser.UpdateStatement; @@ -2657,6 +2660,51 @@ else if (expression.hasSpaceAfterCase()) { } } + @Override + public void visit(CastExpression expression) { + super.visit(expression); + int position = queryPosition.getPosition(expression) - corrections.peek(); + String identifier = expression.getIdentifier(); + + // Within CAST + if (isPositionWithin(position, identifier)) { + addIdentifier(identifier); + addIdentificationVariables(); + addFunctionIdentifiers(expression.getParent().findQueryBNF(expression)); + } + // After "CAST(" + else if (expression.hasLeftParenthesis()) { + int length = identifier.length() + 1 /* '(' */; + + // Right after "CAST(" + if (position == length) { + addIdentificationVariables(); + addFunctionIdentifiers(expression.getEncapsulatedExpressionQueryBNFId()); + } + else if (expression.hasExpression()) { + Expression scalarExpression = expression.getExpression(); + + if (isComplete(scalarExpression)) { + length += scalarExpression.getLength(); + + if (expression.hasSpaceAfterExpression()) { + length++; + + // Right before "AS" or database type + if (position == length) { + addAggregateIdentifiers(expression.getEncapsulatedExpressionQueryBNFId()); + proposals.addIdentifier(AS); + } + // Within "AS" + else if (isPositionWithin(position, length, AS)) { + proposals.addIdentifier(AS); + } + } + } + } + } + } + @Override public void visit(CoalesceExpression expression) { super.visit(expression); @@ -2881,6 +2929,12 @@ public void visit(CountFunction expression) { visitAggregateFunction(expression); } + @Override + public void visit(DatabaseType expression) { + super.visit(expression); + // Nothing to do, this is database specific + } + @Override public void visit(DateTime expression) { super.visit(expression); @@ -3849,6 +3903,58 @@ public void visit(TypeExpression expression) { visitSingleEncapsulatedExpression(expression, IdentificationVariableType.ALL); } + @Override + public void visit(UnionClause expression) { + super.visit(expression); + int position = queryPosition.getPosition(expression) - corrections.peek(); + String identifier = expression.getIdentifier(); + + // Within + if (isPositionWithin(position, identifier)) { + proposals.addIdentifier(EXCEPT); + proposals.addIdentifier(INTERSECT); + proposals.addIdentifier(UNION); + } + // After " " + else if (expression.hasSpaceAfterIdentifier()) { + int length = identifier.length() + SPACE_LENGTH; + + // Right after " " + if (position == length) { + proposals.addIdentifier(ALL); + + if (!expression.hasAll()) { + addIdentifier(SELECT); + } + } + // Within "ALL" + else if (isPositionWithin(position, length, ALL)) { + addIdentifier(ALL); + } + else { + if ((position == length) && !expression.hasAll()) { + proposals.addIdentifier(SELECT); + } + else { + + if (expression.hasAll()) { + length += 3 /* ALL */; + } + + // After "ALL " + if (expression.hasSpaceAfterAll()) { + length += SPACE_LENGTH; + + // Right after "ALL " + if (position == length) { + proposals.addIdentifier(SELECT); + } + } + } + } + } + } + @Override public void visit(UnknownExpression expression) { super.visit(expression); @@ -5534,6 +5640,12 @@ public void visit(CaseExpression expression) { expression.hasEnd(); } + @Override + public void visit(CastExpression expression) { + appendable = !conditionalExpression && + expression.hasRightParenthesis(); + } + @Override public void visit(CoalesceExpression expression) { appendable = !conditionalExpression && @@ -5653,6 +5765,11 @@ public void visit(CountFunction expression) { expression.hasRightParenthesis(); } + @Override + public void visit(DatabaseType expression) { + // Always complete since it's a single word + } + @Override public void visit(DateTime expression) { @@ -7390,6 +7507,28 @@ else if (expression.hasCaseOperand()) { } } + @Override + public void visit(CastExpression expression) { + + if (badExpression) { + return; + } + + if (expression.hasScalarExpression() && + !expression.hasAs() && + !expression.hasDatabaseType() && + !expression.hasRightParenthesis()) { + + expression.getExpression().accept(this); + } + + if (queryPosition.getExpression() == null) { + queryPosition.setExpression(expression); + } + + queryPosition.addPosition(expression, expression.getLength() - correction); + } + @Override public void visit(CoalesceExpression expression) { visitAbstractSingleEncapsulatedExpression(expression); @@ -7565,6 +7704,11 @@ public void visit(CountFunction expression) { visitAbstractSingleEncapsulatedExpression(expression); } + @Override + public void visit(DatabaseType expression) { + visitAbstractDoubleEncapsulatedExpression(expression); + } + @Override public void visit(DateTime expression) { @@ -8425,6 +8569,24 @@ public void visit(TypeExpression expression) { visitAbstractSingleEncapsulatedExpression(expression); } + @Override + public void visit(UnionClause expression) { + + if (badExpression) { + return; + } + + if (expression.hasQuery()) { + expression.getQuery().accept(this); + } + + if (queryPosition.getExpression() == null) { + queryPosition.setExpression(expression); + } + + queryPosition.addPosition(expression, expression.getLength() - correction); + } + @Override public void visit(UnknownExpression expression) { // Nothing to do, this is the expression that needs diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/DefaultGrammarValidator.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/DefaultGrammarValidator.java index dcf0e0b55e6..4539a21df17 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/DefaultGrammarValidator.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/DefaultGrammarValidator.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 @@ -54,11 +54,6 @@ protected LiteralVisitor buildLiteralVisitor() { return new DefaultLiteralVisitor(); } - @Override - protected OwningClauseVisitor buildOwningClauseVisitor() { - return new OwningClauseVisitor(); - } - @Override protected boolean isJoinFetchIdentifiable() { return false; diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/EclipseLinkContentAssistVisitor.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/EclipseLinkContentAssistVisitor.java index f3c00cc4841..0ca0169edd2 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/EclipseLinkContentAssistVisitor.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/EclipseLinkContentAssistVisitor.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 @@ -16,7 +16,6 @@ package org.eclipse.persistence.jpa.jpql.tools; import static org.eclipse.persistence.jpa.jpql.parser.AbstractExpression.DOT; -import static org.eclipse.persistence.jpa.jpql.parser.Expression.ALL; import static org.eclipse.persistence.jpa.jpql.parser.Expression.AS; import static org.eclipse.persistence.jpa.jpql.parser.Expression.AS_OF; import static org.eclipse.persistence.jpa.jpql.parser.Expression.CONNECT_BY; @@ -46,11 +45,9 @@ import org.eclipse.persistence.jpa.jpql.parser.AbstractPathExpression; import org.eclipse.persistence.jpa.jpql.parser.AbstractSelectStatement; import org.eclipse.persistence.jpa.jpql.parser.AsOfClause; -import org.eclipse.persistence.jpa.jpql.parser.CastExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpressionBNF; import org.eclipse.persistence.jpa.jpql.parser.ConnectByClause; -import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; import org.eclipse.persistence.jpa.jpql.parser.DefaultEclipseLinkJPQLGrammar; import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkExpressionVisitor; import org.eclipse.persistence.jpa.jpql.parser.Expression; @@ -290,51 +287,6 @@ else if (expression.hasTimestamp()) { } } - @Override - public void visit(CastExpression expression) { - super.visit(expression); - int position = queryPosition.getPosition(expression) - corrections.peek(); - String identifier = expression.getIdentifier(); - - // Within CAST - if (isPositionWithin(position, identifier)) { - addIdentifier(identifier); - addIdentificationVariables(); - addFunctionIdentifiers(expression.getParent().findQueryBNF(expression)); - } - // After "CAST(" - else if (expression.hasLeftParenthesis()) { - int length = identifier.length() + 1 /* '(' */; - - // Right after "CAST(" - if (position == length) { - addIdentificationVariables(); - addFunctionIdentifiers(expression.getEncapsulatedExpressionQueryBNFId()); - } - else if (expression.hasExpression()) { - Expression scalarExpression = expression.getExpression(); - - if (isComplete(scalarExpression)) { - length += scalarExpression.getLength(); - - if (expression.hasSpaceAfterExpression()) { - length++; - - // Right before "AS" or database type - if (position == length) { - addAggregateIdentifiers(expression.getEncapsulatedExpressionQueryBNFId()); - proposals.addIdentifier(AS); - } - // Within "AS" - else if (isPositionWithin(position, length, AS)) { - proposals.addIdentifier(AS); - } - } - } - } - } - } - @Override public void visit(ConnectByClause expression) { super.visit(expression); @@ -357,12 +309,6 @@ else if (expression.hasSpaceAfterConnectBy()) { } } - @Override - public void visit(DatabaseType expression) { - super.visit(expression); - // Nothing to do, this is database specific - } - @Override public void visit(ExtractExpression expression) { super.visit(expression); @@ -647,58 +593,6 @@ public void visit(TableVariableDeclaration expression) { } } - @Override - public void visit(UnionClause expression) { - super.visit(expression); - int position = queryPosition.getPosition(expression) - corrections.peek(); - String identifier = expression.getIdentifier(); - - // Within - if (isPositionWithin(position, identifier)) { - proposals.addIdentifier(EXCEPT); - proposals.addIdentifier(INTERSECT); - proposals.addIdentifier(UNION); - } - // After " " - else if (expression.hasSpaceAfterIdentifier()) { - int length = identifier.length() + SPACE_LENGTH; - - // Right after " " - if (position == length) { - proposals.addIdentifier(ALL); - - if (!expression.hasAll()) { - addIdentifier(SELECT); - } - } - // Within "ALL" - else if (isPositionWithin(position, length, ALL)) { - addIdentifier(ALL); - } - else { - if ((position == length) && !expression.hasAll()) { - proposals.addIdentifier(SELECT); - } - else { - - if (expression.hasAll()) { - length += 3 /* ALL */; - } - - // After "ALL " - if (expression.hasSpaceAfterAll()) { - length += SPACE_LENGTH; - - // Right after "ALL " - if (position == length) { - proposals.addIdentifier(SELECT); - } - } - } - } - } - } - @Override protected void visitThirdPartyPathExpression(AbstractPathExpression expression, String variableName) { @@ -740,12 +634,6 @@ public void visit(AsOfClause expression) { } } - @Override - public void visit(CastExpression expression) { - appendable = !conditionalExpression && - expression.hasRightParenthesis(); - } - @Override public void visit(ConnectByClause expression) { if (expression.hasExpression()) { @@ -753,11 +641,6 @@ public void visit(ConnectByClause expression) { } } - @Override - public void visit(DatabaseType expression) { - // Always complete since it's a single word - } - @Override public void visit(ExtractExpression expression) { appendable = !conditionalExpression && @@ -817,13 +700,6 @@ public void visit(TableVariableDeclaration expression) { expression.getIdentificationVariable().accept(this); } } - - @Override - public void visit(UnionClause expression) { - if (expression.hasQuery()) { - expression.getQuery().accept(this); - } - } } // Made static final for performance reasons. @@ -852,28 +728,6 @@ public void visit(AsOfClause expression) { queryPosition.addPosition(expression, expression.getLength() - correction); } - @Override - public void visit(CastExpression expression) { - - if (badExpression) { - return; - } - - if (expression.hasScalarExpression() && - !expression.hasAs() && - !expression.hasDatabaseType() && - !expression.hasRightParenthesis()) { - - expression.getExpression().accept(this); - } - - if (queryPosition.getExpression() == null) { - queryPosition.setExpression(expression); - } - - queryPosition.addPosition(expression, expression.getLength() - correction); - } - @Override public void visit(ConnectByClause expression) { @@ -892,11 +746,6 @@ public void visit(ConnectByClause expression) { queryPosition.addPosition(expression, expression.getLength() - correction); } - @Override - public void visit(DatabaseType expression) { - visitAbstractDoubleEncapsulatedExpression(expression); - } - @Override public void visit(ExtractExpression expression) { visitAbstractSingleEncapsulatedExpression(expression); @@ -998,24 +847,6 @@ else if (!expression.hasAs()) { queryPosition.addPosition(expression, expression.getLength() - correction); } - - @Override - public void visit(UnionClause expression) { - - if (badExpression) { - return; - } - - if (expression.hasQuery()) { - expression.getQuery().accept(this); - } - - if (queryPosition.getExpression() == null) { - queryPosition.setExpression(expression); - } - - queryPosition.addPosition(expression, expression.getLength() - correction); - } } // Made static final for performance reasons. diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/EclipseLinkResolverBuilder.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/EclipseLinkResolverBuilder.java index c4eff81f522..6a669974bc3 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/EclipseLinkResolverBuilder.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/EclipseLinkResolverBuilder.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 @@ -16,9 +16,7 @@ package org.eclipse.persistence.jpa.jpql.tools; import org.eclipse.persistence.jpa.jpql.parser.AsOfClause; -import org.eclipse.persistence.jpa.jpql.parser.CastExpression; import org.eclipse.persistence.jpa.jpql.parser.ConnectByClause; -import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkExpressionVisitor; import org.eclipse.persistence.jpa.jpql.parser.ExtractExpression; import org.eclipse.persistence.jpa.jpql.parser.HierarchicalQueryClause; @@ -27,7 +25,6 @@ import org.eclipse.persistence.jpa.jpql.parser.StartWithClause; import org.eclipse.persistence.jpa.jpql.parser.TableExpression; import org.eclipse.persistence.jpa.jpql.parser.TableVariableDeclaration; -import org.eclipse.persistence.jpa.jpql.parser.UnionClause; import org.eclipse.persistence.jpa.jpql.tools.resolver.ResolverBuilder; /** @@ -59,21 +56,11 @@ public void visit(AsOfClause expression) { resolver = buildClassResolver(Object.class); } - @Override - public void visit(CastExpression expression) { - resolver = buildClassResolver(Object.class); - } - @Override public void visit(ConnectByClause expression) { resolver = buildClassResolver(Object.class); } - @Override - public void visit(DatabaseType expression) { - resolver = buildClassResolver(Object.class); - } - @Override public void visit(ExtractExpression expression) { resolver = buildClassResolver(Object.class); @@ -108,9 +95,4 @@ public void visit(TableExpression expression) { public void visit(TableVariableDeclaration expression) { resolver = buildClassResolver(Object.class); } - - @Override - public void visit(UnionClause expression) { - resolver = buildClassResolver(Object.class); - } } 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 3fe5ce75cc2..4532e3927b9 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 @@ -46,6 +46,7 @@ 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.DatabaseType; import org.eclipse.persistence.jpa.jpql.parser.DateTime; import org.eclipse.persistence.jpa.jpql.parser.DeleteClause; import org.eclipse.persistence.jpa.jpql.parser.DeleteStatement; @@ -830,6 +831,11 @@ public void visit(CountFunction expression) { this.stateObject = stateObject; } + @Override + public void visit(DatabaseType expression) { + // TODO + } + @Override public void visit(DateTime expression) { DateTimeStateObject stateObject = new DateTimeStateObject(parent, expression.getText()); diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/EclipseLinkStateObjectBuilder.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/EclipseLinkStateObjectBuilder.java index 56624269ea4..71e2e5eaa29 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/EclipseLinkStateObjectBuilder.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/tools/model/EclipseLinkStateObjectBuilder.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 @@ -18,9 +18,7 @@ import org.eclipse.persistence.jpa.jpql.EclipseLinkLiteralVisitor; import org.eclipse.persistence.jpa.jpql.LiteralVisitor; import org.eclipse.persistence.jpa.jpql.parser.AsOfClause; -import org.eclipse.persistence.jpa.jpql.parser.CastExpression; import org.eclipse.persistence.jpa.jpql.parser.ConnectByClause; -import org.eclipse.persistence.jpa.jpql.parser.DatabaseType; import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkExpressionVisitor; import org.eclipse.persistence.jpa.jpql.parser.ExtractExpression; import org.eclipse.persistence.jpa.jpql.parser.HierarchicalQueryClause; @@ -29,7 +27,6 @@ import org.eclipse.persistence.jpa.jpql.parser.StartWithClause; import org.eclipse.persistence.jpa.jpql.parser.TableExpression; import org.eclipse.persistence.jpa.jpql.parser.TableVariableDeclaration; -import org.eclipse.persistence.jpa.jpql.parser.UnionClause; /** * The default implementation of {@link BasicStateObjectBuilder}, which provides support based on @@ -64,21 +61,11 @@ protected LiteralVisitor buildLiteralVisitor() { public void visit(AsOfClause expression) { } - @Override - public void visit(CastExpression expression) { - // TODO - } - @Override public void visit(ConnectByClause expression) { // TODO } - @Override - public void visit(DatabaseType expression) { - // TODO - } - @Override public void visit(ExtractExpression expression) { // TODO @@ -112,9 +99,4 @@ public void visit(TableExpression expression) { public void visit(TableVariableDeclaration expression) { // TODO } - - @Override - public void visit(UnionClause expression) { - // TODO - } } 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 38e2e0a0027..43e5e2be25b 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 @@ -44,6 +44,7 @@ import org.eclipse.persistence.jpa.jpql.parser.BadExpression; import org.eclipse.persistence.jpa.jpql.parser.BetweenExpression; import org.eclipse.persistence.jpa.jpql.parser.CaseExpression; +import org.eclipse.persistence.jpa.jpql.parser.CastExpression; import org.eclipse.persistence.jpa.jpql.parser.CoalesceExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression; import org.eclipse.persistence.jpa.jpql.parser.CollectionMemberDeclaration; @@ -54,6 +55,7 @@ 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.DatabaseType; import org.eclipse.persistence.jpa.jpql.parser.DateTime; import org.eclipse.persistence.jpa.jpql.parser.DeleteClause; import org.eclipse.persistence.jpa.jpql.parser.DeleteStatement; @@ -120,6 +122,7 @@ import org.eclipse.persistence.jpa.jpql.parser.TreatExpression; import org.eclipse.persistence.jpa.jpql.parser.TrimExpression; import org.eclipse.persistence.jpa.jpql.parser.TypeExpression; +import org.eclipse.persistence.jpa.jpql.parser.UnionClause; import org.eclipse.persistence.jpa.jpql.parser.UnknownExpression; import org.eclipse.persistence.jpa.jpql.parser.UpdateClause; import org.eclipse.persistence.jpa.jpql.parser.UpdateItem; @@ -477,6 +480,11 @@ public void visit(CaseExpression expression) { ); } + @Override + public void visit(CastExpression expression) { + resolver = buildClassResolver(Object.class); + } + @Override public void visit(CoalesceExpression expression) { visitCollectionEquivalentExpression(expression.getExpression(), null); @@ -563,6 +571,11 @@ public void visit(CountFunction expression) { resolver = buildClassResolver(Long.class); } + @Override + public void visit(DatabaseType expression) { + resolver = buildClassResolver(Object.class); + } + @Override public void visit(DateTime expression) { @@ -1096,6 +1109,11 @@ public void visit(UpperExpression expression) { resolver = buildClassResolver(String.class); } + @Override + public void visit(UnionClause expression) { + resolver = buildClassResolver(Object.class); + } + @Override public void visit(ValueExpression expression) { diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/AllGrammarValidatorTests.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/AllGrammarValidatorTests.java index 2701cf78bd9..b5cc9585f34 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/AllGrammarValidatorTests.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/AllGrammarValidatorTests.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 @@ -22,6 +22,7 @@ import org.eclipse.persistence.jpa.tests.jpql.parser.JPQLGrammarTools; import org.eclipse.persistence.jpa.tests.jpql.tools.DefaultGrammarValidatorTest2_0; import org.eclipse.persistence.jpa.tests.jpql.tools.DefaultGrammarValidatorTest2_1; +import org.eclipse.persistence.jpa.tests.jpql.tools.DefaultGrammarValidatorTest3_2; import org.junit.runner.RunWith; import org.junit.runners.Suite.SuiteClasses; @@ -36,6 +37,7 @@ @SuiteClasses({ AllGrammarValidatorTests.AllDefaultGrammarValidatorTest2_0.class, AllGrammarValidatorTests.AllDefaultGrammarValidatorTest2_1.class, + AllGrammarValidatorTests.AllDefaultGrammarValidatorTest3_2.class, AllGrammarValidatorTests.AllEclipseLinkGrammarValidatorTest.class, AllGrammarValidatorTests.AllEclipseLinkGrammarValidatorTest2_4.class, AllGrammarValidatorTests.AllEclipseLinkGrammarValidatorTest2_5.class, @@ -87,6 +89,26 @@ static JPQLGrammar[] buildJPQLGrammars() { } } + /** + * This test suite tests JPQL queries written following the grammar defined in the JPA 3.2 spec + * and makes sure the various JPQL grammars that support it parses them correctly. + */ + @SuiteClasses({ + DefaultGrammarValidatorTest3_2.class, + }) + @RunWith(JPQLTestRunner.class) + public static class AllDefaultGrammarValidatorTest3_2 { + + private AllDefaultGrammarValidatorTest3_2() { + super(); + } + + @JPQLGrammarTestHelper + static JPQLGrammar[] buildJPQLGrammars() { + return JPQLGrammarTools.allDefaultJPQLGrammars(JPAVersion.VERSION_3_2); + } + } + /** * This test suite tests JPQL queries written following the grammar defined in the JPA 2.0 spec * with the extension provided by EclipseLink 2.0, 2.1, 2.2 and 2.3 and makes sure the various 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 index 7a6f630351c..e7859cbceed 100644 --- 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 @@ -95,4 +95,12 @@ public static String query_RightFunction_Where() { return "SELECT c FROM Customer c WHERE RIGHT(c.firstName, 4) = 'John'"; } + public static String query_Union01() { + return "Select a from Address a where a.city = 'Ottawa' " + + "union Select a2 from Address a2 " + + "union all Select a2 from Address a2 " + + "intersect Select a from Address a where a.city = 'Ottawa' " + + "except Select a from Address a where a.city = 'Ottawa'"; + } + } 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 index 429b9f62f58..7363293535b 100644 --- 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 @@ -26,7 +26,9 @@ * This test suite runs {@link JPQLParserTests3_2} using JPQL grammar written for JPA 3.2. */ @Suite.SuiteClasses({ - JPQLParserTests3_2.class + JPQLParserTests3_2.class, + CastExpressionTest.class, + UnionClauseTest.class }) @RunWith(JPQLTestRunner.class) public class AllJPQLParserTests3_2 { 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 index f5f85749a64..e9606528ab0 100644 --- 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 @@ -18,7 +18,6 @@ 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; @@ -36,21 +35,27 @@ import static org.eclipse.persistence.jpa.tests.jpql.JPQLQueries3_2.query_RightFunction_Select02; import static org.eclipse.persistence.jpa.tests.jpql.JPQLQueries3_2.query_RightFunction_Select03; import static org.eclipse.persistence.jpa.tests.jpql.JPQLQueries3_2.query_RightFunction_Where; +import static org.eclipse.persistence.jpa.tests.jpql.JPQLQueries3_2.query_Union01; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.concatPipes; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.count; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.except; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.from; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.groupBy; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.having; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.intersect; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.left; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.leftJoin; 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.replace; -import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.resultVariable; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.right; 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.subFrom; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.subSelect; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.union; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.unionAll; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.variable; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.where; @@ -339,18 +344,39 @@ public void test_Query_RightFunction_Where() { } } -// @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") + @Test + public void test_Query_Union01() { + + // Select a from Address a where a.city = 'Ottawa' + // union Select a2 from Address a2 + // union all Select a2 from Address a2 + // intersect Select a from Address a where a.city = 'Ottawa' + // except Select a from Address a where a.city = 'Ottawa' + + SelectStatementTester selectStatement = selectStatement( + select(variable("a")), + from("Address", "a"), + where(path("a.city").equal(string("'Ottawa'"))), + union( + subSelect(variable("a2")), + subFrom("Address", "a2") + ), + unionAll( + subSelect(variable("a2")), + subFrom("Address", "a2") + ), + intersect( + subSelect(variable("a")), + subFrom("Address", "a"), + where(path("a.city").equal(string("'Ottawa'"))) + ), + except( + subSelect(variable("a")), + subFrom("Address", "a"), + where(path("a.city").equal(string("'Ottawa'"))) + ) ); - testQuery(query_013(), selectStatement); + testQuery(query_Union01(), selectStatement); } - } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/DefaultGrammarValidatorTest3_2.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/DefaultGrammarValidatorTest3_2.java new file mode 100644 index 00000000000..f6c0ff31338 --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/tools/DefaultGrammarValidatorTest3_2.java @@ -0,0 +1,820 @@ +/* + * 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.tests.jpql.tools; + +import org.eclipse.persistence.jpa.jpql.AbstractGrammarValidator; +import org.eclipse.persistence.jpa.jpql.JPAVersion; +import org.eclipse.persistence.jpa.jpql.JPQLQueryProblem; +import org.eclipse.persistence.jpa.jpql.tools.DefaultGrammarValidator; +import org.eclipse.persistence.jpa.tests.jpql.AbstractGrammarValidatorTest; +import org.eclipse.persistence.jpa.tests.jpql.parser.JPQLQueryStringFormatter; +import org.junit.Test; + +import java.util.List; + +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.CastExpression_InvalidExpression; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.CastExpression_MissingDatabaseType; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.CastExpression_MissingExpression; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.CastExpression_MissingLeftParenthesis; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.CastExpression_MissingRightParenthesis; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.DatabaseType_InvalidFirstExpression; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.DatabaseType_InvalidSecondExpression; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.DatabaseType_MissingComma; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.DatabaseType_MissingFirstExpression; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.DatabaseType_MissingLeftParenthesis; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.DatabaseType_MissingRightParenthesis; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.DatabaseType_MissingSecondExpression; +import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.UnionClause_MissingExpression; + +/** + * The unit-test class used for testing a JPQL query grammatically when the JPA version is 3.2. + * + * @since 4.1 + * @author Radek Felcman + */ +@SuppressWarnings("nls") +public class DefaultGrammarValidatorTest3_2 extends AbstractGrammarValidatorTest { + + @Override + protected AbstractGrammarValidator buildValidator() { + return new DefaultGrammarValidator(jpqlGrammar); + } + + @Override + protected boolean isJoinFetchIdentifiable() { + return false; + } + + @Override + protected boolean isSubqueryAllowedAnywhere() { + return jpqlGrammar.getJPAVersion().isNewerThanOrEqual(JPAVersion.VERSION_3_2); + } + + @Test + public void test_UnionClause_MissingExpression_01() throws Exception { + + String jpqlQuery = "select e from Employee e intersect all select p from Product p where p.id <> 2"; + List problems = validate(jpqlQuery); + testHasNoProblems(problems); + } + + @Test + public void test_UnionClause_MissingExpression_02() throws Exception { + + String jpqlQuery = "select e from Employee e intersect"; + int startPosition = "select e from Employee e intersect".length(); + int endPosition = jpqlQuery.length(); + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + UnionClause_MissingExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_UnionClause_MissingExpression_03() throws Exception { + + String jpqlQuery = "select e from Employee e intersect "; + int startPosition = "select e from Employee e intersect ".length(); + int endPosition = jpqlQuery.length(); + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + UnionClause_MissingExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_UnionClause_MissingExpression_04() throws Exception { + + String jpqlQuery = "select e from Employee e intersect all"; + int startPosition = "select e from Employee e intersect all".length(); + int endPosition = jpqlQuery.length(); + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + UnionClause_MissingExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_UnionClause_MissingExpression_05() throws Exception { + + String jpqlQuery = "select e from Employee e intersect all "; + int startPosition = "select e from Employee e intersect all ".length(); + int endPosition = jpqlQuery.length(); + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + UnionClause_MissingExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_InvalidExpression_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName as char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, CastExpression_InvalidExpression); + } + + @Test + public void test_CastExpression_MissingDatabaseType_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, CastExpression_MissingDatabaseType); + } + + @Test + public void test_CastExpression_MissingDatabaseType_2() throws Exception { + + String jpqlQuery = "Select cast(e.firstName as char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, CastExpression_MissingDatabaseType); + } + + @Test + public void test_CastExpression_MissingDatabaseType_3() throws Exception { + + String jpqlQuery = "Select cast(e.firstName) from Employee e"; + int startPosition = "Select cast(e.firstName".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + CastExpression_MissingDatabaseType, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_MissingDatabaseType_4() throws Exception { + + String jpqlQuery = "Select cast(e.firstName ) from Employee e"; + int startPosition = "Select cast(e.firstName ".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery, buildStringFormatter_2()); + + testHasOnlyOneProblem( + problems, + CastExpression_MissingDatabaseType, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_MissingDatabaseType_5() throws Exception { + + String jpqlQuery = "Select cast(e.firstName as) from Employee e"; + int startPosition = "Select cast(e.firstName as".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + CastExpression_MissingDatabaseType, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_MissingDatabaseType_6() throws Exception { + + String jpqlQuery = "Select cast(e.firstName as ) from Employee e"; + int startPosition = "Select cast(e.firstName as ".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery, buildStringFormatter_3()); + + testHasOnlyOneProblem( + problems, + CastExpression_MissingDatabaseType, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_MissingDatabaseType_7() throws Exception { + + String jpqlQuery = "Select cast(e.firstName from Employee e"; + int startPosition = "Select cast(e.firstName ".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasProblem( + problems, + CastExpression_MissingDatabaseType, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_MissingDatabaseType_8() throws Exception { + + String jpqlQuery = "Select cast(e.firstName as from Employee e"; + int startPosition = "Select cast(e.firstName as ".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasProblem( + problems, + CastExpression_MissingDatabaseType, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_MissingExpression_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName as char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, CastExpression_MissingExpression); + } + + @Test + public void test_CastExpression_MissingExpression_2() throws Exception { + + String jpqlQuery = "Select cast(as char) from Employee e"; + int startPosition = "Select cast(".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + CastExpression_MissingExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_MissingLeftParenthesis_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName as char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, CastExpression_MissingLeftParenthesis); + } + + @Test + public void test_CastExpression_MissingLeftParenthesis_2() throws Exception { + + String jpqlQuery = "Select cast from Employee e"; + int startPosition = "Select cast".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + CastExpression_MissingLeftParenthesis, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_MissingLeftParenthesis_3() throws Exception { + + String jpqlQuery = "Select cast e.firstName as char) from Employee e"; + int startPosition = "Select cast".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + CastExpression_MissingLeftParenthesis, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_MissingLeftParenthesis_4() throws Exception { + + String jpqlQuery = "Select cast as from Employee e"; + int startPosition = "Select cast".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasProblem( + problems, + CastExpression_MissingLeftParenthesis, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_MissingRightParenthesis_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, CastExpression_MissingRightParenthesis); + } + + @Test + public void test_CastExpression_MissingRightParenthesis_2() throws Exception { + + String jpqlQuery = "Select cast(e.firstName as char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, CastExpression_MissingRightParenthesis); + } + + @Test + public void test_CastExpression_MissingRightParenthesis_3() throws Exception { + + String jpqlQuery = "Select cast(e.firstName as char(2) from Employee e"; + int startPosition = "Select cast(e.firstName as char(2)".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + CastExpression_MissingRightParenthesis, + startPosition, + endPosition + ); + } + + @Test + public void test_CastExpression_MissingRightParenthesis_4() throws Exception { + + String jpqlQuery = "Select cast(e.firstName as char from Employee e"; + int startPosition = "Select cast(e.firstName as char".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + CastExpression_MissingRightParenthesis, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_InvalidFirstExpression_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_InvalidFirstExpression); + } + + @Test + public void test_DatabaseType_InvalidFirstExpression_2() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_InvalidFirstExpression); + } + + @Test + public void test_DatabaseType_InvalidFirstExpression_3() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2, 2)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_InvalidFirstExpression); + } + + @Test + public void test_DatabaseType_InvalidFirstExpression_4() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(avg(e.age), 2)) from Employee e"; + int startPosition = "Select cast(e.firstName char(".length(); + int endPosition = "Select cast(e.firstName char(avg(e.age)".length(); + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + DatabaseType_InvalidFirstExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_InvalidSecondExpression_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_InvalidSecondExpression); + } + + @Test + public void test_DatabaseType_InvalidSecondExpression_2() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_InvalidSecondExpression); + } + + @Test + public void test_DatabaseType_InvalidSecondExpression_3() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2, 2)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_InvalidSecondExpression); + } + + @Test + public void test_DatabaseType_InvalidSecondExpression_4() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2, avg(e.age))) from Employee e"; + int startPosition = "Select cast(e.firstName char(2, ".length(); + int endPosition = "Select cast(e.firstName char(2, avg(e.age)".length(); + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + DatabaseType_InvalidSecondExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingComma_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2, 2)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingComma); + } + + @Test + public void test_DatabaseType_MissingComma_2() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingComma); + } + + @Test + public void test_DatabaseType_MissingComma_3() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2 2)) from Employee e"; + int startPosition = "Select cast(e.firstName char(2".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + DatabaseType_MissingComma, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingFirstExpression_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingFirstExpression); + } + + @Test + public void test_DatabaseType_MissingFirstExpression_2() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingFirstExpression); + } + + @Test + public void test_DatabaseType_MissingFirstExpression_3() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2,)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingFirstExpression); + } + + @Test + public void test_DatabaseType_MissingFirstExpression_4() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(, 2)) from Employee e"; + int startPosition = "Select cast(e.firstName char(".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + DatabaseType_MissingFirstExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingFirstExpression_5() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(,)) from Employee e"; + int startPosition = "Select cast(e.firstName char(".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasProblem( + problems, + DatabaseType_MissingFirstExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingLeftParenthesis_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingLeftParenthesis); + } + + @Test + public void test_DatabaseType_MissingLeftParenthesis_2() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char()) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingLeftParenthesis); + } + + @Test + public void test_DatabaseType_MissingLeftParenthesis_3() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(3)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingLeftParenthesis); + } + + @Test + public void test_DatabaseType_MissingLeftParenthesis_4() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(3,)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingLeftParenthesis); + } + + @Test + public void test_DatabaseType_MissingLeftParenthesis_5() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(3, )) from Employee e"; + List problems = validate(jpqlQuery, buildStringFormatter_4()); + testDoesNotHaveProblem(problems, DatabaseType_MissingLeftParenthesis); + } + + @Test + public void test_DatabaseType_MissingLeftParenthesis_6() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(3, 3)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingLeftParenthesis); + } + + @Test + public void test_DatabaseType_MissingLeftParenthesis_7() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char 3)) from Employee e"; + int startPosition = "Select cast(e.firstName char".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + DatabaseType_MissingLeftParenthesis, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingLeftParenthesis_8() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char 3, 3)) from Employee e"; + int startPosition = "Select cast(e.firstName char".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + DatabaseType_MissingLeftParenthesis, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingRightParenthesis_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingRightParenthesis); + } + + @Test + public void test_DatabaseType_MissingRightParenthesis_2() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char()) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingRightParenthesis); + } + + @Test + public void test_DatabaseType_MissingRightParenthesis_3() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2 from Employee e"; + int startPosition = "Select cast(e.firstName char(2".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasProblem( + problems, + DatabaseType_MissingRightParenthesis, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingRightParenthesis_4() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2, from Employee e"; + int startPosition = "Select cast(e.firstName char(2, ".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasProblem( + problems, + DatabaseType_MissingRightParenthesis, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingRightParenthesis_5() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2, 2 from Employee e"; + int startPosition = "Select cast(e.firstName char(2, 2".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasProblem( + problems, + DatabaseType_MissingRightParenthesis, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingSecondExpression_1() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingFirstExpression); + } + + @Test + public void test_DatabaseType_MissingSecondExpression_2() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2, 2)) from Employee e"; + List problems = validate(jpqlQuery); + testDoesNotHaveProblem(problems, DatabaseType_MissingFirstExpression); + } + + @Test + public void test_DatabaseType_MissingSecondExpression_3() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(, 2)) from Employee e"; + int startPosition = "Select cast(e.firstName char(".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + DatabaseType_MissingFirstExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingSecondExpression_4() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2,)) from Employee e"; + int startPosition = "Select cast(e.firstName char(2,".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasOnlyOneProblem( + problems, + DatabaseType_MissingSecondExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingSecondExpression_5() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(2, )) from Employee e"; + int startPosition = "Select cast(e.firstName char(2, ".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery, buildStringFormatter_4()); + + testHasOnlyOneProblem( + problems, + DatabaseType_MissingSecondExpression, + startPosition, + endPosition + ); + } + + @Test + public void test_DatabaseType_MissingSecondExpression_6() throws Exception { + + String jpqlQuery = "Select cast(e.firstName char(,)) from Employee e"; + int startPosition = "Select cast(e.firstName char(,".length(); + int endPosition = startPosition; + + List problems = validate(jpqlQuery); + + testHasProblem( + problems, + DatabaseType_MissingSecondExpression, + startPosition, + endPosition + ); + } + + private JPQLQueryStringFormatter buildStringFormatter_2() { + return query -> query.replace("firstName)", "firstName )"); + } + + private JPQLQueryStringFormatter buildStringFormatter_3() { + return query -> query.replace(")", " )"); + } + + private JPQLQueryStringFormatter buildStringFormatter_4() { + return query -> query.replace(",", ", "); + } + +}