ConverterProvider.getSubstraitRelNodeConverter makes subclassing SubstraitRelNodeConverter the supported way to convert a custom relation, and applyRelCommon, applyRemap and applyOutputNames are protected -- so calling one is an obligation on the subclass, with nothing to enforce it.
The repository's own worked example does not:
|
static class CustomSubstraitRelNodeConverter extends SubstraitRelNodeConverter { |
|
|
|
public CustomSubstraitRelNodeConverter( |
|
SimpleExtension.ExtensionCollection extensions, |
|
RelDataTypeFactory typeFactory, |
|
RelBuilder relBuilder) { |
|
super(extensions, typeFactory, relBuilder); |
|
} |
|
|
|
@Override |
|
public RelNode visit(ExtensionLeaf extensionLeaf, Context context) { |
|
if (extensionLeaf.getDetail() instanceof ColumnAppendDetail) { |
|
ColumnAppendDetail cad = (ColumnAppendDetail) extensionLeaf.getDetail(); |
|
RexLiteral literal = (RexLiteral) cad.literal.accept(this.expressionRexConverter, context); |
|
RelOptCluster cluster = relBuilder.getCluster(); |
|
RelTraitSet traits = cluster.traitSet(); |
|
return new ColumnAppenderRel( |
|
relBuilder.getCluster(), traits, literal, Collections.emptyList()); |
|
} |
|
throw new UnsupportedOperationException("detail was not ColumnAppendDetail"); |
|
} |
|
|
|
@Override |
|
public RelNode visit(ExtensionSingle extensionSingle, Context context) throws RuntimeException { |
|
if (extensionSingle.getDetail() instanceof ColumnAppendDetail) { |
|
ColumnAppendDetail cad = (ColumnAppendDetail) extensionSingle.getDetail(); |
|
RelNode input = extensionSingle.getInput().accept(this, context); |
|
RexLiteral literal = (RexLiteral) cad.literal.accept(this.expressionRexConverter, context); |
|
return new ColumnAppenderRel( |
|
input.getCluster(), input.getTraitSet(), literal, List.of(input)); |
|
} |
|
throw new UnsupportedOperationException("detail was not ColumnAppendDetail"); |
|
} |
|
|
|
@Override |
|
public RelNode visit(ExtensionMulti extensionMulti, Context context) throws RuntimeException { |
|
if (extensionMulti.getDetail() instanceof ColumnAppendDetail) { |
|
ColumnAppendDetail cad = (ColumnAppendDetail) extensionMulti.getDetail(); |
|
List<RelNode> inputs = |
|
extensionMulti.getInputs().stream() |
|
.map(input -> input.accept(this, context)) |
|
.collect(Collectors.toList()); |
|
RexLiteral literal = (RexLiteral) cad.literal.accept(this.expressionRexConverter, context); |
|
return new ColumnAppenderRel( |
|
inputs.get(0).getCluster(), inputs.get(0).getTraitSet(), literal, inputs); |
|
} |
|
throw new UnsupportedOperationException("detail was not ColumnAppendDetail"); |
All three overrides return their RelNode directly, which is the omission #1160 fixed for VirtualTableScan. An extension converter written from this pattern drops the emit mapping and the hint names of every custom relation it converts, and the plan it produces does not describe the relation it came from.
Two ways out: have the base class apply RelCommon around a subclass hook -- visit final, a convertExtensionLeaf for the subclass to override -- so the obligation cannot be missed, or model the call in the example and state it on the seam's Javadoc.
Found while reviewing #1189.
ConverterProvider.getSubstraitRelNodeConvertermakes subclassingSubstraitRelNodeConverterthe supported way to convert a custom relation, andapplyRelCommon,applyRemapandapplyOutputNamesareprotected-- so calling one is an obligation on the subclass, with nothing to enforce it.The repository's own worked example does not:
substrait-java/isthmus/src/test/java/io/substrait/isthmus/RelExtensionRoundtripTest.java
Lines 202 to 248 in 934a60e
All three overrides return their
RelNodedirectly, which is the omission #1160 fixed forVirtualTableScan. An extension converter written from this pattern drops the emit mapping and the hint names of every custom relation it converts, and the plan it produces does not describe the relation it came from.Two ways out: have the base class apply
RelCommonaround a subclass hook --visitfinal, aconvertExtensionLeaffor the subclass to override -- so the obligation cannot be missed, or model the call in the example and state it on the seam's Javadoc.Found while reviewing #1189.