/** * Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link * org.eigenbase.rel.TableAccessRel}. */ public TrimResult trimFields( final TableAccessRelBase tableAccessRel, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final int fieldCount = tableAccessRel.getRowType().getFieldCount(); if (fieldsUsed.equals(Util.bitSetBetween(0, fieldCount)) && extraFields.isEmpty()) { return trimFields((RelNode) tableAccessRel, fieldsUsed, extraFields); } final RelNode newTableAccessRel = tableAccessRel.project(fieldsUsed, extraFields); final Mapping mapping = createMapping(fieldsUsed, fieldCount); return new TrimResult(newTableAccessRel, mapping); }
/** * Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link org.eigenbase.rel.ValuesRel}. */ public TrimResult trimFields( ValuesRel values, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = values.getRowType(); final int fieldCount = rowType.getFieldCount(); // If they are asking for no fields, we can't give them what they want, // because zero-column records are illegal. Give them the last field, // which is unlikely to be a system field. if (fieldsUsed.isEmpty()) { fieldsUsed = Util.bitSetBetween(fieldCount - 1, fieldCount); } // If all fields are used, return unchanged. if (fieldsUsed.equals(Util.bitSetBetween(0, fieldCount))) { Mapping mapping = Mappings.createIdentity(fieldCount); return new TrimResult(values, mapping); } List<List<RexLiteral>> newTuples = new ArrayList<List<RexLiteral>>(); for (List<RexLiteral> tuple : values.getTuples()) { List<RexLiteral> newTuple = new ArrayList<RexLiteral>(); for (int field : Util.toIter(fieldsUsed)) { newTuple.add(tuple.get(field)); } newTuples.add(newTuple); } final Mapping mapping = createMapping(fieldsUsed, fieldCount); RelDataType newRowType = values .getCluster() .getTypeFactory() .createStructType(Mappings.apply3(mapping, rowType.getFieldList())); final ValuesRel newValues = new ValuesRel(values.getCluster(), newRowType, newTuples); return new TrimResult(newValues, mapping); }
/** 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); }