Search in sources :

Example 1 with IndexSortablePlanNode

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;
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) IndexSortablePlanNode(org.voltdb.plannodes.IndexSortablePlanNode) OrderByPlanNode(org.voltdb.plannodes.OrderByPlanNode) SendPlanNode(org.voltdb.plannodes.SendPlanNode) ReceivePlanNode(org.voltdb.plannodes.ReceivePlanNode) MergeReceivePlanNode(org.voltdb.plannodes.MergeReceivePlanNode) MergeReceivePlanNode(org.voltdb.plannodes.MergeReceivePlanNode)

Example 2 with IndexSortablePlanNode

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;
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) IndexSortablePlanNode(org.voltdb.plannodes.IndexSortablePlanNode) StmtSubqueryScan(org.voltdb.planner.parseinfo.StmtSubqueryScan) BranchNode(org.voltdb.planner.parseinfo.BranchNode) SubqueryLeafNode(org.voltdb.planner.parseinfo.SubqueryLeafNode) IndexUseForOrderBy(org.voltdb.plannodes.IndexUseForOrderBy)

Example 3 with IndexSortablePlanNode

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;
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) IndexSortablePlanNode(org.voltdb.plannodes.IndexSortablePlanNode) AbstractScanPlanNode(org.voltdb.plannodes.AbstractScanPlanNode) AbstractJoinPlanNode(org.voltdb.plannodes.AbstractJoinPlanNode) Constraint(org.voltdb.catalog.Constraint) IndexUseForOrderBy(org.voltdb.plannodes.IndexUseForOrderBy)

Example 4 with IndexSortablePlanNode

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;
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) IndexSortablePlanNode(org.voltdb.plannodes.IndexSortablePlanNode) AbstractExpression(org.voltdb.expressions.AbstractExpression) IndexScanPlanNode(org.voltdb.plannodes.IndexScanPlanNode) MaterializedScanPlanNode(org.voltdb.plannodes.MaterializedScanPlanNode) AbstractSubqueryExpression(org.voltdb.expressions.AbstractSubqueryExpression) Index(org.voltdb.catalog.Index) IndexUseForOrderBy(org.voltdb.plannodes.IndexUseForOrderBy)

Aggregations

AbstractPlanNode (org.voltdb.plannodes.AbstractPlanNode)4 IndexSortablePlanNode (org.voltdb.plannodes.IndexSortablePlanNode)4 IndexUseForOrderBy (org.voltdb.plannodes.IndexUseForOrderBy)3 Constraint (org.voltdb.catalog.Constraint)1 Index (org.voltdb.catalog.Index)1 AbstractExpression (org.voltdb.expressions.AbstractExpression)1 AbstractSubqueryExpression (org.voltdb.expressions.AbstractSubqueryExpression)1 BranchNode (org.voltdb.planner.parseinfo.BranchNode)1 StmtSubqueryScan (org.voltdb.planner.parseinfo.StmtSubqueryScan)1 SubqueryLeafNode (org.voltdb.planner.parseinfo.SubqueryLeafNode)1 AbstractJoinPlanNode (org.voltdb.plannodes.AbstractJoinPlanNode)1 AbstractScanPlanNode (org.voltdb.plannodes.AbstractScanPlanNode)1 IndexScanPlanNode (org.voltdb.plannodes.IndexScanPlanNode)1 MaterializedScanPlanNode (org.voltdb.plannodes.MaterializedScanPlanNode)1 MergeReceivePlanNode (org.voltdb.plannodes.MergeReceivePlanNode)1 OrderByPlanNode (org.voltdb.plannodes.OrderByPlanNode)1 ReceivePlanNode (org.voltdb.plannodes.ReceivePlanNode)1 SendPlanNode (org.voltdb.plannodes.SendPlanNode)1