package edu.brown.optimizer.optimizations;
import java.util.Collection;
import java.util.List;
import org.apache.log4j.Logger;
import org.voltdb.catalog.Table;
import org.voltdb.plannodes.AbstractJoinPlanNode;
import org.voltdb.plannodes.AbstractPlanNode;
import org.voltdb.plannodes.AbstractScanPlanNode;
import org.voltdb.plannodes.ProjectionPlanNode;
import org.voltdb.plannodes.ReceivePlanNode;
import org.voltdb.plannodes.SendPlanNode;
import org.voltdb.utils.Pair;
import edu.brown.catalog.CatalogUtil;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.optimizer.PlanOptimizerState;
import edu.brown.plannodes.PlanNodeUtil;
import edu.brown.utils.CollectionUtil;
public class RemoveDistributedReplicatedTableJoinOptimization extends AbstractOptimization {
private static final Logger LOG = Logger.getLogger(RemoveDistributedReplicatedTableJoinOptimization.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
public RemoveDistributedReplicatedTableJoinOptimization(PlanOptimizerState state) {
super(state);
}
@Override
public Pair<Boolean, AbstractPlanNode> optimize(final AbstractPlanNode rootNode) {
// Skip single-partition query plans
if (PlanNodeUtil.isDistributedQuery(rootNode) == false) {
if (debug.val)
LOG.debug("SKIP - Not a distributed query plan");
return (Pair.of(false, rootNode));
}
// Walk through the query plan tree and see if there is a join
// where the outer table is replicated but it is being
// scanned in a separate PlanFragment
boolean modified = false;
for (AbstractJoinPlanNode join_node : PlanNodeUtil.getPlanNodes(rootNode, AbstractJoinPlanNode.class)) {
if (debug.val)
LOG.debug("Examining " + join_node);
// Check whether the chain below this node is
// RECIEVE -> SEND -> SCAN
assert (join_node.getChildPlanNodeCount() >= 1);
AbstractPlanNode child = join_node.getChild(0);
if (debug.val)
LOG.debug(join_node + " -> " + child);
if (child instanceof ReceivePlanNode) {
ReceivePlanNode recv_node = (ReceivePlanNode) child;
SendPlanNode send_node = (SendPlanNode) child.getChild(0);
List<AbstractPlanNode> children = send_node.getChildren();
assert (children != null);
if (children.size() > 1)
continue;
if (children.get(0).getChildPlanNodeCount() > 0)
continue;
if ((children.get(0) instanceof AbstractScanPlanNode) == false)
continue;
AbstractPlanNode leaf_node = children.get(0);
assert (leaf_node instanceof AbstractScanPlanNode);
Collection<Table> leaf_tables = CatalogUtil.getReferencedTablesForPlanNode(state.catalog_db, leaf_node);
assert (leaf_tables.size() == 1);
Table leaf_tbl = CollectionUtil.first(leaf_tables);
Collection<Table> join_tables = CatalogUtil.getReferencedTablesForPlanNode(state.catalog_db, join_node);
Table join_tbl = null;
for (Table catalog_tbl : join_tables) {
if (catalog_tbl.equals(leaf_tbl) == false) {
join_tbl = catalog_tbl;
break;
}
} // FOR
assert (join_tbl != null);
// If either table is replicated, then the leaf scan should be linked
// directly with the Join PlanNode
if (join_tbl.getIsreplicated() || leaf_tbl.getIsreplicated()) {
AbstractPlanNode parent = join_node.getParent(0);
assert (parent != null);
parent.clearChildren();
parent.addAndLinkChild(recv_node);
state.markDirty(parent);
state.markDirty(recv_node);
join_node.clearParents();
join_node.clearChildren();
leaf_node.clearParents();
join_node.addAndLinkChild(leaf_node);
state.markDirty(join_node);
state.markDirty(leaf_node);
// HACK: If the parent is a ProjectionPlanNode, then we'll want
// to duplicate it so that we make sure that our original
// SEND/RECIEVE nodes have the right offsets
if (parent instanceof ProjectionPlanNode) {
AbstractPlanNode parent_clone = null;
try {
parent_clone = (AbstractPlanNode) parent.clone(false, false);
} catch (CloneNotSupportedException ex) {
throw new RuntimeException(ex);
}
assert (parent_clone != null);
assert (parent != parent_clone);
parent_clone.addAndLinkChild(join_node);
send_node.clearChildren();
send_node.addAndLinkChild(parent_clone);
} else {
send_node.clearChildren();
send_node.addAndLinkChild(join_node);
}
state.markDirty(send_node);
modified = true;
}
}
} // FOR
return (Pair.of(modified, rootNode));
}
}