/** * Trims the fields of an input relational expression. * * @param rel Relational expression * @param input Input relational expression, whose fields to trim * @param fieldsUsed Bitmap of fields needed by the consumer * @return New relational expression and its field mapping */ protected TrimResult trimChild( RelNode rel, RelNode input, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { Util.discard(rel); if (input.getClass().getName().endsWith("MedMdrClassExtentRel")) { // MedMdrJoinRule cannot handle Join of Project of // MedMdrClassExtentRel, only naked MedMdrClassExtentRel. // So, disable trimming. fieldsUsed = Util.bitSetBetween(0, input.getRowType().getFieldCount()); } return dispatchTrimFields(input, fieldsUsed, extraFields); }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link AggregateRel}. */ public TrimResult trimFields( AggregateRel aggregate, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { // Fields: // // | sys fields | group fields | agg functions | // // Two kinds of trimming: // // 1. If agg rel has system fields but none of these are used, create an // agg rel with no system fields. // // 2. If aggregate functions are not used, remove them. // // But grouping fields stay, even if they are not used. final RelDataType rowType = aggregate.getRowType(); // Compute which input fields are used. BitSet inputFieldsUsed = new BitSet(); // 1. group fields are always used for (int i : Util.toIter(aggregate.getGroupSet())) { inputFieldsUsed.set(i); } // 2. agg functions for (AggregateCall aggCall : aggregate.getAggCallList()) { for (int i : aggCall.getArgList()) { inputFieldsUsed.set(i); } } // Create input with trimmed columns. final RelNode input = aggregate.getInput(0); final Set<RelDataTypeField> inputExtraFields = Collections.emptySet(); final TrimResult trimResult = trimChild(aggregate, input, inputFieldsUsed, inputExtraFields); final RelNode newInput = trimResult.left; final Mapping inputMapping = trimResult.right; // If the input is unchanged, and we need to project all columns, // there's nothing to do. if (input == newInput && fieldsUsed.equals(Util.bitSetBetween(0, rowType.getFieldCount()))) { return new TrimResult(aggregate, Mappings.createIdentity(rowType.getFieldCount())); } // Which agg calls are used by our consumer? final int groupCount = aggregate.getGroupSet().cardinality(); int j = groupCount; int usedAggCallCount = 0; for (int i = 0; i < aggregate.getAggCallList().size(); i++) { if (fieldsUsed.get(j++)) { ++usedAggCallCount; } } // Offset due to the number of system fields having changed. Mapping mapping = Mappings.create( MappingType.InverseSurjection, rowType.getFieldCount(), groupCount + usedAggCallCount); final BitSet newGroupSet = Mappings.apply(inputMapping, aggregate.getGroupSet()); // Populate mapping of where to find the fields. System and grouping // fields first. for (IntPair pair : inputMapping) { if (pair.source < groupCount) { mapping.set(pair.source, pair.target); } } // Now create new agg calls, and populate mapping for them. final List<AggregateCall> newAggCallList = new ArrayList<AggregateCall>(); j = groupCount; for (AggregateCall aggCall : aggregate.getAggCallList()) { if (fieldsUsed.get(j)) { AggregateCall newAggCall = new AggregateCall( aggCall.getAggregation(), aggCall.isDistinct(), Mappings.apply2(inputMapping, aggCall.getArgList()), aggCall.getType(), aggCall.getName()); if (newAggCall.equals(aggCall)) { newAggCall = aggCall; // immutable -> canonize to save space } mapping.set(j, groupCount + newAggCallList.size()); newAggCallList.add(newAggCall); } ++j; } RelNode newAggregate = new AggregateRel(aggregate.getCluster(), newInput, newGroupSet, newAggCallList); assert newAggregate.getClass() == aggregate.getClass(); return new TrimResult(newAggregate, mapping); }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link ProjectRel}. */ public TrimResult trimFields( ProjectRel project, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = project.getRowType(); final int fieldCount = rowType.getFieldCount(); final RelNode input = project.getChild(); final RelDataType inputRowType = input.getRowType(); // Which fields are required from the input? BitSet inputFieldsUsed = new BitSet(inputRowType.getFieldCount()); final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<RelDataTypeField>(extraFields); RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputFieldsUsed, inputExtraFields); for (Ord<RexNode> ord : Ord.zip(project.getProjects())) { if (fieldsUsed.get(ord.i)) { ord.e.accept(inputFinder); } } // Create input with trimmed columns. TrimResult trimResult = trimChild(project, input, inputFieldsUsed, inputExtraFields); RelNode newInput = trimResult.left; final Mapping inputMapping = trimResult.right; // If the input is unchanged, and we need to project all columns, // there's nothing we can do. if (newInput == input && fieldsUsed.cardinality() == fieldCount) { return new TrimResult(project, Mappings.createIdentity(fieldCount)); } // Some parts of the system can't handle rows with zero fields, so // pretend that one field is used. if (fieldsUsed.cardinality() == 0) { final Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, 1); final RexLiteral expr = project.getCluster().getRexBuilder().makeExactLiteral(BigDecimal.ZERO); RelDataType newRowType = project .getCluster() .getTypeFactory() .createStructType( Collections.singletonList(expr.getType()), Collections.singletonList("DUMMY")); ProjectRel newProject = new ProjectRel( project.getCluster(), project.getCluster().traitSetOf(RelCollationImpl.EMPTY), newInput, Collections.<RexNode>singletonList(expr), newRowType, project.getFlags()); return new TrimResult(newProject, mapping); } // Build new project expressions, and populate the mapping. List<RexNode> newProjectExprList = new ArrayList<RexNode>(); final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(inputMapping, newInput); final Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, fieldsUsed.cardinality()); for (Ord<RexNode> ord : Ord.zip(project.getProjects())) { if (fieldsUsed.get(ord.i)) { mapping.set(ord.i, newProjectExprList.size()); RexNode newProjectExpr = ord.e.accept(shuttle); newProjectExprList.add(newProjectExpr); } } final RelDataType newRowType = project .getCluster() .getTypeFactory() .createStructType(Mappings.apply3(mapping, rowType.getFieldList())); final List<RelCollation> newCollations = RexUtil.apply(inputMapping, project.getCollationList()); final RelNode newProject; if (RemoveTrivialProjectRule.isIdentity( newProjectExprList, newRowType, newInput.getRowType())) { // The new project would be the identity. It is equivalent to return // its child. newProject = newInput; } else { newProject = new ProjectRel( project.getCluster(), project .getCluster() .traitSetOf( newCollations.isEmpty() ? RelCollationImpl.EMPTY : newCollations.get(0)), newInput, newProjectExprList, newRowType, project.getFlags()); assert newProject.getClass() == project.getClass(); } return new TrimResult(newProject, mapping); }