Commit e2b1ca1
committed
Locking phase 3 follow-up — close the SI-visible DELETE gap. Phase 3 shipped SNAPSHOT + RCSI but the documented limitation (SI readers can't see committed-deleted rows their snapshot pre-dates; SI writers can't raise Msg 3960 on RC-deleted target rows) needed a tombstoned-slot iteration pass. Both ship now. **Reader-side**:
BatchContext.WrapWithRowConflictChecks gets a second pass under snapshot — after the live-heap iteration, walks the per-table RowVersions dict for entries whose live slot is tombstoned (HeapTable.Heap.IsSlotTombstoned) and yields the historical payload via the new VersionStore.ResolveTombstonedSlotForSnapshot(chain, snapshotXid, readerTx). The new helper handles two cases: in-flight foreign delete (chain.WriterTx is the deleting tx, my snapshot needs the pre-write payload from chain history) and committed delete (chain.IsDeletedLive with LiveXmin > my snapshot, history walked the same way). New Heap.IsSlotTombstoned / HeapPage.IsSlotTombstoned expose the per-slot tombstone bit so the live-heap-iteration filter doesn't double-yield. **Writer-side**: new Simulation.CheckSnapshotConflictOnTombstonedRows(context, table, where, sourceView) private partial-class helper called at the top of UPDATE's and DELETE's ExecuteUpdateAgainstTable / ExecuteDeleteAgainstTable paths (after their respective WHERE-collection loops, before the mutation loop). Under SNAPSHOT iso with an allocated tx.SnapshotXid, walks the version chain dict for tombstoned slots; decodes each snapshot-visible historical payload via DecodeFullRow + EvaluateComputedColumns; builds the same ResolveOriginal closure shape as the regular iteration (full view-column-mapping support); evaluates WHERE; raises Msg 3960 verbatim + auto-rollback if WHERE matches a tombstoned-but-visible row. The DELETE call site shares the helper through the partial-class scope (one definition in Simulation.Update.cs, called from Simulation.Delete.cs). Probe-confirmed against SQL Server 2025 (2026-05-14): SI reader sees v=200 for an RC-deleted row when snapshot pre-dates the delete; SI UPDATE / DELETE on the same row raise Msg 3960 with @@TRANCOUNT = 0 post-throw (auto-rollback). 3 new SnapshotIsolationTests: SI reader sees deleted row's pre-delete payload + correct count post-DELETE; SI UPDATE on RC-deleted row → Msg 3960 + auto-rollback; SI DELETE on RC-deleted row → Msg 3960. Total snapshot test count now 17 (was 14). docs/claude/locking.md "Known phase-3 limitations" section replaces the two SI-DELETE bullets with a new "Tombstoned-slot snapshot pass" section documenting the wiring; CLAUDE.md Not modeled opener tightens to "Key-range locks, ALTER SCHEMA TRANSFER Sch-M, row-lock cleanup on DELETE" (the SI-visible-DELETE bullet is gone) and the phase-3 paragraph adds the tombstoned-slot pass description. Remaining phase-3 limitations are now just three small ones: multi-update-within-one-tx history collapse, version-store garbage collection, and the sys.dm_tran_version_store DMV — none affecting standard application workloads.1 parent 97f5a62 commit e2b1ca1
9 files changed
Lines changed: 224 additions & 12 deletions
File tree
- SqlServerSimulator.Tests
- SqlServerSimulator
- Parser
- Simulation
- Storage
- docs/claude
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
159 | 159 | | |
160 | 160 | | |
161 | 161 | | |
162 | | - | |
| 162 | + | |
163 | 163 | | |
164 | 164 | | |
165 | 165 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
241 | 241 | | |
242 | 242 | | |
243 | 243 | | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
244 | 315 | | |
245 | 316 | | |
246 | 317 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
654 | 654 | | |
655 | 655 | | |
656 | 656 | | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
657 | 676 | | |
658 | 677 | | |
659 | 678 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
163 | 163 | | |
164 | 164 | | |
165 | 165 | | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
166 | 174 | | |
167 | 175 | | |
168 | 176 | | |
| |||
0 commit comments