Skip to content

fix(engine): leave the row address out of the stored-schema check - #906

Merged
ragnorc merged 1 commit into
ModernRelay:mainfrom
ragnorc:fix/hydrate-row-address-schema-check
Oct 9, 2026
Merged

ragnorc merged 1 commit into
ModernRelay:mainfrom
ragnorc:fix/hydrate-row-address-schema-check

Conversation

@ragnorc

@ragnorc ragnorc commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Why

main is red since #887 (4c7a160a). #887 merged before CI finished on its last head, which included the merge with #901. Every query that hydrates return columns after a limit fails:

query failed: consumed node:Doc._rowaddr has no declared field

#901 checks every column a node read consumes against the table's declared stored schema. #887 makes a hydrated binding's scan read Lance's row address _rowaddr, which is not a stored field, so the check refuses it. The failing tests on main are the hydration owners from #887:

  • engine::report::tests::a_hydrating_top_k_builds_one_operator_above_its_limit
  • engine_v2_memory::a_wide_column_read_under_a_limit_follows_its_result
  • engine_v2_memory::hydrated_copies_of_a_joined_row_stay_within_the_chunk_bound
  • engine_v2_plan_replay::a_hydrating_top_k_replays
  • GQT v2/planner/hydrate_return_columns_above_the_limit.gqt

What changes

  • ScanColumns::stored_columns is the one list of stored fields a node read consumes: its projection, or every non-Blob column, without _rowaddr. The table scan (NodeRead::resolve) and the destination read by id (operators/scan/input.rs) both pass it to check_stored_schema, so neither treats the row address as a stored field.
  • HydrateExec now runs the same stored-schema check on the columns it takes. It checks each binding's pinned dataset against the declared node schema before reading, as feat(query): type the IR and plan every cast and aggregate #901 requires for every consumed physical column.

Evidence

  • Locally, with this change, all five owners that fail on main pass: the report test, both engine_v2_memory hydration tests, a_hydrating_top_k_replays and the GQT case. nested_hydration_uses_the_query_pool_and_releases_on_refusal passes too.
  • The GQT case's hydrate $var claims cover both changed read paths: its traversal steps hydrate a destination through a hash join and through id lookup.

Since typed reads check every column a node read consumes against the
table's declared stored schema, a hydrated binding's scan failed with
`consumed node:<Type>._rowaddr has no declared field`: it reads Lance's row
address, which is no stored field.

`ScanColumns::stored_columns` is now the one list of stored fields a node
read consumes, without `_rowaddr`; the table scan and the destination read
by id both pass it to the check. `HydrateExec` runs the same check on the
columns it takes from each binding's pinned dataset.
@ragnorc
ragnorc added this pull request to the merge queue Oct 9, 2026
Merged via the queue into ModernRelay:main with commit 0303348 Oct 9, 2026
31 checks passed
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