private void onMatchRight(RelOptRuleCall call) { final JoinRelBase topJoin = call.rel(0); final JoinRelBase bottomJoin = call.rel(1); final RelNode relC = call.rel(2); final RelNode relA = bottomJoin.getLeft(); final RelNode relB = bottomJoin.getRight(); final RelOptCluster cluster = topJoin.getCluster(); // topJoin // / \ // bottomJoin C // / \ // A B final int aCount = relA.getRowType().getFieldCount(); final int bCount = relB.getRowType().getFieldCount(); final int cCount = relC.getRowType().getFieldCount(); final BitSet bBitSet = BitSets.range(aCount, aCount + bCount); // becomes // // newTopJoin // / \ // newBottomJoin B // / \ // A C // If either join is not inner, we cannot proceed. // (Is this too strict?) if (topJoin.getJoinType() != JoinRelType.INNER || bottomJoin.getJoinType() != JoinRelType.INNER) { return; } // Split the condition of topJoin into a conjunction. Each of the // parts that does not use columns from B can be pushed down. final List<RexNode> intersecting = new ArrayList<RexNode>(); final List<RexNode> nonIntersecting = new ArrayList<RexNode>(); split(topJoin.getCondition(), bBitSet, intersecting, nonIntersecting); // If there's nothing to push down, it's not worth proceeding. if (nonIntersecting.isEmpty()) { return; } // Split the condition of bottomJoin into a conjunction. Each of the // parts that use columns from B will need to be pulled up. final List<RexNode> bottomIntersecting = new ArrayList<RexNode>(); final List<RexNode> bottomNonIntersecting = new ArrayList<RexNode>(); split(bottomJoin.getCondition(), bBitSet, bottomIntersecting, bottomNonIntersecting); // target: | A | C | // source: | A | B | C | final Mappings.TargetMapping bottomMapping = Mappings.createShiftMapping( aCount + bCount + cCount, 0, 0, aCount, aCount, aCount + bCount, cCount); List<RexNode> newBottomList = new ArrayList<RexNode>(); new RexPermuteInputsShuttle(bottomMapping, relA, relC) .visitList(nonIntersecting, newBottomList); final Mappings.TargetMapping bottomBottomMapping = Mappings.createShiftMapping(aCount + bCount, 0, 0, aCount); new RexPermuteInputsShuttle(bottomBottomMapping, relA, relC) .visitList(bottomNonIntersecting, newBottomList); final RexBuilder rexBuilder = cluster.getRexBuilder(); RexNode newBottomCondition = RexUtil.composeConjunction(rexBuilder, newBottomList, false); final JoinRelBase newBottomJoin = bottomJoin.copy( bottomJoin.getTraitSet(), newBottomCondition, relA, relC, bottomJoin.getJoinType()); // target: | A | C | B | // source: | A | B | C | final Mappings.TargetMapping topMapping = Mappings.createShiftMapping( aCount + bCount + cCount, 0, 0, aCount, aCount + cCount, aCount, bCount, aCount, aCount + bCount, cCount); List<RexNode> newTopList = new ArrayList<RexNode>(); new RexPermuteInputsShuttle(topMapping, newBottomJoin, relB) .visitList(intersecting, newTopList); new RexPermuteInputsShuttle(topMapping, newBottomJoin, relB) .visitList(bottomIntersecting, newTopList); RexNode newTopCondition = RexUtil.composeConjunction(rexBuilder, newTopList, false); @SuppressWarnings("SuspiciousNameCombination") final JoinRelBase newTopJoin = topJoin.copy( topJoin.getTraitSet(), newTopCondition, newBottomJoin, relB, topJoin.getJoinType()); assert !Mappings.isIdentity(topMapping); final RelNode newProject = RelFactories.createProject(projectFactory, newTopJoin, Mappings.asList(topMapping)); call.transformTo(newProject); }
/** * Creates a relational expression that projects the given fields of the input. * * <p>Optimizes if the fields are the identity projection. * * @param child Input relational expression * @param posList Source of each projected field * @return Relational expression that projects given fields */ public static RelNode createProject(final RelNode child, final List<Integer> posList) { return RelFactories.createProject(RelFactories.DEFAULT_PROJECT_FACTORY, child, posList); }