Skip to content

feat(data): enforce residual filters in the task reader - #1065

Open
kamcheungting-db wants to merge 2 commits into
apache:mainfrom
kamcheungting-db:feat/task-residual-enforcement
Open

kamcheungting-db wants to merge 2 commits into
apache:mainfrom
kamcheungting-db:feat/task-residual-enforcement

Conversation

@kamcheungting-db

Copy link
Copy Markdown
Contributor

FileScanTaskReader gains two Options: case_sensitive (matching the planning scan's setting, because task residuals are unbound) and apply_residuals (default false, keeping the serial reader's results unchanged; the parallel reader will always enable it).

When enabled, each task's residual is enforced row by row after deletes and before the final projection with the existing Evaluator. Residual-only columns are appended to the read schema and projected away again, so a filter can reference columns absent from the output projection. Residuals are bound against the table schema with the configured case sensitivity; unresolvable or nested references fail with explicit errors instead of silently returning unfiltered rows. ReaderOptions.filter is still forwarded to format readers as a pruning hint, but nothing relies on it.

The merge-on-read stream source now also serves tasks without deletes when a residual must be enforced: delete filtering and residual evaluation are both optional, and the pass-through fast path only triggers when no filter removed a row.

Tests cover residuals with position and equality deletes, filter columns absent from the projection, case-insensitive residuals, null values, always-false residuals, kept row identities, apply_residuals disabled, and explicit errors for unknown, case-mismatched, and nested references.

FileScanTaskReader gains two Options: case_sensitive (matching the planning
scan's setting, because task residuals are unbound) and apply_residuals
(default false, keeping the serial reader's results unchanged; the parallel
reader will always enable it).

When enabled, each task's residual is enforced row by row after deletes and
before the final projection with the existing Evaluator. Residual-only
columns are appended to the read schema and projected away again, so a
filter can reference columns absent from the output projection. Residuals
are bound against the table schema with the configured case sensitivity;
unresolvable or nested references fail with explicit errors instead of
silently returning unfiltered rows. ReaderOptions.filter is still forwarded
to format readers as a pruning hint, but nothing relies on it.

The merge-on-read stream source now also serves tasks without deletes when
a residual must be enforced: delete filtering and residual evaluation are
both optional, and the pass-through fast path only triggers when no filter
removed a row.

Tests cover residuals with position and equality deletes, filter columns
absent from the projection, case-insensitive residuals, null values,
always-false residuals, kept row identities, apply_residuals disabled, and
explicit errors for unknown, case-mismatched, and nested references.
Use std::ranges::sort for the residual field ids, and keep std::iota for
the row-index selection with a NOLINT: ranges::iota requires C++23, but
the repo builds with C++20 compatibility (same pattern as
parquet_reader.cc).

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant