/** * Divides one mapping by another. * * <p>{@code divide(A, B)} returns a mapping C such that B . C (the mapping B followed by the * mapping C) is equivalent to A. * * @param mapping1 First mapping * @param mapping2 Second mapping * @return Mapping mapping3 such that mapping1 = mapping2 . mapping3 */ public static Mapping divide(Mapping mapping1, Mapping mapping2) { if (mapping1.getSourceCount() != mapping2.getSourceCount()) { throw new IllegalArgumentException(); } Mapping remaining = create( MappingType.INVERSE_SURJECTION, mapping2.getTargetCount(), mapping1.getTargetCount()); for (int target = 0; target < mapping1.getTargetCount(); ++target) { int source = mapping1.getSourceOpt(target); if (source >= 0) { int x = mapping2.getTarget(source); remaining.set(x, target); } } return remaining; }
/** * Multiplies one mapping by another. * * <p>{@code divide(A, B)} returns a mapping C such that B . C (the mapping B followed by the * mapping C) is equivalent to A. * * @param mapping1 First mapping * @param mapping2 Second mapping * @return Mapping mapping3 such that mapping1 = mapping2 . mapping3 */ public static Mapping multiply(Mapping mapping1, Mapping mapping2) { if (mapping1.getTargetCount() != mapping2.getSourceCount()) { throw new IllegalArgumentException(); } Mapping product = create( MappingType.INVERSE_SURJECTION, mapping1.getSourceCount(), mapping2.getTargetCount()); for (int source = 0; source < mapping1.getSourceCount(); ++source) { int x = mapping1.getTargetOpt(source); if (x >= 0) { int target = mapping2.getTarget(x); product.set(source, target); } } return product; }
/** * Applies a mapping to a list. * * @param mapping Mapping * @param list List * @param <T> Element type * @return List with elements permuted according to mapping */ public static <T> List<T> apply(final Mapping mapping, final List<T> list) { if (mapping.getSourceCount() != list.size()) { // REVIEW: too strict? throw new IllegalArgumentException( "mapping source count " + mapping.getSourceCount() + " does not match list size " + list.size()); } final int targetCount = mapping.getTargetCount(); final List<T> list2 = new ArrayList<T>(targetCount); for (int target = 0; target < targetCount; ++target) { final int source = mapping.getSource(target); list2.add(list.get(source)); } return list2; }
/** * Invokes {@link #trimFields}, or the appropriate method for the type of the rel parameter, using * multi-method dispatch. * * @param rel Relational expression * @param fieldsUsed Bitmap of fields needed by the consumer * @return New relational expression and its field mapping */ protected final TrimResult dispatchTrimFields( RelNode rel, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final TrimResult trimResult = trimFieldsDispatcher.invoke(rel, fieldsUsed, extraFields); final RelNode newRel = trimResult.left; final Mapping mapping = trimResult.right; final int fieldCount = rel.getRowType().getFieldCount(); assert mapping.getSourceCount() == fieldCount : "source: " + mapping.getSourceCount() + " != " + fieldCount; final int newFieldCount = newRel.getRowType().getFieldCount(); assert mapping.getTargetCount() + extraFields.size() == newFieldCount : "target: " + mapping.getTargetCount() + " + " + extraFields.size() + " != " + newFieldCount; if (Bug.TodoFixed) assert newFieldCount > 0 : "rel has no fields after trim: " + rel; if (newRel.equals(rel)) { return new TrimResult(rel, mapping); } return trimResult; }
/** 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); }
public int getTargetCount() { return parent.getSourceCount(); }