Skip to content

core: RelCopyOnWriteVisitor never visits a read relation's best-effort filter, so a rewrite there is silently dropped #1278

Description

@nielspardon

Description

AbstractReadRel exposes both getFilter() and getBestEffortFilter(), but the four read-relation visits in RelCopyOnWriteVisitor rewrite only the first: visit(NamedScan), visit(LocalFiles), visit(VirtualTableScan) and visit(ExtensionTable) each compute visitOptionalExpression(rel.getFilter(), context), decide "unchanged" from that alone, and rebuild without ever visiting the best-effort filter. A rewrite that lands there is discarded, and because the visit then returns Optional.empty(), the traversal above the read collapses to "unchanged" as well. (getProjection() is a MaskExpression and carries no expressions, so there is nothing for the visitor to rewrite in it.)

This is the same shape as #1231 and the MultiBucketExchange expression fixed alongside it: a child that is never visited, and so cannot reach the allEmpty(...) decision. It is distinct from #1204, which is about isthmus dropping these fields when converting to and from Calcite rather than when rewriting.

Reproduction

Using the negateI32LiteralsVisitor helper already in RelCopyOnWriteVisitorTest, over the same predicate in both cases:

NamedScan base =
    (NamedScan) sb.namedScan(Arrays.asList("t"), Arrays.asList("a"), Arrays.asList(R.I32));
Expression pred = sb.equal(sb.i32(5), sb.i32(5));

NamedScan.builder().from(base).filter(pred).build()
    .accept(negateI32LiteralsVisitor(), EmptyVisitationContext.INSTANCE);
// => Optional[NamedScan{..., filter=equal:any_any(I32Literal{value=-5}, I32Literal{value=-5}), ...}]

NamedScan.builder().from(base).bestEffortFilter(pred).build()
    .accept(negateI32LiteralsVisitor(), EmptyVisitationContext.INSTANCE);
// => Optional.empty

Measured on main at 944b921 — pre-existing, not a regression.

Suggested fix

Visit getBestEffortFilter() alongside getFilter() in all four read-relation visits, fold it into the allEmpty(...) check and set it on the builder — the shape those methods already use for filter.

🤖 Generated with AI

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcorePull requests that update java code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions