// implement RelOptRule public void onMatch(RelOptRuleCall call) { ProjectRel origProj = call.rel(0); JoinRel joinRel = call.rel(1); // locate all fields referenced in the projection and join condition; // determine which inputs are referenced in the projection and // join condition; if all fields are being referenced and there are no // special expressions, no point in proceeding any further PushProjector pushProject = new PushProjector(origProj, joinRel.getCondition(), joinRel, preserveExprCondition); if (pushProject.locateAllRefs()) { return; } // create left and right projections, projecting only those // fields referenced on each side RelNode leftProjRel = pushProject.createProjectRefsAndExprs(joinRel.getLeft(), true, false); RelNode rightProjRel = pushProject.createProjectRefsAndExprs(joinRel.getRight(), true, true); // convert the join condition to reference the projected columns RexNode newJoinFilter = null; int[] adjustments = pushProject.getAdjustments(); if (joinRel.getCondition() != null) { List<RelDataTypeField> projJoinFieldList = new ArrayList<RelDataTypeField>(); projJoinFieldList.addAll(joinRel.getSystemFieldList()); projJoinFieldList.addAll(leftProjRel.getRowType().getFieldList()); projJoinFieldList.addAll(rightProjRel.getRowType().getFieldList()); newJoinFilter = pushProject.convertRefsAndExprs(joinRel.getCondition(), projJoinFieldList, adjustments); } // create a new joinrel with the projected children JoinRel newJoinRel = new JoinRel( joinRel.getCluster(), leftProjRel, rightProjRel, newJoinFilter, joinRel.getJoinType(), Collections.<String>emptySet(), joinRel.isSemiJoinDone(), joinRel.getSystemFieldList()); // put the original project on top of the join, converting it to // reference the modified projection list ProjectRel topProject = pushProject.createNewProject(newJoinRel, adjustments); call.transformTo(topProject); }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link JoinRel}. */ public TrimResult trimFields(JoinRel join, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = join.getRowType(); final int fieldCount = rowType.getFieldCount(); final RexNode conditionExpr = join.getCondition(); final int systemFieldCount = join.getSystemFieldList().size(); // Add in fields used in the condition. BitSet fieldsUsedPlus = (BitSet) fieldsUsed.clone(); final Set<RelDataTypeField> combinedInputExtraFields = new LinkedHashSet<RelDataTypeField>(extraFields); RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(fieldsUsedPlus, combinedInputExtraFields); conditionExpr.accept(inputFinder); // If no system fields are used, we can remove them. int systemFieldUsedCount = 0; for (int i = 0; i < systemFieldCount; ++i) { if (fieldsUsed.get(i)) { ++systemFieldUsedCount; } } final int newSystemFieldCount; if (systemFieldUsedCount == 0) { newSystemFieldCount = 0; } else { newSystemFieldCount = systemFieldCount; } int offset = systemFieldCount; int changeCount = 0; int newFieldCount = newSystemFieldCount; List<RelNode> newInputs = new ArrayList<RelNode>(2); List<Mapping> inputMappings = new ArrayList<Mapping>(); List<Integer> inputExtraFieldCounts = new ArrayList<Integer>(); for (RelNode input : join.getInputs()) { final RelDataType inputRowType = input.getRowType(); final int inputFieldCount = inputRowType.getFieldCount(); // Compute required mapping. BitSet inputFieldsUsed = new BitSet(inputFieldCount); for (int bit : Util.toIter(fieldsUsedPlus)) { if (bit >= offset && bit < offset + inputFieldCount) { inputFieldsUsed.set(bit - offset); } } // If there are system fields, we automatically use the // corresponding field in each input. if (newSystemFieldCount > 0) { // calling with newSystemFieldCount == 0 should be safe but hits // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6222207 inputFieldsUsed.set(0, newSystemFieldCount); } // FIXME: We ought to collect extra fields for each input // individually. For now, we assume that just one input has // on-demand fields. Set<RelDataTypeField> inputExtraFields = input.getRowType().getField("_extra") == null ? Collections.<RelDataTypeField>emptySet() : combinedInputExtraFields; inputExtraFieldCounts.add(inputExtraFields.size()); TrimResult trimResult = trimChild(join, input, inputFieldsUsed, inputExtraFields); newInputs.add(trimResult.left); if (trimResult.left != input) { ++changeCount; } final Mapping inputMapping = trimResult.right; inputMappings.add(inputMapping); // Move offset to point to start of next input. offset += inputFieldCount; newFieldCount += inputMapping.getTargetCount() + inputExtraFields.size(); } Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, newFieldCount); for (int i = 0; i < newSystemFieldCount; ++i) { mapping.set(i, i); } offset = systemFieldCount; int newOffset = newSystemFieldCount; for (int i = 0; i < inputMappings.size(); i++) { Mapping inputMapping = inputMappings.get(i); for (IntPair pair : inputMapping) { mapping.set(pair.source + offset, pair.target + newOffset); } offset += inputMapping.getSourceCount(); newOffset += inputMapping.getTargetCount() + inputExtraFieldCounts.get(i); } if (changeCount == 0 && mapping.isIdentity()) { return new TrimResult(join, Mappings.createIdentity(fieldCount)); } // Build new join. final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(mapping, newInputs.get(0), newInputs.get(1)); RexNode newConditionExpr = conditionExpr.accept(shuttle); final JoinRel newJoin = join.copy(join.getTraitSet(), newConditionExpr, newInputs.get(0), newInputs.get(1)); return new TrimResult(newJoin, mapping); }