Skip to content

Commit 333bbdc

Browse files
committed
Corrections to "Not modeled" and "Quirks" lists after audit.
1 parent 92af40a commit 333bbdc

3 files changed

Lines changed: 14 additions & 12 deletions

File tree

CLAUDE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
176176
## Not modeled
177177

178178
- **Key-range locks** — sole remaining phase 4+ deferral. See [`locking.md`](docs/claude/locking.md) for what does ship (full 8-mode matrix, SNAPSHOT/RCSI, DMVs, Msg 1205/1222/3952/3960).
179-
- `BEGIN DISTRIBUTED TRANSACTION` / `BEGIN TRANSACTION ... WITH MARK` raise `NotSupportedException` at dispatch.
179+
- `BEGIN DISTRIBUTED TRANSACTION` raises `NotSupportedException` at dispatch. `BEGIN TRANSACTION <name> WITH MARK 'm'` raises **Msg 319** at parse (the parser doesn't accept `WITH` here); bare named transactions (`BEGIN TRAN t1`) ship.
180+
- **`CREATE DATABASE`** / **`CREATE ASSEMBLY`** — Msg 102 at parse. Adding databases routes through `Simulation.ImportBacpac`; CLR assemblies aren't modeled at all.
180181
- **Cross-database DML** (`INSERT`/`UPDATE`/`DELETE`/`MERGE` through a 3-part name targeting a different database) raises `NotSupportedException` via `BatchContext.RejectCrossDatabaseMutation` — trigger scope swapping, identity allocation routing, undo-log scoping, FK validation across DB boundaries pending. Issue `USE <db>` and reference via a 1- or 2-part name. Cross-database SELECT / JOIN / catalog-view reads (`SELECT * FROM other.dbo.t`, `dbA.sys.tables`) ship. **Four-part `linkedserver.db.schema.t` names** surface as Msg 208 instead of real SQL Server's Msg 7202 (no linked-server model). See [`schemas.md`](docs/claude/schemas.md).
181182
- **`SET <option>` accept-list** (`Simulation.Set.cs`) covers XACT_ABORT, all ANSI/session-state toggles, `STATISTICS {IO|TIME|XML|PROFILE}`, value-taking options (`TEXTSIZE`/`DATEFIRST`/etc.) — all parse-and-discard. Unknown SET → Msg 195. `SET @v`, `IDENTITY_INSERT`, `NOCOUNT`, `LOCK_TIMEOUT`, `TRANSACTION ISOLATION LEVEL` carry semantic effect.
182183
- **`ALTER DATABASE … SET` / `COLLATE` surface** — see [`database-options.md`](docs/claude/database-options.md). Most options parse-and-discard; `COMPATIBILITY_LEVEL`, `ALLOW_SNAPSHOT_ISOLATION`, `READ_COMMITTED_SNAPSHOT` are load-bearing.
@@ -191,7 +192,7 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
191192
- **`CREATE SCHEMA <schema_element>` greedy form** — simulator dispatches trailing CREATE/GRANT as their own statements rather than as part of the same CREATE SCHEMA. Same end state for the common idiom; mismatched-grammar trailers raise.
192193
- **`CREATE SCHEMA sys` / `INFORMATION_SCHEMA`** — Msg 2760, matching real SQL Server. The schemas exist as catalog-view hosts.
193194
- T-SQL `GOTO` / labels — `IF` / `WHILE` / `BREAK` / `CONTINUE` / `RETURN` ship; unconditional jumps don't.
194-
- **Programmable-object top-level gaps**: CLR functions, logon triggers, INSTEAD OF UPDATE/DELETE on non-updatable views, JOIN-view single-base UPDATE/DELETE, OUTPUT through views, multi-source alias-form UPDATE/DELETE through views (Msg 4405). **Natively-compiled procedures** ship at parser-fidelity tier (`WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS …` + `BEGIN ATOMIC` dispatch the body; ATOMIC's transaction boundary falls through to session isolation). See [`programmable.md`](docs/claude/programmable.md).
195+
- **Programmable-object top-level gaps**: CLR functions, logon triggers, INSTEAD OF UPDATE/DELETE on non-updatable views, JOIN-view single-base UPDATE/DELETE, OUTPUT through views, multi-source alias-form UPDATE/DELETE through views (Msg 4405). **Natively-compiled procedures** and **CLR procedures** (`CREATE PROCEDURE … AS EXTERNAL NAME …`) ship at parser-fidelity tier (`WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS …` + `BEGIN ATOMIC` dispatch the natively-compiled body; ATOMIC's transaction boundary falls through to session isolation. CLR-procedure bodies parse silently and `EXEC` is a no-op). See [`programmable.md`](docs/claude/programmable.md).
195196
- **PRINT semantic gaps** — Msg 1046 subquery-in-operand not raised; non-string formatting uses `CoerceTo(varchar(8000))` instead of PRINT-specific style 0; 8000/4000-byte truncation not enforced. `InfoMessage` event surface itself ships (see `SimulatedDbConnection.InfoMessage`); these are the residual fidelity gaps in what each entry carries.
196197
- **`ALTER TABLE` out-of-scope**: DROP PERIOD FOR SYSTEM_TIME, REBUILD, SWITCH PARTITION, `ALTER COLUMN ADD/DROP {PERSISTED|MASKED|ROWGUIDCOL|SPARSE}`, identity → non-integer type change, multi-constraint ADD in one statement. Modeled shapes in [`alter-table.md`](docs/claude/alter-table.md).
197198
- **`hierarchyid` / `geography` / `geometry` byte-identical CAST encoding** — currently simulator-native; cross-engine byte transfer deferred. See [`hierarchyid.md`](docs/claude/hierarchyid.md), [`spatial.md`](docs/claude/spatial.md).
@@ -203,10 +204,9 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
203204
- `APPROX_COUNT_DISTINCT`: implemented as exact `COUNT(DISTINCT)`.
204205
- `decimal` / `numeric`: backed by .NET `decimal`. Values requiring more than 28 significant digits aren't modeled (declarations up through `decimal(38, *)` accepted so storage byte-width matches).
205206
- `float` text formatting: .NET `G15`/`G7` rather than SQL Server's `1e+015`-style scientific.
206-
- Auto-generated PK / UNIQUE / CHECK constraint names: `PK__<table>__<hex>` / `UQ__...` / `CK__<table>__[col__]<hex>`; the 16-hex suffix is a deterministic FNV-1a hash, not SQL Server's object-id-derived hex (stable across runs but won't byte-match).
207+
- Auto-generated constraint names: PK / UNIQUE shape `PK__<table8>__<16hex>` / `UQ__<table8>__<16hex>` (16-hex 64-bit FNV-1a); CK / FK / DF shape `CK__<table8>__[<col8>__]<8hex>` (8-hex 32-bit FNV-1a). Both are deterministic across runs, distinct from SQL Server's object-id-derived hex (so won't byte-match).
207208
- **DELETE / UPDATE leak page space**: deleted (or relocated) row payload bytes stay in their original page until process exit; only the slot is tombstoned. Slot directory entries also never reused.
208209
- **DELETE / UPDATE leak LOB chains**: orphaned LOB chains stay in `Heap.LobPages`. Other rows reference LOB pages by stable index, so list compaction would corrupt them.
209-
- **Mass-shift UPDATE on a unique key**: `UPDATE t SET k = k + 1` where k is PK/UNIQUE may spuriously raise Msg 2627 — the two-phase validator compares each affected row's new key against other affected rows' new keys, so post-shift values overlapping pre-shift values trigger a false positive.
210210
- **`GetBytes`/`GetChars` materialize, don't stream**: each call decodes the full column value via `RowDecoder` and slices into the caller's buffer. Behavior matches per-call observation; the streaming-memory guarantee doesn't.
211211
- **`SELECT INTO` string `+` reads as nullable**: real SQL Server projects `cs + 'x'` (both NOT NULL) as NOT NULL; the simulator can't statically distinguish string-concat from integer-add at projection-schema time (the dispatch happens runtime on operand types), so all `Add` results read as nullable. Conservative; no test reliance on string-`+`-non-null.
212212
- **`SELECT INTO` from a CTE drops identity + nullability**: CTE bindings synthesize their wrapper `HeapColumn` entries with `nullable: true` and no identity, so the analyzer treats CTE sources as derived plans. Real SQL Server preserves both through simple single-source CTEs. Fix requires propagating column metadata through CTE bindings.

SqlServerSimulator/Simulation/Simulation.Update.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -924,11 +924,11 @@ private static SqlValue ResolveAcrossMutationTuple(FromSource[] sources, byte[]?
924924
/// tuple is checked against (a) every other affected row's new key and
925925
/// (b) every non-affected heap row's existing key. Self-collision (a row
926926
/// matching its own pre-update self) is impossible because affected
927-
/// addresses are excluded from the heap-side scan. Edge case not
928-
/// modeled: SQL Server allows mass "shift" updates like
929-
/// <c>UPDATE t SET k = k + 1</c> over a unique-key column — the simulator's
930-
/// per-row check fails when the shifted value matches another affected
931-
/// row's pre-shift value pattern (CLAUDE.md flags this as a quirk).
927+
/// addresses are excluded from the heap-side scan. Mass-shift updates
928+
/// (<c>UPDATE t SET k = k + 1</c>) work correctly because (a) only
929+
/// compares new-vs-new among affected rows — overlap with the pre-shift
930+
/// snapshot via (b) only fires when a non-affected row's existing key
931+
/// genuinely collides with the new value (a true violation).
932932
/// </summary>
933933
private static void EnforceKeyConstraintsForUpdate(HeapTable table, List<(int PageIndex, int SlotIndex, SqlValue[] FullNew, SqlValue[]? FullOld)> affected)
934934
{

SqlServerSimulator/Simulation/Simulation.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ public Simulation()
5252
/// to a Simulation that has no databases (so the all-T-SQL use case
5353
/// keeps working without an explicit import / CREATE DATABASE).
5454
/// <see cref="ImportBacpac(System.IO.Stream, out Storage.Bacpac.BacpacImportResult, Storage.Bacpac.BacpacImportOptions?)"/>
55-
/// adds further entries; <c>USE &lt;db&gt;</c> isn't wired up yet, so
56-
/// <see cref="SimulatedDbConnection.CurrentDatabase"/> picks the lazy
57-
/// seed when present, else the sole entry when there's exactly one.
55+
/// adds further entries; <c>USE &lt;db&gt;</c> switches a session's
56+
/// <see cref="SimulatedDbConnection.CurrentDatabase"/> across entries
57+
/// (Msg 911 on miss). Fresh connections pick the lazy seed when present,
58+
/// else the alphabetically-first entry (see
59+
/// <see cref="SimulatedDbConnection"/>'s ResolveInitialDatabase).
5860
/// </summary>
5961
internal readonly Dictionary<string, Database> Databases = new(Collation.Default);
6062

0 commit comments

Comments
 (0)