use of org.voltdb.types.PlanNodeType in project voltdb by VoltDB.
the class InlineOrderByIntoMergeReceive method applyOptimization.
/**
* For MP queries, the coordinator's OrderBy node can be replaced with
* a specialized Receive node that merges individual partitions results
* into a final result set if the partitions result set is sorted
* in the order matching the ORDER BY order
*
* @param orderbyNode - ORDER BY node to optimize
* @return optimized plan
*/
AbstractPlanNode applyOptimization(OrderByPlanNode orderbyNode) {
// Find all child RECEIVE nodes. We are not interested in the MERGERECEIVE nodes there
// because they could only come from subqueries.
List<AbstractPlanNode> receives = orderbyNode.findAllNodesOfType(PlanNodeType.RECEIVE);
if (receives.isEmpty()) {
return orderbyNode;
}
assert (receives.size() == 1);
ReceivePlanNode receive = (ReceivePlanNode) receives.get(0);
// Make sure that this receive node belongs to the same coordinator fragment that
// the ORDER BY node does. Alternatively, it could belong to a distributed subquery.
// Walk up the tree starting at the receive node until we hit either a scan node
// (distributed subquery) or the original order by node (distributed order by)
// Collect all nodes that are currently in between ORDER BY and RECEIVE nodes
// If the optimization is possible, they will be converted to inline nodes of
// the MERGE RECEIVE node. The expected node types are:
// LIMIT, AGGREGATE/PARTIALAGGREGATE/HASHAGGREGATE
// The HASHAGGREGATE must be convertible to AGGREGATE or PARTIALAGGREGATE for optimization
// to be applicable.
// LIMIT can be already inline with ORDER BY node
AbstractPlanNode limitNode = orderbyNode.getInlinePlanNode(PlanNodeType.LIMIT);
AbstractPlanNode aggregateNode = null;
AbstractPlanNode inlineCandidate = receive.getParent(0);
while (orderbyNode != inlineCandidate) {
if (inlineCandidate instanceof AbstractScanPlanNode) {
// it's a subquery
return orderbyNode;
}
PlanNodeType nodeType = inlineCandidate.getPlanNodeType();
if (nodeType == PlanNodeType.LIMIT && limitNode == null) {
limitNode = inlineCandidate;
} else if ((nodeType == PlanNodeType.AGGREGATE || nodeType == PlanNodeType.PARTIALAGGREGATE) && aggregateNode == null) {
aggregateNode = inlineCandidate;
} else if (nodeType == PlanNodeType.HASHAGGREGATE && aggregateNode == null) {
aggregateNode = convertToSerialAggregation(inlineCandidate, orderbyNode);
if (PlanNodeType.HASHAGGREGATE == aggregateNode.getPlanNodeType()) {
return orderbyNode;
}
} else {
// Don't know how to handle this node or there is already a node of this type
return orderbyNode;
}
// move up one node
assert (inlineCandidate.getParentCount() == 1);
inlineCandidate = inlineCandidate.getParent(0);
}
assert (receive.getChildCount() == 1);
AbstractPlanNode partitionRoot = receive.getChild(0);
if (!partitionRoot.isOutputOrdered(orderbyNode.getSortExpressions(), orderbyNode.getSortDirections())) {
// Partition results are not ordered
return orderbyNode;
}
// the new MERGERECIEVE node.. All in-between nodes will be inlined
assert (orderbyNode.getParentCount() <= 1);
AbstractPlanNode rootNode = (orderbyNode.getParentCount() == 1) ? orderbyNode.getParent(0) : null;
MergeReceivePlanNode mergeReceive = new MergeReceivePlanNode();
assert (receive.getChildCount() == 1);
mergeReceive.addAndLinkChild(receive.getChild(0));
receive.removeFromGraph();
if (rootNode == null) {
rootNode = mergeReceive;
} else {
rootNode.clearChildren();
rootNode.addAndLinkChild(mergeReceive);
}
// Add inline ORDER BY node and remove inline LIMIT node if any
mergeReceive.addInlinePlanNode(orderbyNode);
if (limitNode != null) {
orderbyNode.removeInlinePlanNode(PlanNodeType.LIMIT);
}
// Add inline aggregate
if (aggregateNode != null) {
if (limitNode != null) {
// Inline LIMIT with aggregate
aggregateNode.addInlinePlanNode(limitNode);
}
mergeReceive.addInlinePlanNode(aggregateNode);
}
// Add LIMIT if it is exist and wasn't inline with aggregate node
if (limitNode != null && aggregateNode == null) {
mergeReceive.addInlinePlanNode(limitNode);
}
// return the new root
return rootNode;
}
use of org.voltdb.types.PlanNodeType in project voltdb by VoltDB.
the class testPlannerTester method testLoadJoinType.
public void testLoadJoinType() throws FileNotFoundException {
AbstractPlanNode pn = null;
pn = compile("select * from l, t where l.b=t.b limit ?;");
plannerTester.setUpForTest(m_currentDir + "tests/frontend/org/voltdb/planner/testplans-plannerTester-ddl.sql", "testplans-plannerTester-ddl");
System.out.println(pn.toExplainPlanString());
System.out.println(pn.toJSONString());
plannerTester.writePlanToFile(pn, m_homeDir, "prettyJson.txt", "");
ArrayList<String> getsql = new ArrayList<>();
AbstractPlanNode pn2 = plannerTester.loadPlanFromFile(m_homeDir + "prettyJson.txt", getsql);
System.out.println(pn2.toExplainPlanString());
ArrayList<AbstractPlanNode> list1 = pn.getPlanNodeList();
ArrayList<AbstractPlanNode> list2 = pn2.getPlanNodeList();
assertTrue(list1.size() == list2.size());
for (int i = 0; i < list1.size(); i++) {
Map<PlanNodeType, AbstractPlanNode> inlineNodes1 = list1.get(i).getInlinePlanNodes();
Map<PlanNodeType, AbstractPlanNode> inlineNodes2 = list2.get(i).getInlinePlanNodes();
if (inlineNodes1 != null) {
assertTrue(inlineNodes1.size() == inlineNodes2.size());
}
}
}
use of org.voltdb.types.PlanNodeType in project voltdb by VoltDB.
the class PlannerTestCase method assertLeftChain.
/**
* Assert that a plan's left-most branch is made up of plan nodes of
* expected classes.
* @param expectedClasses a list of expected AbstractPlanNode classes
* @param actualPlan the top of a plan node tree expected to have instances
* of the expected classes along its left-most branch
* listed from top to bottom.
*/
protected static void assertLeftChain(AbstractPlanNode start, PlanNodeType... nodeTypes) {
AbstractPlanNode pn = start;
for (PlanNodeType type : nodeTypes) {
assertFalse("Child node(s) are missing from the actual plan chain.", pn == null);
if (!type.equals(pn.getPlanNodeType())) {
fail("Expecting plan node of type " + type + ", " + "instead found " + pn.getPlanNodeType() + ".");
}
pn = (pn.getChildCount() > 0) ? pn.getChild(0) : null;
}
assertTrue("Actual plan chain was longer than expected", pn == null);
}
use of org.voltdb.types.PlanNodeType in project voltdb by VoltDB.
the class PlannerTestCase method assertTopDownTree.
/**
* Assert that a plan node tree contains the expected types of plan nodes
* in the order listed, assuming a top-down left-to-right depth-first
* traversal through the child vector. A null plan node type in the list
* will match any plan node or subtree at the corresponding position.
**/
protected static void assertTopDownTree(AbstractPlanNode start, PlanNodeType... nodeTypes) {
Stack<AbstractPlanNode> stack = new Stack<>();
stack.push(start);
for (PlanNodeType type : nodeTypes) {
// Process each node before its children or later siblings.
AbstractPlanNode parent;
try {
parent = stack.pop();
} catch (EmptyStackException ese) {
fail("No node was found in the tree to match node type " + type);
// This dead code hushes warnings.
return;
}
int childCount = parent.getChildCount();
if (type == null) {
// A null type wildcard matches any child TREE or NODE.
System.out.println("DEBUG: Suggestion -- expect " + parent.getPlanNodeType() + " with " + childCount + " direct children.");
continue;
}
assertEquals(type, parent.getPlanNodeType());
// Iterate from the last child to the first.
while (childCount > 0) {
// Push each child to be processed before its parent's
// or its own later (already pushed) siblings.
stack.push(parent.getChild(--childCount));
}
}
assertTrue("Extra plan node(s) (" + stack.size() + ") were found in the tree with no node type to match", stack.isEmpty());
}
use of org.voltdb.types.PlanNodeType in project voltdb by VoltDB.
the class AbstractPlanNode method toJSONString.
public void toJSONString(JSONStringer stringer) throws JSONException {
stringer.keySymbolValuePair(Members.ID.name(), m_id);
stringer.keySymbolValuePair(Members.PLAN_NODE_TYPE.name(), getPlanNodeType().toString());
if (m_inlineNodes.size() > 0) {
PlanNodeType[] types = new PlanNodeType[m_inlineNodes.size()];
int i = 0;
for (PlanNodeType type : m_inlineNodes.keySet()) {
types[i++] = type;
}
Arrays.sort(types);
stringer.key(Members.INLINE_NODES.name()).array();
for (PlanNodeType type : types) {
AbstractPlanNode node = m_inlineNodes.get(type);
assert (node != null);
stringer.value(node);
}
stringer.endArray();
}
if (m_children.size() > 0) {
stringer.key(Members.CHILDREN_IDS.name()).array();
for (AbstractPlanNode node : m_children) {
stringer.value(node.getPlanNodeId().intValue());
}
stringer.endArray();
}
outputSchemaToJSON(stringer);
}
Aggregations