blob: 083fb2f1a5fd98d7a998916281af69550d5d801d [file] [log] [blame]
/*
* Copyright (c) 1998, 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 from Oracle TopLink
package org.eclipse.persistence.internal.expressions;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.history.AsOfClause;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.history.DecoratedDatabaseTable;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.querykeys.QueryKey;
/**
* Superclass for all expression that have a context.
* i.e. a base expression.
*/
public abstract class DataExpression extends BaseExpression {
protected List<Expression> derivedTables;
protected List<Expression> derivedFields;
protected boolean hasBeenNormalized;
protected TableAliasLookup tableAliases;
protected AsOfClause asOfClause;
/**
* DataExpression constructor comment.
*/
protected DataExpression() {
super();
}
/**
* INTERNAL:
* Return if the expression is equal to the other.
* This is used to allow dynamic expression's SQL to be cached.
*/
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!super.equals(object)) {
return false;
}
DataExpression expression = (DataExpression) object;
return ((this.baseExpression == expression.getBaseExpression()) || ((this.baseExpression != null) && this.baseExpression.equals(expression.getBaseExpression())))
&& ((getAsOfClause() == expression.getAsOfClause()) || ((getAsOfClause() != null) && getAsOfClause().equals(expression.getAsOfClause())));
}
public void addDerivedField(Expression addThis) {
if (derivedFields == null) {
derivedFields = new ArrayList();
}
derivedFields.add(addThis);
}
public void addDerivedTable(Expression addThis) {
if (derivedTables == null) {
derivedTables = new ArrayList();
}
derivedTables.add(addThis);
}
@Override
public Expression asOf(AsOfClause clause) {
asOfClause = clause;
return this;
}
/**
* INTERNAL:
* Find the alias for a given table
*/
@Override
public DatabaseTable aliasForTable(DatabaseTable table) {
if (tableAliases == null) {
if (this.baseExpression == null) {
return null;
}
return this.baseExpression.aliasForTable(table);
}
return tableAliases.keyAtValue(table);
}
/**
* INTERNAL:
* Alias a particular table within this node
*/
@Override
protected void assignAlias(String name, DatabaseTable table) {
if (!getBuilder().getSession().getProject().hasGenericHistorySupport()) {
assignAlias(new DecoratedDatabaseTable(name, getAsOfClause()), table);
} else {
assignAlias(new DatabaseTable(name), table);
}
}
/**
* INTERNAL:
* Alias a particular table within this node
*/
protected void assignAlias(DatabaseTable alias, DatabaseTable table) {
if (tableAliases == null) {
tableAliases = new TableAliasLookup();
}
tableAliases.put(alias, table);
}
/**
* INTERNAL:
*/
public void clearAliases() {
tableAliases = null;
}
public List<Expression> copyCollection(List<Expression> in, Map alreadyDone) {
if (in == null) {
return null;
}
List<Expression> result = new ArrayList(in.size());
for (Expression exp : in) {
result.add(exp.copiedVersionFrom(alreadyDone));
}
return result;
}
/**
* INTERNAL:
*/
public Expression existingDerivedField(DatabaseField field) {
if (this.derivedFields == null) {
return null;
}
for (Expression exp : this.derivedFields) {
if (((FieldExpression)exp).getField().equals(field)) {
return exp;
}
}
return null;
}
/**
* INTERNAL:
*/
public Expression existingDerivedTable(DatabaseTable table) {
if (this.derivedTables == null) {
return null;
}
for (Expression exp : this.derivedTables) {
if (((TableExpression)exp).getTable().equals(table)) {
return exp;
}
}
return null;
}
/**
* INTERNAL:
* Return the field appropriately aliased
*/
public DatabaseField getAliasedField() {
return null;
}
@Override
public AsOfClause getAsOfClause() {
return asOfClause;
}
public ClassDescriptor getDescriptor() {
return null;
}
/**
* INTERNAL:
*/
public DatabaseField getField() {
return null;
}
@Override
public Expression getField(String fieldName) {
DatabaseField field = new DatabaseField(fieldName);
return getField(field);
}
@Override
public Expression getField(DatabaseField field) {
Expression existing = existingDerivedField(field);
if (existing != null) {
return existing;
}
return newDerivedField(field);
}
/**
* INTERNAL:
* Return the descriptor which contains this query key.
*/
public ClassDescriptor getContainingDescriptor() {
return ((DataExpression)this.baseExpression).getDescriptor();
}
public DatabaseMapping getMapping() {
if (this.baseExpression == null) {
return null;
}
ClassDescriptor descriptor = getContainingDescriptor();
if (descriptor == null) {
return null;
}
return descriptor.getObjectBuilder().getMappingForAttributeName(getName());
}
public QueryKey getQueryKeyOrNull() {
return null;
}
@Override
public Expression getTable(String tableName) {
DatabaseTable table = new DatabaseTable(tableName);
return getTable(table);
}
@Override
public Expression getTable(DatabaseTable table) {
Expression existing = existingDerivedTable(table);
if (existing != null) {
return existing;
}
return newDerivedTable(table);
}
/**
* INTERNAL:
* Return the aliases used. For CR#2456 must never lazily initialize as also used for Expression identity.
*/
@Override
public TableAliasLookup getTableAliases() {
return tableAliases;
}
/**
* INTERNAL:
* Did the normalizer already add additional joins to the where clause due to
* this query key representing a foreign reference mapping?
* This insures that join criteria (for any query key expression) is not
* added twice.
* <p>
* New meaning: DataExpressions are often iterated on multiple times during
* normalize, but Function/Relation expressions only once. Adding a has
* been normalized flag improves performance and is required in some
* applications, such as have temporal query criteria been added.
*/
public boolean hasBeenNormalized() {
return hasBeenNormalized;
}
@Override
public boolean hasAsOfClause() {
return ((getAsOfClause() != null) && (getAsOfClause().getValue() != null));
}
@Override
public boolean hasBeenAliased() {
return ((tableAliases != null) && (tableAliases.size() != 0));
}
protected boolean hasDerivedFields() {
return derivedFields != null;
}
protected boolean hasDerivedTables() {
return derivedTables != null;
}
/**
* INTERNAL:
*/
public boolean isAttribute() {
return false;
}
@Override
public boolean isDataExpression() {
return true;
}
/**
* INTERNAL:
* For iterating using an inner class
*/
@Override
public void iterateOn(ExpressionIterator iterator) {
super.iterateOn(iterator);
if (baseExpression != null) {
baseExpression.iterateOn(iterator);
}
}
/**
* INTERNAL:
*/
public Expression newDerivedField(DatabaseField field) {
FieldExpression result = new FieldExpression(field, this);
addDerivedField(result);
return result;
}
/**
* INTERNAL:
*/
public Expression newDerivedTable(DatabaseTable table) {
TableExpression result = new TableExpression(table);
result.setBaseExpression(this);
addDerivedTable(result);
return result;
}
/**
* ADVANCED: Return an expression representing a sub-select in the from clause.
* <p> Example:
* <pre>
* builder.getAlias(builder.subQuery(reportQuery)).get("type").equal("S");
* </pre>
*/
@Override
public Expression getAlias(Expression subSelect) {
TableExpression result = new FromSubSelectExpression((SubSelectExpression)subSelect);
result.setBaseExpression(this);
return result;
}
/**
* INTERNAL:
* Normalize the expression into a printable structure.
* Any joins must be added to form a new root.
*/
@Override
public Expression normalize(ExpressionNormalizer normalizer) {
if (this.hasBeenNormalized) {
return this;
}
this.hasBeenNormalized = true;
if (this.baseExpression != null) {
// First normalize the base.
setBaseExpression(this.baseExpression.normalize(normalizer));
if (getAsOfClause() == null) {
asOf(this.baseExpression.getAsOfClause());
}
}
return super.normalize(normalizer);
}
/**
* INTERNAL:
* Used for cloning.
*/
@Override
protected void postCopyIn(Map alreadyDone) {
super.postCopyIn(alreadyDone);
clearAliases();
this.derivedFields = copyCollection(this.derivedFields, alreadyDone);
this.derivedTables = copyCollection(this.derivedTables, alreadyDone);
}
/**
* INTERNAL:
* Print SQL onto the stream, using the ExpressionPrinter for context
*/
@Override
public void printSQL(ExpressionSQLPrinter printer) {
printer.printField(getAliasedField());
}
public void setHasBeenNormalized(boolean value) {
hasBeenNormalized = value;
}
/**
* INTERNAL:
* For CR#2456, Table identity involves having two tables sharing the same
* aliasing table.
*/
public void setTableAliases(TableAliasLookup tableAliases) {
if (this.tableAliases == null) {
this.tableAliases = tableAliases;
}
}
public String tableAliasesDescription() {
if (tableAliases == null) {
return "";
}
return tableAliases.toString();
}
/**
* Print the base for debuggin purposes.
*/
@Override
public void writeSubexpressionsTo(BufferedWriter writer, int indent) throws IOException {
if (this.baseExpression != null) {
this.baseExpression.toString(writer, indent);
}
}
}