/** * Creates a relational expression which permutes the output fields of a relational expression * according to a permutation. * * <p>Optimizations: * * <ul> * <li>If the relational expression is a {@link CalcRel} or {@link ProjectRel} which is already * acting as a permutation, combines the new permutation with the old; * <li>If the permutation is the identity, returns the original relational expression. * </ul> * * <p>If a permutation is combined with its inverse, these optimizations would combine to remove * them both. * * @param rel Relational expression * @param permutation Permutation to apply to fields * @param fieldNames Field names; if null, or if a particular entry is null, the name of the * permuted field is used * @return relational expression which permutes its input fields */ public static RelNode permute(RelNode rel, Permutation permutation, List<String> fieldNames) { if (permutation.isIdentity()) { return rel; } if (rel instanceof CalcRel) { CalcRel calcRel = (CalcRel) rel; Permutation permutation1 = calcRel.getProgram().getPermutation(); if (permutation1 != null) { Permutation permutation2 = permutation.product(permutation1); return permute(rel, permutation2, null); } } if (rel instanceof ProjectRel) { Permutation permutation1 = ((ProjectRel) rel).getPermutation(); if (permutation1 != null) { Permutation permutation2 = permutation.product(permutation1); return permute(rel, permutation2, null); } } final List<RelDataType> outputTypeList = new ArrayList<RelDataType>(); final List<String> outputNameList = new ArrayList<String>(); final List<RexNode> exprList = new ArrayList<RexNode>(); final List<RexLocalRef> projectRefList = new ArrayList<RexLocalRef>(); final List<RelDataTypeField> fields = rel.getRowType().getFieldList(); for (int i = 0; i < permutation.getTargetCount(); i++) { int target = permutation.getTarget(i); final RelDataTypeField targetField = fields.get(target); outputTypeList.add(targetField.getType()); outputNameList.add( ((fieldNames == null) || (fieldNames.size() <= i) || (fieldNames.get(i) == null)) ? targetField.getName() : fieldNames.get(i)); exprList.add(rel.getCluster().getRexBuilder().makeInputRef(fields.get(i).getType(), i)); final int source = permutation.getSource(i); projectRefList.add(new RexLocalRef(source, fields.get(source).getType())); } final RexProgram program = new RexProgram( rel.getRowType(), exprList, projectRefList, null, rel.getCluster().getTypeFactory().createStructType(outputTypeList, outputNameList)); return new CalcRel( rel.getCluster(), rel.getTraitSet(), rel, program.getOutputRowType(), program, Collections.<RelCollation>emptyList()); }
/** * Trims a child relational expression, then adds back a dummy project to restore the fields that * were removed. * * <p>Sounds pointless? It causes unused fields to be removed further down the tree (towards the * leaves), but it ensure that the consuming relational expression continues to see the same * fields. * * @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 trimChildRestore( RelNode rel, RelNode input, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { TrimResult trimResult = trimChild(rel, input, fieldsUsed, extraFields); if (trimResult.right.isIdentity()) { return trimResult; } final RelDataType rowType = input.getRowType(); List<RelDataTypeField> fieldList = rowType.getFieldList(); final List<RexNode> exprList = new ArrayList<RexNode>(); final List<String> nameList = rowType.getFieldNames(); RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); assert trimResult.right.getSourceCount() == fieldList.size(); for (int i = 0; i < fieldList.size(); i++) { int source = trimResult.right.getTargetOpt(i); RelDataTypeField field = fieldList.get(i); exprList.add( source < 0 ? rexBuilder.makeZeroLiteral(field.getType()) : rexBuilder.makeInputRef(field.getType(), source)); } RelNode project = CalcRel.createProject(trimResult.left, exprList, nameList); return new TrimResult(project, Mappings.createIdentity(fieldList.size())); }
/** * Creates a relational expression which projects the output fields of a relational expression * according to a partial mapping. * * <p>A partial mapping is weaker than a permutation: every target has one source, but a source * may have 0, 1 or more than one targets. Usually the result will have fewer fields than the * source, unless some source fields are projected multiple times. * * <p>This method could optimize the result as {@link #permute} does, but does not at present. * * @param rel Relational expression * @param mapping Mapping from source fields to target fields. The mapping type must obey the * constaints {@link MappingType#isMandatorySource()} and {@link * MappingType#isSingleSource()}, as does {@link MappingType#InverseFunction}. * @param fieldNames Field names; if null, or if a particular entry is null, the name of the * permuted field is used * @return relational expression which projects a subset of the input fields */ public static RelNode projectMapping(RelNode rel, Mapping mapping, List<String> fieldNames) { assert mapping.getMappingType().isSingleSource(); assert mapping.getMappingType().isMandatorySource(); if (mapping.isIdentity()) { return rel; } final List<RelDataType> outputTypeList = new ArrayList<RelDataType>(); final List<String> outputNameList = new ArrayList<String>(); final List<RexNode> exprList = new ArrayList<RexNode>(); final List<RexLocalRef> projectRefList = new ArrayList<RexLocalRef>(); final List<RelDataTypeField> fields = rel.getRowType().getFieldList(); for (int i = 0; i < fields.size(); i++) { final RelDataTypeField field = fields.get(i); exprList.add(rel.getCluster().getRexBuilder().makeInputRef(field.getType(), i)); } for (int i = 0; i < mapping.getTargetCount(); i++) { int source = mapping.getSource(i); final RelDataTypeField sourceField = fields.get(source); outputTypeList.add(sourceField.getType()); outputNameList.add( ((fieldNames == null) || (fieldNames.size() <= i) || (fieldNames.get(i) == null)) ? sourceField.getName() : fieldNames.get(i)); projectRefList.add(new RexLocalRef(source, sourceField.getType())); } final RexProgram program = new RexProgram( rel.getRowType(), exprList, projectRefList, null, rel.getCluster().getTypeFactory().createStructType(outputTypeList, outputNameList)); return new CalcRel( rel.getCluster(), rel.getTraitSet(), rel, program.getOutputRowType(), program, Collections.<RelCollation>emptyList()); }