Rel.Remap holds nothing but its index list and validates none of it.
|
/** |
|
* Reorders and/or selects a relation's output fields by index, producing the emitted record type. |
|
*/ |
|
@Value.Immutable |
|
abstract class Remap { |
|
/** |
|
* Returns the output field indices, in emission order. |
|
* |
|
* @return the field indices |
|
*/ |
|
public abstract List<Integer> indices(); |
|
|
|
/** |
|
* Applies this remap to the given record type, selecting and reordering fields by index. |
|
* |
|
* @param initial the record type to remap |
|
* @return the remapped record type |
|
*/ |
|
public Type.Struct remap(Type.Struct initial) { |
|
List<Type> types = initial.fields(); |
|
return TypeCreator.of(initial.nullable()).struct(indices().stream().map(i -> types.get(i))); |
|
} |
|
|
|
/** |
|
* Creates a remap that emits the given field indices in order. |
|
* |
|
* @param fields the field indices to emit |
|
* @return the remap |
|
*/ |
|
public static Remap of(Iterable<Integer> fields) { |
|
return ImmutableRemap.builder().addAllIndices(fields).build(); |
|
} |
ProtoRelConverter copies common.emit.output_mapping through verbatim, so whatever a producer wrote reaches the POJO, and the first thing to fail is whoever indexes with it.
An out-of-range index reaches remap(Type.Struct)'s types.get(i), or isthmus's applyRemap:
|
private RelNode applyRemap(RelNode relNode, Rel.Remap remap) { |
|
RelDataType rowType = relNode.getRowType(); |
|
// By index rather than by name: a virtual table's row type comes straight from its schema, |
|
// which Calcite never uniquifies, so a name can stand for more than one field. |
|
List<RexNode> rexList = |
|
remap.indices().stream() |
|
.map(index -> new RexInputRef(index, rowType.getFieldList().get(index).getType())) |
|
.collect(java.util.stream.Collectors.toList()); |
|
return relBuilder.push(relNode).project(rexList).build(); |
|
} |
For a two-column relation and emit = [5] that is java.lang.IndexOutOfBoundsException: Index 5 out of bounds for length 2, naming neither the relation nor the mapping. An empty index list builds too, and yields a node with no columns that no Substrait relation describes.
remap(Type.Struct) is where the arity is known, so a range check belongs there, naming the index and the arity as an IllegalArgumentException. Whether a repeated index is legal is a spec question rather than one to settle here: it currently converts, and isthmus silently invents a name for the second copy (Remap.of(List.of(0, 0)) over [col1, col2] comes back as [col1, col10] through Calcite's uniquifier).
Found while reviewing #1189.
Rel.Remapholds nothing but its index list and validates none of it.substrait-java/core/src/main/java/io/substrait/relation/Rel.java
Lines 152 to 183 in 934a60e
ProtoRelConvertercopiescommon.emit.output_mappingthrough verbatim, so whatever a producer wrote reaches the POJO, and the first thing to fail is whoever indexes with it.An out-of-range index reaches
remap(Type.Struct)'stypes.get(i), or isthmus'sapplyRemap:substrait-java/isthmus/src/main/java/io/substrait/isthmus/SubstraitRelNodeConverter.java
Lines 1310 to 1319 in 934a60e
For a two-column relation and
emit = [5]that isjava.lang.IndexOutOfBoundsException: Index 5 out of bounds for length 2, naming neither the relation nor the mapping. An empty index list builds too, and yields a node with no columns that no Substrait relation describes.remap(Type.Struct)is where the arity is known, so a range check belongs there, naming the index and the arity as anIllegalArgumentException. Whether a repeated index is legal is a spec question rather than one to settle here: it currently converts, and isthmus silently invents a name for the second copy (Remap.of(List.of(0, 0))over[col1, col2]comes back as[col1, col10]through Calcite's uniquifier).Found while reviewing #1189.