Search in sources :

Example 41 with AbstractPlanNode

use of org.voltdb.plannodes.AbstractPlanNode 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;
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) AbstractExpression(org.voltdb.expressions.AbstractExpression) OrderByPlanNode(org.voltdb.plannodes.OrderByPlanNode) WindowFunctionPlanNode(org.voltdb.plannodes.WindowFunctionPlanNode) WindowFunctionExpression(org.voltdb.expressions.WindowFunctionExpression) SortDirectionType(org.voltdb.types.SortDirectionType) Constraint(org.voltdb.catalog.Constraint) IndexUseForOrderBy(org.voltdb.plannodes.IndexUseForOrderBy)

Example 42 with AbstractPlanNode

use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.

the class QueryPlanner method compileFromXML.

/**
     * Find the best plan given the VoltXMLElement.  By best here we mean the plan
     * which is scored the best according to our plan metric scoring.  The plan
     * metric scoring takes into account join order and index use, but it does
     * not take into account the output schema.  Consequently, we don't compute the
     * output schema for the plan nodes until after the best plan is discovered.
     *
     * The order here is:
     * <ol>
     * <li>
     *   Parse the VoltXMLElement to create an AbstractParsedStatement.  This has
     *   a second effect of loading lists of join orders and access paths for planning.
     *   For us, and access path is a way of scanning something scannable.  It's a generalization
     *   of the notion of scanning a table or an index.
     * </li>
     * <li>
     *   Create a PlanAssembler, and ask it for the best cost plan.  This uses the
     *   side data created by the parser in the previous step.
     * </li>
     * <li>
     *   If the plan is read only, slap a SendPlanNode on the front.  Presumably
     *   an insert, delete or upsert will have added the SendPlanNode into the plan node tree already.
     * </li>
     * <li>
     *   Compute the output schema.  This computes the output schema for each
     *   node recursively, using a node specific method.
     * </li>
     * <li>
     *   Resolve the column indices.  This makes sure that the indices of all
     *   TVEs in the output columns refer to the right input columns.
     * </li>
     * <li>
     *   Do some final cleaning up and verifying of the plan.  For example,
     *   We renumber the nodes starting at 1.
     * </li>
     * </ol>
     *
     * @param xmlSQL
     * @param paramValues
     * @return
     */
private CompiledPlan compileFromXML(VoltXMLElement xmlSQL, String[] paramValues) {
    // Get a parsed statement from the xml
    // The callers of compilePlan are ready to catch any exceptions thrown here.
    AbstractParsedStmt parsedStmt = AbstractParsedStmt.parse(m_sql, xmlSQL, paramValues, m_db, m_joinOrder);
    if (parsedStmt == null) {
        m_recentErrorMsg = "Failed to parse SQL statement: " + getOriginalSql();
        return null;
    }
    if (m_isUpsert) {
        // no insert/upsert with joins
        if (parsedStmt.m_tableList.size() != 1) {
            m_recentErrorMsg = "UPSERT is supported only with one single table: " + getOriginalSql();
            return null;
        }
        Table tb = parsedStmt.m_tableList.get(0);
        Constraint pkey = null;
        for (Constraint ct : tb.getConstraints()) {
            if (ct.getType() == ConstraintType.PRIMARY_KEY.getValue()) {
                pkey = ct;
                break;
            }
        }
        if (pkey == null) {
            m_recentErrorMsg = "Unsupported UPSERT table without primary key: " + getOriginalSql();
            return null;
        }
    }
    m_planSelector.outputParsedStatement(parsedStmt);
    // Init Assembler. Each plan assembler requires a new instance of the PlanSelector
    // to keep track of the best plan
    PlanAssembler assembler = new PlanAssembler(m_db, m_partitioning, (PlanSelector) m_planSelector.clone());
    // find the plan with minimal cost
    CompiledPlan bestPlan = assembler.getBestCostPlan(parsedStmt);
    // make sure we got a winner
    if (bestPlan == null) {
        if (m_debuggingStaticModeToRetryOnError) {
            assembler.getBestCostPlan(parsedStmt);
        }
        m_recentErrorMsg = assembler.getErrorMessage();
        if (m_recentErrorMsg == null) {
            m_recentErrorMsg = "Unable to plan for statement. Error unknown.";
        }
        return null;
    }
    if (bestPlan.isReadOnly()) {
        SendPlanNode sendNode = new SendPlanNode();
        // connect the nodes to build the graph
        sendNode.addAndLinkChild(bestPlan.rootPlanGraph);
        // this plan is final, generate schema and resolve all the column index references
        bestPlan.rootPlanGraph = sendNode;
    }
    // Execute the generateOutputSchema and resolveColumnIndexes once for the best plan
    bestPlan.rootPlanGraph.generateOutputSchema(m_db);
    bestPlan.rootPlanGraph.resolveColumnIndexes();
    if (parsedStmt instanceof ParsedSelectStmt) {
        List<SchemaColumn> columns = bestPlan.rootPlanGraph.getOutputSchema().getColumns();
        ((ParsedSelectStmt) parsedStmt).checkPlanColumnMatch(columns);
    }
    // Output the best plan debug info
    assembler.finalizeBestCostPlan();
    // reset all the plan node ids for a given plan
    // this makes the ids deterministic
    bestPlan.resetPlanNodeIds(1);
    // split up the plan everywhere we see send/receive into multiple plan fragments
    List<AbstractPlanNode> receives = bestPlan.rootPlanGraph.findAllNodesOfClass(AbstractReceivePlanNode.class);
    if (receives.size() > 1) {
        // Have too many receive node for two fragment plan limit
        m_recentErrorMsg = "This join of multiple partitioned tables is too complex. " + "Consider simplifying its subqueries: " + getOriginalSql();
        return null;
    }
    /*/ enable for debug ...
        if (receives.size() > 1) {
            System.out.println(plan.rootPlanGraph.toExplainPlanString());
        }
        // ... enable for debug */
    if (receives.size() == 1) {
        AbstractReceivePlanNode recvNode = (AbstractReceivePlanNode) receives.get(0);
        fragmentize(bestPlan, recvNode);
    }
    return bestPlan;
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) AbstractReceivePlanNode(org.voltdb.plannodes.AbstractReceivePlanNode) Table(org.voltdb.catalog.Table) Constraint(org.voltdb.catalog.Constraint) SendPlanNode(org.voltdb.plannodes.SendPlanNode) SchemaColumn(org.voltdb.plannodes.SchemaColumn)

Example 43 with AbstractPlanNode

use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.

the class QueryPlanner method replaceInsertPlanNodeWithUpsert.

public static AbstractPlanNode replaceInsertPlanNodeWithUpsert(AbstractPlanNode root) {
    if (root == null) {
        return null;
    }
    List<AbstractPlanNode> inserts = root.findAllNodesOfType(PlanNodeType.INSERT);
    if (inserts.size() == 1) {
        InsertPlanNode insertNode = (InsertPlanNode) inserts.get(0);
        insertNode.setUpsert(true);
    }
    return root;
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) InsertPlanNode(org.voltdb.plannodes.InsertPlanNode)

Example 44 with AbstractPlanNode

use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.

the class PlanAssembler method isValidAggregateNodeForLimitPushdown.

private static boolean isValidAggregateNodeForLimitPushdown(AbstractPlanNode aggregateNode, List<ParsedColInfo> orderBys, boolean orderByCoversAllGroupBy) {
    if (aggregateNode instanceof AggregatePlanNode == false) {
        return false;
    }
    if (aggregateNode.getParentCount() == 0) {
        return false;
    }
    // Limitation: can only push past coordinating aggregation nodes
    if (!((AggregatePlanNode) aggregateNode).m_isCoordinatingAggregator) {
        return false;
    }
    AbstractPlanNode parent = aggregateNode.getParent(0);
    AbstractPlanNode orderByNode = null;
    if (parent instanceof OrderByPlanNode) {
        orderByNode = parent;
    } else if (parent instanceof ProjectionPlanNode && parent.getParentCount() > 0 && parent.getParent(0) instanceof OrderByPlanNode) {
        // Xin really wants inline project with aggregation
        orderByNode = parent.getParent(0);
    }
    if (orderByNode == null) {
        // the limit should not be pushed down.
        return false;
    }
    if ((!orderByCoversAllGroupBy) || isOrderByAggregationValue(orderBys)) {
        return false;
    }
    return true;
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) HashAggregatePlanNode(org.voltdb.plannodes.HashAggregatePlanNode) AggregatePlanNode(org.voltdb.plannodes.AggregatePlanNode) PartialAggregatePlanNode(org.voltdb.plannodes.PartialAggregatePlanNode) OrderByPlanNode(org.voltdb.plannodes.OrderByPlanNode) ProjectionPlanNode(org.voltdb.plannodes.ProjectionPlanNode)

Example 45 with AbstractPlanNode

use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.

the class TestColumnTrimmingPlans method testEng585Plan.

public void testEng585Plan() {
    AbstractPlanNode pn = null;
    pn = compile("select max(s.int2) as foo from s, t where s.s_pk = t.s_pk and t.t_pk1 = ?;");
    // TODO: To actually detect ENG-585 regression, we'd have to program a check that the join is
    // materializing narrow rows of fewer than 5 columns. Yeah.
    System.out.println(pn.toJSONString());
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode)

Aggregations

AbstractPlanNode (org.voltdb.plannodes.AbstractPlanNode)259 AbstractExpression (org.voltdb.expressions.AbstractExpression)55 IndexScanPlanNode (org.voltdb.plannodes.IndexScanPlanNode)48 ProjectionPlanNode (org.voltdb.plannodes.ProjectionPlanNode)48 AbstractScanPlanNode (org.voltdb.plannodes.AbstractScanPlanNode)46 SeqScanPlanNode (org.voltdb.plannodes.SeqScanPlanNode)44 AggregatePlanNode (org.voltdb.plannodes.AggregatePlanNode)37 NestLoopPlanNode (org.voltdb.plannodes.NestLoopPlanNode)37 HashAggregatePlanNode (org.voltdb.plannodes.HashAggregatePlanNode)29 OrderByPlanNode (org.voltdb.plannodes.OrderByPlanNode)29 ReceivePlanNode (org.voltdb.plannodes.ReceivePlanNode)27 SendPlanNode (org.voltdb.plannodes.SendPlanNode)27 MergeReceivePlanNode (org.voltdb.plannodes.MergeReceivePlanNode)20 AbstractReceivePlanNode (org.voltdb.plannodes.AbstractReceivePlanNode)16 NestLoopIndexPlanNode (org.voltdb.plannodes.NestLoopIndexPlanNode)16 SchemaColumn (org.voltdb.plannodes.SchemaColumn)15 NodeSchema (org.voltdb.plannodes.NodeSchema)14 UnionPlanNode (org.voltdb.plannodes.UnionPlanNode)14 LimitPlanNode (org.voltdb.plannodes.LimitPlanNode)12 PlanNodeType (org.voltdb.types.PlanNodeType)12