Search in sources :

Example 11 with IndexScanPlanNode

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

the class TestIndexSelection method checkIndexPredicateContains.

private void checkIndexPredicateContains(AbstractPlanNode pn, String... columns) {
    assertEquals(1, pn.getChildCount());
    pn = pn.getChild(0);
    assertEquals(PlanNodeType.INDEXSCAN, pn.getPlanNodeType());
    IndexScanPlanNode ipn = (IndexScanPlanNode) pn;
    AbstractExpression pred = ipn.getPredicate();
    assertNotNull(pred);
    List<TupleValueExpression> tves = pred.findAllTupleValueSubexpressions();
    for (TupleValueExpression tve : tves) {
        boolean match = false;
        for (String column : columns) {
            if (tve.getColumnName().equals(column)) {
                match = true;
                break;
            }
        }
        assertTrue(match);
    }
}
Also used : TupleValueExpression(org.voltdb.expressions.TupleValueExpression) AbstractExpression(org.voltdb.expressions.AbstractExpression) IndexScanPlanNode(org.voltdb.plannodes.IndexScanPlanNode)

Example 12 with IndexScanPlanNode

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

the class TestPlansCount method testCountStar002.

public void testCountStar002() {
    List<AbstractPlanNode> pn = compileToFragments("SELECT POINTS, count(*) from T1 Group by POINTS");
    AbstractPlanNode p = pn.get(0).getChild(0);
    assertTrue(p instanceof IndexScanPlanNode);
    assertNotNull(p.getInlinePlanNode(PlanNodeType.AGGREGATE));
    pn = compileToFragments("SELECT POINTS, count(1) from T1 Group by POINTS");
    p = pn.get(0).getChild(0);
    assertTrue(p instanceof IndexScanPlanNode);
    assertNotNull(p.getInlinePlanNode(PlanNodeType.AGGREGATE));
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) IndexScanPlanNode(org.voltdb.plannodes.IndexScanPlanNode)

Example 13 with IndexScanPlanNode

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

the class TestJoinOrder method testMicroOptimizationJoinOrder.

public void testMicroOptimizationJoinOrder() {
    // Microoptimization can be used for determinism only when working with replicated tables or
    // single-partition queries.
    List<AbstractPlanNode> pns;
    AbstractPlanNode n;
    pns = compileWithJoinOrderToFragments("select * from J1, P2 where A=B and A=1", "J1, P2");
    n = pns.get(0).getChild(0).getChild(0);
    assertTrue(((IndexScanPlanNode) n.getChild(0)).getTargetTableName().equals("J1"));
    assertTrue(((SeqScanPlanNode) n.getChild(1)).getTargetTableName().equals("P2"));
    pns = compileWithJoinOrderToFragments("select * from I1, T2 where A=B", "I1, T2");
    //* enable to debug */ System.out.println(pns.get(0).toExplainPlanString());
    n = pns.get(0).getChild(0).getChild(0);
    assertTrue(((IndexScanPlanNode) n.getChild(0)).getTargetTableName().equals("I1"));
    assertTrue(((SeqScanPlanNode) n.getChild(1)).getTargetTableName().equals("T2"));
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) SeqScanPlanNode(org.voltdb.plannodes.SeqScanPlanNode) IndexScanPlanNode(org.voltdb.plannodes.IndexScanPlanNode)

Example 14 with IndexScanPlanNode

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

the class PlanAssembler method switchToIndexScanForGroupBy.

/**
     * For a seqscan feeding a GROUP BY, consider substituting an IndexScan
     * that pre-sorts by the GROUP BY keys.
     * If a candidate is already an indexscan,
     * simply calculate GROUP BY column coverage
     *
     * @param candidate
     * @param gbInfo
     * @return true when planner can switch to index scan
     *         from a sequential scan, and when the index scan
     *         has no parent plan node or the candidate is already
     *         an indexscan and covers all or some GROUP BY columns
     */
private boolean switchToIndexScanForGroupBy(AbstractPlanNode candidate, IndexGroupByInfo gbInfo) {
    if (!m_parsedSelect.isGrouped()) {
        return false;
    }
    if (candidate instanceof IndexScanPlanNode) {
        calculateIndexGroupByInfo((IndexScanPlanNode) candidate, gbInfo);
        if (gbInfo.m_coveredGroupByColumns != null && !gbInfo.m_coveredGroupByColumns.isEmpty()) {
            // The candidate index does cover all or some
            // of the GROUP BY columns and can be serialized
            gbInfo.m_indexAccess = candidate;
            return true;
        }
        return false;
    }
    AbstractPlanNode sourceSeqScan = findSeqScanCandidateForGroupBy(candidate);
    if (sourceSeqScan == null) {
        return false;
    }
    assert (sourceSeqScan instanceof SeqScanPlanNode);
    AbstractPlanNode parent = null;
    if (sourceSeqScan.getParentCount() > 0) {
        parent = sourceSeqScan.getParent(0);
    }
    AbstractPlanNode indexAccess = indexAccessForGroupByExprs((SeqScanPlanNode) sourceSeqScan, gbInfo);
    if (indexAccess.getPlanNodeType() != PlanNodeType.INDEXSCAN) {
        // does not find proper index to replace sequential scan
        return false;
    }
    gbInfo.m_indexAccess = indexAccess;
    if (parent != null) {
        // have a parent and would like to replace
        // the sequential scan with an index scan
        indexAccess.clearParents();
        // For two children join node, index 0 is its outer side
        parent.replaceChild(0, indexAccess);
        return false;
    }
    // parent is null and switched to index scan from sequential scan
    return true;
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) SeqScanPlanNode(org.voltdb.plannodes.SeqScanPlanNode) IndexScanPlanNode(org.voltdb.plannodes.IndexScanPlanNode)

Example 15 with IndexScanPlanNode

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

the class SelectSubPlanAssembler method getSelectSubPlanForJoin.

/**
     * Given a join node and plan-sub-graph for outer and inner sub-nodes,
     * construct the plan-sub-graph for that node.
     *
     * @param joinNode A parent join node.
     * @param outerPlan The outer node plan-sub-graph.
     * @param innerPlan The inner node plan-sub-graph.
     * @return A completed plan-sub-graph
     * or null if a valid plan can not be produced for given access paths.
     */
private IndexSortablePlanNode getSelectSubPlanForJoin(BranchNode joinNode, AbstractPlanNode outerPlan, AbstractPlanNode innerPlan) {
    // Filter (post-join) expressions
    ArrayList<AbstractExpression> whereClauses = new ArrayList<>();
    whereClauses.addAll(joinNode.m_whereInnerList);
    whereClauses.addAll(joinNode.m_whereInnerOuterList);
    if (joinNode.getJoinType() == JoinType.FULL) {
        // For all other join types, the whereOuterList expressions were pushed down to the outer node
        whereClauses.addAll(joinNode.m_whereOuterList);
    }
    assert (joinNode.getRightNode() != null);
    JoinNode innerJoinNode = joinNode.getRightNode();
    AccessPath innerAccessPath = innerJoinNode.m_currentAccessPath;
    // We may need to add a send/receive pair to the inner plan for the special case.
    // This trick only works once per plan, BUT once the partitioned data has been
    // received on the coordinator, it can be treated as replicated data in later
    // joins, which MAY help with later outer joins with replicated data.
    boolean needInnerSendReceive = m_partitioning.requiresTwoFragments() && !innerPlan.hasReplicatedResult() && outerPlan.hasReplicatedResult() && joinNode.getJoinType() != JoinType.INNER;
    // When the inner plan is an IndexScan, there MAY be a choice of whether to join using a
    // NestLoopJoin (NLJ) or a NestLoopIndexJoin (NLIJ). The NLJ will have an advantage over the
    // NLIJ in the cases where it applies, since it does a single access or iteration over the index
    // and caches the result, where the NLIJ does an index access or iteration for each outer row.
    // The NestLoopJoin applies when the inner IndexScan is driven only by parameter and constant
    // expressions determined at the start of the query. That requires that none of the IndexScan's
    // various expressions that drive the index access may reference columns from the outer row
    // -- they can only reference columns of the index's base table (the indexed expressions)
    // as well as constants and parameters. The IndexScan's "otherExprs" expressions that only
    // drive post-filtering are not an issue since the NestLoopJoin does feature per-outer-tuple
    // post-filtering on each pass over the cached index scan result.
    // The special case of an OUTER JOIN of replicated outer row data with a partitioned inner
    // table requires that the partitioned data be sent to the coordinator prior to the join.
    // This limits the join option to NLJ. The index scan must make a single index access on
    // each partition and cache the result at the coordinator for post-filtering.
    // This requires that the index access be based on parameters or constants only
    // -- the replicated outer row data will only be available later at the coordinator,
    // so it can not drive the per-partition index scan.
    // If the NLJ option is precluded for the usual reason (outer-row-based indexing) AND
    // the NLIJ is precluded by the special case (OUTER JOIN of replicated outer rows and
    // partitioned inner rows) this method returns null, effectively rejecting this indexed
    // access path for the inner node. Other access paths or join orders may prove more successful.
    boolean canHaveNLJ = true;
    boolean canHaveNLIJ = true;
    if (innerPlan instanceof IndexScanPlanNode) {
        if (hasInnerOuterIndexExpression(joinNode.getRightNode().getTableAlias(), innerAccessPath.indexExprs, innerAccessPath.initialExpr, innerAccessPath.endExprs)) {
            canHaveNLJ = false;
        }
    } else {
        canHaveNLIJ = false;
    }
    if (needInnerSendReceive) {
        canHaveNLIJ = false;
    }
    // partition columns
    if (joinNode.getJoinType() == JoinType.FULL && m_partitioning.requiresTwoFragments() && !outerPlan.hasReplicatedResult() && innerPlan.hasReplicatedResult()) {
        canHaveNLIJ = false;
        canHaveNLJ = false;
    }
    AbstractJoinPlanNode ajNode = null;
    if (canHaveNLJ) {
        NestLoopPlanNode nljNode = new NestLoopPlanNode();
        // get all the clauses that join the applicable two tables
        // Copy innerAccessPath.joinExprs to leave it unchanged,
        // avoiding accumulation of redundant expressions when
        // joinClauses gets built up for various alternative plans.
        ArrayList<AbstractExpression> joinClauses = new ArrayList<>(innerAccessPath.joinExprs);
        if ((innerPlan instanceof IndexScanPlanNode) || (innerPlan instanceof NestLoopIndexPlanNode && innerPlan.getChild(0) instanceof MaterializedScanPlanNode)) {
            // InnerPlan is an IndexScan OR an NLIJ of a MaterializedScan
            // (IN LIST) and an IndexScan. In this case, the inner and
            // inner-outer non-index join expressions (if any) are in the
            // indexScan's otherExpr. The former should stay as IndexScanPlan
            // predicates but the latter need to be pulled up into NLJ
            // predicates because the IndexScan is executed once, not once
            // per outer tuple.
            ArrayList<AbstractExpression> otherExprs = new ArrayList<>();
            // PLEASE do not update the "innerAccessPath.otherExprs", it may be reused
            // for other path evaluation on the other outer side join.
            List<AbstractExpression> innerExpr = filterSingleTVEExpressions(innerAccessPath.otherExprs, otherExprs);
            joinClauses.addAll(otherExprs);
            IndexScanPlanNode scanNode = null;
            if (innerPlan instanceof IndexScanPlanNode) {
                scanNode = (IndexScanPlanNode) innerPlan;
            } else {
                assert (innerPlan instanceof NestLoopIndexPlanNode);
                scanNode = ((NestLoopIndexPlanNode) innerPlan).getInlineIndexScan();
            }
            scanNode.setPredicate(innerExpr);
        } else if (innerJoinNode instanceof BranchNode && joinNode.getJoinType() != JoinType.INNER) {
            // If the innerJoinNode is a LEAF node OR if the join type is an INNER join,
            // the conditions that apply to the inner side
            // have been applied as predicates to the inner scan node already.
            // otherExpr of innerAccessPath comes from its parentNode's joinInnerList.
            // For Outer join (LEFT or FULL), it could mean a join predicate on the table of
            // the inner node ONLY, that can not be pushed down.
            joinClauses.addAll(innerAccessPath.otherExprs);
        }
        nljNode.setJoinPredicate(ExpressionUtil.combinePredicates(joinClauses));
        // combine the tails plan graph with the new head node
        nljNode.addAndLinkChild(outerPlan);
        // right child node.
        if (needInnerSendReceive) {
            // This trick only works once per plan.
            if (outerPlan.hasAnyNodeOfClass(AbstractReceivePlanNode.class) || innerPlan.hasAnyNodeOfClass(AbstractReceivePlanNode.class)) {
                return null;
            }
            innerPlan = addSendReceivePair(innerPlan);
        }
        nljNode.addAndLinkChild(innerPlan);
        ajNode = nljNode;
    } else if (canHaveNLIJ) {
        NestLoopIndexPlanNode nlijNode = new NestLoopIndexPlanNode();
        IndexScanPlanNode innerNode = (IndexScanPlanNode) innerPlan;
        // Set IndexScan predicate. The INNER join expressions for a FULL join come from
        // the innerAccessPath.joinExprs and need to be combined with the other join expressions
        innerNode.setPredicate(innerAccessPath.joinExprs, innerAccessPath.otherExprs);
        nlijNode.addInlinePlanNode(innerPlan);
        // combine the tails plan graph with the new head node
        nlijNode.addAndLinkChild(outerPlan);
        ajNode = nlijNode;
    } else {
        m_recentErrorMsg = "Unsupported special case of complex OUTER JOIN between replicated outer table and partitioned inner table.";
        return null;
    }
    ajNode.setJoinType(joinNode.getJoinType());
    ajNode.setPreJoinPredicate(ExpressionUtil.combinePredicates(joinNode.m_joinOuterList));
    ajNode.setWherePredicate(ExpressionUtil.combinePredicates(whereClauses));
    ajNode.resolveSortDirection();
    return ajNode;
}
Also used : AbstractReceivePlanNode(org.voltdb.plannodes.AbstractReceivePlanNode) JoinNode(org.voltdb.planner.parseinfo.JoinNode) IndexScanPlanNode(org.voltdb.plannodes.IndexScanPlanNode) AbstractJoinPlanNode(org.voltdb.plannodes.AbstractJoinPlanNode) ArrayList(java.util.ArrayList) NestLoopPlanNode(org.voltdb.plannodes.NestLoopPlanNode) BranchNode(org.voltdb.planner.parseinfo.BranchNode) AbstractExpression(org.voltdb.expressions.AbstractExpression) MaterializedScanPlanNode(org.voltdb.plannodes.MaterializedScanPlanNode) NestLoopIndexPlanNode(org.voltdb.plannodes.NestLoopIndexPlanNode)

Aggregations

IndexScanPlanNode (org.voltdb.plannodes.IndexScanPlanNode)60 AbstractPlanNode (org.voltdb.plannodes.AbstractPlanNode)48 AbstractExpression (org.voltdb.expressions.AbstractExpression)22 NestLoopIndexPlanNode (org.voltdb.plannodes.NestLoopIndexPlanNode)15 SeqScanPlanNode (org.voltdb.plannodes.SeqScanPlanNode)14 NestLoopPlanNode (org.voltdb.plannodes.NestLoopPlanNode)11 ProjectionPlanNode (org.voltdb.plannodes.ProjectionPlanNode)8 AggregatePlanNode (org.voltdb.plannodes.AggregatePlanNode)7 OrderByPlanNode (org.voltdb.plannodes.OrderByPlanNode)6 ArrayList (java.util.ArrayList)5 Index (org.voltdb.catalog.Index)5 ReceivePlanNode (org.voltdb.plannodes.ReceivePlanNode)5 AbstractReceivePlanNode (org.voltdb.plannodes.AbstractReceivePlanNode)4 AbstractScanPlanNode (org.voltdb.plannodes.AbstractScanPlanNode)4 HashAggregatePlanNode (org.voltdb.plannodes.HashAggregatePlanNode)4 SendPlanNode (org.voltdb.plannodes.SendPlanNode)4 TupleValueExpression (org.voltdb.expressions.TupleValueExpression)3 IndexCountPlanNode (org.voltdb.plannodes.IndexCountPlanNode)3 StmtTableScan (org.voltdb.planner.parseinfo.StmtTableScan)2 AbstractJoinPlanNode (org.voltdb.plannodes.AbstractJoinPlanNode)2