/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb.planner.microoptimizations;
import java.util.ArrayList;
import org.voltdb.plannodes.AbstractJoinPlanNode;
import org.voltdb.plannodes.AbstractPlanNode;
import org.voltdb.plannodes.AbstractScanPlanNode;
import org.voltdb.plannodes.AggregatePlanNode;
import org.voltdb.plannodes.LimitPlanNode;
import org.voltdb.plannodes.ProjectionPlanNode;
public class PushdownLimits extends MicroOptimization {
@Override
protected AbstractPlanNode recursivelyApply(AbstractPlanNode plan)
{
assert(plan != null);
// depth first:
// find LimitPlanNodes with exactly one child
// where that child is an AbstractScanPlanNode
// disconnect the LimitPlanNode
// and inline the LimitPlanNode in to the AbstractScanPlanNode
ArrayList<AbstractPlanNode> children = new ArrayList<AbstractPlanNode>();
for (int i = 0; i < plan.getChildCount(); i++)
children.add(plan.getChild(i));
plan.clearChildren();
for (AbstractPlanNode child : children) {
// TODO this will break when children feed multiple parents
child = recursivelyApply(child);
child.clearParents();
plan.addAndLinkChild(child);
}
if ( ! (plan instanceof LimitPlanNode)) {
return plan;
}
if (plan.getChildCount() != 1) {
assert(plan.getChildCount() == 1);
return plan;
}
AbstractPlanNode child = plan.getChild(0);
// push into Scans
if (child instanceof AbstractScanPlanNode) {
// scan node can not have inline aggregation because ee apply scan limit first
// in future, this limit can be aggregate inline node.
if (AggregatePlanNode.getInlineAggregationNode(child) != null) {
return plan;
}
plan.clearChildren();
child.clearParents();
child.addInlinePlanNode(plan);
return child;
}
// push down through Projection
// Replace the chain plan/limit . child/projection . leaf/whatever
// with recursivelyApply(child/projection . plan/limit . leaf/whatever)
// == child/projection . recursivelyApply(plan/limit . leaf/whatever)
if (child instanceof ProjectionPlanNode) {
assert (child.getChildCount() == 1);
AbstractPlanNode leaf = child.getChild(0);
leaf.clearParents();
plan.clearChildren();
plan.addAndLinkChild(leaf);
child.clearChildren();
child.clearParents();
child.addAndLinkChild(plan);
return recursivelyApply(child);
}
// push into JOINs
if (child instanceof AbstractJoinPlanNode) {
plan.clearChildren();
child.clearParents();
child.addInlinePlanNode(plan);
// TODO: ENG-5399 for LEFT OUTER join with no post-filter, can also push a modified
// preliminary pushdown-style limit+offset limit node to the left child.
// AbstractJoinPlanNode ajpn = (AbstractJoinPlanNode)child;
// if (ajpn.getWherePredicate() == null && ajpn.getJoinType() == JoinType.LEFT) {
// AbstractPlanNode leaf = ajpn.getChild(0);
// leaf.clearParents();
// LimitPlanNode copy = new LimitPlanNode();
// copy.set... (See ParsedSelectStmt as an example).
// copy.addAndLinkChild(leaf);
// // push down further in the left child if it's a scan or a join
// AbstractPlanNode limited = recursivelyApply(copy);
// ajpn.replaceChild(leaf, limited);
// }
return child;
}
return plan;
}
}