Search in sources :

Example 1 with Filter

use of io.crate.planner.operators.Filter in project crate by crate.

the class MoveFilterBeneathRename method apply.

@Override
public LogicalPlan apply(Filter plan, Captures captures, TableStats tableStats, TransactionContext txnCtx, NodeContext nodeCtx) {
    Rename rename = captures.get(renameCapture);
    Filter newFilter = new Filter(rename.source(), FieldReplacer.replaceFields(plan.query(), rename::resolveField));
    return rename.replaceSources(List.of(newFilter));
}
Also used : Filter(io.crate.planner.operators.Filter) Rename(io.crate.planner.operators.Rename)

Example 2 with Filter

use of io.crate.planner.operators.Filter in project crate by crate.

the class MoveFilterBeneathWindowAgg method apply.

@Override
public LogicalPlan apply(Filter filter, Captures captures, TableStats tableStats, TransactionContext txnCtx, NodeContext nodeCtx) {
    WindowAgg windowAgg = captures.get(windowAggCapture);
    WindowDefinition windowDefinition = windowAgg.windowDefinition();
    List<WindowFunction> windowFunctions = windowAgg.windowFunctions();
    Predicate<Symbol> containsWindowFunction = symbol -> symbol instanceof WindowFunction && windowFunctions.contains(symbol);
    Symbol predicate = filter.query();
    List<Symbol> filterParts = AndOperator.split(predicate);
    ArrayList<Symbol> remainingFilterSymbols = new ArrayList<>();
    ArrayList<Symbol> windowPartitionedBasedFilters = new ArrayList<>();
    for (Symbol part : filterParts) {
        if (SymbolVisitors.any(containsWindowFunction, part) == false && windowDefinition.partitions().containsAll(extractColumns(part))) {
            windowPartitionedBasedFilters.add(part);
        } else {
            remainingFilterSymbols.add(part);
        }
    }
    assert remainingFilterSymbols.size() > 0 || windowPartitionedBasedFilters.size() > 0 : "Splitting the filter symbol must result in at least one group";
    /* SELECT ROW_NUMBER() OVER(PARTITION BY id)
         * WHERE `x = 1`
         *
         * `x` is not the window partition column.
         * We cannot push down the filter as it would change the window aggregation value
         *
         */
    if (windowPartitionedBasedFilters.isEmpty()) {
        return null;
    }
    /* SELECT ROW_NUMBER() OVER(PARTITION BY id)
         * WHERE `id = 1`
         * remainingFilterSymbols:                  []
         * windowPartitionedBasedFilters:           [id = 1]
         *
         * Filter
         *  |
         * WindowsAgg
         *
         * transforms to
         *
         * WindowAgg
         *  |
         * Filter (id = 1)
         */
    if (remainingFilterSymbols.isEmpty()) {
        return transpose(filter, windowAgg);
    }
    /* WHERE `ROW_NUMBER() OVER(PARTITION BY id) = 2 AND id = 1`
         * remainingFilterSymbols:                  [ROW_NUMBER() OVER(PARTITION BY id) = 2]
         * windowPartitionedBasedFilters:           [id = 1]
         *
         * Filter
         *  |
         * WindowsAgg
         *
         * transforms to
         *
         * Filter (ROW_NUMBER() OVER(PARTITION BY id) = 2)
         *  |
         * WindowAgg
         *  |
         * Filter (id = 1)
         */
    LogicalPlan newWindowAgg = windowAgg.replaceSources(List.of(new Filter(windowAgg.source(), AndOperator.join(windowPartitionedBasedFilters))));
    return new Filter(newWindowAgg, AndOperator.join(remainingFilterSymbols));
}
Also used : TransactionContext(io.crate.metadata.TransactionContext) NodeContext(io.crate.metadata.NodeContext) LogicalPlanner.extractColumns(io.crate.planner.operators.LogicalPlanner.extractColumns) LogicalPlan(io.crate.planner.operators.LogicalPlan) Captures(io.crate.planner.optimizer.matcher.Captures) Predicate(java.util.function.Predicate) Util.transpose(io.crate.planner.optimizer.rule.Util.transpose) Pattern(io.crate.planner.optimizer.matcher.Pattern) Rule(io.crate.planner.optimizer.Rule) SymbolVisitors(io.crate.expression.symbol.SymbolVisitors) ArrayList(java.util.ArrayList) List(java.util.List) AndOperator(io.crate.expression.operator.AndOperator) TableStats(io.crate.statistics.TableStats) WindowDefinition(io.crate.analyze.WindowDefinition) Symbol(io.crate.expression.symbol.Symbol) Pattern.typeOf(io.crate.planner.optimizer.matcher.Pattern.typeOf) WindowFunction(io.crate.expression.symbol.WindowFunction) Patterns.source(io.crate.planner.optimizer.matcher.Patterns.source) Filter(io.crate.planner.operators.Filter) WindowAgg(io.crate.planner.operators.WindowAgg) Capture(io.crate.planner.optimizer.matcher.Capture) WindowFunction(io.crate.expression.symbol.WindowFunction) WindowAgg(io.crate.planner.operators.WindowAgg) Filter(io.crate.planner.operators.Filter) Symbol(io.crate.expression.symbol.Symbol) ArrayList(java.util.ArrayList) LogicalPlan(io.crate.planner.operators.LogicalPlan) WindowDefinition(io.crate.analyze.WindowDefinition)

Example 3 with Filter

use of io.crate.planner.operators.Filter in project crate by crate.

the class RewriteFilterOnOuterJoinToInnerJoin method apply.

@Override
public LogicalPlan apply(Filter filter, Captures captures, TableStats tableStats, TransactionContext txnCtx, NodeContext nodeCtx) {
    var symbolEvaluator = new SymbolEvaluator(txnCtx, nodeCtx, SubQueryResults.EMPTY);
    NestedLoopJoin nl = captures.get(nlCapture);
    Symbol query = filter.query();
    Map<Set<RelationName>, Symbol> splitQueries = QuerySplitter.split(query);
    if (splitQueries.size() == 1 && splitQueries.keySet().iterator().next().size() > 1) {
        return null;
    }
    LogicalPlan lhs = nl.sources().get(0);
    LogicalPlan rhs = nl.sources().get(1);
    Set<RelationName> leftName = lhs.getRelationNames();
    Set<RelationName> rightName = rhs.getRelationNames();
    Symbol leftQuery = splitQueries.remove(leftName);
    Symbol rightQuery = splitQueries.remove(rightName);
    final LogicalPlan newLhs;
    final LogicalPlan newRhs;
    final boolean newJoinIsInnerJoin;
    switch(nl.joinType()) {
        case LEFT:
            /* LEFT OUTER JOIN -> NULL rows are generated for the RHS if the join-condition doesn't match
                 *
                 * cr> select t1.x as t1x, t2.x as t2x from t1 left join t2 on t1.x = t2.x;
                 * +-----+------+
                 * | t1x |  t2x |
                 * +-----+------+
                 * |   3 |    3 |
                 * |   2 |    2 |
                 * |   1 | NULL |
                 * +-----+------+
                 */
            newLhs = getNewSource(leftQuery, lhs);
            if (rightQuery == null) {
                newRhs = rhs;
                newJoinIsInnerJoin = false;
            } else if (couldMatchOnNull(rightQuery, symbolEvaluator)) {
                newRhs = rhs;
                newJoinIsInnerJoin = false;
                splitQueries.put(rightName, rightQuery);
            } else {
                newRhs = getNewSource(rightQuery, rhs);
                newJoinIsInnerJoin = true;
            }
            break;
        case RIGHT:
            /* RIGHT OUTER JOIN -> NULL rows are generated for the LHS if the join-condition doesn't match

                 * cr> select t1.x as t1x, t2.x as t2x from t1 right join t2 on t1.x = t2.x;
                 * +------+-----+
                 * |  t1x | t2x |
                 * +------+-----+
                 * |    3 |   3 |
                 * |    2 |   2 |
                 * | NULL |   4 |
                 * +------+-----+
                 */
            if (leftQuery == null) {
                newLhs = lhs;
                newJoinIsInnerJoin = false;
            } else if (couldMatchOnNull(leftQuery, symbolEvaluator)) {
                newLhs = lhs;
                newJoinIsInnerJoin = false;
                splitQueries.put(leftName, leftQuery);
            } else {
                newLhs = getNewSource(leftQuery, lhs);
                newJoinIsInnerJoin = true;
            }
            newRhs = getNewSource(rightQuery, rhs);
            break;
        case FULL:
            if (couldMatchOnNull(leftQuery, symbolEvaluator)) {
                newLhs = lhs;
            } else {
                newLhs = getNewSource(leftQuery, lhs);
                if (leftQuery != null) {
                    splitQueries.put(leftName, leftQuery);
                }
            }
            if (couldMatchOnNull(rightQuery, symbolEvaluator)) {
                newRhs = rhs;
            } else {
                newRhs = getNewSource(rightQuery, rhs);
            }
            /*
                 * Filters on each side must be put back into the Filter as each side can generate NULL's on outer joins
                 * which must be filtered out AFTER the join operation.
                 * In case the filter is only on one side, the join could be rewritten to a LEFT/RIGHT OUTER.
                 * TODO: Create a dedicated rule RewriteFilterOnOuterJoinToLeftOrRight
                 *
                 * cr> select t1.x as t1x, t2.x as t2x, t2.y as t2y from t1 full outer join t2 on t1.x = t2.x where t2y = 1;
                 * +------+------+------+
                 * |  t1x |  t2x |  t2y |
                 * +------+------+------+
                 * |    3 |    3 |    1 |
                 * |    2 |    2 |    1 |
                 * | NULL |    4 |    1 |
                 * +------+------+------+
                 */
            if (leftQuery != null) {
                splitQueries.put(leftName, leftQuery);
            }
            if (rightQuery != null) {
                splitQueries.put(rightName, rightQuery);
            }
            newJoinIsInnerJoin = newLhs != lhs && newRhs != rhs;
            break;
        default:
            throw new UnsupportedOperationException("The Rule to rewrite filter+outer-joins to inner joins must not be run on joins of type=" + nl.joinType());
    }
    if (newLhs == lhs && newRhs == rhs) {
        return null;
    }
    NestedLoopJoin newJoin = new NestedLoopJoin(newLhs, newRhs, newJoinIsInnerJoin ? JoinType.INNER : nl.joinType(), nl.joinCondition(), nl.isFiltered(), nl.topMostLeftRelation(), nl.orderByWasPushedDown(), true);
    assert newJoin.outputs().equals(nl.outputs()) : "Outputs after rewrite must be the same as before";
    return splitQueries.isEmpty() ? newJoin : new Filter(newJoin, AndOperator.join(splitQueries.values()));
}
Also used : Set(java.util.Set) Filter(io.crate.planner.operators.Filter) Symbol(io.crate.expression.symbol.Symbol) NestedLoopJoin(io.crate.planner.operators.NestedLoopJoin) RelationName(io.crate.metadata.RelationName) LogicalPlan(io.crate.planner.operators.LogicalPlan) SymbolEvaluator(io.crate.analyze.SymbolEvaluator)

Example 4 with Filter

use of io.crate.planner.operators.Filter in project crate by crate.

the class FilterOnJoinsUtil method moveQueryBelowJoin.

static LogicalPlan moveQueryBelowJoin(Symbol query, LogicalPlan join) {
    if (!WhereClause.canMatch(query)) {
        return join.replaceSources(List.of(getNewSource(query, join.sources().get(0)), getNewSource(query, join.sources().get(1))));
    }
    Map<Set<RelationName>, Symbol> splitQuery = QuerySplitter.split(query);
    int initialParts = splitQuery.size();
    if (splitQuery.size() == 1 && splitQuery.keySet().iterator().next().size() > 1) {
        return null;
    }
    assert join.sources().size() == 2 : "Join operator must only have 2 children, LHS and RHS";
    LogicalPlan lhs = join.sources().get(0);
    LogicalPlan rhs = join.sources().get(1);
    Set<RelationName> leftName = lhs.getRelationNames();
    Set<RelationName> rightName = rhs.getRelationNames();
    Symbol queryForLhs = splitQuery.remove(leftName);
    Symbol queryForRhs = splitQuery.remove(rightName);
    LogicalPlan newLhs = getNewSource(queryForLhs, lhs);
    LogicalPlan newRhs = getNewSource(queryForRhs, rhs);
    LogicalPlan newJoin = join.replaceSources(List.of(newLhs, newRhs));
    if (splitQuery.isEmpty()) {
        return newJoin;
    } else if (initialParts == splitQuery.size()) {
        return null;
    } else {
        return new Filter(newJoin, AndOperator.join(splitQuery.values()));
    }
}
Also used : Set(java.util.Set) Filter(io.crate.planner.operators.Filter) Symbol(io.crate.expression.symbol.Symbol) RelationName(io.crate.metadata.RelationName) LogicalPlan(io.crate.planner.operators.LogicalPlan)

Example 5 with Filter

use of io.crate.planner.operators.Filter in project crate by crate.

the class CapturePatternTest method testCapturePatternAddsToCaptureOnMatch.

@Test
public void testCapturePatternAddsToCaptureOnMatch() {
    Filter filter = new Filter(mock(LogicalPlan.class), Literal.BOOLEAN_TRUE);
    Capture<Filter> captureFilter = new Capture<>();
    Pattern<Filter> filterPattern = Pattern.typeOf(Filter.class).capturedAs(captureFilter);
    CapturePattern<Filter> pattern = new CapturePattern<>(captureFilter, filterPattern);
    Match<Filter> match = pattern.accept(filter, Captures.empty());
    Filter capturedFilter = match.captures().get(captureFilter);
    assertThat(capturedFilter, Matchers.sameInstance(filter));
}
Also used : Filter(io.crate.planner.operators.Filter) LogicalPlan(io.crate.planner.operators.LogicalPlan) Test(org.junit.Test)

Aggregations

Filter (io.crate.planner.operators.Filter)14 Symbol (io.crate.expression.symbol.Symbol)11 LogicalPlan (io.crate.planner.operators.LogicalPlan)10 TableStats (io.crate.statistics.TableStats)7 Test (org.junit.Test)7 WindowFunction (io.crate.expression.symbol.WindowFunction)6 WindowAgg (io.crate.planner.operators.WindowAgg)6 CrateDummyClusterServiceUnitTest (io.crate.test.integration.CrateDummyClusterServiceUnitTest)6 ArrayList (java.util.ArrayList)3 RelationName (io.crate.metadata.RelationName)2 Set (java.util.Set)2 SymbolEvaluator (io.crate.analyze.SymbolEvaluator)1 WindowDefinition (io.crate.analyze.WindowDefinition)1 AndOperator (io.crate.expression.operator.AndOperator)1 SymbolVisitors (io.crate.expression.symbol.SymbolVisitors)1 Symbols (io.crate.expression.symbol.Symbols)1 NodeContext (io.crate.metadata.NodeContext)1 TransactionContext (io.crate.metadata.TransactionContext)1 Collect (io.crate.planner.operators.Collect)1 GroupHashAggregate (io.crate.planner.operators.GroupHashAggregate)1