You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/claude/dml.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -74,7 +74,7 @@ Creates a destination table from the projection's inferred schema, then copies r
74
74
`Simulation.Merge.cs:ExecuteMerge` is a single-pass walk:
75
75
76
76
1.**Materialize source** once into `List<SqlValue[]>` via the parse-time `Func<BatchContext, List<SqlValue[]>>` materializer. `VALUES`-form evaluates the tuple expressions; `SELECT`-form runs `Selection.Execute` and decodes via `RowDecoder`; the bare-table / view form iterates the underlying heap or view selection respectively, then runs `EvaluateComputedColumns` per row so source-side computed columns are observable from the ON predicate / SET / INSERT projections.
77
-
2. **Phase A — target × source**: for each target heap row, enumerate source rows; ON evaluates with a combined resolver wired to both target alias and source alias. Multiple-match collection feeds the Msg 8672 guard. For each target with ≥ 1 match, walk WHEN MATCHED clauses; first clause whose `AND` is satisfied (or absent) wins. For each target with 0 matches, walk WHEN NOT MATCHED BY SOURCE clauses the same way. Action gets queued (`pendingInserts` / `pendingUpdates` / `pendingDeletes`) along with the `(page, slot)` address + pre-update and post-update row snapshots. **Seek-accelerated when applicable**: when the ON carries a seekable target equality, there's no WHEN NOT MATCHED BY SOURCE clause, and the target isn't a view, Phase A inverts — it seeks matching targets per source row (`Selection.TryPrepareMergeTargetSeek`) and visits only matched targets, re-running the full ON per candidate (residual filter), first-source-wins and heap order preserved. ~9× faster on a large target; see [`indexes.md`](indexes.md#merge-target-seeking-loop-inversion). Declines to the full scan otherwise.
77
+
2. **Phase A — target × source**: for each target heap row, enumerate source rows; ON evaluates with a combined resolver wired to both target alias and source alias. Multiple-match collection feeds the Msg 8672 guard. For each target with ≥ 1 match, walk WHEN MATCHED clauses; first clause whose `AND` is satisfied (or absent) wins. For each target with 0 matches, walk WHEN NOT MATCHED BY SOURCE clauses the same way. Action gets queued (`pendingInserts` / `pendingUpdates` / `pendingDeletes`) along with the `(page, slot)` address + pre-update and post-update row snapshots. **Seek-accelerated when applicable**: when the ON carries a seekable target equality and the target isn't a view, the match phase inverts — it seeks matching targets per source row (`Selection.TryPrepareMergeTargetSeek`), re-running the full ON per candidate (residual filter), first-source-wins and heap order preserved. With no WHEN NOT MATCHED BY SOURCE clause it then visits only matched targets; with one it walks the heap once applying the precomputed matches (BY-SOURCE for the rest) — dropping the inner source loop either way. ~9× faster on a large target; see [`indexes.md`](indexes.md#merge-target-seeking-loop-inversion). Declines to the full scan for a view target or a non-seekable ON.
78
78
3.**Phase B — unmatched sources**: for each source row that didn't match any target, the single WHEN NOT MATCHED BY TARGET clause's AND condition is evaluated; if true, queue an INSERT.
79
79
4.**Phase C — commit**: PK / UNIQUE validation runs on the union of pending inserts + updates via `EnforceKeyConstraintsForUpdate` (inserts use sentinel `(-1, i)` addresses). If a violation surfaces, every queued mutation is abandoned and the statement-atomic undo log already captures the no-heap-writes state. Then deletes tombstone, updates rewrite, inserts append, in that order.
80
80
5.**Phase D — OUTPUT**: walk queued INSERT rows → UPDATE rows → DELETE rows; the `MergeOutputProjection` resolves `INSERTED.col` / `DELETED.col` / source-alias / `$action`. For each row, the unmatched side projects all-NULL.
0 commit comments