Search in sources :

Example 6 with StmtTargetTableScan

use of org.voltdb.planner.parseinfo.StmtTargetTableScan in project voltdb by VoltDB.

the class SeqScanPlanNode method computeCostEstimates.

@Override
public void computeCostEstimates(long childOutputTupleCountEstimate, DatabaseEstimates estimates, ScalarValueHints[] paramHints) {
    if (m_isSubQuery) {
        // Get estimates from the sub-query
        // @TODO For the sub-query the cost estimates will be calculated separately
        // At the moment its contribution to the parent's cost plan is irrelevant because
        // all parent plans have the same best cost plan for the sub-query
        m_estimatedProcessedTupleCount = SUBQUERY_TABLE_ESTIMATES_HACK.minTuples;
        m_estimatedOutputTupleCount = SUBQUERY_TABLE_ESTIMATES_HACK.minTuples;
        return;
    }
    Table target = ((StmtTargetTableScan) m_tableScan).getTargetTable();
    TableEstimates tableEstimates = estimates.getEstimatesForTable(target.getTypeName());
    // This maxTuples value estimates the number of tuples fetched from the sequential scan.
    // It's a vague measure of the cost of the scan.
    // Its accuracy depends a lot on what kind of post-filtering or projection needs to happen, if any.
    // The tuplesRead value is also used to estimate the number of RESULT rows, regardless of
    // how effective post-filtering might be -- as if all rows passed the filters.
    // This is at least semi-consistent with the ignoring of post-filter effects in IndexScanPlanNode.
    // In effect, though, it gives index scans an "unfair" advantage when they reduce the estimated result size
    // by taking into account the indexed filters -- follow-on plan steps, sorts (etc.), are costed lower
    // as if they are operating on fewer rows than would have come out of the seqscan,
    // though that's nonsense.
    // In any case, it's important to keep an eye on any changes (discounts) to SeqScanPlanNode's costing
    // here to make sure that SeqScanPlanNode never gains an unfair advantage over IndexScanPlanNode.
    m_estimatedProcessedTupleCount = tableEstimates.maxTuples;
    m_estimatedOutputTupleCount = tableEstimates.maxTuples;
}
Also used : Table(org.voltdb.catalog.Table) TableEstimates(org.voltdb.compiler.DatabaseEstimates.TableEstimates) StmtTargetTableScan(org.voltdb.planner.parseinfo.StmtTargetTableScan)

Example 7 with StmtTargetTableScan

use of org.voltdb.planner.parseinfo.StmtTargetTableScan in project voltdb by VoltDB.

the class CatalogUtil method updateUsageAnnotations.

/**
     * Given plan graphs and a SQL stmt, compute a bi-directonal usage map between
     * schema (indexes, table & views) and SQL/Procedures.
     * Use "annotation" objects to store this extra information in the catalog
     * during compilation and catalog report generation.
     */
public static void updateUsageAnnotations(Database db, Statement stmt, AbstractPlanNode topPlan, AbstractPlanNode bottomPlan) {
    Map<String, StmtTargetTableScan> tablesRead = new TreeMap<>();
    Collection<String> indexes = new TreeSet<>();
    if (topPlan != null) {
        topPlan.getTablesAndIndexes(tablesRead, indexes);
    }
    if (bottomPlan != null) {
        bottomPlan.getTablesAndIndexes(tablesRead, indexes);
    }
    String updated = "";
    if (!stmt.getReadonly()) {
        updated = topPlan.getUpdatedTable();
        if (updated == null) {
            updated = bottomPlan.getUpdatedTable();
        }
        assert (updated.length() > 0);
    }
    Set<String> readTableNames = tablesRead.keySet();
    stmt.setTablesread(StringUtils.join(readTableNames, ","));
    stmt.setTablesupdated(updated);
    Set<String> tableDotIndexNames = new TreeSet<>();
    for (Table table : db.getTables()) {
        if (readTableNames.contains(table.getTypeName())) {
            readTableNames.remove(table.getTypeName());
            for (String indexName : indexes) {
                Index index = table.getIndexes().get(indexName);
                if (index != null) {
                    tableDotIndexNames.add(table.getTypeName() + "." + index.getTypeName());
                }
            }
        }
    }
    String indexString = StringUtils.join(tableDotIndexNames, ",");
    stmt.setIndexesused(indexString);
    assert (tablesRead.size() == 0);
}
Also used : VoltTable(org.voltdb.VoltTable) Table(org.voltdb.catalog.Table) TreeSet(java.util.TreeSet) StmtTargetTableScan(org.voltdb.planner.parseinfo.StmtTargetTableScan) Index(org.voltdb.catalog.Index) TreeMap(java.util.TreeMap)

Example 8 with StmtTargetTableScan

use of org.voltdb.planner.parseinfo.StmtTargetTableScan in project voltdb by VoltDB.

the class SubPlanAssembler method getRelevantAccessPathForIndex.

/**
     * Given a table, a set of predicate expressions and a specific index, find the best way to
     * access the data using the given index, or return null if no good way exists.
     *
     * @param table The table we want data from.
     * @param exprs The set of predicate expressions.
     * @param index The index we want to use to access the data.
     * @return A valid access path using the data or null if none found.
     */
protected AccessPath getRelevantAccessPathForIndex(StmtTableScan tableScan, List<AbstractExpression> exprs, Index index) {
    if (tableScan instanceof StmtTargetTableScan == false) {
        return null;
    }
    // Copy the expressions to a new working list that can be culled as filters are processed.
    List<AbstractExpression> filtersToCover = new ArrayList<>(exprs);
    boolean indexIsGeographical;
    String exprsjson = index.getExpressionsjson();
    // This list remains null if the index is just on simple columns.
    List<AbstractExpression> indexedExprs = null;
    // This vector of indexed columns remains null if indexedExprs is in use.
    List<ColumnRef> indexedColRefs = null;
    int[] indexedColIds = null;
    int keyComponentCount;
    if (exprsjson.isEmpty()) {
        // Don't bother to build a dummy indexedExprs list for a simple index on columns.
        // Just leave it null and handle this simpler case specially via indexedColRefs or
        // indexedColIds, all along the way.
        indexedColRefs = CatalogUtil.getSortedCatalogItems(index.getColumns(), "index");
        keyComponentCount = indexedColRefs.size();
        indexedColIds = new int[keyComponentCount];
        int ii = 0;
        for (ColumnRef cr : indexedColRefs) {
            indexedColIds[ii++] = cr.getColumn().getIndex();
        }
        indexIsGeographical = isAGeoColumnIndex(indexedColRefs);
    } else {
        try {
            // This MAY want to happen once when the plan is loaded from the catalog
            // and cached in a sticky cached index-to-expressions map?
            indexedExprs = AbstractExpression.fromJSONArrayString(exprsjson, tableScan);
            keyComponentCount = indexedExprs.size();
        } catch (JSONException e) {
            e.printStackTrace();
            assert (false);
            return null;
        }
        indexIsGeographical = isAGeoExpressionIndex(indexedExprs);
    }
    AccessPath retval = new AccessPath();
    retval.index = index;
    // in tandem with other indexes within one more powerful indexscan.
    if (indexIsGeographical) {
        return getRelevantAccessPathForGeoIndex(retval, tableScan, indexedExprs, indexedColRefs, filtersToCover);
    }
    // Hope for the best -- full coverage with equality matches on every expression in the index.
    retval.use = IndexUseType.COVERING_UNIQUE_EQUALITY;
    // Try to use the index scan's inherent ordering to implement the ORDER BY clause.
    // The effects of determineIndexOrdering are reflected in
    // retval.sortDirection, orderSpoilers, nSpoilers and bindingsForOrder.
    // In some borderline cases, the determination to use the index's order is optimistic and
    // provisional; it can be undone later in this function as new info comes to light.
    int[] orderSpoilers = new int[keyComponentCount];
    List<AbstractExpression> bindingsForOrder = new ArrayList<>();
    int nSpoilers = determineIndexOrdering(tableScan, keyComponentCount, indexedExprs, indexedColRefs, retval, orderSpoilers, bindingsForOrder);
    // Use as many covering indexed expressions as possible to optimize comparator expressions that can use them.
    // Start with equality comparisons on as many (prefix) indexed expressions as possible.
    int coveredCount = 0;
    // If determineIndexOrdering found one or more spoilers,
    // index key components that might interfere with the desired ordering of the result,
    // their ill effects are eliminated when they are constrained to be equal to constants.
    // These are called "recovered spoilers".
    // When their count reaches the count of spoilers, the order of the result will be as desired.
    // Initial "prefix key component" spoilers can be recovered in the normal course
    // of finding prefix equality filters for those key components.
    // The spoiler key component positions are listed (ascending) in orderSpoilers.
    // After the last prefix equality filter has been found,
    // nRecoveredSpoilers in comparison to nSpoilers may indicate remaining unrecovered spoilers.
    // That edge case motivates a renewed search for (non-prefix) equality filters solely for the purpose
    // of recovering the spoilers and confirming the relevance of the result's index ordering.
    int nRecoveredSpoilers = 0;
    AbstractExpression coveringExpr = null;
    int coveringColId = -1;
    // Currently, an index can be used with at most one IN LIST filter expression.
    // Otherwise, MaterializedScans would have to be multi-column and populated by a cross-product
    // of multiple lists OR multiple MaterializedScans would have to be cross-joined to get a
    // multi-column LHS for the injected NestLoopIndexJoin used for IN LIST indexing.
    // So, note the one IN LIST filter when it is found, mostly to remember that one has been found.
    // This has implications for what kinds of filters on other key components can be included in
    // the index scan.
    IndexableExpression inListExpr = null;
    for (; (coveredCount < keyComponentCount) && !filtersToCover.isEmpty(); ++coveredCount) {
        if (indexedExprs == null) {
            coveringColId = indexedColIds[coveredCount];
        } else {
            coveringExpr = indexedExprs.get(coveredCount);
        }
        // Equality filters get first priority.
        boolean allowIndexedJoinFilters = (inListExpr == null);
        IndexableExpression eqExpr = getIndexableExpressionFromFilters(// The only difference is that NULL is not distinct from NULL, but NULL != NULL. (ENG-11096)
        ExpressionType.COMPARE_EQUAL, ExpressionType.COMPARE_NOTDISTINCT, coveringExpr, coveringColId, tableScan, filtersToCover, allowIndexedJoinFilters, EXCLUDE_FROM_POST_FILTERS);
        if (eqExpr == null) {
            // So, only the first IN LIST filter matching a key component is considered.
            if (inListExpr == null) {
                // Also, it can not be considered if there was a prior key component that has an
                // equality filter that is based on another table.
                // Accepting an IN LIST filter implies rejecting later any filters based on other
                // tables' columns.
                inListExpr = getIndexableExpressionFromFilters(ExpressionType.COMPARE_IN, ExpressionType.COMPARE_IN, coveringExpr, coveringColId, tableScan, filtersToCover, false, EXCLUDE_FROM_POST_FILTERS);
                if (inListExpr != null) {
                    // were based on constants and/or parameters.
                    for (AbstractExpression eq_comparator : retval.indexExprs) {
                        AbstractExpression otherExpr = eq_comparator.getRight();
                        if (otherExpr.hasTupleValueSubexpression()) {
                            // Can't index this IN LIST filter without some kind of three-way NLIJ,
                            // so, add it to the post-filters.
                            AbstractExpression in_list_comparator = inListExpr.getOriginalFilter();
                            retval.otherExprs.add(in_list_comparator);
                            inListExpr = null;
                            break;
                        }
                    }
                    eqExpr = inListExpr;
                }
            }
            if (eqExpr == null) {
                break;
            }
        }
        AbstractExpression comparator = eqExpr.getFilter();
        retval.indexExprs.add(comparator);
        retval.bindings.addAll(eqExpr.getBindings());
        // A non-empty endExprs has the later side effect of invalidating descending sort order
        // in all cases except the edge case of full coverage equality comparison.
        // Even that case must be further qualified to exclude indexed IN-LIST
        // unless/until the MaterializedScan can be configured to iterate in descending order
        // (vs. always ascending).
        // In the case of the IN LIST expression, both the search key and the end condition need
        // to be rewritten to enforce equality in turn with each list element "row" produced by
        // the MaterializedScan. This happens in getIndexAccessPlanForTable.
        retval.endExprs.add(comparator);
        // In this case, consider the spoiler recovered.
        if (nRecoveredSpoilers < nSpoilers && orderSpoilers[nRecoveredSpoilers] == coveredCount) {
            // In the case of IN-LIST equality, the key component will not have a constant value.
            if (eqExpr != inListExpr) {
                // One recovery closer to confirming the sort order.
                ++nRecoveredSpoilers;
            }
        }
    }
    // which happens to be the only use case for non-scannable (i.e. HASH) indexes.
    if (coveredCount == keyComponentCount) {
        // All remaining filters get covered as post-filters
        // to be applied after the "random access" to the exact index key.
        retval.otherExprs.addAll(filtersToCover);
        if (retval.sortDirection != SortDirectionType.INVALID) {
            // This IS an odd (maybe non-existent) case
            // -- equality filters found on on all ORDER BY expressions?
            // That said, with all key components covered, there can't be any spoilers.
            retval.bindings.addAll(bindingsForOrder);
        }
        return retval;
    }
    if (!IndexType.isScannable(index.getType())) {
        // Failure to equality-match all expressions in a non-scannable index is unacceptable.
        return null;
    }
    // by continuing the search for (non-prefix) constant equality filters.
    if (nRecoveredSpoilers < nSpoilers) {
        // There's an order to spoil.
        assert (retval.sortDirection != SortDirectionType.INVALID);
        // Try to associate each skipped index key component with an equality filter.
        // If a key component equals a constant, its value can't actually spoil the ordering.
        // This extra checking is only needed when all of these conditions hold:
        //   -- There are three or more index key components.
        //   -- Two or more of them are in the ORDER BY clause
        //   -- One or more of them are "spoilers", i.e. are not in the ORDER BY clause.
        //   -- A "spoiler" falls between two non-spoilers in the index key component list.
        // e.g. "CREATE INDEX ... ON (A, B, C);" then "SELECT ... WHERE B=? ORDER BY A, C;"
        List<AbstractExpression> otherBindingsForOrder = recoverOrderSpoilers(orderSpoilers, nSpoilers, nRecoveredSpoilers, indexedExprs, indexedColIds, tableScan, filtersToCover);
        if (otherBindingsForOrder == null) {
            // Some order spoiler didn't have an equality filter.
            // Invalidate the provisional indexed ordering.
            retval.sortDirection = SortDirectionType.INVALID;
            retval.m_stmtOrderByIsCompatible = false;
            retval.m_windowFunctionUsesIndex = NO_INDEX_USE;
            // suddenly irrelevant
            bindingsForOrder.clear();
        } else {
            // Any non-null bindings list, even an empty one,
            // denotes success -- all spoilers were equality filtered.
            bindingsForOrder.addAll(otherBindingsForOrder);
        }
    }
    IndexableExpression startingBoundExpr = null;
    IndexableExpression endingBoundExpr = null;
    if (!filtersToCover.isEmpty()) {
        // A scannable index allows inequality matches, but only on the first key component
        // missing a usable equality comparator.
        // Look for a double-ended bound on it.
        // This is always the result of an edge case:
        // "indexed-general-expression LIKE prefix-constant".
        // The simpler case "column LIKE prefix-constant"
        // has already been re-written by the HSQL parser
        // into separate upper and lower bound inequalities.
        IndexableExpression doubleBoundExpr = getIndexableExpressionFromFilters(ExpressionType.COMPARE_LIKE, ExpressionType.COMPARE_LIKE, coveringExpr, coveringColId, tableScan, filtersToCover, false, EXCLUDE_FROM_POST_FILTERS);
        // This MIGHT not always provide the most selective filtering.
        if (doubleBoundExpr != null) {
            startingBoundExpr = doubleBoundExpr.extractStartFromPrefixLike();
            endingBoundExpr = doubleBoundExpr.extractEndFromPrefixLike();
        } else {
            boolean allowIndexedJoinFilters = (inListExpr == null);
            // Look for a lower bound.
            startingBoundExpr = getIndexableExpressionFromFilters(ExpressionType.COMPARE_GREATERTHAN, ExpressionType.COMPARE_GREATERTHANOREQUALTO, coveringExpr, coveringColId, tableScan, filtersToCover, allowIndexedJoinFilters, EXCLUDE_FROM_POST_FILTERS);
            // Look for an upper bound.
            endingBoundExpr = getIndexableExpressionFromFilters(ExpressionType.COMPARE_LESSTHAN, ExpressionType.COMPARE_LESSTHANOREQUALTO, coveringExpr, coveringColId, tableScan, filtersToCover, allowIndexedJoinFilters, EXCLUDE_FROM_POST_FILTERS);
        }
        if (startingBoundExpr != null) {
            AbstractExpression lowerBoundExpr = startingBoundExpr.getFilter();
            retval.indexExprs.add(lowerBoundExpr);
            retval.bindings.addAll(startingBoundExpr.getBindings());
            if (lowerBoundExpr.getExpressionType() == ExpressionType.COMPARE_GREATERTHAN) {
                retval.lookupType = IndexLookupType.GT;
            } else {
                assert (lowerBoundExpr.getExpressionType() == ExpressionType.COMPARE_GREATERTHANOREQUALTO);
                retval.lookupType = IndexLookupType.GTE;
            }
            retval.use = IndexUseType.INDEX_SCAN;
        }
        if (endingBoundExpr != null) {
            AbstractExpression upperBoundComparator = endingBoundExpr.getFilter();
            retval.use = IndexUseType.INDEX_SCAN;
            retval.bindings.addAll(endingBoundExpr.getBindings());
            // do not do the reverse scan optimization
            if (retval.sortDirection != SortDirectionType.DESC && (startingBoundExpr != null || retval.sortDirection == SortDirectionType.ASC)) {
                retval.endExprs.add(upperBoundComparator);
                if (retval.lookupType == IndexLookupType.EQ) {
                    retval.lookupType = IndexLookupType.GTE;
                }
            } else {
                // only do reverse scan optimization when no lowerBoundExpr and lookup type is either < or <=.
                if (upperBoundComparator.getExpressionType() == ExpressionType.COMPARE_LESSTHAN) {
                    retval.lookupType = IndexLookupType.LT;
                } else {
                    assert upperBoundComparator.getExpressionType() == ExpressionType.COMPARE_LESSTHANOREQUALTO;
                    retval.lookupType = IndexLookupType.LTE;
                }
                // that here to optimize out the "NOT NULL" comparator for NOT NULL columns
                if (startingBoundExpr == null) {
                    AbstractExpression newComparator = new OperatorExpression(ExpressionType.OPERATOR_NOT, new OperatorExpression(ExpressionType.OPERATOR_IS_NULL), null);
                    newComparator.getLeft().setLeft(upperBoundComparator.getLeft());
                    newComparator.finalizeValueTypes();
                    retval.otherExprs.add(newComparator);
                } else {
                    int lastIdx = retval.indexExprs.size() - 1;
                    retval.indexExprs.remove(lastIdx);
                    AbstractExpression lowerBoundComparator = startingBoundExpr.getFilter();
                    retval.endExprs.add(lowerBoundComparator);
                }
                // add to indexExprs because it will be used as part of searchKey
                retval.indexExprs.add(upperBoundComparator);
                // initialExpr is set for both cases
                // but will be used for LTE and only when overflow case of LT.
                // 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".
                retval.initialExpr.addAll(retval.indexExprs);
            }
        }
    }
    if (endingBoundExpr == null) {
        if (retval.sortDirection == SortDirectionType.DESC) {
            // Optimizable to use reverse scan.
            if (retval.endExprs.size() == 0) {
                // no prefix equality filters
                if (startingBoundExpr != null) {
                    retval.indexExprs.clear();
                    AbstractExpression comparator = startingBoundExpr.getFilter();
                    retval.endExprs.add(comparator);
                    // 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".
                    retval.initialExpr.addAll(retval.indexExprs);
                // Look up type here does not matter in EE, because the # of active search keys is 0.
                // EE use m_index->moveToEnd(false) to get END, setting scan to reverse scan.
                // retval.lookupType = IndexLookupType.LTE;
                }
            } else {
                // there are prefix equality filters -- possible for a reverse scan?
                // set forward scan.
                retval.sortDirection = SortDirectionType.INVALID;
            // Turn this part on when we have EE support for reverse scan with query GT and GTE.
            /*
                    boolean isReverseScanPossible = true;
                    if (filtersToCover.size() > 0) {
                        // Look forward to see the remainning filters.
                        for (int ii = coveredCount + 1; ii < keyComponentCount; ++ii) {
                            if (indexedExprs == null) {
                                coveringColId = indexedColIds[ii];
                            } else {
                                coveringExpr = indexedExprs.get(ii);
                            }
                            // Equality filters get first priority.
                            boolean allowIndexedJoinFilters = (inListExpr == null);
                            IndexableExpression eqExpr = getIndexableExpressionFromFilters(
                                ExpressionType.COMPARE_EQUAL, ExpressionType.COMPARE_EQUAL,
                                coveringExpr, coveringColId, table, filtersToCover,
                                allowIndexedJoinFilters, KEEP_IN_POST_FILTERS);
                            if (eqExpr == null) {
                                isReverseScanPossible = false;
                            }
                        }
                    }
                    if (isReverseScanPossible) {
                        if (startingBoundExpr != null) {
                            int lastIdx = retval.indexExprs.size() -1;
                            retval.indexExprs.remove(lastIdx);

                            AbstractExpression comparator = startingBoundExpr.getFilter();
                            retval.endExprs.add(comparator);
                            retval.initialExpr.addAll(retval.indexExprs);

                            retval.lookupType = IndexLookupType.LTE;
                        }

                    } else {
                        // set forward scan.
                        retval.sortDirection = SortDirectionType.INVALID;
                    }
                    */
            }
        }
    }
    // index not relevant to expression
    if (retval.indexExprs.size() == 0 && retval.endExprs.size() == 0 && retval.sortDirection == SortDirectionType.INVALID) {
        return null;
    }
    // components that are not being filtered.
    if (retval.indexExprs.size() < keyComponentCount) {
        correctAccessPathForPrefixKeyCoverage(retval, startingBoundExpr);
    }
    // All remaining filters get applied as post-filters
    // on tuples fetched from the index.
    retval.otherExprs.addAll(filtersToCover);
    if (retval.sortDirection != SortDirectionType.INVALID) {
        retval.bindings.addAll(bindingsForOrder);
    }
    return retval;
}
Also used : ArrayList(java.util.ArrayList) JSONException(org.json_voltpatches.JSONException) AbstractExpression(org.voltdb.expressions.AbstractExpression) OperatorExpression(org.voltdb.expressions.OperatorExpression) StmtTargetTableScan(org.voltdb.planner.parseinfo.StmtTargetTableScan) ColumnRef(org.voltdb.catalog.ColumnRef)

Example 9 with StmtTargetTableScan

use of org.voltdb.planner.parseinfo.StmtTargetTableScan in project voltdb by VoltDB.

the class AbstractPlanNode method hasReplicatedResult.

public boolean hasReplicatedResult() {
    Map<String, StmtTargetTableScan> tablesRead = new TreeMap<>();
    getTablesAndIndexes(tablesRead, null);
    for (StmtTableScan tableScan : tablesRead.values()) {
        if (!tableScan.getIsReplicated()) {
            return false;
        }
    }
    return true;
}
Also used : StmtTargetTableScan(org.voltdb.planner.parseinfo.StmtTargetTableScan) JSONString(org.json_voltpatches.JSONString) TreeMap(java.util.TreeMap) StmtTableScan(org.voltdb.planner.parseinfo.StmtTableScan)

Example 10 with StmtTargetTableScan

use of org.voltdb.planner.parseinfo.StmtTargetTableScan in project voltdb by VoltDB.

the class AbstractParsedStmt method parseTable.

/**
     *
     * @param tableNode
     */
private void parseTable(VoltXMLElement tableNode) {
    String tableName = tableNode.attributes.get("table");
    assert (tableName != null);
    String tableAlias = tableNode.attributes.get("tablealias");
    if (tableAlias == null) {
        tableAlias = tableName;
    }
    // Hsql rejects name conflicts in a single query
    m_tableAliasListAsJoinOrder.add(tableAlias);
    VoltXMLElement subqueryElement = null;
    // Possible sub-query
    for (VoltXMLElement childNode : tableNode.children) {
        if (!childNode.name.equals("tablesubquery")) {
            continue;
        }
        if (childNode.children.isEmpty()) {
            continue;
        }
        // sub-query FROM (SELECT ...)
        subqueryElement = childNode.children.get(0);
        break;
    }
    // add table to the query cache before processing the JOIN/WHERE expressions
    // The order is important because processing sub-query expressions assumes that
    // the sub-query is already registered
    StmtTableScan tableScan = null;
    Table table = null;
    // In case of a subquery we need to preserve its filter expressions
    AbstractExpression simplifiedSubqueryFilter = null;
    if (subqueryElement == null) {
        table = getTableFromDB(tableName);
        assert (table != null);
        tableScan = addTableToStmtCache(table, tableAlias);
        m_tableList.add(table);
    } else {
        AbstractParsedStmt subquery = parseFromSubQuery(subqueryElement);
        StmtSubqueryScan subqueryScan = addSubqueryToStmtCache(subquery, tableAlias);
        tableScan = subqueryScan;
        StmtTargetTableScan simpler = simplifierForSubquery(subquery);
        if (simpler != null) {
            tableScan = addSimplifiedSubqueryToStmtCache(subqueryScan, simpler);
            table = simpler.getTargetTable();
            // Extract subquery's filters
            assert (subquery.m_joinTree != null);
            // Adjust the table alias in all TVEs from the eliminated
            // subquery expressions. Example:
            // SELECT TA2.CA FROM (SELECT C CA FROM T TA1 WHERE C > 0) TA2
            // The table alias TA1 from the original TVE (T)TA1.C from the
            // subquery WHERE condition needs to be replaced with the alias
            // TA2. The new TVE will be (T)TA2.C.
            // The column alias does not require an adjustment.
            simplifiedSubqueryFilter = subquery.m_joinTree.getAllFilters();
            List<TupleValueExpression> tves = ExpressionUtil.getTupleValueExpressions(simplifiedSubqueryFilter);
            for (TupleValueExpression tve : tves) {
                tve.setTableAlias(tableScan.getTableAlias());
                tve.setOrigStmtId(m_stmtId);
            }
        }
    }
    AbstractExpression joinExpr = parseJoinCondition(tableNode);
    AbstractExpression whereExpr = parseWhereCondition(tableNode);
    if (simplifiedSubqueryFilter != null) {
        // Add subqueruy's expressions as JOIN filters to make sure they will
        // stay at the node level in case of an OUTER joins and won't affect
        // the join simplification process:
        // select * from T LEFT JOIN (select C FROM T1 WHERE C > 2) S ON T.C = S.C;
        joinExpr = (joinExpr != null) ? ExpressionUtil.combine(joinExpr, simplifiedSubqueryFilter) : simplifiedSubqueryFilter;
    }
    // The join type of the leaf node is always INNER
    // For a new tree its node's ids start with 0 and keep incrementing by 1
    int nodeId = (m_joinTree == null) ? 0 : m_joinTree.getId() + 1;
    JoinNode leafNode;
    if (table != null) {
        assert (tableScan instanceof StmtTargetTableScan);
        leafNode = new TableLeafNode(nodeId, joinExpr, whereExpr, (StmtTargetTableScan) tableScan);
    } else {
        assert (tableScan instanceof StmtSubqueryScan);
        leafNode = new SubqueryLeafNode(nodeId, joinExpr, whereExpr, (StmtSubqueryScan) tableScan);
        leafNode.updateContentDeterminismMessage(((StmtSubqueryScan) tableScan).calculateContentDeterminismMessage());
    }
    if (m_joinTree == null) {
        // this is the first table
        m_joinTree = leafNode;
    } else {
        // Build the tree by attaching the next table always to the right
        // The node's join type is determined by the type of its right node
        JoinType joinType = JoinType.get(tableNode.attributes.get("jointype"));
        assert (joinType != JoinType.INVALID);
        JoinNode joinNode = new BranchNode(nodeId + 1, joinType, m_joinTree, leafNode);
        m_joinTree = joinNode;
    }
}
Also used : StmtSubqueryScan(org.voltdb.planner.parseinfo.StmtSubqueryScan) TupleValueExpression(org.voltdb.expressions.TupleValueExpression) Table(org.voltdb.catalog.Table) TableLeafNode(org.voltdb.planner.parseinfo.TableLeafNode) JoinNode(org.voltdb.planner.parseinfo.JoinNode) JoinType(org.voltdb.types.JoinType) VoltXMLElement(org.hsqldb_voltpatches.VoltXMLElement) Constraint(org.voltdb.catalog.Constraint) StmtTableScan(org.voltdb.planner.parseinfo.StmtTableScan) BranchNode(org.voltdb.planner.parseinfo.BranchNode) AbstractExpression(org.voltdb.expressions.AbstractExpression) SubqueryLeafNode(org.voltdb.planner.parseinfo.SubqueryLeafNode) StmtTargetTableScan(org.voltdb.planner.parseinfo.StmtTargetTableScan)

Aggregations

StmtTargetTableScan (org.voltdb.planner.parseinfo.StmtTargetTableScan)14 Table (org.voltdb.catalog.Table)9 AbstractExpression (org.voltdb.expressions.AbstractExpression)8 StmtTableScan (org.voltdb.planner.parseinfo.StmtTableScan)7 Column (org.voltdb.catalog.Column)6 ArrayList (java.util.ArrayList)5 JSONException (org.json_voltpatches.JSONException)5 TupleValueExpression (org.voltdb.expressions.TupleValueExpression)5 HashMap (java.util.HashMap)4 HashSet (java.util.HashSet)4 ColumnRef (org.voltdb.catalog.ColumnRef)4 Constraint (org.voltdb.catalog.Constraint)4 Index (org.voltdb.catalog.Index)4 TreeMap (java.util.TreeMap)3 StmtSubqueryScan (org.voltdb.planner.parseinfo.StmtSubqueryScan)3 SchemaColumn (org.voltdb.plannodes.SchemaColumn)3 List (java.util.List)2 SimpleDateFormat (java.text.SimpleDateFormat)1 Date (java.util.Date)1 Map (java.util.Map)1