use of org.voltdb.plannodes.IndexSortablePlanNode in project voltdb by VoltDB.
the class InlineOrderByIntoMergeReceive method applyOptimization.
/**
* Convert ReceivePlanNodes into MergeReceivePlanNodes when the
* RECEIVE node's nearest parent is a window function. We won't
* have any inline limits or aggregates here, so this is somewhat
* simpler than the order by case.
*
* @param plan
* @return
*/
private AbstractPlanNode applyOptimization(WindowFunctionPlanNode plan) {
assert (plan.getChildCount() == 1);
assert (plan.getChild(0) != null);
AbstractPlanNode child = plan.getChild(0);
assert (child != null);
// an order by node.
if (!(child instanceof OrderByPlanNode)) {
return plan;
}
OrderByPlanNode onode = (OrderByPlanNode) child;
child = onode.getChild(0);
// for this optimization to work.
if (!(child instanceof ReceivePlanNode)) {
return plan;
}
ReceivePlanNode receiveNode = (ReceivePlanNode) child;
assert (receiveNode.getChildCount() == 1);
child = receiveNode.getChild(0);
// The Receive node needs a send node child.
assert (child instanceof SendPlanNode);
SendPlanNode sendNode = (SendPlanNode) child;
child = sendNode.getChild(0);
// returns the number in the plan node.
if (!(child instanceof IndexSortablePlanNode)) {
return plan;
}
IndexSortablePlanNode indexed = (IndexSortablePlanNode) child;
if (indexed.indexUse().getWindowFunctionUsesIndex() != 0) {
return plan;
}
// Remove the Receive node and the Order by node
// and replace them with a MergeReceive node. Leave
// the order by node inline in the MergeReceive node,
// since we need it to calculate the merge.
plan.clearChildren();
receiveNode.removeFromGraph();
MergeReceivePlanNode mrnode = new MergeReceivePlanNode();
mrnode.addInlinePlanNode(onode);
mrnode.addAndLinkChild(sendNode);
plan.addAndLinkChild(mrnode);
return plan;
}
use of org.voltdb.plannodes.IndexSortablePlanNode in project voltdb by VoltDB.
the class SelectSubPlanAssembler method getSelectSubPlanForJoinNode.
/**
* Given a specific join node and access path set for inner and outer tables, construct the plan
* that gives the right tuples.
*
* @param joinNode The join node to build the plan for.
* @param isInnerTable True if the join node is the inner node in the join
* @return A completed plan-sub-graph that should match the correct tuples from the
* correct tables.
*/
private AbstractPlanNode getSelectSubPlanForJoinNode(JoinNode joinNode) {
assert (joinNode != null);
if (joinNode instanceof BranchNode) {
BranchNode branchJoinNode = (BranchNode) joinNode;
// Outer node
AbstractPlanNode outerScanPlan = getSelectSubPlanForJoinNode(branchJoinNode.getLeftNode());
if (outerScanPlan == null) {
return null;
}
// Inner Node.
AbstractPlanNode innerScanPlan = getSelectSubPlanForJoinNode((branchJoinNode).getRightNode());
if (innerScanPlan == null) {
return null;
}
// Join Node
IndexSortablePlanNode answer = getSelectSubPlanForJoin(branchJoinNode, outerScanPlan, innerScanPlan);
// branch node is an inner join.
if ((answer != null) && (branchJoinNode.getJoinType() == JoinType.INNER) && outerScanPlan instanceof IndexSortablePlanNode) {
IndexUseForOrderBy indexUseForJoin = answer.indexUse();
IndexUseForOrderBy indexUseFromScan = ((IndexSortablePlanNode) outerScanPlan).indexUse();
indexUseForJoin.setWindowFunctionUsesIndex(indexUseFromScan.getWindowFunctionUsesIndex());
indexUseForJoin.setWindowFunctionIsCompatibleWithOrderBy(indexUseFromScan.isWindowFunctionCompatibleWithOrderBy());
indexUseForJoin.setFinalExpressionOrderFromIndexScan(indexUseFromScan.getFinalExpressionOrderFromIndexScan());
indexUseForJoin.setSortOrderFromIndexScan(indexUseFromScan.getSortOrderFromIndexScan());
}
if (answer == null) {
return null;
}
return answer.planNode();
}
// End of recursion
AbstractPlanNode scanNode = getAccessPlanForTable(joinNode);
// Connect the sub-query tree if any
if (joinNode instanceof SubqueryLeafNode) {
StmtSubqueryScan tableScan = ((SubqueryLeafNode) joinNode).getSubqueryScan();
CompiledPlan subQueryPlan = tableScan.getBestCostPlan();
assert (subQueryPlan != null);
assert (subQueryPlan.rootPlanGraph != null);
// The sub-query best cost plan needs to be un-linked from the previous parent plan
// it's the same child plan that gets re-attached to many parents one at a time
subQueryPlan.rootPlanGraph.disconnectParents();
scanNode.addAndLinkChild(subQueryPlan.rootPlanGraph);
}
return scanNode;
}
use of org.voltdb.plannodes.IndexSortablePlanNode 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.IndexSortablePlanNode in project voltdb by VoltDB.
the class SubPlanAssembler method getIndexAccessPlanForTable.
/**
* Get an index scan access plan for a table.
*
* @param tableAliasIndex The table to get data from.
* @param path The access path to access the data in the table (index/scan/etc).
* @return An index scan plan node OR,
in one edge case, an NLIJ of a MaterializedScan and an index scan plan node.
*/
private static AbstractPlanNode getIndexAccessPlanForTable(StmtTableScan tableScan, AccessPath path) {
// now assume this will be an index scan and get the relevant index
Index index = path.index;
IndexScanPlanNode scanNode = new IndexScanPlanNode(tableScan, index);
AbstractPlanNode resultNode = scanNode;
// set sortDirection here because it might be used for IN list
scanNode.setSortDirection(path.sortDirection);
// the one element of indexExprs.
for (AbstractExpression expr : path.indexExprs) {
if (path.lookupType == IndexLookupType.GEO_CONTAINS) {
scanNode.addSearchKeyExpression(expr);
scanNode.addCompareNotDistinctFlag(false);
continue;
}
AbstractExpression exprRightChild = expr.getRight();
assert (exprRightChild != null);
if (expr.getExpressionType() == ExpressionType.COMPARE_IN) {
// Replace this method's result with an injected NLIJ.
resultNode = injectIndexedJoinWithMaterializedScan(exprRightChild, scanNode);
// Extract a TVE from the LHS MaterializedScan for use by the IndexScan in its new role.
MaterializedScanPlanNode matscan = (MaterializedScanPlanNode) resultNode.getChild(0);
AbstractExpression elemExpr = matscan.getOutputExpression();
assert (elemExpr != null);
// Replace the IN LIST condition in the end expression referencing all the list elements
// with a more efficient equality filter referencing the TVE for each element in turn.
replaceInListFilterWithEqualityFilter(path.endExprs, exprRightChild, elemExpr);
// Set up the similar VectorValue --> TVE replacement of the search key expression.
exprRightChild = elemExpr;
}
if (exprRightChild instanceof AbstractSubqueryExpression) {
// DEAD CODE with the guards on index: ENG-8203
assert (false);
}
scanNode.addSearchKeyExpression(exprRightChild);
// If the index expression is an "IS NOT DISTINCT FROM" comparison, let the NULL values go through. (ENG-11096)
scanNode.addCompareNotDistinctFlag(expr.getExpressionType() == ExpressionType.COMPARE_NOTDISTINCT);
}
// create the IndexScanNode with all its metadata
scanNode.setLookupType(path.lookupType);
scanNode.setBindings(path.bindings);
scanNode.setEndExpression(ExpressionUtil.combinePredicates(path.endExprs));
scanNode.setPredicate(path.otherExprs);
// Propagate the sorting information
// into the scan node from the access path.
// The initial expression is needed to control a (short?) forward scan to adjust the start of a reverse
// iteration after it had to initially settle for starting at "greater than a prefix key".
scanNode.setInitialExpression(ExpressionUtil.combinePredicates(path.initialExpr));
scanNode.setSkipNullPredicate();
scanNode.setEliminatedPostFilters(path.eliminatedPostExprs);
if (scanNode instanceof IndexSortablePlanNode) {
IndexUseForOrderBy indexUse = ((IndexSortablePlanNode) scanNode).indexUse();
indexUse.setWindowFunctionUsesIndex(path.m_windowFunctionUsesIndex);
indexUse.setSortOrderFromIndexScan(path.sortDirection);
indexUse.setWindowFunctionIsCompatibleWithOrderBy(path.m_stmtOrderByIsCompatible);
indexUse.setFinalExpressionOrderFromIndexScan(path.m_finalExpressionOrder);
}
return resultNode;
}
Aggregations