/* This file is part of VoltDB. * Copyright (C) 2008-2010 VoltDB L.L.C. * * VoltDB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VoltDB 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb.planner.microoptimizations; import java.util.ArrayList; import java.util.List; import org.voltdb.planner.CompiledPlan; import org.voltdb.plannodes.AbstractPlanNode; import org.voltdb.plannodes.DistinctPlanNode; import org.voltdb.types.PlanNodeType; public class PushdownReceiveDominators implements MicroOptimization { @Override public List<CompiledPlan> apply(CompiledPlan plan) { ArrayList<CompiledPlan> retval = new ArrayList<CompiledPlan>(); AbstractPlanNode planGraph = plan.fragments.get(0).planGraph; planGraph.calculateDominators(); List<AbstractPlanNode> receiveNodes = planGraph.findAllNodesOfType(PlanNodeType.RECEIVE); for (AbstractPlanNode pn : receiveNodes) { if (processReceiveNode(pn)) { // could make the graph transformation more complex // to avoid this recalculation; however, we expect graphs to // be relatively small and the total set of transformations // performed to also be small, making the cost of recalculation // an okay trade-off v. the complexity of a more efficient // implementation planGraph.calculateDominators(); } } // modified plan in place retval.add(plan); return retval; } /** * @param receive * @return true of a transformation requiring recalculation * of the dominator state occurred. */ private boolean processReceiveNode(AbstractPlanNode receive) { boolean modifiedGraph = false; // walk the dominators for the receive node and move them // after the receive/send pair as possible for (AbstractPlanNode pn : receive.getDominators()) { if (pn.getPlanNodeType() == PlanNodeType.DISTINCT) { modifiedGraph = pushdownDistinct(receive, pn) || modifiedGraph; } } return modifiedGraph; } /** * If a RECEIVE is dominated by a DISTINCT, that DISTINCT can be executed * by the remote partition. If the DISTINCT includes a unique key, the * dominating DISTINCT can be fully removed. Otherwise, it must remain as * a post filter on possibly duplicate data from the children. * * @param receive RECEIVE node being pushed past * @param distinct DISTINCT node transformed * @return */ private boolean pushdownDistinct(AbstractPlanNode receive, AbstractPlanNode distinct) { // distinct must be an immediate parent of receive if (distinct.hasChild(receive) == false) return false; // distinct must have a single parent. AbstractPlanNode distinct_parent = distinct.getParent(0); if (distinct.getParentPlanNodeCount() > 1) return false; // receive must have a send child AbstractPlanNode send = receive.getChild(0); if (send.getPlanNodeType() != PlanNodeType.SEND) { assert(false) : "receive without send child?"; return false; } // passes requirements to transform! // TODO: Determine if distinct.getDistinctColumnIndex() is a uniquely-valued column boolean distinct_on_unique_column = false; if (distinct_on_unique_column) { distinct.removeFromGraph(); distinct_parent.addAndLinkChild(receive); send.addIntermediary(distinct); } else { assert(distinct.isInline() == false); DistinctPlanNode new_distinct = ((DistinctPlanNode)distinct).produceCopyForTransformation(); send.addIntermediary(new_distinct); } return true; } }