Search in sources :

Example 36 with AbstractExpression

use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.

the class PlanAssembler method simplifyOuterJoin.

/**
     * Outer join simplification using null rejection.
     * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.43.2531
     * Outerjoin Simplification and Reordering for Query Optimization
     * by Cesar A. Galindo-Legaria , Arnon Rosenthal
     * Algorithm:
     * Traverse the join tree top-down:
     *  For each join node n1 do:
     *    For each expression expr (join and where) at the node n1
     *      For each join node n2 descended from n1 do:
     *          If expr rejects nulls introduced by n2 inner table, then
     *              - convert LEFT OUTER n2 to an INNER join.
     *              - convert FULL OUTER n2 to RIGHT OUTER join
     *          If expr rejects nulls introduced by n2 outer table, then
     *              - convert RIGHT OUTER n2 to an INNER join.
     *              - convert FULL OUTER n2 to LEFT OUTER join
     */
private static void simplifyOuterJoin(BranchNode joinTree) {
    assert (joinTree != null);
    List<AbstractExpression> exprs = new ArrayList<>();
    JoinNode leftNode = joinTree.getLeftNode();
    JoinNode rightNode = joinTree.getRightNode();
    // WHERE expressions need to be evaluated for NULL-rejection
    if (leftNode.getWhereExpression() != null) {
        exprs.add(leftNode.getWhereExpression());
    }
    if (rightNode.getWhereExpression() != null) {
        exprs.add(rightNode.getWhereExpression());
    }
    simplifyOuterJoinRecursively(joinTree, exprs);
}
Also used : AbstractExpression(org.voltdb.expressions.AbstractExpression) JoinNode(org.voltdb.planner.parseinfo.JoinNode) ArrayList(java.util.ArrayList)

Example 37 with AbstractExpression

use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.

the class PlanAssembler method calculateGroupbyColumnsCovered.

private List<Integer> calculateGroupbyColumnsCovered(Index index, String fromTableAlias, List<AbstractExpression> bindings) {
    List<Integer> coveredGroupByColumns = new ArrayList<>();
    List<ParsedColInfo> groupBys = m_parsedSelect.groupByColumns();
    String exprsjson = index.getExpressionsjson();
    if (exprsjson.isEmpty()) {
        List<ColumnRef> indexedColRefs = CatalogUtil.getSortedCatalogItems(index.getColumns(), "index");
        for (int j = 0; j < indexedColRefs.size(); j++) {
            String indexColumnName = indexedColRefs.get(j).getColumn().getName();
            // ignore order of keys in GROUP BY expr
            int ithCovered = 0;
            boolean foundPrefixedColumn = false;
            for (; ithCovered < groupBys.size(); ithCovered++) {
                AbstractExpression gbExpr = groupBys.get(ithCovered).expression;
                if (!(gbExpr instanceof TupleValueExpression)) {
                    continue;
                }
                TupleValueExpression gbTVE = (TupleValueExpression) gbExpr;
                // TVE column index has not been resolved currently
                if (fromTableAlias.equals(gbTVE.getTableAlias()) && indexColumnName.equals(gbTVE.getColumnName())) {
                    foundPrefixedColumn = true;
                    break;
                }
            }
            if (!foundPrefixedColumn) {
                // no prefix match any more
                break;
            }
            coveredGroupByColumns.add(ithCovered);
            if (coveredGroupByColumns.size() == groupBys.size()) {
                // covered all group by columns already
                break;
            }
        }
    } else {
        StmtTableScan fromTableScan = m_parsedSelect.getStmtTableScanByAlias(fromTableAlias);
        // either pure expression index or mix of expressions and simple columns
        List<AbstractExpression> indexedExprs = null;
        try {
            indexedExprs = AbstractExpression.fromJSONArrayString(exprsjson, fromTableScan);
        } catch (JSONException e) {
            e.printStackTrace();
            // This case sounds impossible
            return coveredGroupByColumns;
        }
        for (AbstractExpression indexExpr : indexedExprs) {
            // ignore order of keys in GROUP BY expr
            List<AbstractExpression> binding = null;
            for (int ithCovered = 0; ithCovered < groupBys.size(); ithCovered++) {
                AbstractExpression gbExpr = groupBys.get(ithCovered).expression;
                binding = gbExpr.bindingToIndexedExpression(indexExpr);
                if (binding != null) {
                    bindings.addAll(binding);
                    coveredGroupByColumns.add(ithCovered);
                    break;
                }
            }
            // no prefix match any more or covered all group by columns already
            if (binding == null || coveredGroupByColumns.size() == groupBys.size()) {
                break;
            }
        }
    }
    return coveredGroupByColumns;
}
Also used : TupleValueExpression(org.voltdb.expressions.TupleValueExpression) ArrayList(java.util.ArrayList) JSONException(org.json_voltpatches.JSONException) Constraint(org.voltdb.catalog.Constraint) StmtTableScan(org.voltdb.planner.parseinfo.StmtTableScan) AbstractExpression(org.voltdb.expressions.AbstractExpression) ColumnRef(org.voltdb.catalog.ColumnRef)

Example 38 with AbstractExpression

use of org.voltdb.expressions.AbstractExpression 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 39 with AbstractExpression

use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.

the class SelectSubPlanAssembler method generateInnerAccessPaths.

/**
     * Generate all possible access paths for an inner node in a join.
     * The set of potential index expressions depends whether the inner node can be inlined
     * with the NLIJ or not. In the former case, inner and inner-outer join expressions can
     * be considered for the index access. In the latter, only inner join expressions qualifies.
     *
     * @param parentNode A parent node to the node to generate paths to.
     */
private void generateInnerAccessPaths(BranchNode parentNode) {
    JoinNode innerChildNode = parentNode.getRightNode();
    assert (innerChildNode != null);
    // In case of inner join WHERE and JOIN expressions can be merged
    if (parentNode.getJoinType() == JoinType.INNER) {
        parentNode.m_joinInnerOuterList.addAll(parentNode.m_whereInnerOuterList);
        parentNode.m_whereInnerOuterList.clear();
        parentNode.m_joinInnerList.addAll(parentNode.m_whereInnerList);
        parentNode.m_whereInnerList.clear();
    }
    if (innerChildNode instanceof BranchNode) {
        generateOuterAccessPaths((BranchNode) innerChildNode);
        generateInnerAccessPaths((BranchNode) innerChildNode);
        // The inner node is a join node itself. Only naive access path is possible
        innerChildNode.m_accessPaths.add(getRelevantNaivePath(parentNode.m_joinInnerOuterList, parentNode.m_joinInnerList));
        return;
    }
    // The inner table can have multiple index access paths based on
    // inner and inner-outer join expressions plus the naive one.
    List<AbstractExpression> filterExprs = null;
    List<AbstractExpression> postExprs = null;
    // the inner join expression will effectively filter out inner tuple prior to the NLJ.
    if (parentNode.getJoinType() != JoinType.FULL) {
        filterExprs = parentNode.m_joinInnerList;
    } else {
        postExprs = parentNode.m_joinInnerList;
    }
    StmtTableScan innerTable = innerChildNode.getTableScan();
    assert (innerTable != null);
    innerChildNode.m_accessPaths.addAll(getRelevantAccessPathsForTable(innerTable, parentNode.m_joinInnerOuterList, filterExprs, postExprs));
    // If there are inner expressions AND inner-outer expressions, it could be that there
    // are indexed access paths that use elements of both in the indexing expressions,
    // especially in the case of a compound index.
    // These access paths can not be considered for use with an NLJ because they rely on
    // inner-outer expressions.
    // If there is a possibility that NLIJ will not be an option due to the
    // "special case" processing that puts a send/receive plan between the join node
    // and its inner child node, other access paths need to be considered that use the
    // same indexes as those identified so far but in a simpler, less effective way
    // that does not rely on inner-outer expressions.
    // The following simplistic method of finding these access paths is to force
    // inner-outer expressions to be handled as NLJ-compatible post-filters and repeat
    // the search for access paths.
    // This will typically generate some duplicate access paths, including the naive
    // sequential scan path and any indexed paths that happened to use only the inner
    // expressions.
    // For now, we deal with this redundancy by dropping (and re-generating) all
    // access paths EXCPT those that reference the inner-outer expressions.
    // TODO: implementing access path hash and equality and possibly using a "Set"
    // would allow deduping as new access paths are added OR
    // the simplified access path search process could be based on
    // the existing indexed access paths -- for each access path that "hasInnerOuterIndexExpression"
    // try to generate and add a simpler access path using the same index,
    // this time with the inner-outer expressions used only as non-indexable post-filters.
    // Don't bother generating these redundant or inferior access paths unless there is
    // an inner-outer expression and a chance that NLIJ will be taken out of the running.
    boolean mayNeedInnerSendReceive = (!m_partitioning.wasSpecifiedAsSingle()) && (m_partitioning.getCountOfPartitionedTables() > 0) && (parentNode.getJoinType() != JoinType.INNER) && !innerTable.getIsReplicated();
    if (mayNeedInnerSendReceive && !parentNode.m_joinInnerOuterList.isEmpty()) {
        List<AccessPath> innerOuterAccessPaths = new ArrayList<>();
        for (AccessPath innerAccessPath : innerChildNode.m_accessPaths) {
            if ((innerAccessPath.index != null) && hasInnerOuterIndexExpression(innerChildNode.getTableAlias(), innerAccessPath.indexExprs, innerAccessPath.initialExpr, innerAccessPath.endExprs)) {
                innerOuterAccessPaths.add(innerAccessPath);
            }
        }
        if (parentNode.getJoinType() != JoinType.FULL) {
            filterExprs = parentNode.m_joinInnerList;
            postExprs = parentNode.m_joinInnerOuterList;
        } else {
            // For FULL join type the inner join expressions must be part of the post predicate
            // in order to stay at the join node and not be pushed down to the inner node
            filterExprs = null;
            postExprs = new ArrayList<>(parentNode.m_joinInnerList);
            postExprs.addAll(parentNode.m_joinInnerOuterList);
        }
        Collection<AccessPath> nljAccessPaths = getRelevantAccessPathsForTable(innerTable, null, filterExprs, postExprs);
        innerChildNode.m_accessPaths.clear();
        innerChildNode.m_accessPaths.addAll(nljAccessPaths);
        innerChildNode.m_accessPaths.addAll(innerOuterAccessPaths);
    }
    assert (innerChildNode.m_accessPaths.size() > 0);
}
Also used : BranchNode(org.voltdb.planner.parseinfo.BranchNode) AbstractExpression(org.voltdb.expressions.AbstractExpression) JoinNode(org.voltdb.planner.parseinfo.JoinNode) ArrayList(java.util.ArrayList) StmtTableScan(org.voltdb.planner.parseinfo.StmtTableScan)

Example 40 with AbstractExpression

use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.

the class PlanAssembler method indexAccessForGroupByExprs.

// Turn sequential scan to index scan for group by if possible
private AbstractPlanNode indexAccessForGroupByExprs(SeqScanPlanNode root, IndexGroupByInfo gbInfo) {
    if (root.isSubQuery()) {
        // sub-query edge case will not be handled now
        return root;
    }
    String fromTableAlias = root.getTargetTableAlias();
    assert (fromTableAlias != null);
    List<ParsedColInfo> groupBys = m_parsedSelect.groupByColumns();
    Table targetTable = m_catalogDb.getTables().get(root.getTargetTableName());
    assert (targetTable != null);
    CatalogMap<Index> allIndexes = targetTable.getIndexes();
    List<Integer> maxCoveredGroupByColumns = new ArrayList<>();
    ArrayList<AbstractExpression> maxCoveredBindings = null;
    Index pickedUpIndex = null;
    boolean foundAllGroupByCoveredIndex = false;
    for (Index index : allIndexes) {
        if (!IndexType.isScannable(index.getType())) {
            continue;
        }
        if (!index.getPredicatejson().isEmpty()) {
            // do not try to look at Partial/Sparse index
            continue;
        }
        ArrayList<AbstractExpression> bindings = new ArrayList<>();
        List<Integer> coveredGroupByColumns = calculateGroupbyColumnsCovered(index, fromTableAlias, bindings);
        if (coveredGroupByColumns.size() > maxCoveredGroupByColumns.size()) {
            maxCoveredGroupByColumns = coveredGroupByColumns;
            pickedUpIndex = index;
            maxCoveredBindings = bindings;
            if (maxCoveredGroupByColumns.size() == groupBys.size()) {
                foundAllGroupByCoveredIndex = true;
                break;
            }
        }
    }
    if (pickedUpIndex == null) {
        return root;
    }
    IndexScanPlanNode indexScanNode = new IndexScanPlanNode(root, null, pickedUpIndex, SortDirectionType.INVALID);
    indexScanNode.setForGroupingOnly();
    indexScanNode.setBindings(maxCoveredBindings);
    gbInfo.m_coveredGroupByColumns = maxCoveredGroupByColumns;
    gbInfo.m_canBeFullySerialized = foundAllGroupByCoveredIndex;
    return indexScanNode;
}
Also used : Table(org.voltdb.catalog.Table) IndexScanPlanNode(org.voltdb.plannodes.IndexScanPlanNode) ArrayList(java.util.ArrayList) Index(org.voltdb.catalog.Index) AbstractExpression(org.voltdb.expressions.AbstractExpression)

Aggregations

AbstractExpression (org.voltdb.expressions.AbstractExpression)215 TupleValueExpression (org.voltdb.expressions.TupleValueExpression)59 AbstractPlanNode (org.voltdb.plannodes.AbstractPlanNode)55 ArrayList (java.util.ArrayList)43 SeqScanPlanNode (org.voltdb.plannodes.SeqScanPlanNode)26 SchemaColumn (org.voltdb.plannodes.SchemaColumn)25 NestLoopPlanNode (org.voltdb.plannodes.NestLoopPlanNode)23 Constraint (org.voltdb.catalog.Constraint)22 IndexScanPlanNode (org.voltdb.plannodes.IndexScanPlanNode)22 HashSet (java.util.HashSet)21 Column (org.voltdb.catalog.Column)21 AbstractScanPlanNode (org.voltdb.plannodes.AbstractScanPlanNode)21 JSONException (org.json_voltpatches.JSONException)19 ColumnRef (org.voltdb.catalog.ColumnRef)19 Table (org.voltdb.catalog.Table)17 AbstractSubqueryExpression (org.voltdb.expressions.AbstractSubqueryExpression)16 ParameterValueExpression (org.voltdb.expressions.ParameterValueExpression)16 StmtTableScan (org.voltdb.planner.parseinfo.StmtTableScan)16 ExpressionType (org.voltdb.types.ExpressionType)16 VoltXMLElement (org.hsqldb_voltpatches.VoltXMLElement)14