Skip to content

Provide semantics-aware plan comparison (order-insensitive equality) #994

Description

@nielspardon

Background

Split out from #203. That issue reported that a proto → POJO → proto roundtrip reorders extension declarations. The conclusion there was that this is not a bug: the Substrait spec doesn't constrain extension ordering, and substrait-java allocates extension anchors in discovery order during POJO → proto conversion (deterministic and harmless). We don't intend to guarantee that an externally-authored plan's extension ordering survives a roundtrip.

However, a distinct and still-open suggestion came out of that discussion (from @vbarua, @vibhatha, and @danepitkin): two plans that differ only in spec-irrelevant details (e.g. extension anchor numbering / declaration ordering) currently compare as not equal, because io.substrait.plan.Plan uses the stock order-sensitive equals/hashCode generated by Immutables.

If we consider these plans to be equal, this would be a good argument for a smarter equals method.

Proposal

Provide a semantics-aware comparison for plans — a way to determine that two plans are equivalent modulo details the spec does not constrain (extension anchor values, extension declaration ordering, etc.). Prior art: substrait-cpp's ReferenceNormalizer (normalize anchors in place, then compare).

Options to consider:

  • A normalize() / plan-normalization utility that canonicalizes anchor numbering and declaration ordering, so normalized plans can be compared with ordinary equals.
  • A dedicated comparator/equivalence helper rather than overriding Plan.equals (overriding equals on the Immutables POJO is likely too invasive and surprising).

Scope note

This is about semantic equivalence only; it does not change or guarantee wire-format/roundtrip ordering, which remains unconstrained per the spec.

🤖 Generated with AI

Metadata

Metadata

Assignees

No one assigned

    Labels

    corePull requests that update java code

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions