Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.modeshape.jcr.query.plan.PlanUtil Maven / Gradle / Ivy
/*
* ModeShape (http://www.modeshape.org)
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
* See the AUTHORS.txt file in the distribution for a full listing of
* individual contributors.
*
* ModeShape is free software. Unless otherwise indicated, all code in ModeShape
* is licensed to you under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* ModeShape is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.modeshape.jcr.query.plan;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.modeshape.jcr.query.QueryContext;
import org.modeshape.jcr.query.model.And;
import org.modeshape.jcr.query.model.ArithmeticOperand;
import org.modeshape.jcr.query.model.ArithmeticOperator;
import org.modeshape.jcr.query.model.Between;
import org.modeshape.jcr.query.model.BindVariableName;
import org.modeshape.jcr.query.model.ChildNode;
import org.modeshape.jcr.query.model.ChildNodeJoinCondition;
import org.modeshape.jcr.query.model.Column;
import org.modeshape.jcr.query.model.Comparison;
import org.modeshape.jcr.query.model.Constraint;
import org.modeshape.jcr.query.model.DescendantNode;
import org.modeshape.jcr.query.model.DescendantNodeJoinCondition;
import org.modeshape.jcr.query.model.DynamicOperand;
import org.modeshape.jcr.query.model.EquiJoinCondition;
import org.modeshape.jcr.query.model.FullTextSearch;
import org.modeshape.jcr.query.model.FullTextSearchScore;
import org.modeshape.jcr.query.model.JoinCondition;
import org.modeshape.jcr.query.model.Length;
import org.modeshape.jcr.query.model.LowerCase;
import org.modeshape.jcr.query.model.NodeDepth;
import org.modeshape.jcr.query.model.NodeLocalName;
import org.modeshape.jcr.query.model.NodeName;
import org.modeshape.jcr.query.model.NodePath;
import org.modeshape.jcr.query.model.Not;
import org.modeshape.jcr.query.model.Or;
import org.modeshape.jcr.query.model.Ordering;
import org.modeshape.jcr.query.model.PropertyExistence;
import org.modeshape.jcr.query.model.PropertyValue;
import org.modeshape.jcr.query.model.ReferenceValue;
import org.modeshape.jcr.query.model.Relike;
import org.modeshape.jcr.query.model.SameNode;
import org.modeshape.jcr.query.model.SameNodeJoinCondition;
import org.modeshape.jcr.query.model.SelectorName;
import org.modeshape.jcr.query.model.SetCriteria;
import org.modeshape.jcr.query.model.StaticOperand;
import org.modeshape.jcr.query.model.Subquery;
import org.modeshape.jcr.query.model.UpperCase;
import org.modeshape.jcr.query.model.Visitors;
import org.modeshape.jcr.query.model.Visitors.AbstractVisitor;
import org.modeshape.jcr.query.plan.PlanNode.Property;
import org.modeshape.jcr.query.plan.PlanNode.Type;
import org.modeshape.jcr.query.validate.ImmutableColumn;
import org.modeshape.jcr.query.validate.Schemata;
import org.modeshape.jcr.query.validate.Schemata.Table;
import org.modeshape.jcr.query.validate.Schemata.View;
/**
* Utilities for working with {@link PlanNode}s.
*/
public class PlanUtil {
/**
* Collected the minimum set of columns from the supplied table that are required by or used within the plan at the supplied
* node or above. This method first looks to see if the supplied node is a {@link Type#PROJECT} node, and if so immediately
* returns that node's list of {@link Property#PROJECT_COLUMNS projected columns}. Otherwise, this method finds all of the
* {@link Type#SOURCE} nodes below the supplied plan node, and accumulates the set of sources for which the columns are to be
* found. The method then walks up the plan, looking for references to columns of the supplied table, starting with the
* supplied node and walking up until a {@link PlanNode.Type#PROJECT PROJECT} node is found or the top of the plan is reached.
*
* The resulting column list will always contain at the front the {@link Property#PROJECT_COLUMNS columns} from the nearest
* PROJECT ancestor, followed by any columns required by criteria. This is done so that the processing of the nearest PROJECT
* ancestor node can simply use the sublist of each tuple.
*
*
* @param context the query context; may not be null
* @param planNode the plan node at which the required columns need to be found; may not be null
* @return the list of {@link Column} objects that are used in the plan; never null but possibly empty if no columns are
* actually needed
*/
public static List findRequiredColumns( QueryContext context,
PlanNode planNode ) {
List columns = null;
PlanNode node = planNode;
// First find the columns from the nearest PROJECT ancestor ...
do {
switch (node.getType()) {
case PROJECT:
columns = node.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
node = null;
break;
default:
node = node.getParent();
break;
}
} while (node != null);
// Find the names of the selectors ...
Set names = new HashSet();
for (PlanNode source : planNode.findAllAtOrBelow(Type.SOURCE)) {
names.add(source.getProperty(Property.SOURCE_NAME, SelectorName.class));
SelectorName alias = source.getProperty(Property.SOURCE_ALIAS, SelectorName.class);
if (alias != null) names.add(alias);
}
// Add the PROJECT columns first ...
RequiredColumnVisitor collectionVisitor = new RequiredColumnVisitor(names);
if (columns != null) {
for (Column projectedColumn : columns) {
collectionVisitor.visit(projectedColumn);
}
}
// Now add the columns from the JOIN, SELECT, PROJECT and SORT ancestors ...
node = planNode;
do {
switch (node.getType()) {
case JOIN:
Constraint criteria = node.getProperty(Property.JOIN_CONSTRAINTS, Constraint.class);
JoinCondition joinCondition = node.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
Visitors.visitAll(criteria, collectionVisitor);
Visitors.visitAll(joinCondition, collectionVisitor);
break;
case SELECT:
criteria = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
Visitors.visitAll(criteria, collectionVisitor);
break;
case SORT:
List orderBys = node.getPropertyAsList(Property.SORT_ORDER_BY, Object.class);
if (orderBys != null && !orderBys.isEmpty()) {
if (orderBys.get(0) instanceof Ordering) {
for (int i = 0; i != orderBys.size(); ++i) {
Ordering ordering = (Ordering)orderBys.get(i);
Visitors.visitAll(ordering, collectionVisitor);
}
}
}
break;
case PROJECT:
if (node != planNode) {
// Already handled above, but we can stop looking for columns ...
return collectionVisitor.getRequiredColumns();
}
break;
default:
break;
}
node = node.getParent();
} while (node != null);
return collectionVisitor.getRequiredColumns();
}
public static List findRequiredColumnTypes( QueryContext context,
List columns,
PlanNode node ) {
if (node.getType() == Type.PROJECT) {
assert node.getChildCount() == 1;
node = node.getFirstChild();
}
// See if there are any PROJECT nodes below this node ...
List projects = node.findAllFirstNodesAtOrBelow(Type.PROJECT);
if (!projects.isEmpty()) {
List types = new ArrayList(columns.size());
for (Column column : columns) {
boolean added = false;
for (PlanNode project : projects) {
List projectedColumns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
List projectedTypes = project.getPropertyAsList(Property.PROJECT_COLUMN_TYPES, String.class);
if (projectedTypes == null) continue;
for (int i = 0; i != projectedColumns.size(); ++i) {
Column projectedColumn = projectedColumns.get(i);
if (column.equals(projectedColumn)) {
types.add(projectedTypes.get(i));
added = true;
break;
}
}
if (added) break;
}
}
if (types.size() == columns.size()) return types;
}
// Otherwise, look for the sources ...
List types = new ArrayList(columns.size());
List sources = node.findAllAtOrBelow(Type.SOURCE);
for (Column column : columns) {
boolean added = false;
for (PlanNode source : sources) {
SelectorName alias = source.getProperty(Property.SOURCE_ALIAS, SelectorName.class);
SelectorName name = source.getProperty(Property.SOURCE_NAME, SelectorName.class);
if ((alias != null && alias.equals(column.selectorName())) || name.equals(column.selectorName())) {
List sourceColumns = source.getPropertyAsList(Property.SOURCE_COLUMNS, Schemata.Column.class);
for (Schemata.Column sourceColumn : sourceColumns) {
if (sourceColumn.getName().equals(column.getColumnName())
|| sourceColumn.getName().equals(column.getPropertyName())) {
types.add(sourceColumn.getPropertyTypeName());
added = true;
break;
}
}
if (added) break;
}
}
if (!added) {
// The column could not be found in the source, but it may be referring to a residual property.
// Therefore, we'll use the default type ...
types.add(context.getTypeSystem().getDefaultType());
}
}
return types;
}
public static class RequiredColumnVisitor extends AbstractVisitor {
private final Set names;
private final List columns = new LinkedList();
private final Set requiredColumnNames = new HashSet();
public RequiredColumnVisitor( Set names ) {
this.names = names;
}
@Override
public void visit( PropertyExistence existence ) {
requireColumn(existence.selectorName(), existence.getPropertyName());
}
@Override
public void visit( PropertyValue value ) {
requireColumn(value.selectorName(), value.getPropertyName());
}
@Override
public void visit( ReferenceValue value ) {
String propertyName = value.getPropertyName();
if (propertyName != null) {
requireColumn(value.selectorName(), propertyName);
}
}
@Override
public void visit( EquiJoinCondition condition ) {
requireColumn(condition.selector1Name(), condition.getProperty1Name());
requireColumn(condition.selector2Name(), condition.getProperty2Name());
}
@Override
public void visit( Column column ) {
requireColumn(column.selectorName(), column.getPropertyName(), column.getColumnName());
}
protected void requireColumn( SelectorName selector,
String propertyName ) {
requireColumn(selector, propertyName, null);
}
protected void requireColumn( SelectorName selector,
String propertyName,
String alias ) {
if (names.contains(selector)) {
// The column is part of the table we're interested in ...
if (alias != null && !alias.equals(propertyName)) {
if (requiredColumnNames.add(propertyName) && requiredColumnNames.add(alias)) {
columns.add(new Column(selector, propertyName, alias));
}
} else {
if (requiredColumnNames.add(propertyName)) {
alias = propertyName;
columns.add(new Column(selector, propertyName, alias));
}
}
}
}
/**
* Get the columns that are required.
*
* @return the columns; never null
*/
public List getRequiredColumns() {
return columns;
}
}
public static void replaceReferencesToRemovedSource( QueryContext context,
PlanNode planNode,
Map rewrittenSelectors ) {
switch (planNode.getType()) {
case PROJECT:
List columns = planNode.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
for (int i = 0; i != columns.size(); ++i) {
Column column = columns.get(i);
SelectorName replacement = rewrittenSelectors.get(column.selectorName());
if (replacement != null) {
columns.set(i, new Column(replacement, column.getPropertyName(), column.getColumnName()));
}
}
break;
case SELECT:
Constraint constraint = planNode.getProperty(Property.SELECT_CRITERIA, Constraint.class);
Constraint newConstraint = replaceReferencesToRemovedSource(context, constraint, rewrittenSelectors);
if (constraint != newConstraint) {
planNode.setProperty(Property.SELECT_CRITERIA, newConstraint);
}
break;
case SORT:
List orderBys = planNode.getPropertyAsList(Property.SORT_ORDER_BY, Object.class);
if (orderBys != null && !orderBys.isEmpty()) {
if (orderBys.get(0) instanceof SelectorName) {
for (int i = 0; i != orderBys.size(); ++i) {
SelectorName selectorName = (SelectorName)orderBys.get(i);
SelectorName replacement = rewrittenSelectors.get(selectorName);
if (replacement != null) {
orderBys.set(i, replacement);
}
}
} else {
for (int i = 0; i != orderBys.size(); ++i) {
Ordering ordering = (Ordering)orderBys.get(i);
DynamicOperand operand = ordering.getOperand();
orderBys.set(i, replaceReferencesToRemovedSource(context, operand, rewrittenSelectors));
}
}
}
break;
case JOIN:
// Update the join condition ...
JoinCondition joinCondition = planNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
JoinCondition newCondition = replaceReferencesToRemovedSource(context, joinCondition, rewrittenSelectors);
if (joinCondition != newCondition) {
planNode.setProperty(Property.JOIN_CONDITION, newCondition);
}
// Update the join criteria (if there are some) ...
List constraints = planNode.getPropertyAsList(Property.JOIN_CONSTRAINTS, Constraint.class);
if (constraints != null && !constraints.isEmpty()) {
for (int i = 0; i != constraints.size(); ++i) {
Constraint old = constraints.get(i);
Constraint replacement = replaceReferencesToRemovedSource(context, old, rewrittenSelectors);
if (replacement != old) {
constraints.set(i, replacement);
}
}
}
break;
case SOURCE:
// Check the source alias ...
SelectorName sourceAlias = planNode.getProperty(Property.SOURCE_ALIAS, SelectorName.class);
SelectorName replacement = rewrittenSelectors.get(sourceAlias);
if (replacement == null) {
// Try the source name ...
SelectorName sourceName = planNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
replacement = rewrittenSelectors.get(sourceName);
}
if (replacement != null) {
planNode.setProperty(Property.SOURCE_ALIAS, replacement);
planNode.getSelectors().remove(sourceAlias);
planNode.getSelectors().add(replacement);
}
break;
case GROUP:
case SET_OPERATION:
case DUP_REMOVE:
case LIMIT:
case NULL:
case DEPENDENT_QUERY:
case ACCESS:
// None of these have to be changed ...
break;
}
// Update the selectors referenced by the node ...
Set selectorsToAdd = null;
for (Iterator iter = planNode.getSelectors().iterator(); iter.hasNext();) {
SelectorName replacement = rewrittenSelectors.get(iter.next());
if (replacement != null) {
iter.remove();
if (selectorsToAdd == null) selectorsToAdd = new HashSet();
selectorsToAdd.add(replacement);
}
}
if (selectorsToAdd != null) planNode.getSelectors().addAll(selectorsToAdd);
// Now call recursively on the children ...
for (PlanNode child : planNode) {
replaceReferencesToRemovedSource(context, child, rewrittenSelectors);
}
}
public static DynamicOperand replaceReferencesToRemovedSource( QueryContext context,
DynamicOperand operand,
Map rewrittenSelectors ) {
if (operand instanceof FullTextSearchScore) {
FullTextSearchScore score = (FullTextSearchScore)operand;
SelectorName replacement = rewrittenSelectors.get(score.selectorName());
if (replacement == null) return score;
return new FullTextSearchScore(replacement);
}
if (operand instanceof Length) {
Length operation = (Length)operand;
PropertyValue wrapped = operation.getPropertyValue();
SelectorName replacement = rewrittenSelectors.get(wrapped.selectorName());
if (replacement == null) return operand;
return new Length(new PropertyValue(replacement, wrapped.getPropertyName()));
}
if (operand instanceof LowerCase) {
LowerCase operation = (LowerCase)operand;
SelectorName replacement = rewrittenSelectors.get(operation.selectorName());
if (replacement == null) return operand;
return new LowerCase(replaceReferencesToRemovedSource(context, operation.getOperand(), rewrittenSelectors));
}
if (operand instanceof UpperCase) {
UpperCase operation = (UpperCase)operand;
SelectorName replacement = rewrittenSelectors.get(operation.selectorName());
if (replacement == null) return operand;
return new UpperCase(replaceReferencesToRemovedSource(context, operation.getOperand(), rewrittenSelectors));
}
if (operand instanceof NodeName) {
NodeName name = (NodeName)operand;
SelectorName replacement = rewrittenSelectors.get(name.selectorName());
if (replacement == null) return name;
return new NodeName(replacement);
}
if (operand instanceof NodeLocalName) {
NodeLocalName name = (NodeLocalName)operand;
SelectorName replacement = rewrittenSelectors.get(name.selectorName());
if (replacement == null) return name;
return new NodeLocalName(replacement);
}
if (operand instanceof PropertyValue) {
PropertyValue value = (PropertyValue)operand;
SelectorName replacement = rewrittenSelectors.get(value.selectorName());
if (replacement == null) return operand;
return new PropertyValue(replacement, value.getPropertyName());
}
if (operand instanceof ReferenceValue) {
ReferenceValue value = (ReferenceValue)operand;
SelectorName replacement = rewrittenSelectors.get(value.selectorName());
if (replacement == null) return operand;
return new ReferenceValue(replacement, value.getPropertyName());
}
if (operand instanceof NodeDepth) {
NodeDepth depth = (NodeDepth)operand;
SelectorName replacement = rewrittenSelectors.get(depth.selectorName());
if (replacement == null) return operand;
return new NodeDepth(replacement);
}
if (operand instanceof NodePath) {
NodePath path = (NodePath)operand;
SelectorName replacement = rewrittenSelectors.get(path.selectorName());
if (replacement == null) return operand;
return new NodePath(replacement);
}
return operand;
}
public static Constraint replaceReferencesToRemovedSource( QueryContext context,
Constraint constraint,
Map rewrittenSelectors ) {
if (constraint instanceof And) {
And and = (And)constraint;
Constraint left = replaceReferencesToRemovedSource(context, and.left(), rewrittenSelectors);
Constraint right = replaceReferencesToRemovedSource(context, and.right(), rewrittenSelectors);
if (left == and.left() && right == and.right()) return and;
return new And(left, right);
}
if (constraint instanceof Or) {
Or or = (Or)constraint;
Constraint left = replaceReferencesToRemovedSource(context, or.left(), rewrittenSelectors);
Constraint right = replaceReferencesToRemovedSource(context, or.right(), rewrittenSelectors);
if (left == or.left() && right == or.right()) return or;
return new Or(left, right);
}
if (constraint instanceof Not) {
Not not = (Not)constraint;
Constraint wrapped = replaceReferencesToRemovedSource(context, not.getConstraint(), rewrittenSelectors);
if (wrapped == not.getConstraint()) return not;
return new Not(wrapped);
}
if (constraint instanceof SameNode) {
SameNode sameNode = (SameNode)constraint;
SelectorName replacement = rewrittenSelectors.get(sameNode.selectorName());
if (replacement == null) return sameNode;
return new SameNode(replacement, sameNode.getPath());
}
if (constraint instanceof ChildNode) {
ChildNode childNode = (ChildNode)constraint;
SelectorName replacement = rewrittenSelectors.get(childNode.selectorName());
if (replacement == null) return childNode;
return new ChildNode(replacement, childNode.getParentPath());
}
if (constraint instanceof DescendantNode) {
DescendantNode descendantNode = (DescendantNode)constraint;
SelectorName replacement = rewrittenSelectors.get(descendantNode.selectorName());
if (replacement == null) return descendantNode;
return new DescendantNode(replacement, descendantNode.getAncestorPath());
}
if (constraint instanceof PropertyExistence) {
PropertyExistence existence = (PropertyExistence)constraint;
SelectorName replacement = rewrittenSelectors.get(existence.selectorName());
if (replacement == null) return existence;
return new PropertyExistence(replacement, existence.getPropertyName());
}
if (constraint instanceof FullTextSearch) {
FullTextSearch search = (FullTextSearch)constraint;
SelectorName replacement = rewrittenSelectors.get(search.selectorName());
if (replacement == null) return search;
return new FullTextSearch(replacement, search.getPropertyName(), search.fullTextSearchExpression());
}
if (constraint instanceof Between) {
Between between = (Between)constraint;
DynamicOperand lhs = between.getOperand();
StaticOperand lower = between.getLowerBound(); // Current only a literal; therefore, no reference to selector
StaticOperand upper = between.getUpperBound(); // Current only a literal; therefore, no reference to selector
DynamicOperand newLhs = replaceReferencesToRemovedSource(context, lhs, rewrittenSelectors);
if (lhs == newLhs) return between;
return new Between(newLhs, lower, upper, between.isLowerBoundIncluded(), between.isUpperBoundIncluded());
}
if (constraint instanceof Comparison) {
Comparison comparison = (Comparison)constraint;
DynamicOperand lhs = comparison.getOperand1();
StaticOperand rhs = comparison.getOperand2(); // Current only a literal; therefore, no reference to selector
DynamicOperand newLhs = replaceReferencesToRemovedSource(context, lhs, rewrittenSelectors);
if (lhs == newLhs) return comparison;
return new Comparison(newLhs, comparison.operator(), rhs);
}
if (constraint instanceof SetCriteria) {
SetCriteria criteria = (SetCriteria)constraint;
DynamicOperand lhs = criteria.leftOperand();
DynamicOperand newLhs = replaceReferencesToRemovedSource(context, lhs, rewrittenSelectors);
if (lhs == newLhs) return constraint;
return new SetCriteria(newLhs, criteria.rightOperands());
}
return constraint;
}
public static JoinCondition replaceReferencesToRemovedSource( QueryContext context,
JoinCondition joinCondition,
Map rewrittenSelectors ) {
if (joinCondition instanceof EquiJoinCondition) {
EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
SelectorName replacement1 = rewrittenSelectors.get(condition.selector1Name());
SelectorName replacement2 = rewrittenSelectors.get(condition.selector2Name());
if (replacement1 == condition.selector1Name() && replacement2 == condition.selector2Name()) return condition;
return new EquiJoinCondition(replacement1, condition.getProperty1Name(), replacement2, condition.getProperty2Name());
}
if (joinCondition instanceof SameNodeJoinCondition) {
SameNodeJoinCondition condition = (SameNodeJoinCondition)joinCondition;
SelectorName replacement1 = rewrittenSelectors.get(condition.selector1Name());
SelectorName replacement2 = rewrittenSelectors.get(condition.selector2Name());
if (replacement1 == condition.selector1Name() && replacement2 == condition.selector2Name()) return condition;
return new SameNodeJoinCondition(replacement1, replacement2, condition.getSelector2Path());
}
if (joinCondition instanceof ChildNodeJoinCondition) {
ChildNodeJoinCondition condition = (ChildNodeJoinCondition)joinCondition;
SelectorName childSelector = rewrittenSelectors.get(condition.childSelectorName());
SelectorName parentSelector = rewrittenSelectors.get(condition.parentSelectorName());
if (childSelector == condition.childSelectorName() && parentSelector == condition.parentSelectorName()) return condition;
return new ChildNodeJoinCondition(parentSelector, childSelector);
}
if (joinCondition instanceof DescendantNodeJoinCondition) {
DescendantNodeJoinCondition condition = (DescendantNodeJoinCondition)joinCondition;
SelectorName ancestor = rewrittenSelectors.get(condition.ancestorSelectorName());
SelectorName descendant = rewrittenSelectors.get(condition.descendantSelectorName());
if (ancestor == condition.ancestorSelectorName() && descendant == condition.descendantSelectorName()) return condition;
return new ChildNodeJoinCondition(ancestor, descendant);
}
return joinCondition;
}
public static void replaceViewReferences( QueryContext context,
PlanNode topOfViewInPlan,
ColumnMapping mappings ) {
assert topOfViewInPlan != null;
SelectorName viewName = mappings.getOriginalName();
// Walk up the plan to change any references to the view columns into references to the source columns...
PlanNode node = topOfViewInPlan;
List potentiallyRemovableSources = new LinkedList();
do {
// Move to the parent ...
replaceViewReferences(context, node, mappings, viewName, potentiallyRemovableSources);
node = node.getParent();
} while (node != null);
// Remove any source node that was the view but that is no longer used in any higher node ...
for (PlanNode sourceNode : potentiallyRemovableSources) {
boolean stillRequired = false;
node = sourceNode.getParent();
while (node != null) {
if (node.getSelectors().contains(viewName)) {
stillRequired = true;
break;
}
node = node.getParent();
}
if (!stillRequired) {
assert sourceNode.getParent() != null;
sourceNode.extractFromParent();
}
}
}
protected static void replaceViewReferences( QueryContext context,
PlanNode node,
ColumnMapping mappings,
SelectorName viewName,
List potentiallyRemovableSources ) {
assert node != null;
assert viewName != null;
// Remove the view from the selectors ...
if (node.getSelectors().remove(viewName)) {
switch (node.getType()) {
case PROJECT:
// Adjust the columns ...
List columns = node.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
if (columns != null) {
for (int i = 0; i != columns.size(); ++i) {
Column column = columns.get(i);
if (column.selectorName().equals(viewName)) {
// This column references the view ...
String columnName = column.getPropertyName();
String columnAlias = column.getColumnName();
// Find the source column that this view column corresponds to ...
Column sourceColumn = mappings.getMappedColumn(columnName);
if (sourceColumn != null) {
SelectorName sourceName = sourceColumn.selectorName();
// Replace the view column with one that uses the same alias but that references the
// source
// column ...
columns.set(i, new Column(sourceName, sourceColumn.getPropertyName(), columnAlias));
node.addSelector(sourceName);
} else {
if (mappings.getMappedSelectorNames().size() == 1) {
SelectorName sourceName = mappings.getSingleMappedSelectorName();
if (sourceName != null) {
Column newColumn = null;
if (columnName != null) {
newColumn = new Column(sourceName, columnName, columnAlias);
} else {
newColumn = new Column(sourceName);
}
columns.set(i, newColumn);
node.addSelector(sourceName);
} else {
node.addSelector(column.selectorName());
}
} else {
node.addSelector(column.selectorName());
}
}
} else {
node.addSelector(column.selectorName());
}
}
}
break;
case SELECT:
Constraint constraint = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
Constraint newConstraint = replaceReferences(context, constraint, mappings, node);
if (constraint != newConstraint) {
node.getSelectors().clear();
node.addSelectors(Visitors.getSelectorsReferencedBy(newConstraint));
node.setProperty(Property.SELECT_CRITERIA, newConstraint);
} else {
node.addSelector(viewName);
}
break;
case SOURCE:
SelectorName sourceName = node.getProperty(Property.SOURCE_NAME, SelectorName.class);
assert sourceName.equals(sourceName); // selector name already matches
potentiallyRemovableSources.add(node);
break;
case JOIN:
JoinCondition joinCondition = node.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
JoinCondition newJoinCondition = replaceViewReferences(context, joinCondition, mappings, node);
node.getSelectors().clear();
node.setProperty(Property.JOIN_CONDITION, newJoinCondition);
node.addSelectors(Visitors.getSelectorsReferencedBy(newJoinCondition));
List joinConstraints = node.getPropertyAsList(Property.JOIN_CONSTRAINTS, Constraint.class);
if (joinConstraints != null && !joinConstraints.isEmpty()) {
List newConstraints = new ArrayList(joinConstraints.size());
for (Constraint joinConstraint : joinConstraints) {
newConstraint = replaceReferences(context, joinConstraint, mappings, node);
newConstraints.add(newConstraint);
node.addSelectors(Visitors.getSelectorsReferencedBy(newConstraint));
}
node.setProperty(Property.JOIN_CONSTRAINTS, newConstraints);
}
break;
case ACCESS:
// Add all the selectors used by the subnodes ...
for (PlanNode child : node) {
node.addSelectors(child.getSelectors());
}
break;
case SORT:
// The selector names and Ordering objects needs to be changed ...
List orderings = node.getPropertyAsList(Property.SORT_ORDER_BY, Ordering.class);
List newOrderings = new ArrayList(orderings.size());
node.getSelectors().clear();
for (Ordering ordering : orderings) {
DynamicOperand operand = ordering.getOperand();
DynamicOperand newOperand = replaceViewReferences(context, operand, mappings, node);
if (newOperand != operand) {
ordering = new Ordering(newOperand, ordering.order());
}
node.addSelectors(Visitors.getSelectorsReferencedBy(ordering));
newOrderings.add(ordering);
}
node.setProperty(Property.SORT_ORDER_BY, newOrderings);
break;
case GROUP:
// Don't yet use GROUP BY
case SET_OPERATION:
case DEPENDENT_QUERY:
case DUP_REMOVE:
case LIMIT:
case NULL:
break;
}
}
}
public static Constraint replaceReferences( QueryContext context,
Constraint constraint,
ColumnMapping mapping,
PlanNode node ) {
if (constraint instanceof And) {
And and = (And)constraint;
Constraint left = replaceReferences(context, and.left(), mapping, node);
Constraint right = replaceReferences(context, and.right(), mapping, node);
if (left == and.left() && right == and.right()) return and;
return new And(left, right);
}
if (constraint instanceof Or) {
Or or = (Or)constraint;
Constraint left = replaceReferences(context, or.left(), mapping, node);
Constraint right = replaceReferences(context, or.right(), mapping, node);
if (left == or.left() && right == or.right()) return or;
return new Or(left, right);
}
if (constraint instanceof Not) {
Not not = (Not)constraint;
Constraint wrapped = replaceReferences(context, not.getConstraint(), mapping, node);
return wrapped == not.getConstraint() ? not : new Not(wrapped);
}
if (constraint instanceof SameNode) {
SameNode sameNode = (SameNode)constraint;
if (!mapping.getOriginalName().equals(sameNode.selectorName())) return sameNode;
if (!mapping.isMappedToSingleSelector()) return sameNode;
SelectorName selector = mapping.getSingleMappedSelectorName();
node.addSelector(selector);
return new SameNode(selector, sameNode.getPath());
}
if (constraint instanceof ChildNode) {
ChildNode childNode = (ChildNode)constraint;
if (!mapping.getOriginalName().equals(childNode.selectorName())) return childNode;
if (!mapping.isMappedToSingleSelector()) return childNode;
SelectorName selector = mapping.getSingleMappedSelectorName();
node.addSelector(selector);
return new ChildNode(selector, childNode.getParentPath());
}
if (constraint instanceof DescendantNode) {
DescendantNode descendantNode = (DescendantNode)constraint;
if (!mapping.getOriginalName().equals(descendantNode.selectorName())) return descendantNode;
if (!mapping.isMappedToSingleSelector()) return descendantNode;
SelectorName selector = mapping.getSingleMappedSelectorName();
node.addSelector(selector);
return new DescendantNode(selector, descendantNode.getAncestorPath());
}
if (constraint instanceof PropertyExistence) {
PropertyExistence existence = (PropertyExistence)constraint;
if (!mapping.getOriginalName().equals(existence.selectorName())) return existence;
Column sourceColumn = mapping.getMappedColumn(existence.getPropertyName());
if (sourceColumn == null) return existence;
node.addSelector(sourceColumn.selectorName());
return new PropertyExistence(sourceColumn.selectorName(), sourceColumn.getPropertyName());
}
if (constraint instanceof FullTextSearch) {
FullTextSearch search = (FullTextSearch)constraint;
if (!mapping.getOriginalName().equals(search.selectorName())) return search;
Column sourceColumn = mapping.getMappedColumn(search.getPropertyName());
if (sourceColumn == null) {
if (search.getPropertyName() == null && mapping.getMappedSelectorNames().size() == 1) {
SelectorName newSelectorName = mapping.getSingleMappedSelectorName();
if (newSelectorName != null) {
node.addSelector(newSelectorName);
return new FullTextSearch(newSelectorName, search.getPropertyName(),
search.fullTextSearchExpression(), search.getFullTextSearchExpression());
}
}
return search;
}
node.addSelector(sourceColumn.selectorName());
return new FullTextSearch(sourceColumn.selectorName(), sourceColumn.getPropertyName(),
search.fullTextSearchExpression(), search.getFullTextSearchExpression());
}
if (constraint instanceof SetCriteria) {
SetCriteria set = (SetCriteria)constraint;
DynamicOperand oldLeft = set.leftOperand();
Set selectorNames = oldLeft.selectorNames();
if (selectorNames.size() == 1 && !selectorNames.contains(mapping.getOriginalName())) return set;
DynamicOperand newLeft = replaceViewReferences(context, oldLeft, mapping, node);
if (newLeft == oldLeft) return set;
return new SetCriteria(newLeft, set.rightOperands());
}
if (constraint instanceof Between) {
Between between = (Between)constraint;
DynamicOperand lhs = between.getOperand();
StaticOperand lower = between.getLowerBound(); // Current only a literal; therefore, no reference to selector
StaticOperand upper = between.getUpperBound(); // Current only a literal; therefore, no reference to selector
DynamicOperand newLhs = replaceViewReferences(context, lhs, mapping, node);
if (lhs == newLhs) return between;
return new Between(newLhs, lower, upper, between.isLowerBoundIncluded(), between.isUpperBoundIncluded());
}
if (constraint instanceof Comparison) {
Comparison comparison = (Comparison)constraint;
DynamicOperand lhs = comparison.getOperand1();
StaticOperand rhs = comparison.getOperand2(); // Current only a literal; therefore, no reference to selector
DynamicOperand newLhs = replaceViewReferences(context, lhs, mapping, node);
if (lhs == newLhs) return comparison;
return new Comparison(newLhs, comparison.operator(), rhs);
}
return constraint;
}
public static DynamicOperand replaceViewReferences( QueryContext context,
DynamicOperand operand,
ColumnMapping mapping,
PlanNode node ) {
if (operand instanceof ArithmeticOperand) {
ArithmeticOperand arith = (ArithmeticOperand)operand;
DynamicOperand newLeft = replaceViewReferences(context, arith.getLeft(), mapping, node);
DynamicOperand newRight = replaceViewReferences(context, arith.getRight(), mapping, node);
return new ArithmeticOperand(newLeft, arith.operator(), newRight);
}
if (operand instanceof FullTextSearchScore) {
FullTextSearchScore score = (FullTextSearchScore)operand;
if (!mapping.getOriginalName().equals(score.selectorName())) return score;
if (mapping.isMappedToSingleSelector()) {
return new FullTextSearchScore(mapping.getSingleMappedSelectorName());
}
// There are multiple mappings, so we have to create a composite score ...
DynamicOperand composite = null;
for (SelectorName name : mapping.getMappedSelectorNames()) {
FullTextSearchScore mappedScore = new FullTextSearchScore(name);
if (composite == null) {
composite = mappedScore;
} else {
composite = new ArithmeticOperand(composite, ArithmeticOperator.ADD, mappedScore);
}
}
return composite;
}
if (operand instanceof Length) {
Length operation = (Length)operand;
return new Length((PropertyValue)replaceViewReferences(context, operation.getPropertyValue(), mapping, node));
}
if (operand instanceof LowerCase) {
LowerCase operation = (LowerCase)operand;
return new LowerCase(replaceViewReferences(context, operation.getOperand(), mapping, node));
}
if (operand instanceof UpperCase) {
UpperCase operation = (UpperCase)operand;
return new UpperCase(replaceViewReferences(context, operation.getOperand(), mapping, node));
}
if (operand instanceof NodeName) {
NodeName name = (NodeName)operand;
if (!mapping.getOriginalName().equals(name.selectorName())) return name;
if (!mapping.isMappedToSingleSelector()) return name;
node.addSelector(mapping.getSingleMappedSelectorName());
return new NodeName(mapping.getSingleMappedSelectorName());
}
if (operand instanceof NodeLocalName) {
NodeLocalName name = (NodeLocalName)operand;
if (!mapping.getOriginalName().equals(name.selectorName())) return name;
if (!mapping.isMappedToSingleSelector()) return name;
node.addSelector(mapping.getSingleMappedSelectorName());
return new NodeLocalName(mapping.getSingleMappedSelectorName());
}
if (operand instanceof PropertyValue) {
PropertyValue value = (PropertyValue)operand;
if (!mapping.getOriginalName().equals(value.selectorName())) return value;
Column sourceColumn = mapping.getMappedColumn(value.getPropertyName());
if (sourceColumn == null) {
if (mapping.getMappedSelectorNames().size() == 1) {
SelectorName newSelectorName = mapping.getSingleMappedSelectorName();
if (newSelectorName != null) {
node.addSelector(newSelectorName);
return new PropertyValue(newSelectorName, value.getPropertyName());
}
}
return value;
}
node.addSelector(sourceColumn.selectorName());
return new PropertyValue(sourceColumn.selectorName(), sourceColumn.getPropertyName());
}
if (operand instanceof ReferenceValue) {
ReferenceValue value = (ReferenceValue)operand;
if (!mapping.getOriginalName().equals(value.selectorName())) return value;
Column sourceColumn = mapping.getMappedColumn(value.getPropertyName());
if (sourceColumn == null) return value;
node.addSelector(sourceColumn.selectorName());
return new ReferenceValue(sourceColumn.selectorName(), sourceColumn.getPropertyName());
}
if (operand instanceof NodeDepth) {
NodeDepth depth = (NodeDepth)operand;
if (!mapping.getOriginalName().equals(depth.selectorName())) return depth;
if (!mapping.isMappedToSingleSelector()) return depth;
node.addSelector(mapping.getSingleMappedSelectorName());
return new NodeDepth(mapping.getSingleMappedSelectorName());
}
if (operand instanceof NodePath) {
NodePath path = (NodePath)operand;
if (!mapping.getOriginalName().equals(path.selectorName())) return path;
if (!mapping.isMappedToSingleSelector()) return path;
node.addSelector(mapping.getSingleMappedSelectorName());
return new NodePath(mapping.getSingleMappedSelectorName());
}
return operand;
}
public static JoinCondition replaceViewReferences( QueryContext context,
JoinCondition joinCondition,
ColumnMapping mapping,
PlanNode node ) {
if (joinCondition instanceof EquiJoinCondition) {
EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
SelectorName replacement1 = condition.selector1Name();
SelectorName replacement2 = condition.selector2Name();
String property1 = condition.getProperty1Name();
String property2 = condition.getProperty2Name();
if (replacement1.equals(mapping.getOriginalName())) {
Column sourceColumn = mapping.getMappedColumn(property1);
if (sourceColumn != null) {
replacement1 = sourceColumn.selectorName();
property1 = sourceColumn.getPropertyName();
}
}
if (replacement2.equals(mapping.getOriginalName())) {
Column sourceColumn = mapping.getMappedColumn(property2);
if (sourceColumn != null) {
replacement2 = sourceColumn.selectorName();
property2 = sourceColumn.getPropertyName();
}
}
if (replacement1 == condition.selector1Name() && replacement2 == condition.selector2Name()) return condition;
node.addSelector(replacement1, replacement2);
return new EquiJoinCondition(replacement1, property1, replacement2, property2);
}
// All the remaining conditions can only be rewritten if there is a single source ...
if (!mapping.isMappedToSingleSelector()) return joinCondition;
// There is only a single source ...
SelectorName viewName = mapping.getOriginalName();
SelectorName sourceName = mapping.getSingleMappedSelectorName();
if (joinCondition instanceof SameNodeJoinCondition) {
SameNodeJoinCondition condition = (SameNodeJoinCondition)joinCondition;
SelectorName replacement1 = condition.selector1Name();
SelectorName replacement2 = condition.selector2Name();
if (replacement1.equals(viewName)) replacement1 = sourceName;
if (replacement2.equals(viewName)) replacement2 = sourceName;
if (replacement1 == condition.selector1Name() && replacement2 == condition.selector2Name()) return condition;
node.addSelector(replacement1, replacement2);
if (condition.getSelector2Path() == null) return new SameNodeJoinCondition(replacement1, replacement2);
return new SameNodeJoinCondition(replacement1, replacement2, condition.getSelector2Path());
}
if (joinCondition instanceof ChildNodeJoinCondition) {
ChildNodeJoinCondition condition = (ChildNodeJoinCondition)joinCondition;
SelectorName childSelector = condition.childSelectorName();
SelectorName parentSelector = condition.parentSelectorName();
if (childSelector.equals(viewName)) childSelector = sourceName;
if (parentSelector.equals(viewName)) parentSelector = sourceName;
if (childSelector == condition.childSelectorName() && parentSelector == condition.parentSelectorName()) return condition;
node.addSelector(childSelector, parentSelector);
return new ChildNodeJoinCondition(parentSelector, childSelector);
}
if (joinCondition instanceof DescendantNodeJoinCondition) {
DescendantNodeJoinCondition condition = (DescendantNodeJoinCondition)joinCondition;
SelectorName ancestor = condition.ancestorSelectorName();
SelectorName descendant = condition.descendantSelectorName();
if (ancestor.equals(viewName)) ancestor = sourceName;
if (descendant.equals(viewName)) descendant = sourceName;
if (ancestor == condition.ancestorSelectorName() && descendant == condition.descendantSelectorName()) return condition;
node.addSelector(ancestor, descendant);
return new DescendantNodeJoinCondition(ancestor, descendant);
}
return joinCondition;
}
public static ColumnMapping createMappingFor( View view,
PlanNode viewPlan ) {
ColumnMapping mapping = new ColumnMapping(view.getName());
// Find the PROJECT node in the view plan ...
PlanNode project = viewPlan.findAtOrBelow(Type.PROJECT);
assert project != null;
// Get the Columns from the PROJECT in the plan node ...
List projectedColumns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
// Get the Schemata columns defined by the view ...
List viewColumns = view.getColumns();
for (int i = 0; i != viewColumns.size(); ++i) {
Column projectedColunn = projectedColumns.get(i);
String viewColumnName = viewColumns.get(i).getName();
mapping.map(viewColumnName, projectedColunn);
}
if (viewColumns.size() < projectedColumns.size()) {
// There are some extra projected columns, likely because the view did not know about them ...
for (int i = viewColumns.size(); i != projectedColumns.size(); ++i) {
Column projectedColunn = projectedColumns.get(i);
String viewColumnName = projectedColunn.getPropertyName();
mapping.map(viewColumnName, projectedColunn);
}
}
return mapping;
}
public static ColumnMapping createMappingForAliased( SelectorName viewAlias,
View view,
PlanNode viewPlan ) {
ColumnMapping mapping = new ColumnMapping(viewAlias);
// Find the PROJECT node in the view plan ...
PlanNode project = viewPlan.findAtOrBelow(Type.PROJECT);
assert project != null;
// Get the Columns from the PROJECT in the plan node ...
List projectedColumns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
// Get the Schemata columns defined by the view ...
List viewColumns = view.getColumns();
for (int i = 0; i != viewColumns.size(); ++i) {
Column projectedColunn = projectedColumns.get(i);
String viewColumnName = viewColumns.get(i).getName();
mapping.map(viewColumnName, projectedColunn);
}
if (viewColumns.size() < projectedColumns.size()) {
// There are some extra projected columns, likely because the view did not know about them ...
for (int i = viewColumns.size(); i != projectedColumns.size(); ++i) {
Column projectedColunn = projectedColumns.get(i);
String viewColumnName = projectedColunn.getPropertyName();
mapping.map(viewColumnName, projectedColunn);
}
}
return mapping;
}
public static ColumnMapping createMappingForAliased( SelectorName tableAlias,
Table table,
PlanNode tableSourceNode ) {
ColumnMapping mapping = new ColumnMapping(tableAlias);
// Find the projected columns on the nearest PROJECT ...
PlanNode project = tableSourceNode.findAncestor(Type.PROJECT);
assert project != null;
// Get the Columns from the PROJECT in the plan node ...
List projectedColumns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
for (int i = 0; i != projectedColumns.size(); ++i) {
Column projectedColumn = projectedColumns.get(i);
Column projectedColumnInTable = projectedColumns.get(i).with(table.getName());
org.modeshape.jcr.query.validate.Schemata.Column column = table.getColumn(projectedColumnInTable.getPropertyName());
mapping.map(column.getName(), projectedColumnInTable);
if (projectedColumn.getColumnName() != null) {
// The projected column has an alias, so add a mapping for it, too
mapping.map(projectedColumn.getColumnName(), projectedColumnInTable);
}
}
return mapping;
}
public static void setSelectorsOnSubplan( PlanNode subplan,
Set selectors ) {
subplan.getSelectors().clear();
subplan.getSelectors().addAll(selectors);
for (PlanNode child : subplan.getChildren()) {
setSelectorsOnSubplan(child, selectors);
}
}
/**
* Defines how the view columns are mapped (or resolved) into the columns from the source tables.
*/
public static class ColumnMapping {
private final SelectorName originalName;
private final Map mappedColumnsByOriginalColumnName = new HashMap();
private final Set mappedSelectorNames = new LinkedHashSet(); // maintains insertion order
public ColumnMapping( SelectorName originalName ) {
this.originalName = originalName;
}
public void map( String originalColumnName,
Column projectedColumn ) {
mappedColumnsByOriginalColumnName.put(originalColumnName, projectedColumn);
mappedSelectorNames.add(projectedColumn.selectorName());
}
public SelectorName getOriginalName() {
return originalName;
}
public Column getMappedColumn( String viewColumnName ) {
return mappedColumnsByOriginalColumnName.get(viewColumnName);
}
public boolean isMappedToSingleSelector() {
return mappedSelectorNames.size() == 1;
}
/**
* @return tableNames
*/
public Set getMappedSelectorNames() {
return mappedSelectorNames;
}
public SelectorName getSingleMappedSelectorName() {
return isMappedToSingleSelector() ? mappedSelectorNames.iterator().next() : null;
}
}
public static Constraint replaceAliasesWithProperties( QueryContext context,
Constraint constraint,
Map propertyByAlias ) {
if (propertyByAlias.isEmpty()) return constraint;
if (constraint instanceof And) {
And and = (And)constraint;
Constraint left = replaceAliasesWithProperties(context, and.left(), propertyByAlias);
Constraint right = replaceAliasesWithProperties(context, and.right(), propertyByAlias);
if (left == and.left() && right == and.right()) return and;
return new And(left, right);
}
if (constraint instanceof Or) {
Or or = (Or)constraint;
Constraint left = replaceAliasesWithProperties(context, or.left(), propertyByAlias);
Constraint right = replaceAliasesWithProperties(context, or.right(), propertyByAlias);
if (left == or.left() && right == or.right()) return or;
return new Or(left, right);
}
if (constraint instanceof Not) {
Not not = (Not)constraint;
Constraint wrapped = replaceAliasesWithProperties(context, not.getConstraint(), propertyByAlias);
if (wrapped == not.getConstraint()) return not;
return new Not(wrapped);
}
if (constraint instanceof SameNode) {
return constraint;
}
if (constraint instanceof ChildNode) {
return constraint;
}
if (constraint instanceof DescendantNode) {
return constraint;
}
if (constraint instanceof PropertyExistence) {
return constraint;
}
if (constraint instanceof FullTextSearch) {
return constraint;
}
if (constraint instanceof Between) {
Between between = (Between)constraint;
DynamicOperand lhs = replaceAliasesWithProperties(context, between.getOperand(), propertyByAlias);
StaticOperand lower = between.getLowerBound(); // Current only a literal; therefore, no alias
StaticOperand upper = between.getUpperBound(); // Current only a literal; therefore, no alias
if (lower == between.getOperand()) return between;
return new Between(lhs, lower, upper, between.isLowerBoundIncluded(), between.isUpperBoundIncluded());
}
if (constraint instanceof Comparison) {
Comparison comparison = (Comparison)constraint;
DynamicOperand lhs = replaceAliasesWithProperties(context, comparison.getOperand1(), propertyByAlias);
if (lhs == comparison.getOperand1()) return comparison;
return new Comparison(lhs, comparison.operator(), comparison.getOperand2());
}
if(constraint instanceof Relike) {
Relike relike = (Relike) constraint;
StaticOperand op1 = relike.getOperand1();
PropertyValue op2 = relike.getOperand2();
PropertyValue newOp2 = replaceAliasesWithProperties(context, op2, propertyByAlias);
if (op2 == newOp2) return relike;
return new Relike(op1, op2);
}
if (constraint instanceof SetCriteria) {
SetCriteria criteria = (SetCriteria)constraint;
DynamicOperand lhs = replaceAliasesWithProperties(context, criteria.leftOperand(), propertyByAlias);
if (lhs == criteria.leftOperand()) return criteria;
return new SetCriteria(lhs, criteria.rightOperands());
}
return constraint;
}
protected static PropertyValue replaceAliasesWithProperties( QueryContext context,
PropertyValue value,
Map propertyByAlias ) {
String original = value.getPropertyName();
String propName = propertyByAlias.get(original);
return propName != null ? new PropertyValue(value.selectorName(), propName) : value;
}
public static DynamicOperand replaceAliasesWithProperties( QueryContext context,
DynamicOperand operand,
Map propertyByAlias ) {
if (operand instanceof ArithmeticOperand) {
ArithmeticOperand arith = (ArithmeticOperand)operand;
DynamicOperand newLeft = replaceAliasesWithProperties(context, arith.getLeft(), propertyByAlias);
DynamicOperand newRight = replaceAliasesWithProperties(context, arith.getRight(), propertyByAlias);
if (newLeft == arith.getLeft() && newRight == arith.getRight()) return arith;
return new ArithmeticOperand(newLeft, arith.operator(), newRight);
}
if (operand instanceof FullTextSearchScore) {
return operand;
}
if (operand instanceof Length) {
Length operation = (Length)operand;
PropertyValue value = operation.getPropertyValue();
PropertyValue newValue = replaceAliasesWithProperties(context, value, propertyByAlias);
if (newValue == value) return operation;
return new Length(newValue);
}
if (operand instanceof LowerCase) {
LowerCase operation = (LowerCase)operand;
DynamicOperand oldOperand = operation.getOperand();
DynamicOperand newOperand = replaceAliasesWithProperties(context, oldOperand, propertyByAlias);
if (oldOperand == newOperand) return operation;
return new LowerCase(newOperand);
}
if (operand instanceof UpperCase) {
UpperCase operation = (UpperCase)operand;
DynamicOperand oldOperand = operation.getOperand();
DynamicOperand newOperand = replaceAliasesWithProperties(context, oldOperand, propertyByAlias);
if (oldOperand == newOperand) return operation;
return new UpperCase(newOperand);
}
if (operand instanceof NodeName) {
return operand;
}
if (operand instanceof NodeLocalName) {
return operand;
}
if (operand instanceof PropertyValue) {
PropertyValue value = (PropertyValue)operand;
return replaceAliasesWithProperties(context, value, propertyByAlias);
}
if (operand instanceof ReferenceValue) {
ReferenceValue value = (ReferenceValue)operand;
String original = value.getPropertyName();
String propName = propertyByAlias.get(original);
if (propName == null) return value;
return new ReferenceValue(value.selectorName(), propName);
}
if (operand instanceof NodeDepth) {
return operand;
}
if (operand instanceof NodePath) {
return operand;
}
return operand;
}
public static Constraint replaceSubqueriesWithBindVariables( QueryContext context,
Constraint constraint,
Map subqueriesByVariableName ) {
if (constraint instanceof And) {
And and = (And)constraint;
Constraint left = replaceSubqueriesWithBindVariables(context, and.left(), subqueriesByVariableName);
Constraint right = replaceSubqueriesWithBindVariables(context, and.right(), subqueriesByVariableName);
if (left == and.left() && right == and.right()) return and;
return new And(left, right);
}
if (constraint instanceof Or) {
Or or = (Or)constraint;
Constraint left = replaceSubqueriesWithBindVariables(context, or.left(), subqueriesByVariableName);
Constraint right = replaceSubqueriesWithBindVariables(context, or.right(), subqueriesByVariableName);
if (left == or.left() && right == or.right()) return or;
return new Or(left, right);
}
if (constraint instanceof Not) {
Not not = (Not)constraint;
Constraint wrapped = replaceSubqueriesWithBindVariables(context, not.getConstraint(), subqueriesByVariableName);
if (wrapped == not.getConstraint()) return not;
return new Not(wrapped);
}
if (constraint instanceof SameNode) {
return constraint;
}
if (constraint instanceof ChildNode) {
return constraint;
}
if (constraint instanceof DescendantNode) {
return constraint;
}
if (constraint instanceof PropertyExistence) {
return constraint;
}
if (constraint instanceof FullTextSearch) {
return constraint;
}
if (constraint instanceof Between) {
Between between = (Between)constraint;
DynamicOperand lhs = between.getOperand();
StaticOperand lower = between.getLowerBound(); // Current only a literal; therefore, no reference to selector
StaticOperand upper = between.getUpperBound(); // Current only a literal; therefore, no reference to selector
StaticOperand newLower = replaceSubqueriesWithBindVariables(context, lower, subqueriesByVariableName);
StaticOperand newUpper = replaceSubqueriesWithBindVariables(context, upper, subqueriesByVariableName);
if (lower == newLower && upper == newUpper) return between;
return new Between(lhs, newLower, newUpper, between.isLowerBoundIncluded(), between.isUpperBoundIncluded());
}
if (constraint instanceof Comparison) {
Comparison comparison = (Comparison)constraint;
DynamicOperand lhs = comparison.getOperand1();
StaticOperand rhs = comparison.getOperand2(); // Current only a literal; therefore, no reference to selector
StaticOperand newRhs = replaceSubqueriesWithBindVariables(context, rhs, subqueriesByVariableName);
if (rhs == newRhs) return comparison;
return new Comparison(lhs, comparison.operator(), newRhs);
}
if( constraint instanceof Relike) {
Relike relike = (Relike)constraint;
StaticOperand op1 = relike.getOperand1();
PropertyValue op2 = relike.getOperand2();
StaticOperand newOp1 = replaceSubqueriesWithBindVariables(context, op1, subqueriesByVariableName);
if(op1 == newOp1) return relike;
return new Relike(op1, op2);
}
if (constraint instanceof SetCriteria) {
SetCriteria criteria = (SetCriteria)constraint;
DynamicOperand lhs = criteria.leftOperand();
boolean foundSubquery = false;
List newStaticOperands = new LinkedList();
for (StaticOperand rhs : criteria.rightOperands()) {
StaticOperand newRhs = replaceSubqueriesWithBindVariables(context, rhs, subqueriesByVariableName);
newStaticOperands.add(newRhs);
if (rhs != newRhs) {
foundSubquery = true;
}
}
if (!foundSubquery) return criteria;
return new SetCriteria(lhs, newStaticOperands);
}
return constraint;
}
public static StaticOperand replaceSubqueriesWithBindVariables( QueryContext context,
StaticOperand staticOperand,
Map subqueriesByVariableName ) {
if (staticOperand instanceof Subquery) {
Subquery subquery = (Subquery)staticOperand;
// Create a variable name ...
int i = 1;
String variableName = Subquery.VARIABLE_PREFIX;
while (context.getVariables().containsKey(variableName + i)) {
++i;
}
variableName = variableName + i;
subqueriesByVariableName.put(variableName, subquery);
context.getVariables().put(variableName, null);
// Replace with a variable substitution ...
return new BindVariableName(variableName);
}
return staticOperand;
}
public static PlanNode addMissingProjectColumns( QueryContext context,
PlanNode node,
List allProjectedColumns ) {
PlanNode project = node.findAtOrBelow(Type.PROJECT);
if (project == null) return node;
PlanNode source = node.findAtOrBelow(Type.SOURCE);
List sourceColumns = source.getPropertyAsList(Property.SOURCE_COLUMNS, Schemata.Column.class);
List projected = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
List projectedTypes = project.getPropertyAsList(Property.PROJECT_COLUMN_TYPES, String.class);
Set projectedPropertyNames = new HashSet();
for (Column p : projected) {
projectedPropertyNames.add(p.getPropertyName());
}
String defaultTypeName = context.getTypeSystem().getDefaultType();
List newSourceColumns = new ArrayList(sourceColumns);
boolean added = false;
for (Column expected : allProjectedColumns) {
if (!project.getSelectors().contains(expected.selectorName())) {
// The column does not belong to this project node ...
continue;
}
if (projectedPropertyNames.contains(expected.getPropertyName())) continue;
// Add the column ...
projected.add(expected);
projectedTypes.add(defaultTypeName);
// Now add a Schemata column for the missing column ...
Schemata.Column sourceColumn = new AbsentColumn(expected.getPropertyName(), defaultTypeName);
newSourceColumns.add(sourceColumn);
added = true;
}
if (added) {
source.setProperty(Property.SOURCE_COLUMNS, newSourceColumns);
}
return node;
}
protected static class AbsentColumn extends ImmutableColumn {
protected AbsentColumn( String name,
String type ) {
super(name, type);
}
}
}