Package edu.brown.optimizer.optimizations

Source Code of edu.brown.optimizer.optimizations.ProjectionPushdownOptimization

package edu.brown.optimizer.optimizations;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.collections15.set.ListOrderedSet;
import org.apache.log4j.Logger;
import org.voltdb.catalog.Table;
import org.voltdb.expressions.AbstractExpression;
import org.voltdb.expressions.TupleValueExpression;
import org.voltdb.planner.PlanAssembler;
import org.voltdb.planner.PlanColumn;
import org.voltdb.plannodes.AbstractPlanNode;
import org.voltdb.plannodes.AbstractScanPlanNode;
import org.voltdb.plannodes.NestLoopIndexPlanNode;
import org.voltdb.plannodes.ProjectionPlanNode;
import org.voltdb.plannodes.SendPlanNode;
import org.voltdb.types.PlanNodeType;
import org.voltdb.utils.Pair;

import edu.brown.expressions.ExpressionUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.optimizer.PlanOptimizerState;
import edu.brown.optimizer.PlanOptimizerUtil;
import edu.brown.plannodes.PlanNodeTreeWalker;
import edu.brown.plannodes.PlanNodeUtil;
import edu.brown.utils.CollectionUtil;
import edu.brown.utils.StringUtil;

/**
* @author pavlo
*/
public class ProjectionPushdownOptimization extends AbstractOptimization {
    private static final Logger LOG = Logger.getLogger(ProjectionPushdownOptimization.class);
    private static final LoggerBoolean debug = new LoggerBoolean();

    private final Set<PlanColumn> new_output_cols = new ListOrderedSet<PlanColumn>();
    private final Set<Table> new_output_tables = new HashSet<Table>();
    private final AtomicBoolean modified = new AtomicBoolean(false);
   
    public ProjectionPushdownOptimization(PlanOptimizerState state) {
        super(state);
    }

    @Override
    public Pair<Boolean, AbstractPlanNode> optimize(final AbstractPlanNode root) {
        modified.set(false);
        new PlanNodeTreeWalker(false) {
            @Override
            protected void callback(AbstractPlanNode element) {
                // ---------------------------------------------------
                // ADD INLINE PROJECTION TO LEAF SCANS
                // ---------------------------------------------------
                if (element.getChildPlanNodeCount() == 0 && element instanceof AbstractScanPlanNode) {
                    if (element.getInlinePlanNode(PlanNodeType.PROJECTION) != null) {
                        if (debug.val)
                            LOG.debug("SKIP - " + element + " already has an inline ProjectionPlanNode");
                    } else {
                        try {
                            if (addProjection(element, true) == false) {
                                this.stop();
                                return;
                            }
                        } catch (Throwable ex) {
                            throw new RuntimeException("Failed to add projection for " + element, ex);
                        }
                        modified.set(true);
                    }
                }
                // ---------------------------------------------------
                // Distributed NestLoopIndexPlanNode
                // This is where the NestLoopIndexPlanNode immediately passes
                // its intermediate results to a SendPlanNode
                // ---------------------------------------------------
                else if (element instanceof NestLoopIndexPlanNode && element.getParent(0) instanceof SendPlanNode) {
                    assert (state.join_node_index.size() == state.join_tbl_mapping.size()) : "Join data structures don't have the same size";
                    if (state.join_tbl_mapping.get(element) == null)
                        LOG.error("BUSTED:\n" + state);
                    assert (state.join_tbl_mapping.get(element) != null) : element + " does NOT exist in join map";

                    try {
                        if (addProjection(element, false) == false) {
                            this.stop();
                            return;
                        }
                    } catch (Throwable ex) {
                        throw new RuntimeException("Failed to add projection for " + element, ex);
                    }
                    modified.set(true);
                }
            }
        }.traverse(root);
        return (Pair.of(modified.get(), root));
    }

    private boolean addProjection(final AbstractPlanNode node, boolean inline) {
        assert ((node instanceof ProjectionPlanNode) == false) : String.format("Trying to add a new Projection after " + node);

        // Look at the output columns of the given AbstractPlanNode and figure out
        // which of those columns we're going to need up above in the tree
        Set<PlanColumn> referenced = PlanOptimizerUtil.extractReferencedColumns(state, node);
        if (debug.val)
            LOG.debug(String.format("All referenced columns above %s:\n%s", node, StringUtil.join("\n", referenced)));

        // Look over the PlanColumns that we need above us, and add the ones that
        // are coming out of the parent. Those are the ones that we want to include
        new_output_cols.clear();
        new_output_tables.clear();
        for (Integer col_guid : node.getOutputColumnGUIDs()) {
            PlanColumn col = state.plannerContext.get(col_guid);
            assert (col != null) : "Invalid PlanColumn #" + col_guid;
            for (PlanColumn ref_col : referenced) {
                if (ref_col.equals(col, true, true)) {
                    new_output_cols.add(col);
                    new_output_tables.addAll(ExpressionUtil.getReferencedTables(state.catalog_db, col.getExpression()));
                    break;
                }
            }
        } // FOR
        if (new_output_cols.isEmpty())
            LOG.warn("BUSTED:\n" + PlanNodeUtil.debug(PlanNodeUtil.getRoot(node)) + "\n" + state);
        assert (new_output_cols.isEmpty() == false) : String.format("No new output columns for " + node);

        // Check whether the output PlanColumns all reference the same table,
        // and we have the same number of PlanColumns as that table has in the
        // catalog
        // This means that we don't need the projection because they're doing a
        // SELECT *
        if (new_output_tables.size() == 1) {
            Table catalog_tbl = CollectionUtil.first(new_output_tables);
            assert (catalog_tbl != null);
            // Only create the projection if the number of columns we need to
            // output is less then the total number of columns for the table
            if (new_output_cols.size() == catalog_tbl.getColumns().size()) {
                if (debug.val)
                    LOG.debug("SKIP - All columns needed in query. No need for inline projection on " + catalog_tbl);
                return (false);
            }
        }

        // Now create a new ProjectionPlanNode that outputs just those columns
        ProjectionPlanNode proj_node = new ProjectionPlanNode(state.plannerContext, PlanAssembler.getNextPlanNodeId());
        this.populateProjectionPlanNode(node, proj_node, new_output_cols);

        // Add a projection above this node
        // TODO: Add the ability to automatically check whether we can make
        // something inline
        if (inline) {
            // Add projection inline to scan node
            node.addInlinePlanNode(proj_node);
            assert (proj_node.isInline());

            // Then make sure that we update it's output columns to match the
            // inline output
            node.getOutputColumnGUIDs().clear();
            node.getOutputColumnGUIDs().addAll(proj_node.getOutputColumnGUIDs());
        } else {
            AbstractPlanNode parent = node.getParent(0);
            assert (parent != null);
            node.clearParents();
            parent.clearChildren();
            proj_node.addAndLinkChild(node);
            parent.addAndLinkChild(proj_node);
        }

        // Mark the new ProjectionPlanNode as dirty
        state.markDirty(proj_node);
        state.markDirty(node);

        if (debug.val)
            LOG.debug(String.format("PLANOPT - Added %s%s with %d columns for node %s", (inline ? "inline " : ""), proj_node, proj_node.getOutputColumnGUIDCount(), node));

        return (true);
    }

    private void populateProjectionPlanNode(AbstractPlanNode parent, ProjectionPlanNode proj_node, Collection<PlanColumn> output_columns) {
        if (debug.val)
            LOG.debug(String.format("Populating %s with %d output PlanColumns", proj_node, output_columns.size()));

        AbstractExpression orig_col_exp = null;
        for (PlanColumn plan_col : output_columns) {
            int orig_guid = -1;
            for (Integer guid : parent.getOutputColumnGUIDs()) {
                PlanColumn output_plan_column = state.plannerContext.get(guid);
                if (plan_col.equals(output_plan_column, true, true)) {
                    orig_col_exp = output_plan_column.getExpression();
                    orig_guid = guid;
                    break;
                }
            }
            if (orig_guid == -1) {
                LOG.warn(String.format("Failed to find PlanColumn %s in %s output columns?", plan_col, parent));
            } else {
                assert (orig_col_exp != null);
                AbstractExpression new_col_exp = null;
                try {
                    new_col_exp = (AbstractExpression) orig_col_exp.clone();
                } catch (CloneNotSupportedException ex) {
                    throw new RuntimeException(ex);
                }
                assert (new_col_exp != null);
                PlanColumn new_plan_col = null;
                if (new_col_exp instanceof TupleValueExpression) {
                    new_plan_col = new PlanColumn(orig_guid, new_col_exp, ((TupleValueExpression) new_col_exp).getColumnName(), plan_col.getSortOrder(), plan_col.getStorage());
                    proj_node.appendOutputColumn(new_plan_col);
                    if (debug.val)
                        LOG.debug("Added " + new_plan_col + " to " + proj_node);
                } else if (debug.val) {
                    LOG.debug("Skipped adding " + new_plan_col + " to " + proj_node + "?");
                }
            }
        } // FOR
    }

}
TOP

Related Classes of edu.brown.optimizer.optimizations.ProjectionPushdownOptimization

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.