Search in sources :

Example 1 with JoinType

use of io.crate.planner.node.dql.join.JoinType in project crate by crate.

the class Rewriter method tryRewriteOuterToInnerJoin.

/**
     * Rewrite an Outer join to an inner join if possible.
     * <p>
     * Conditions on OUTER tables are not pushed down when a MultiSourceSelect is initially created because
     * the whereClause needs to be checked AFTER the join
     * (because if the join generates NULL rows the whereClause could become TRUE on those NULL rows)
     * <p>
     * See the following two examples where <code>t2</code> is the OUTER table:
     * <p>
     * <pre>
     *     select * from t1
     *          left join t2 on t1.t2_id = t2.id
     *     where
     *          coalesce(t2.x, 20) > 10   # becomes TRUE for null rows
     * </pre>
     * <p>
     * <p>
     * <p>
     * But if we know that the whereClause cannot possible become TRUE then we can push it down
     * (re-writing this into an inner join)
     * <p>
     * This is possible because all rows that are generated by the left-join would be removed again by the whereClause anyway.
     * <p>
     * <pre>
     *     select * from t1
     *          left join t2 on t1.t2_id = t2.id
     *     where
     *          t2.x > 10   # if t2.x is NULL this is always FALSE
     * </pre>
     */
public static void tryRewriteOuterToInnerJoin(EvaluatingNormalizer normalizer, JoinPair joinPair, List<Symbol> mssOutputSymbols, QuerySpec multiSourceQuerySpec, QualifiedName left, QualifiedName right, QuerySpec leftQuerySpec, QuerySpec rightQuerySpec) {
    assert left.equals(joinPair.left()) : "This JoinPair has a different left Qualified name: " + joinPair.left();
    assert right.equals(joinPair.right()) : "This JoinPair has a different left Qualified name: " + joinPair.right();
    JoinType joinType = joinPair.joinType();
    if (!joinType.isOuter()) {
        return;
    }
    WhereClause where = multiSourceQuerySpec.where();
    if (!where.hasQuery()) {
        return;
    }
    final Map<QualifiedName, QuerySpec> outerRelations = new HashMap<>(2);
    switch(joinType) {
        case LEFT:
            outerRelations.put(right, rightQuerySpec);
            break;
        case RIGHT:
            outerRelations.put(left, leftQuerySpec);
            break;
        case FULL:
            outerRelations.put(left, leftQuerySpec);
            outerRelations.put(right, rightQuerySpec);
            break;
    }
    Map<Set<QualifiedName>, Symbol> splitQueries = QuerySplitter.split(where.query());
    for (QualifiedName outerRelation : outerRelations.keySet()) {
        Symbol outerRelationQuery = splitQueries.remove(Sets.newHashSet(outerRelation));
        if (outerRelationQuery != null) {
            QuerySpec outerSpec = outerRelations.get(outerRelation);
            Symbol symbol = Symbols.replaceField(outerRelationQuery, new Function<Field, Symbol>() {

                @Nullable
                @Override
                public Symbol apply(@Nullable Field input) {
                    if (input != null && input.relation().getQualifiedName().equals(outerRelation)) {
                        return Literal.NULL;
                    }
                    return input;
                }
            });
            Symbol normalized = normalizer.normalize(symbol, null);
            if (WhereClause.canMatch(normalized)) {
                splitQueries.put(Sets.newHashSet(outerRelation), outerRelationQuery);
            } else {
                applyOuterJoinRewrite(joinPair, mssOutputSymbols, multiSourceQuerySpec, outerSpec, outerRelation, splitQueries, outerRelationQuery);
            }
        }
    }
}
Also used : QualifiedName(io.crate.sql.tree.QualifiedName) JoinType(io.crate.planner.node.dql.join.JoinType) Nullable(javax.annotation.Nullable)

Aggregations

JoinType (io.crate.planner.node.dql.join.JoinType)1 QualifiedName (io.crate.sql.tree.QualifiedName)1 Nullable (javax.annotation.Nullable)1