/** * Infers predicates for an Aggregate. * * <p>Pulls up predicates that only contains references to columns in the GroupSet. For e.g. * * <pre> * childPullUpExprs : { a > 7, b + c < 10, a + e = 9} * groupSet : { a, b} * pulledUpExprs : { a > 7} * </pre> */ public RelOptPredicateList getPredicates(Aggregate agg) { RelNode child = agg.getInput(); RelOptPredicateList childInfo = RelMetadataQuery.getPulledUpPredicates(child); List<RexNode> aggPullUpPredicates = new ArrayList<RexNode>(); ImmutableBitSet groupKeys = agg.getGroupSet(); Mapping m = Mappings.create( MappingType.PARTIAL_FUNCTION, child.getRowType().getFieldCount(), agg.getRowType().getFieldCount()); int i = 0; for (int j : groupKeys) { m.set(j, i++); } for (RexNode r : childInfo.pulledUpPredicates) { ImmutableBitSet rCols = RelOptUtil.InputFinder.bits(r); if (groupKeys.contains(rCols)) { r = r.accept(new RexPermuteInputsShuttle(m, child)); aggPullUpPredicates.add(r); } } return RelOptPredicateList.of(aggPullUpPredicates); }
/** * Infers predicates for a project. * * <ol> * <li>create a mapping from input to projection. Map only positions that directly reference an * input column. * <li>Expressions that only contain above columns are retained in the Project's pullExpressions * list. * <li>For e.g. expression 'a + e = 9' below will not be pulled up because 'e' is not in the * projection list. * <pre> * childPullUpExprs: {a > 7, b + c < 10, a + e = 9} * projectionExprs: {a, b, c, e / 2} * projectionPullupExprs: {a > 7, b + c < 10} * </pre> * </ol> */ public RelOptPredicateList getPredicates(Project project) { RelNode child = project.getInput(); final RexBuilder rexBuilder = project.getCluster().getRexBuilder(); RelOptPredicateList childInfo = RelMetadataQuery.getPulledUpPredicates(child); List<RexNode> projectPullUpPredicates = new ArrayList<RexNode>(); ImmutableBitSet.Builder columnsMappedBuilder = ImmutableBitSet.builder(); Mapping m = Mappings.create( MappingType.PARTIAL_FUNCTION, child.getRowType().getFieldCount(), project.getRowType().getFieldCount()); for (Ord<RexNode> o : Ord.zip(project.getProjects())) { if (o.e instanceof RexInputRef) { int sIdx = ((RexInputRef) o.e).getIndex(); m.set(sIdx, o.i); columnsMappedBuilder.set(sIdx); } } // Go over childPullUpPredicates. If a predicate only contains columns in // 'columnsMapped' construct a new predicate based on mapping. final ImmutableBitSet columnsMapped = columnsMappedBuilder.build(); for (RexNode r : childInfo.pulledUpPredicates) { ImmutableBitSet rCols = RelOptUtil.InputFinder.bits(r); if (columnsMapped.contains(rCols)) { r = r.accept(new RexPermuteInputsShuttle(m, child)); projectPullUpPredicates.add(r); } } // Project can also generate constants. We need to include them. for (Ord<RexNode> expr : Ord.zip(project.getProjects())) { if (RexLiteral.isNullLiteral(expr.e)) { projectPullUpPredicates.add( rexBuilder.makeCall( SqlStdOperatorTable.IS_NULL, rexBuilder.makeInputRef(project, expr.i))); } else if (RexUtil.isConstant(expr.e)) { final List<RexNode> args = ImmutableList.of(rexBuilder.makeInputRef(project, expr.i), expr.e); final SqlOperator op = args.get(0).getType().isNullable() || args.get(1).getType().isNullable() ? SqlStdOperatorTable.IS_NOT_DISTINCT_FROM : SqlStdOperatorTable.EQUALS; projectPullUpPredicates.add(rexBuilder.makeCall(op, args)); } } return RelOptPredicateList.of(projectPullUpPredicates); }
private void infer( RexNode predicates, Set<String> allExprsDigests, List<RexNode> inferedPredicates, boolean includeEqualityInference, ImmutableBitSet inferringFields) { for (RexNode r : RelOptUtil.conjunctions(predicates)) { if (!includeEqualityInference && equalityPredicates.contains(r.toString())) { continue; } for (Mapping m : mappings(r)) { RexNode tr = r.accept(new RexPermuteInputsShuttle(m, joinRel.getInput(0), joinRel.getInput(1))); if (inferringFields.contains(RelOptUtil.InputFinder.bits(tr)) && !allExprsDigests.contains(tr.toString()) && !isAlwaysTrue(tr)) { inferedPredicates.add(tr); allExprsDigests.add(tr.toString()); } } } }
/** * The PullUp Strategy is sound but not complete. * * <ol> * <li>We only pullUp inferred predicates for now. Pulling up existing predicates causes an * explosion of duplicates. The existing predicates are pushed back down as new * predicates. Once we have rules to eliminate duplicate Filter conditions, we should * pullUp all predicates. * <li>For Left Outer: we infer new predicates from the left and set them as applicable on the * Right side. No predicates are pulledUp. * <li>Right Outer Joins are handled in an analogous manner. * <li>For Full Outer Joins no predicates are pulledUp or inferred. * </ol> */ public RelOptPredicateList inferPredicates(boolean includeEqualityInference) { List<RexNode> inferredPredicates = new ArrayList<RexNode>(); Set<String> allExprsDigests = new HashSet<String>(this.allExprsDigests); final JoinRelType joinType = joinRel.getJoinType(); switch (joinType) { case INNER: case LEFT: infer( leftChildPredicates, allExprsDigests, inferredPredicates, includeEqualityInference, joinType == JoinRelType.LEFT ? rightFieldsBitSet : allFieldsBitSet); break; } switch (joinType) { case INNER: case RIGHT: infer( rightChildPredicates, allExprsDigests, inferredPredicates, includeEqualityInference, joinType == JoinRelType.RIGHT ? leftFieldsBitSet : allFieldsBitSet); break; } Mappings.TargetMapping rightMapping = Mappings.createShiftMapping( nSysFields + nFieldsLeft + nFieldsRight, 0, nSysFields + nFieldsLeft, nFieldsRight); final RexPermuteInputsShuttle rightPermute = new RexPermuteInputsShuttle(rightMapping, joinRel); Mappings.TargetMapping leftMapping = Mappings.createShiftMapping(nSysFields + nFieldsLeft, 0, nSysFields, nFieldsLeft); final RexPermuteInputsShuttle leftPermute = new RexPermuteInputsShuttle(leftMapping, joinRel); List<RexNode> leftInferredPredicates = new ArrayList<RexNode>(); List<RexNode> rightInferredPredicates = new ArrayList<RexNode>(); for (RexNode iP : inferredPredicates) { ImmutableBitSet iPBitSet = RelOptUtil.InputFinder.bits(iP); if (leftFieldsBitSet.contains(iPBitSet)) { leftInferredPredicates.add(iP.accept(leftPermute)); } else if (rightFieldsBitSet.contains(iPBitSet)) { rightInferredPredicates.add(iP.accept(rightPermute)); } } switch (joinType) { case INNER: Iterable<RexNode> pulledUpPredicates; if (isSemiJoin) { pulledUpPredicates = Iterables.concat( RelOptUtil.conjunctions(leftChildPredicates), leftInferredPredicates); } else { pulledUpPredicates = Iterables.concat( RelOptUtil.conjunctions(leftChildPredicates), RelOptUtil.conjunctions(rightChildPredicates), RelOptUtil.conjunctions(joinRel.getCondition()), inferredPredicates); } return RelOptPredicateList.of( pulledUpPredicates, leftInferredPredicates, rightInferredPredicates); case LEFT: return RelOptPredicateList.of( RelOptUtil.conjunctions(leftChildPredicates), leftInferredPredicates, rightInferredPredicates); case RIGHT: return RelOptPredicateList.of( RelOptUtil.conjunctions(rightChildPredicates), inferredPredicates, EMPTY_LIST); default: assert inferredPredicates.size() == 0; return RelOptPredicateList.EMPTY; } }
// We create the join predicate info object. The object contains the join condition, // split accordingly. If the join condition is not part of the equi-join predicate, // the returned object will be typed as SQLKind.OTHER. private static JoinLeafPredicateInfo constructJoinLeafPredicateInfo( List<RelNode> inputs, List<RelDataTypeField> systemFieldList, RexNode pe) throws CalciteSemanticException { JoinLeafPredicateInfo jlpi = null; List<Integer> filterNulls = new ArrayList<Integer>(); List<List<RexNode>> joinExprs = new ArrayList<List<RexNode>>(); for (int i = 0; i < inputs.size(); i++) { joinExprs.add(new ArrayList<RexNode>()); } // 1. Split leaf join predicate to expressions from left, right RexNode otherConditions = HiveRelOptUtil.splitHiveJoinCondition( systemFieldList, inputs, pe, joinExprs, filterNulls, null); if (otherConditions.isAlwaysTrue()) { // 2. Collect child projection indexes used List<Set<Integer>> projsJoinKeysInChildSchema = new ArrayList<Set<Integer>>(); for (int i = 0; i < inputs.size(); i++) { ImmutableSet.Builder<Integer> projsFromInputJoinKeysInChildSchema = ImmutableSet.builder(); InputReferencedVisitor irvLeft = new InputReferencedVisitor(); irvLeft.apply(joinExprs.get(i)); projsFromInputJoinKeysInChildSchema.addAll(irvLeft.inputPosReferenced); projsJoinKeysInChildSchema.add(projsFromInputJoinKeysInChildSchema.build()); } // 3. Translate projection indexes to join schema, by adding offset. List<Set<Integer>> projsJoinKeysInJoinSchema = new ArrayList<Set<Integer>>(); // The offset of the first input does not need to change. projsJoinKeysInJoinSchema.add(projsJoinKeysInChildSchema.get(0)); for (int i = 1; i < inputs.size(); i++) { int offSet = inputs.get(i - 1).getRowType().getFieldCount(); ImmutableSet.Builder<Integer> projsFromInputJoinKeysInJoinSchema = ImmutableSet.builder(); for (Integer indx : projsJoinKeysInChildSchema.get(i)) { projsFromInputJoinKeysInJoinSchema.add(indx + offSet); } projsJoinKeysInJoinSchema.add(projsFromInputJoinKeysInJoinSchema.build()); } // 4. Construct JoinLeafPredicateInfo jlpi = new JoinLeafPredicateInfo( pe.getKind(), joinExprs, projsJoinKeysInChildSchema, projsJoinKeysInJoinSchema); } else { // 2. Construct JoinLeafPredicateInfo ImmutableBitSet refCols = InputFinder.bits(pe); int count = 0; for (int i = 0; i < inputs.size(); i++) { final int length = inputs.get(i).getRowType().getFieldCount(); ImmutableBitSet inputRange = ImmutableBitSet.range(count, count + length); if (inputRange.contains(refCols)) { joinExprs.get(i).add(pe); } count += length; } jlpi = new JoinLeafPredicateInfo( SqlKind.OTHER, joinExprs, new ArrayList<Set<Integer>>(), new ArrayList<Set<Integer>>()); } return jlpi; }