blob: e6f6716cb2e944a83c604b51bb89424f74b0c837 [file] [log] [blame]
/*
* Copyright (c) 2012, 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.tools.resolver;
import org.eclipse.persistence.jpa.jpql.BaseDeclarationIdentificationVariableFinder;
import org.eclipse.persistence.jpa.jpql.parser.AbstractSchemaName;
import org.eclipse.persistence.jpa.jpql.tools.JPQLQueryContext;
import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedType;
import org.eclipse.persistence.jpa.jpql.tools.spi.IMapping;
import org.eclipse.persistence.jpa.jpql.tools.spi.IType;
import org.eclipse.persistence.jpa.jpql.tools.spi.ITypeDeclaration;
/**
* This {@link Resolver} retrieves the type for an abstract schema name (entity name) if it can
* be resolved otherwise a derived path will be assumed. This {@link Resolver} is used within a
* subquery and it handles the following two cases.
* <p>
* The "root" object in the subquery is an unqualified derived path:
* <pre><code> UPDATE Employee SET name = 'JPQL'
* WHERE (SELECT a FROM addr a)</code></pre>
* and
* The "root" object in the subquery is an entity:
* <pre><code> UPDATE Employee SET name = 'JPQL'
* WHERE (SELECT a FROM Address a)</code></pre>
*
* @version 2.5
* @since 2.5
* @author Pascal Filion
*/
public class SubqueryEntityResolver extends Resolver {
/**
* The {@link AbstractSchemaName} holding onto the the name of the entity.
*/
private AbstractSchemaName abstractSchemaName;
/**
* The actual {@link Resolver} when the abstract schema name is actually an unqualified derived
* path expression.
*/
private Resolver derivedPathResolver;
/**
* The name of the entity or the unqualified derived path.
*/
private String entityName;
/**
* The {@link IManagedType} with the same abstract schema name.
*/
private IManagedType managedType;
/**
* Determines whether the managed type was resolved or not.
*/
private boolean managedTypeResolved;
/**
* The {@link JPQLQueryContext} for the subquery.
*/
private JPQLQueryContext queryContext;
/**
* Creates a new <code>DerivedPathResolver</code>.
*
* @param parent The parent {@link Resolver}, which is never <code>null</code>
* @param queryContext The {@link JPQLQueryContext} for the subquery
* @param abstractSchemaName The {@link AbstractSchemaName} holding onto the the name of the entity
*/
public SubqueryEntityResolver(Resolver parent,
JPQLQueryContext queryContext,
AbstractSchemaName abstractSchemaName) {
super(parent);
this.queryContext = queryContext;
this.abstractSchemaName = abstractSchemaName;
this.entityName = abstractSchemaName.getText();
}
@Override
protected IType buildType() {
IManagedType entity = getManagedType();
return (entity != null) ? entity.getType() : getTypeHelper().objectType();
}
@Override
protected ITypeDeclaration buildTypeDeclaration() {
return getType().getTypeDeclaration();
}
/**
* Returns the name of the entity to resolve.
*
* @return The entity name, which is never <code>null</code>
*/
public String getAbstractSchemaName() {
return entityName;
}
@Override
public IManagedType getManagedType() {
if ((managedType == null) && !managedTypeResolved) {
managedTypeResolved = true;
managedType = getProvider().getEntityNamed(entityName);
if (managedType == null) {
Resolver resolver = resolveDerivePathResolver();
managedType = resolver.getManagedType();
}
}
return managedType;
}
@Override
public IMapping getMapping() {
if (derivedPathResolver != null) {
return derivedPathResolver.getMapping();
}
// The managed type cannot be resolved and this is for a subquery,
// attempt to resolve an unqualified derived path expression
if ((getManagedType() == null) && queryContext.isSubquery()) {
Resolver resolver = resolveDerivePathResolver();
return resolver.getMapping();
}
return super.getMapping();
}
/**
* Creates the {@link Resolver} for a unqualified derived path expression.
*
* @return A non-<code>null</code> {@link Resolver}
*/
protected Resolver resolveDerivePathResolver() {
if (derivedPathResolver == null) {
// Retrieve the identification variable of the top-level query's base declaration
BaseDeclarationIdentificationVariableFinder finder = new BaseDeclarationIdentificationVariableFinder();
abstractSchemaName.accept(finder);
if (finder.expression != null) {
// Retrieve the Resolver for the identification variable
DeclarationResolver parent = queryContext.getParent().getActualDeclarationResolver();
Resolver resolver = parent.getResolver(finder.expression.getVariableName());
// Retrieve the Resolver for the unqualified path expression
Resolver childResolver = resolver.getChild(entityName);
if (childResolver == null) {
childResolver = new StateFieldResolver(resolver, entityName);
}
derivedPathResolver = childResolver;
}
// Invalid query/subquery
if (derivedPathResolver == null) {
derivedPathResolver = new NullResolver(this);
}
}
return derivedPathResolver;
}
@Override
public String toString() {
return entityName;
}
}