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

org.modeshape.jcr.query.optimize.AddJoinConditionColumnsToSources 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.LinkedList;
import java.util.List;
import org.modeshape.jcr.query.QueryContext;
import org.modeshape.jcr.query.model.Column;
import org.modeshape.jcr.query.model.EquiJoinCondition;
import org.modeshape.jcr.query.model.JoinCondition;
import org.modeshape.jcr.query.model.SelectorName;
import org.modeshape.jcr.query.plan.PlanNode;
import org.modeshape.jcr.query.plan.PlanNode.Property;
import org.modeshape.jcr.query.plan.PlanNode.Type;

/**
 * An {@link OptimizerRule} that adds any missing columns required by the join conditions to the appropriate join source.
 */
public class AddJoinConditionColumnsToSources implements OptimizerRule {

    public static final AddJoinConditionColumnsToSources INSTANCE = new AddJoinConditionColumnsToSources();

    @Override
    public PlanNode execute( QueryContext context,
                             PlanNode plan,
                             LinkedList ruleStack ) {
        final boolean includeSourceName = context.getHints().qualifyExpandedColumnNames;

        // For each of the JOIN nodes ...
        for (PlanNode joinNode : plan.findAllAtOrBelow(Type.JOIN)) {
            JoinCondition condition = joinNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
            if (condition instanceof EquiJoinCondition) {
                EquiJoinCondition equiJoinCondition = (EquiJoinCondition)condition;
                SelectorName selector1 = equiJoinCondition.selector1Name();
                Column joinColumn1 = columnFor(equiJoinCondition.selector1Name(),
                                               equiJoinCondition.getProperty1Name(),
                                               includeSourceName);
                Column joinColumn2 = columnFor(equiJoinCondition.selector2Name(),
                                               equiJoinCondition.getProperty2Name(),
                                               includeSourceName);

                // Figure out which side of the join condition goes with which side of the plan nodes ...
                PlanNode left = joinNode.getFirstChild();
                PlanNode right = joinNode.getLastChild();
                if (left.getSelectors().contains(selector1)) {
                    addEquiJoinColumn(context, left, joinColumn1);
                    addEquiJoinColumn(context, right, joinColumn2);
                } else {
                    addEquiJoinColumn(context, left, joinColumn2);
                    addEquiJoinColumn(context, right, joinColumn1);
                }
            }

        }
        return plan;
    }

    /**
     * Make sure that the supplied column is included in the {@link Property#PROJECT_COLUMNS projected columns} on the supplied
     * plan node or its children.
     * 
     * @param context the query context; may not be null
     * @param node the query plan node
     * @param joinColumn the column required by the join
     */
    protected void addEquiJoinColumn( QueryContext context,
                                      PlanNode node,
                                      Column joinColumn ) {
        if (node.getSelectors().contains(joinColumn.selectorName())) {
            // Get the existing projected columns ...
            List columns = node.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
            List types = node.getPropertyAsList(Property.PROJECT_COLUMN_TYPES, String.class);
            if (columns != null && addIfMissing(context, joinColumn, columns, types)) {
                node.setProperty(Property.PROJECT_COLUMNS, columns);
                node.setProperty(Property.PROJECT_COLUMN_TYPES, types);
            }
        }

        // Apply recursively ...
        for (PlanNode child : node) {
            addEquiJoinColumn(context, child, joinColumn);
        }
    }

    /**
     * Check the supplied list of columns for an existing column that matches the supplied {@link Column}, and if none is found
     * add the supplied Column to the list and add an appropriate type.
     * 
     * @param context the query context
     * @param column the column that will be added if not already in the list; may not be null
     * @param columns the list of columns; may not be null
     * @param columnTypes the list of column types; may not be null
     * @return true if the column was added (i.e., the lists were modified), or false if the lists were not modified
     */
    protected boolean addIfMissing( QueryContext context,
                                    Column column,
                                    List columns,
                                    List columnTypes ) {
        for (Column c : columns) {
            if (!c.selectorName().equals(column.selectorName())) continue;
            String cName = c.getPropertyName();
            if (cName.equals(column.getPropertyName()) || cName.equals(column.getColumnName())) return false;
            cName = c.getColumnName();
            if (cName.equals(column.getPropertyName()) || cName.equals(column.getColumnName())) return false;
        }
        columns.add(column);
        columnTypes.add(context.getTypeSystem().getDefaultType());
        return true;
    }

    protected Column columnFor( SelectorName selector,
                                String property,
                                boolean includeSourceName ) {
        String columnName = includeSourceName ? selector.getString() + "." + property : property;
        return new Column(selector, property, columnName);
    }

    @Override
    public String toString() {
        return getClass().getSimpleName();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy