use of org.voltdb.plannodes.IndexUseForOrderBy 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.IndexUseForOrderBy in project voltdb by VoltDB.
the class PlanAssembler method handleWindowedOperators.
/**
* Create nodes for windowed operations.
*
* @param root
* @return
*/
private AbstractPlanNode handleWindowedOperators(AbstractPlanNode root) {
// Get the windowed expression. We need to set its output
// schema from the display list.
WindowFunctionExpression winExpr = m_parsedSelect.getWindowFunctionExpressions().get(0);
assert (winExpr != null);
// This will set the output schema to contain the
// windowed schema column only. In generateOutputSchema
// we will add the input columns.
WindowFunctionPlanNode pnode = new WindowFunctionPlanNode();
pnode.setWindowFunctionExpression(winExpr);
// We always need an order by plan node, even if the sort
// is optimized away by an index. This may be turned
// into an inline order by in a MergeReceivePlanNode.
IndexUseForOrderBy scanNode = findScanNodeForWindowFunction(root);
AbstractPlanNode cnode = null;
int winfunc = (scanNode == null) ? SubPlanAssembler.NO_INDEX_USE : scanNode.getWindowFunctionUsesIndex();
// statement level order by ordering.
if ((SubPlanAssembler.STATEMENT_LEVEL_ORDER_BY_INDEX == winfunc) || (SubPlanAssembler.NO_INDEX_USE == winfunc)) {
// No index. Calculate the expression order here and stuff it into
// the order by node. Note that if we support more than one window
// function this would be the case when scanNode.getWindowFunctionUsesIndex()
// returns a window function number which is different from the number
// of winExpr.
List<AbstractExpression> partitionByExpressions = winExpr.getPartitionByExpressions();
// If the order by expression list contains a partition by expression then
// we won't have to sort by it twice. We sort by the partition by expressions
// first, and we don't care what order we sort by them. So, find the
// sort direction in the order by list and use that in the partition by
// list, and then mark that it was deleted in the order by
// list.
//
// We choose to make this dontsort rather than dosort because the
// Java default value for boolean is false, and we want to sort by
// default.
boolean[] dontsort = new boolean[winExpr.getOrderbySize()];
List<AbstractExpression> orderByExpressions = winExpr.getOrderByExpressions();
List<SortDirectionType> orderByDirections = winExpr.getOrderByDirections();
OrderByPlanNode onode = new OrderByPlanNode();
for (int idx = 0; idx < winExpr.getPartitionbySize(); ++idx) {
SortDirectionType pdir = SortDirectionType.ASC;
AbstractExpression partitionByExpression = partitionByExpressions.get(idx);
int sidx = winExpr.getSortIndexOfOrderByExpression(partitionByExpression);
if (0 <= sidx) {
pdir = orderByDirections.get(sidx);
dontsort[sidx] = true;
}
onode.addSort(partitionByExpression, pdir);
}
for (int idx = 0; idx < winExpr.getOrderbySize(); ++idx) {
if (!dontsort[idx]) {
AbstractExpression orderByExpr = orderByExpressions.get(idx);
SortDirectionType orderByDir = orderByDirections.get(idx);
onode.addSort(orderByExpr, orderByDir);
}
}
onode.addAndLinkChild(root);
cnode = onode;
} else {
assert (scanNode != null);
// inline order by node of a MergeReceive node.
assert (0 == scanNode.getWindowFunctionUsesIndex());
if (m_partitioning.requiresTwoFragments()) {
OrderByPlanNode onode = new OrderByPlanNode();
SortDirectionType dir = scanNode.getSortOrderFromIndexScan();
assert (dir != SortDirectionType.INVALID);
// This was created when the index was determined.
// We cached it in the scan node.
List<AbstractExpression> orderExprs = scanNode.getFinalExpressionOrderFromIndexScan();
assert (orderExprs != null);
for (AbstractExpression ae : orderExprs) {
onode.addSort(ae, dir);
}
// Link in the OrderByNode.
onode.addAndLinkChild(root);
cnode = onode;
} else {
// Don't create and link in the order by node.
cnode = root;
}
}
pnode.addAndLinkChild(cnode);
return pnode;
}
use of org.voltdb.plannodes.IndexUseForOrderBy 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.IndexUseForOrderBy 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