Summary
Latent parity gap surfaced during post-merge review of PR #1031 (fix #1006, own-table resolution across WITH barriers + standalone foreign-embedded nodes). Not reachable with the current corpus — filing so it is not lost when foreign-embedded support is extended to multi-label schemas.
Detail
OwnTableJoinGuard (RAII snapshot/restore of the per-query own_table_joins registry in src/server/query_context.rs) is entered only inside build_chained_with_match_cte_plan — i.e. once per WITH segment. A top-level Cypher UNION with no WITH is rendered by build_cypher_union_render (src/render_plan/plan_builder.rs:660), which gives each arm a fresh CTE-scope guard and a filtered PlanCtx but does not enter an OwnTableJoinGuard.
Consequence: two sibling UNION arms that reuse the same alias name for a foreign-embedded endpoint of different labels could see the first arm's own-table-join request leak into the second arm.
Why it is unreachable today
The registered request is keyed to a specific edge via get_denormalized_node_id_reference at injection time, and the only schemas that exercise the foreign-embedded own-table path are the foreign_selfloop*.yaml self-loop fixtures — where both endpoints share the same label (Person). A divergent-label alias collision across UNION arms cannot be constructed from the existing corpus.
Fix direction
Wrap union-arm rendering in build_cypher_union_render with an OwnTableJoinGuard (mirror the per-WITH-segment scoping) so each arm's own-table requests are snapshot/restored independently.
Acceptance
- A regression test with two top-level
UNION arms reusing one alias for foreign-embedded endpoints of different labels, asserting each arm injects its own correct own-table join (no cross-arm leak).
Source: post-merge review of PR #1031 (commit 7abc355). Ref invariant: "same-table denorm ⇒ empty property_mappings", verified across all current repo schemas.
Summary
Latent parity gap surfaced during post-merge review of PR #1031 (fix #1006, own-table resolution across WITH barriers + standalone foreign-embedded nodes). Not reachable with the current corpus — filing so it is not lost when foreign-embedded support is extended to multi-label schemas.
Detail
OwnTableJoinGuard(RAII snapshot/restore of the per-queryown_table_joinsregistry insrc/server/query_context.rs) is entered only insidebuild_chained_with_match_cte_plan— i.e. once per WITH segment. A top-level CypherUNIONwith no WITH is rendered bybuild_cypher_union_render(src/render_plan/plan_builder.rs:660), which gives each arm a fresh CTE-scope guard and a filteredPlanCtxbut does not enter anOwnTableJoinGuard.Consequence: two sibling UNION arms that reuse the same alias name for a foreign-embedded endpoint of different labels could see the first arm's own-table-join request leak into the second arm.
Why it is unreachable today
The registered request is keyed to a specific edge via
get_denormalized_node_id_referenceat injection time, and the only schemas that exercise the foreign-embedded own-table path are theforeign_selfloop*.yamlself-loop fixtures — where both endpoints share the same label (Person). A divergent-label alias collision across UNION arms cannot be constructed from the existing corpus.Fix direction
Wrap union-arm rendering in
build_cypher_union_renderwith anOwnTableJoinGuard(mirror the per-WITH-segment scoping) so each arm's own-table requests are snapshot/restored independently.Acceptance
UNIONarms reusing one alias for foreign-embedded endpoints of different labels, asserting each arm injects its own correct own-table join (no cross-arm leak).Source: post-merge review of PR #1031 (commit 7abc355). Ref invariant: "same-table denorm ⇒ empty property_mappings", verified across all current repo schemas.