Search in sources :

Example 1 with NestedLoopJoin

use of io.crate.planner.operators.NestedLoopJoin 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 2 with NestedLoopJoin

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

the class MoveOrderBeneathNestedLoop method apply.

@Override
public LogicalPlan apply(Order order, Captures captures, TableStats tableStats, TransactionContext txnCtx, NodeContext nodeCtx) {
    NestedLoopJoin nestedLoop = captures.get(nlCapture);
    Set<RelationName> relationsInOrderBy = Collections.newSetFromMap(new IdentityHashMap<>());
    Consumer<ScopedSymbol> gatherRelationsFromField = f -> relationsInOrderBy.add(f.relation());
    Consumer<Reference> gatherRelationsFromRef = r -> relationsInOrderBy.add(r.ident().tableIdent());
    OrderBy orderBy = order.orderBy();
    for (Symbol orderExpr : orderBy.orderBySymbols()) {
        FieldsVisitor.visitFields(orderExpr, gatherRelationsFromField);
        RefVisitor.visitRefs(orderExpr, gatherRelationsFromRef);
    }
    if (relationsInOrderBy.size() == 1) {
        RelationName relationInOrderBy = relationsInOrderBy.iterator().next();
        if (relationInOrderBy == nestedLoop.topMostLeftRelation().relationName()) {
            LogicalPlan lhs = nestedLoop.sources().get(0);
            LogicalPlan newLhs = order.replaceSources(List.of(lhs));
            return new NestedLoopJoin(newLhs, nestedLoop.sources().get(1), nestedLoop.joinType(), nestedLoop.joinCondition(), nestedLoop.isFiltered(), nestedLoop.topMostLeftRelation(), true, nestedLoop.isRewriteFilterOnOuterJoinToInnerJoinDone());
        }
    }
    return null;
}
Also used : TransactionContext(io.crate.metadata.TransactionContext) NodeContext(io.crate.metadata.NodeContext) LogicalPlan(io.crate.planner.operators.LogicalPlan) Captures(io.crate.planner.optimizer.matcher.Captures) IdentityHashMap(java.util.IdentityHashMap) RelationName(io.crate.metadata.RelationName) Reference(io.crate.metadata.Reference) Pattern(io.crate.planner.optimizer.matcher.Pattern) Set(java.util.Set) NestedLoopJoin(io.crate.planner.operators.NestedLoopJoin) Rule(io.crate.planner.optimizer.Rule) Consumer(java.util.function.Consumer) Order(io.crate.planner.operators.Order) RefVisitor(io.crate.expression.symbol.RefVisitor) List(java.util.List) OrderBy(io.crate.analyze.OrderBy) TableStats(io.crate.statistics.TableStats) Symbol(io.crate.expression.symbol.Symbol) Pattern.typeOf(io.crate.planner.optimizer.matcher.Pattern.typeOf) ScopedSymbol(io.crate.expression.symbol.ScopedSymbol) Patterns.source(io.crate.planner.optimizer.matcher.Patterns.source) Collections(java.util.Collections) FieldsVisitor(io.crate.expression.symbol.FieldsVisitor) Capture(io.crate.planner.optimizer.matcher.Capture) OrderBy(io.crate.analyze.OrderBy) Reference(io.crate.metadata.Reference) Symbol(io.crate.expression.symbol.Symbol) ScopedSymbol(io.crate.expression.symbol.ScopedSymbol) NestedLoopJoin(io.crate.planner.operators.NestedLoopJoin) RelationName(io.crate.metadata.RelationName) LogicalPlan(io.crate.planner.operators.LogicalPlan) ScopedSymbol(io.crate.expression.symbol.ScopedSymbol)

Aggregations

Symbol (io.crate.expression.symbol.Symbol)2 RelationName (io.crate.metadata.RelationName)2 LogicalPlan (io.crate.planner.operators.LogicalPlan)2 NestedLoopJoin (io.crate.planner.operators.NestedLoopJoin)2 Set (java.util.Set)2 OrderBy (io.crate.analyze.OrderBy)1 SymbolEvaluator (io.crate.analyze.SymbolEvaluator)1 FieldsVisitor (io.crate.expression.symbol.FieldsVisitor)1 RefVisitor (io.crate.expression.symbol.RefVisitor)1 ScopedSymbol (io.crate.expression.symbol.ScopedSymbol)1 NodeContext (io.crate.metadata.NodeContext)1 Reference (io.crate.metadata.Reference)1 TransactionContext (io.crate.metadata.TransactionContext)1 Filter (io.crate.planner.operators.Filter)1 Order (io.crate.planner.operators.Order)1 Rule (io.crate.planner.optimizer.Rule)1 Capture (io.crate.planner.optimizer.matcher.Capture)1 Captures (io.crate.planner.optimizer.matcher.Captures)1 Pattern (io.crate.planner.optimizer.matcher.Pattern)1 Pattern.typeOf (io.crate.planner.optimizer.matcher.Pattern.typeOf)1