Skip to content

isthmus: the extension-converter example never calls applyRelCommon, so a custom relation's emit mapping and hint names are dropped #1216

Description

@nielspardon

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions