use of org.voltdb.plannodes.AbstractJoinPlanNode in project voltdb by VoltDB.
the class PlanAssembler method isOrderByNodeRequired.
/**
* Determine if an OrderByPlanNode is needed. This may return false if the
* statement has no ORDER BY clause, or if the subtree is already producing
* rows in the correct order. Note that a hash aggregate node will cause this
* to return true, and a serial or partial aggregate node may cause this
* to return true.
*
* @param parsedStmt The statement whose plan may need an OrderByPlanNode
* @param root The subtree which may need its output tuples ordered
* @return true if the plan needs an OrderByPlanNode, false otherwise
*/
private static boolean isOrderByNodeRequired(AbstractParsedStmt parsedStmt, AbstractPlanNode root) {
// Only sort when the statement has an ORDER BY.
if (!parsedStmt.hasOrderByColumns()) {
return false;
}
// Skip the explicit ORDER BY plan step if an IndexScan is already providing the equivalent ordering.
// Note that even tree index scans that produce values in their own "key order" only report
// their sort direction != SortDirectionType.INVALID
// when they enforce an ordering equivalent to the one requested in the ORDER BY
// or window function clause. Even an intervening non-hash aggregate will not interfere
// in this optimization.
// Is there a window function between the root and the
// scan or join nodes? Also, does this window function
// use the index.
int numberWindowFunctions = 0;
int numberReceiveNodes = 0;
int numberHashAggregates = 0;
// EE keeps the insertion ORDER so that ORDER BY could apply before DISTINCT.
// However, this probably is not optimal if there are low cardinality results.
// Again, we have to replace the TVEs for ORDER BY clause for these cases in planning.
//
// Find the scan or join node.
AbstractPlanNode probe;
for (probe = root; !((probe instanceof AbstractJoinPlanNode) || (probe instanceof AbstractScanPlanNode)) && (probe != null); probe = (probe.getChildCount() > 0) ? probe.getChild(0) : null) {
// we will have recorded it in the scan or join node.
if (probe.getPlanNodeType() == PlanNodeType.WINDOWFUNCTION) {
numberWindowFunctions += 1;
}
// needs them.
if (probe.getPlanNodeType() == PlanNodeType.RECEIVE) {
numberReceiveNodes += 1;
}
// the ordering, but a serial aggregation does not.
if ((probe.getPlanNodeType() == PlanNodeType.HASHAGGREGATE) || (probe.getPlanNodeType() == PlanNodeType.PARTIALAGGREGATE)) {
numberHashAggregates += 1;
}
}
if (probe == null) {
// to be right. Maybe this should be an assert?
return true;
}
//
if (!(probe instanceof IndexSortablePlanNode)) {
return true;
}
IndexUseForOrderBy indexUse = ((IndexSortablePlanNode) probe).indexUse();
if (indexUse.getSortOrderFromIndexScan() == SortDirectionType.INVALID) {
return true;
}
// an ORDERBY node.
if (numberHashAggregates > 0) {
return true;
}
if (numberWindowFunctions == 0) {
if (indexUse.getWindowFunctionUsesIndex() == SubPlanAssembler.NO_INDEX_USE) {
return true;
}
assert (indexUse.getWindowFunctionUsesIndex() == SubPlanAssembler.STATEMENT_LEVEL_ORDER_BY_INDEX);
// false for SP (numberReceiveNodes == 0);
return numberReceiveNodes > 0;
}
if (numberWindowFunctions == 1) {
// will return 0.
if ((indexUse.getWindowFunctionUsesIndex() != 0) || (!indexUse.isWindowFunctionCompatibleWithOrderBy())) {
return true;
}
// does not need one. So this is a false.
return false;
}
// because we only support one window function.
return true;
}
use of org.voltdb.plannodes.AbstractJoinPlanNode in project voltdb by VoltDB.
the class PlanAssembler method handleMVBasedMultiPartQuery.
private AbstractPlanNode handleMVBasedMultiPartQuery(HashAggregatePlanNode reAggNode, AbstractPlanNode root, boolean edgeCaseOuterJoin) {
MaterializedViewFixInfo mvFixInfo = m_parsedSelect.m_mvFixInfo;
AbstractPlanNode receiveNode = root;
AbstractPlanNode reAggParent = null;
// re-aggregation plan node.
if (root instanceof AbstractReceivePlanNode) {
root = reAggNode;
} else {
List<AbstractPlanNode> recList = root.findAllNodesOfClass(AbstractReceivePlanNode.class);
assert (recList.size() == 1);
receiveNode = recList.get(0);
reAggParent = receiveNode.getParent(0);
boolean result = reAggParent.replaceChild(receiveNode, reAggNode);
assert (result);
}
reAggNode.addAndLinkChild(receiveNode);
reAggNode.m_isCoordinatingAggregator = true;
assert (receiveNode instanceof ReceivePlanNode);
AbstractPlanNode sendNode = receiveNode.getChild(0);
assert (sendNode instanceof SendPlanNode);
AbstractPlanNode sendNodeChild = sendNode.getChild(0);
HashAggregatePlanNode reAggNodeForReplace = null;
if (m_parsedSelect.m_tableList.size() > 1 && !edgeCaseOuterJoin) {
reAggNodeForReplace = reAggNode;
}
boolean find = mvFixInfo.processScanNodeWithReAggNode(sendNode, reAggNodeForReplace);
assert (find);
// receive node with materialized view scan node.
if (m_parsedSelect.m_tableList.size() > 1 && !edgeCaseOuterJoin) {
AbstractPlanNode joinNode = sendNodeChild;
// No agg, limit pushed down at this point.
assert (joinNode instanceof AbstractJoinPlanNode);
// Fix the node after Re-aggregation node.
joinNode.clearParents();
assert (mvFixInfo.m_scanNode != null);
mvFixInfo.m_scanNode.clearParents();
// replace joinNode with MV scan node on each partition.
sendNode.clearChildren();
sendNode.addAndLinkChild(mvFixInfo.m_scanNode);
// its parent will be the parent of the new join node. Update the root node.
if (reAggParent != null) {
reAggParent.replaceChild(reAggNode, joinNode);
root = reAggParent;
} else {
root = joinNode;
}
}
return root;
}
Aggregations