All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.jpa.jpql.EclipseLinkGrammarValidator Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation
//
package org.eclipse.persistence.jpa.jpql;

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;
import org.eclipse.persistence.jpa.jpql.parser.ExtractExpression;
import org.eclipse.persistence.jpa.jpql.parser.HierarchicalQueryClause;
import org.eclipse.persistence.jpa.jpql.parser.InExpression;
import org.eclipse.persistence.jpa.jpql.parser.InputParameter;
import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar;
import org.eclipse.persistence.jpa.jpql.parser.OrderSiblingsByClause;
import org.eclipse.persistence.jpa.jpql.parser.PatternValueBNF;
import org.eclipse.persistence.jpa.jpql.parser.RegexpExpression;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectClause;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement;
import org.eclipse.persistence.jpa.jpql.parser.StartWithClause;
import org.eclipse.persistence.jpa.jpql.parser.StringExpressionBNF;
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.*;
import static org.eclipse.persistence.jpa.jpql.parser.Expression.*;

/**
 * This validator adds EclipseLink extension over what the JPA functional specification had defined.
 * 

* Provisional API: This interface is part of an interim API that is still under development and * expected to change significantly before reaching stability. It is available at this early stage * to solicit feedback from pioneering adopters on the understanding that any code that uses this * API will almost certainly be broken (repeatedly) as the API evolves. * * @version 2.5 * @since 2.4 * @author Pascal Filion */ public class EclipseLinkGrammarValidator extends AbstractGrammarValidator implements EclipseLinkExpressionVisitor { private InExpressionVisitor inExpressionVisitor; private InExpressionWithNestedArrayVisitor inExpressionWithNestedArrayVisitor; /** * Creates a new EclipseLinkGrammarValidator. * * @param jpqlGrammar The {@link JPQLGrammar} that defines how the JPQL query was parsed */ 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 protected String encapsulatedExpressionInvalidKey(ExtractExpression expression) { return ExtractExpression_InvalidExpression; } @Override protected String encapsulatedExpressionMissingKey(ExtractExpression expression) { return ExtractExpression_MissingExpression; } @Override public String leftParenthesisMissingKey(ExtractExpression expression) { return ExtractExpression_MissingLeftParenthesis; } @Override protected int lengthBeforeEncapsulatedExpression(ExtractExpression expression) { return expression.getDatePart().length() + (expression.hasSpaceAfterDatePart() ? 1 : 0) + (expression.hasFrom() ? 4 /* FROM */ : 0) + (expression.hasSpaceAfterFrom() ? 1 : 0); } @Override public String rightParenthesisMissingKey(ExtractExpression expression) { return ExtractExpression_MissingRightParenthesis; } }; } protected InExpressionVisitor buildInExpressionVisitor() { return new InExpressionVisitor(); } protected InExpressionWithNestedArrayVisitor buildInExpressionWithNestedArrayVisitor() { return new InExpressionWithNestedArrayVisitor(this); } @Override protected LiteralVisitor buildLiteralVisitor() { return new EclipseLinkLiteralVisitor(); } @Override protected OwningClauseVisitor buildOwningClauseVisitor() { return new EclipseLinkOwningClauseVisitor(); } protected AbstractSingleEncapsulatedExpressionHelper buildTableExpressionHelper() { return new AbstractSingleEncapsulatedExpressionHelper(this) { @Override protected String encapsulatedExpressionInvalidKey(TableExpression expression) { return TableExpression_InvalidExpression; } @Override protected String encapsulatedExpressionMissingKey(TableExpression expression) { return TableExpression_MissingExpression; } @Override public String leftParenthesisMissingKey(TableExpression expression) { return TableExpression_MissingLeftParenthesis; } @Override public String rightParenthesisMissingKey(TableExpression expression) { return TableExpression_MissingRightParenthesis; } }; } protected AbstractSingleEncapsulatedExpressionHelper castExpressionHelper() { AbstractSingleEncapsulatedExpressionHelper helper = getHelper(CAST); if (helper == null) { helper = buildCastExpressionHelper(); registerHelper(CAST, helper); } 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) { helper = buildExtractExpressionHelper(); registerHelper(EXTRACT, helper); } return helper; } protected InExpressionVisitor getInExpressionVisitor() { if (inExpressionVisitor == null) { inExpressionVisitor = buildInExpressionVisitor(); } return inExpressionVisitor; } protected InExpressionWithNestedArrayVisitor getInExpressionWithNestedArray() { if (inExpressionWithNestedArrayVisitor == null) { inExpressionWithNestedArrayVisitor = buildInExpressionWithNestedArrayVisitor(); } return inExpressionWithNestedArrayVisitor; } @Override protected EclipseLinkOwningClauseVisitor getOwningClauseVisitor() { return (EclipseLinkOwningClauseVisitor) super.getOwningClauseVisitor(); } /** * Determines whether the persistence provider is EclipseLink or not. * * @return true if the persistence provider is EclipseLink; false otherwise */ protected final boolean isEclipseLink() { return DefaultEclipseLinkJPQLGrammar.PROVIDER_NAME.equals(getProvider()); } /** * Determines whether the subquery is part of an IN expression where the * left expression is a nested array. * * @param expression The {@link SimpleSelectClause} of the subquery * @return true if the subquery is in an IN expression and its * left expression is a nested array */ protected boolean isInExpressionWithNestedArray(SimpleSelectClause expression) { InExpressionWithNestedArrayVisitor visitor = getInExpressionWithNestedArray(); try { expression.accept(visitor); return visitor.valid; } finally { visitor.valid = false; } } @Override protected boolean isInputParameterInValidLocation(InputParameter expression) { return true; } @Override protected boolean isJoinFetchIdentifiable() { EclipseLinkVersion version = EclipseLinkVersion.value(getGrammar().getProviderVersion()); return version.isNewerThanOrEqual(EclipseLinkVersion.VERSION_2_4); } @Override protected boolean isMultipleSubquerySelectItemsAllowed(SimpleSelectClause expression) { return isInExpressionWithNestedArray(expression); } protected boolean isOwnedByInExpression(Expression expression) { InExpressionVisitor visitor = getInExpressionVisitor(); expression.accept(visitor); 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()); return version.isNewerThanOrEqual(EclipseLinkVersion.VERSION_2_4); } protected AbstractSingleEncapsulatedExpressionHelper tableExpressionHelper() { AbstractSingleEncapsulatedExpressionHelper helper = getHelper(TABLE); if (helper == null) { helper = buildTableExpressionHelper(); registerHelper(TABLE, helper); } return helper; } @Override protected void validateAbstractSelectClause(AbstractSelectClause expression, boolean multipleSelectItemsAllowed) { // A subquery can have multiple select items if it is // - used as a "root" object in the top-level FROM clause // - defined in a UNION clause // - used in an IN expression // If the flag is false, then the SELECT clause is from a subquery if (!multipleSelectItemsAllowed) { Expression parent = expression.getParent(); multipleSelectItemsAllowed = isOwnedByFromClause (parent) || isOwnedByUnionClause (parent) || isOwnedByInExpression(parent); } super.validateAbstractSelectClause(expression, multipleSelectItemsAllowed); } @Override public void visit(AsOfClause expression) { } @Override public void visit(CastExpression expression) { // Wrong JPA version if (!isEclipseLink()) { 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(ConnectByClause expression) { // TODO: 2.5 } @Override public void visit(DatabaseType expression) { validateAbstractDoubleEncapsulatedExpression(expression, databaseTypeHelper()); } @Override public void visit(ExtractExpression expression) { // Wrong JPA version if (!isEclipseLink()) { addProblem(expression, ExtractExpression_InvalidJPAVersion); } else { validateAbstractSingleEncapsulatedExpression(expression, extractExpressionHelper()); // Missing date part if (expression.hasLeftParenthesis() && !expression.hasDatePart()) { int startPosition = position(expression) + 7 /* EXTRACT */ + (expression.hasLeftParenthesis() ? 1 : 0); addProblem(expression, startPosition, ExtractExpression_MissingDatePart); } } } @Override public void visit(HierarchicalQueryClause expression) { // TODO: 2.5 } @Override public void visit(OrderSiblingsByClause expression) { // TODO } @Override public void visit(RegexpExpression expression) { // Wrong JPA version if (!isEclipseLink()) { addProblem(expression, RegexpExpression_InvalidJPAVersion); } else { // Missing string expression if (!expression.hasStringExpression()) { int startPosition = position(expression); int endPosition = startPosition; addProblem( expression, startPosition, endPosition, RegexpExpression_MissingStringExpression ); } else { Expression stringExpression = expression.getStringExpression(); // Invalid string expression if (!isValid(stringExpression, StringExpressionBNF.ID)) { int startPosition = position(stringExpression); int endPosition = startPosition + length(stringExpression); addProblem( expression, startPosition, endPosition, RegexpExpression_InvalidStringExpression ); } // Validate string expression else { stringExpression.accept(this); } } // Missing pattern value if (!expression.hasPatternValue()) { int startPosition = position(expression) + length(expression.getStringExpression()) + (expression.hasSpaceAfterStringExpression() ? 1 : 0) + 6 /* REGEXP */ + (expression.hasSpaceAfterIdentifier() ? 1 : 0); int endPosition = startPosition; addProblem(expression, startPosition, endPosition, RegexpExpression_MissingPatternValue); } else { Expression patternValue = expression.getStringExpression(); // Invalid string expression if (!isValid(patternValue, PatternValueBNF.ID)) { int startPosition = position(expression) + length(expression.getStringExpression()) + (expression.hasSpaceAfterStringExpression() ? 1 : 0) + 6 /* REGEXP */ + (expression.hasSpaceAfterIdentifier() ? 1 : 0); int endPosition = startPosition + length(patternValue); addProblem( expression, startPosition, endPosition, RegexpExpression_InvalidPatternValue ); } // Validate pattern value else { patternValue.accept(this); } } } } @Override public void visit(StartWithClause expression) { // TODO: 2.5 } @Override public void visit(TableExpression expression) { validateAbstractSingleEncapsulatedExpression(expression, tableExpressionHelper()); } @Override public void visit(TableVariableDeclaration expression) { // Wrong JPA version if (!isEclipseLink()) { addProblem(expression, TableVariableDeclaration_InvalidJPAVersion); } else { TableExpression tableExpression = expression.getTableExpression(); // Validate the table expression tableExpression.accept(this); // The identification variable is missing if (!expression.hasIdentificationVariable()) { int startPosition = position(expression) + length(tableExpression) + (expression.hasSpaceAfterTableExpression() ? 1 : 0) + (expression.hasAs() ? 2 : 0) + (expression.hasSpaceAfterAs() ? 1 : 0); addProblem(expression, startPosition, TableVariableDeclaration_MissingIdentificationVariable); } // Validate the identification variable else { expression.getIdentificationVariable().accept(this); } } } @Override public void visit(UnionClause expression) { // Wrong JPA version if (!isEclipseLink()) { 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); } } // Made static for performance reasons. protected static class InExpressionVisitor extends AbstractEclipseLinkExpressionVisitor { protected InExpression expression; @Override public void visit(InExpression expression) { this.expression = expression; } } // Made static final for performance reasons. protected static final class InExpressionWithNestedArrayVisitor extends AbstractEclipseLinkExpressionVisitor { private final EclipseLinkGrammarValidator visitor; protected InExpressionWithNestedArrayVisitor(EclipseLinkGrammarValidator visitor) { this.visitor = visitor; } /** * Determines whether the left expression of an IN expression is a nested * array when the IN item is a subquery. */ public boolean valid; @Override public void visit(InExpression expression) { valid = visitor.isNestedArray(expression.getExpression()); } @Override public void visit(SimpleSelectClause expression) { expression.getParent().accept(this); } @Override public void visit(SimpleSelectStatement expression) { expression.getParent().accept(this); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy