blob: 360c5755a502550f7b2713ad2b629e42ed567bc1 [file] [log] [blame]
/*
* 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
//
// 09/02/2019-3.0 Alexandre Jacob
// - 527415: Fix code when locale is tr, az or lt
package org.eclipse.persistence.jpa.jpql.parser;
import java.util.Locale;
/**
* This visitor makes sure that all path expressions are fully qualified with a "virtual"
* identification variable if the range variable declaration does not define one. This only applies
* to an <code><b>UPDATE</b></code> or <code><b>DELETE</b></code> queries.
*
* @version 2.5
* @since 2.3
* @author Pascal Filion
*/
public final class FullyQualifyPathExpressionVisitor extends AbstractTraverseChildrenVisitor {
/**
* The "virtual" identification variable if none was defined.
*/
private String variableName;
/**
* Caches this visitor, which is used to determine if the general identification variable is not
* a map key, map value or map entry expression.
*/
private GeneralIdentificationVariableVisitor visitor;
private GeneralIdentificationVariableVisitor generalIdentificationVariableVisitor() {
if (visitor == null) {
visitor = new GeneralIdentificationVariableVisitor();
}
return visitor;
}
@Override
public void visit(AbstractSchemaName expression) {
// The "virtual" variable name will be the entity name
variableName = expression.toActualText().toLowerCase(Locale.ROOT);
}
@Override
public void visit(CollectionMemberDeclaration expression) {
// Do nothing, prevent to do anything for invalid queries
}
@Override
public void visit(CollectionValuedPathExpression expression) {
visitAbstractPathExpression(expression);
}
@Override
public void visit(DeleteClause expression) {
expression.getRangeVariableDeclaration().accept(this);
}
@Override
public void visit(DeleteStatement expression) {
expression.getDeleteClause().accept(this);
// Don't traverse the tree if the path expressions don't need to be virtually qualified
if ((variableName != null) && expression.hasWhereClause()) {
expression.getWhereClause().accept(this);
}
}
@Override
public void visit(IdentificationVariable expression) {
// A null check is required because the query could be invalid/incomplete
// The identification variable should become a state field path expression
expression.setVirtualIdentificationVariable(variableName);
}
@Override
public void visit(Join expression) {
// Do nothing, prevent to do anything for invalid queries
}
@Override
public void visit(RangeVariableDeclaration expression) {
// The "root" object does not have an identification variable,
// then we'll assume all path expressions are unqualified
if (!expression.hasIdentificationVariable()) {
expression.getRootObject().accept(this);
expression.setVirtualIdentificationVariable(variableName);
}
}
@Override
public void visit(SelectStatement expression) {
// Nothing to do because a SELECT query has to have its path expressions fully qualified
}
@Override
public void visit(SimpleSelectStatement expression) {
// Nothing to do because a subquery query has to have its path expressions fully qualified
}
@Override
public void visit(StateFieldPathExpression expression) {
// A null check is required because the query could be invalid/incomplete
visitAbstractPathExpression(expression);
}
@Override
public void visit(UpdateClause expression) {
expression.getRangeVariableDeclaration().accept(this);
// Don't traverse the tree if the path expressions don't need to be virtually qualified
if ((variableName != null) && expression.hasUpdateItems()) {
expression.getUpdateItems().accept(this);
}
}
@Override
public void visit(UpdateStatement expression) {
expression.getUpdateClause().accept(this);
// Don't traverse the tree if the path expressions don't need to be virtually qualified
if ((variableName != null) && expression.hasWhereClause()) {
expression.getWhereClause().accept(this);
}
}
private void visitAbstractPathExpression(AbstractPathExpression expression) {
if (!expression.startsWithDot()) {
// Visit the general identification variable to make sure it's not a map key, map entry
// or map value expression
GeneralIdentificationVariableVisitor visitor = generalIdentificationVariableVisitor();
expression.getIdentificationVariable().accept(visitor);
if (visitor.expression == null) {
expression.setVirtualIdentificationVariable(variableName);
}
}
}
// Made static final for performance reasons.
private static final class GeneralIdentificationVariableVisitor extends AbstractExpressionVisitor {
/**
* The {@link Expression} that was visited, which is a general identification variable but is
* not a identification variable, it's either a map value, map key or map value expression.
*/
private Expression expression;
@Override
public void visit(EntryExpression expression) {
this.expression = expression;
}
@Override
public void visit(KeyExpression expression) {
this.expression = expression;
}
@Override
public void visit(ValueExpression expression) {
this.expression = expression;
}
}
}