protected void perform()

in aios/sql/iquan/java/iquan_core/src/main/java/com/taobao/search/iquan/core/rel/rules/logical/calcite/IquanFilterJoinRule.java [156:339]


    protected void perform(RelOptRuleCall call, Filter filter, Join join) {
        final List<RexNode> deterministicJoinFilters = Lists.<RexNode>newArrayList();
        final ImmutableList.Builder<RexNode> nondeterministicJoinFiltersBuilder =
                ImmutableList.<RexNode>builder();
        for (RexNode expr : RelOptUtil.conjunctions(join.getCondition())) {
            if (RexUtil.isDeterministic(expr)) {
                deterministicJoinFilters.add(expr);
            } else {
                nondeterministicJoinFiltersBuilder.add(expr);
            }
        }
        final List<RexNode> nondeterministicJoinFilters =
                nondeterministicJoinFiltersBuilder.build();
        final List<RexNode> origDeterministicJoinFilters =
                ImmutableList.copyOf(deterministicJoinFilters);

        // If there is only the joinRel,
        // make sure it does not match a cartesian product joinRel
        // (with "true" condition), otherwise this rule will be applied
        // again on the new cartesian product joinRel.
        if (filter == null && deterministicJoinFilters.isEmpty()) {
            return;
        }

        final List<RexNode> deterministicAboveFilters = Lists.<RexNode>newArrayList();
        final ImmutableList.Builder<RexNode> nondeterministicAboveFiltersBuilder =
                ImmutableList.<RexNode>builder();
        if (filter != null) {
            for (RexNode expr : RelOptUtil.conjunctions(filter.getCondition())) {
                if (RexUtil.isDeterministic(expr)) {
                    deterministicAboveFilters.add(expr);
                } else {
                    nondeterministicAboveFiltersBuilder.add(expr);
                }
            }
        }

        final List<RexNode> nondeterministicAboveFilters =
                nondeterministicAboveFiltersBuilder.build();
        final ImmutableList<RexNode> origDeterministicAboveFilters =
                ImmutableList.copyOf(deterministicAboveFilters);

        // Simplify Outer Joins
        JoinRelType joinType = join.getJoinType();
        if (smart
                && !origDeterministicAboveFilters.isEmpty()
                && join.getJoinType() != JoinRelType.INNER) {
            joinType = RelOptUtil.simplifyJoin(join, origDeterministicAboveFilters, joinType);
        }

        final List<RexNode> leftFilters = new ArrayList<>();
        final List<RexNode> rightFilters = new ArrayList<>();

        // TODO - add logic to derive additional filters.	E.g., from
        // (t1.a = 1 AND t2.a = 2) OR (t1.b = 3 AND t2.b = 4), you can
        // derive table filters:
        // (t1.a = 1 OR t1.b = 3)
        // (t2.a = 2 OR t2.b = 4)

        // Try to push down above filters. These are typically where clause
        // filters. They can be pushed down if they are not on the NULL
        // generating side.
        boolean filterPushed = false;
        if (RelOptUtil.classifyFilters(
                join,
                deterministicAboveFilters,
                joinType,
                (joinType == JoinRelType.INNER),
                !joinType.generatesNullsOnLeft(),
                !joinType.generatesNullsOnRight(),
                deterministicJoinFilters,
                leftFilters,
                rightFilters)) {
            filterPushed = true;
        }

        // Move join filters up if needed
        validateJoinFilters(deterministicAboveFilters, deterministicJoinFilters, join, joinType);

        // If no filter got pushed after validate, reset filterPushed flag
        if (leftFilters.isEmpty()
                && rightFilters.isEmpty()
                && deterministicJoinFilters.size() == origDeterministicJoinFilters.size()) {
            if (com.google.common.collect.Sets.newHashSet(deterministicJoinFilters)
                    .equals(com.google.common.collect.Sets.newHashSet(origDeterministicJoinFilters))) {
                filterPushed = false;
            }
        }

        boolean isAntiJoin = join.getJoinType().equals(JoinRelType.ANTI);

        // Try to push down filters in ON clause. A ON clause filter can only be
        // pushed down if it does not affect the non-matching set, i.e. it is
        // not on the side which is preserved.
        // A ON clause filter of anti-join can not be pushed down.
        if (!isAntiJoin && RelOptUtil.classifyFilters(
                join,
                deterministicJoinFilters,
                joinType,
                false,
                !joinType.generatesNullsOnRight(),
                !joinType.generatesNullsOnLeft(),
                deterministicJoinFilters,
                leftFilters,
                rightFilters)) {
            filterPushed = true;
        }

        // if nothing actually got pushed and there is nothing leftover,
        // then this rule is a no-op
        if ((!filterPushed
                && joinType == join.getJoinType())
                || (deterministicJoinFilters.isEmpty()
                && leftFilters.isEmpty()
                && rightFilters.isEmpty())) {
            return;
        }

        // create Filters on top of the children if any filters were
        // pushed to them
        final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
        final RelBuilder relBuilder = call.builder();
        final RelNode leftRel =
                relBuilder.push(join.getLeft()).filter(leftFilters).build();
        final RelNode rightRel =
                relBuilder.push(join.getRight()).filter(rightFilters).build();

        // create the new join node referencing the new children and
        // containing its new join filters (if there are any)
        final ImmutableList<RelDataType> fieldTypes =
                ImmutableList.<RelDataType>builder()
                        .addAll(RelOptUtil.getFieldTypeList(leftRel.getRowType()))
                        .addAll(RelOptUtil.getFieldTypeList(rightRel.getRowType())).build();
        final List<RexNode> leftJoinFilters = ImmutableList.<RexNode>builder()
                .addAll(deterministicJoinFilters)
                .addAll(nondeterministicJoinFilters)
                .build();
        final RexNode joinFilter =
                RexUtil.composeConjunction(rexBuilder,
                        RexUtil.fixUp(rexBuilder, leftJoinFilters, fieldTypes),
                        false);

        // If nothing actually got pushed and there is nothing leftover,
        // then this rule is a no-op
        if (joinFilter.isAlwaysTrue()
                && leftFilters.isEmpty()
                && rightFilters.isEmpty()
                && joinType == join.getJoinType()) {
            return;
        }

        RelNode newJoinRel =
                join.copy(
                        join.getTraitSet(),
                        joinFilter,
                        leftRel,
                        rightRel,
                        joinType,
                        join.isSemiJoinDone());
        call.getPlanner().onCopy(join, newJoinRel);
        if (!leftFilters.isEmpty()) {
            call.getPlanner().onCopy(filter, leftRel);
        }
        if (!rightFilters.isEmpty()) {
            call.getPlanner().onCopy(filter, rightRel);
        }

        relBuilder.push(newJoinRel);

        // Create a project on top of the join if some of the columns have become
        // NOT NULL due to the join-type getting stricter.
        relBuilder.convert(join.getRowType(), false);

        final List<RexNode> leftAboveFilters = ImmutableList.<RexNode>builder()
                .addAll(deterministicAboveFilters)
                .addAll(nondeterministicAboveFilters)
                .build();
        // create a FilterRel on top of the join if needed
        relBuilder.filter(
                RexUtil.fixUp(rexBuilder, leftAboveFilters,
                        RelOptUtil.getFieldTypeList(relBuilder.peek().getRowType())));

        call.transformTo(relBuilder.build());
    }