Skip to content

fix(mapper): unmap fused-away scans; tolerate unmapped SEMI_MASKER targets - #1079

Merged
adsharma merged 1 commit into
mainfrom
fix-1068-fused-scan-semi-masker
Sep 29, 2026
Merged

adsharma merged 1 commit into
mainfrom
fix-1068-fused-scan-semi-masker

Conversation

@adsharma

Copy link
Copy Markdown
Contributor

Fixes #1068.

Root cause

unordered_map::at: key not found was a use-after-free in physical plan mapping:

  1. The hash-join SIP optimizer places a SEMI_MASKER (keyed e.g. on i._ID) targeting the bound-node SCAN_NODE_TABLE on the other side of the join.
  2. When mapping that side, mapExtend's fused ice-disk path absorbs the child scan into the ScanRelTable and discards its separately mapped physical operator, leaving a dangling entry in logicalOpToPhysicalOpMap.
  3. mapSemiMasker then dereferenced the freed pointer (observed reallocated as a different scan at the same address), so initMask's mask-table lookup threw.

Reproduced on a v0.21.0 build (fails on both EXPLAIN and execution); current HEAD only hid it via a later join-order change.

Changes

  • src/processor/map/map_extend.cpp: erase the stale child-scan mapping in fused branches that drop prevOperator.
  • src/processor/map/map_semi_masker.cpp: look up SEMI targets with find() instead of at(), skip per-table masks a target doesn't hold, and drop maskers that attach nothing (result-preserving pass-through).
  • test/test_files/ice_disk/ice_disk_complex_queries.test: new SemiMaskerOnFusedBoundScan regression case producing the exact crashing shape.

Verification

  • Issue repro on icebug-disk: crash → 111 rows, byte-identical to the native-database result.
  • A/B on HEAD build: stashed fix crashes on both the issue shape and the new test query; restored fix passes.
  • ice_disk e2e suite passes (except pre-existing ice_disk_large_node_scan failure from the missing ice-disk-large-test dataset in this checkout).
  • clang-format-18 clean on both edited files.

…rgets

The ice-disk fused rel scan in mapExtend absorbs the child SCAN_NODE_TABLE
and discards its physical operator, leaving a dangling entry in
logicalOpToPhysicalOpMap. A later SEMI_MASKER targeting that scan then
dereferenced freed memory, surfacing as 'unordered_map::at: key not found'
when the reused object held masks for a different table (#1068).

Erase the stale child-scan mapping in fused branches that drop
prevOperator, look up SEMI targets with find() instead of at(), skip table
masks a target does not hold, and drop maskers that attach nothing
(result-preserving pass-through).

Adds an ice-disk regression case (SEMI_MASKER over a fused bound scan)
that crashes pre-fix and returns correct rows post-fix.
@adsharma
adsharma force-pushed the fix-1068-fused-scan-semi-masker branch from 935b69d to e8bdd00 Compare September 29, 2026 19:51
@adsharma
adsharma merged commit 3205a01 into main Sep 29, 2026
4 checks passed
@adsharma
adsharma deleted the fix-1068-fused-scan-semi-masker branch September 29, 2026 20:44
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.

Bug: unordered_map::at on icebug-disk for a direction-changing pattern with a downstream filter (0.21.0 regression)

1 participant