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

org.modeshape.jcr.query.optimize.ReplaceViews Maven / Gradle / Ivy

There is a newer version: 5.4.1.Final
Show newest version
/*
 * ModeShape (http://www.modeshape.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.modeshape.jcr.query.optimize;

import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.modeshape.common.annotation.Immutable;
import org.modeshape.jcr.query.QueryContext;
import org.modeshape.jcr.query.model.Column;
import org.modeshape.jcr.query.model.SelectorName;
import org.modeshape.jcr.query.plan.CanonicalPlanner;
import org.modeshape.jcr.query.plan.PlanNode;
import org.modeshape.jcr.query.plan.PlanNode.Property;
import org.modeshape.jcr.query.plan.PlanNode.Type;
import org.modeshape.jcr.query.plan.PlanUtil;
import org.modeshape.jcr.query.validate.Schemata;
import org.modeshape.jcr.query.validate.Schemata.Table;
import org.modeshape.jcr.query.validate.Schemata.View;

/**
 * An {@link OptimizerRule optimizer rule} that replaces any SOURCE nodes that happen to be {@link View views}. This rewriting
 * changes all of the elements of the plan that reference the SOURCE and it's columns, including criteria, project nodes, etc.
 * 

* For example, here is the portion of a plan that uses a single SOURCE that is defined to use a view. * *

 *          ...
 *           |
 *        SOURCE1
 * 
* * This same SOURCE node is then replaced with the view's definition: * *
 *          ...
 *           |
 *        PROJECT      with the list of columns being SELECTed
 *           |
 *        SELECT1
 *           |         One or more SELECT plan nodes that each have
 *        SELECT2      a single non-join constraint that are then all AND-ed
 *           |         together
 *        SELECTn
 *           |
 *        SOURCE
 * 
* *

*/ @Immutable public class ReplaceViews implements OptimizerRule { public static final ReplaceViews INSTANCE = new ReplaceViews(); @Override public PlanNode execute( QueryContext context, PlanNode plan, LinkedList ruleStack ) { CanonicalPlanner planner = new CanonicalPlanner(); // Prepare the maps that will record the old-to-new mappings from the old view SOURCE nodes to the table SOURCEs ... // For each of the SOURCE nodes ... Schemata schemata = context.getSchemata(); Set processedSources = new HashSet(); boolean foundViews = false; do { foundViews = false; for (PlanNode sourceNode : plan.findAllAtOrBelow(Type.SOURCE)) { if (processedSources.contains(sourceNode)) continue; processedSources.add(sourceNode); // Resolve the node to find the definition in the schemata ... SelectorName tableName = sourceNode.getProperty(Property.SOURCE_NAME, SelectorName.class); SelectorName tableAlias = sourceNode.getProperty(Property.SOURCE_ALIAS, SelectorName.class); Table table = schemata.getTable(tableName); if (table instanceof View) { View view = (View)table; PlanNode viewPlan = planner.createPlan(context, view.getDefinition()); if (viewPlan == null) continue; // there were likely errors when creating the plan // If the view doesn't have an alias, or if the view's alias doesn't match the table's name/alias ... PlanNode viewProjectNode = viewPlan.findAtOrBelow(Type.PROJECT); if (viewProjectNode.getSelectors().size() == 1) { SelectorName tableAliasOrName = tableAlias != null ? tableAlias : tableName; SelectorName viewAlias = viewProjectNode.getSelectors().iterator().next(); // Replace the view's alias ... Map replacements = Collections.singletonMap(viewAlias, tableAliasOrName); PlanUtil.replaceReferencesToRemovedSource(context, viewPlan, replacements); if (!context.getHints().validateColumnExistance) { // Find the next highest PROJECT node above the source ... PlanNode project = sourceNode.findAncestor(Type.PROJECT); if (project != null) { List projectedColumns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class); // There may be columns that don't appear in the source, so make sure they are there ... viewPlan = PlanUtil.addMissingProjectColumns(context, viewProjectNode, projectedColumns); assert viewPlan != null; } } } // Insert the view plan under the parent SOURCE node ... sourceNode.addLastChild(viewPlan); // Remove the source node ... sourceNode.extractFromParent(); // // Replace the original view's name with the name/alias ... PlanNode parent = viewPlan.getParent(); if (parent != null) { PlanUtil.ColumnMapping aliasMappings = null; if (tableAlias != null) { aliasMappings = PlanUtil.createMappingForAliased(tableAlias, view, viewPlan); PlanUtil.replaceViewReferences(context, parent, aliasMappings); } PlanUtil.ColumnMapping viewMappings = PlanUtil.createMappingFor(view, viewPlan); PlanUtil.replaceViewReferences(context, parent, viewMappings); } if (viewPlan.is(Type.PROJECT)) { // The PROJECT from the plan may actually not be needed if there is another PROJECT above it ... PlanNode node = viewPlan.getParent(); while (node != null) { if (node.isOneOf(Type.JOIN)) break; if (node.is(Type.PROJECT) && viewPlan.getSelectors().containsAll(node.getSelectors())) { viewPlan.extractFromParent(); break; } node = node.getParent(); } } foundViews = true; } } } while (foundViews); if (foundViews) { // We'll need to try to push up criteria from the join, but we only should do this after this rule // is completely done ... if (!(ruleStack.getFirst() instanceof RaiseSelectCriteria)) { ruleStack.addFirst(RaiseSelectCriteria.INSTANCE); ruleStack.addFirst(PushSelectCriteria.INSTANCE); } // We re-wrote at least one SOURCE, but the resulting plan tree for the view could actually reference // other views. Therefore, re-run this rule ... ruleStack.addFirst(this); } return plan; } @Override public String toString() { return getClass().getSimpleName(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy