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: CLAUDE.md
+27-2Lines changed: 27 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -62,7 +62,7 @@ Five scopes, one home each. **Add new state to whichever class matches its true
62
62
63
63
-**`Simulation`** = server / instance. Process-shared system tables (`SystemHeapTables`), `NEWSEQUENTIALID` anchor + counter, the `Databases` dictionary. Public surface (`Simulation` ctor + `CreateDbConnection()`) stays on this class.
64
64
-**`Database`** (internal) = one database hosted by the server instance. `Schemas` (named-schema dict, pre-seeded with `dbo`), `CompatibilityLevel`, `VerboseTruncationWarnings`, `rowVersionCounter` (per-DB `@@DBTS`). Every `Simulation` ships with one entry named `Simulation.DefaultDatabaseName` (`"simulated"`); a future `USE <db>` adds entries to the dictionary.
65
-
-**`Schema`** (internal) = one namespace inside a database. `HeapTables` (per-schema table dict), `Functions` (UDFs — abstract `UserDefinedFunction` keyed by name, runtime-typed as either `ScalarFunction` or `InlineTableValuedFunction`). Future views / procs / sequences land here too. Schema-qualified references (`SELECT * FROM audit.t`, `SELECT audit.fn(x)`, `FROM audit.tvf(x)`) route through `Database.Schemas["audit"]`; unqualified references fall back to `Database.DefaultSchemaName` (`"dbo"`).
65
+
-**`Schema`** (internal) = one namespace inside a database. `HeapTables` (per-schema table dict), `Functions` (UDFs — abstract `UserDefinedFunction` keyed by name, runtime-typed as either `ScalarFunction` or `InlineTableValuedFunction`), `Views` (per-schema view dict). Future procs / sequences / triggers land here too. Schema-qualified references (`SELECT * FROM audit.t`, `SELECT audit.fn(x)`, `FROM audit.tvf(x)`, `FROM audit.view1`) route through `Database.Schemas["audit"]`; unqualified references fall back to `Database.DefaultSchemaName` (`"dbo"`).
- **`BatchContext`** (internal, in `Parser/`) = one command execution. Owns the `ParserContext` (parse-time-only scratch — `Token`, `AggregateCollector`, `WindowCollector`, `OuterTypeResolver`, `CteBindings`, `InDefaultClause`, `AllowsWindowExpressions`) and holds batch-lifetime runtime state: `Variables`, `CurrentUndoLog`, `UdfFrame` (non-null when this batch is a scalar-UDF body being dispatched — gates value-form `RETURN` and lands the return value for the caller), plus the per-statement frame `CurrentStatement`. Exposes `TryResolveTable(MultiPartName)` — the routing rule that dispatches `#foo` leaves to `Connection.TempTables` regardless of qualifier; everything else routes through the named schema (or `dbo` for an unqualified reference), with `SystemHeapTables` reachable only as a flat 1-part fallback. `TryResolveFunction(MultiPartName)` resolves 2-/3-part dotted names against the named schema's `Functions` dict; 1-part names return false (real SQL Server rejects bare UDF calls with Msg 195). `TryResolveSchema(MultiPartName)` exposes the dict-bearing schema for CREATE / DROP / TRUNCATE / SELECT INTO. `ParseObjectName(ParserContext)` parses the 1–4-segment dotted form, leaves cursor on the last name segment (standard parser contract), and compresses empty middle segments (so `tempdb..#foo` returns a 2-part name). Threaded explicitly into every `Expression.Run(RuntimeContext runtime)` call via `runtime.Batch`. Scalar-UDF invocation allocates a child `BatchContext` via the UDF-body constructor: parameters pre-seed `Variables`, `UdfFrame` is set, the body source text (captured at CREATE FUNCTION time) is re-tokenized through a synthesized `SimulatedDbCommand`, and the same dispatch loop runs the body.
68
68
-**`StatementContext`** (internal, in `Parser/`) = the dispatch loop's per-statement frame. Allocated once per batch and overwritten in place at the top of each iteration; holds `UtcNow` (the per-statement-freeze the time scalars read). Stored-proc EXEC / TRY-CATCH frames slot in here when added.
@@ -519,6 +519,31 @@ Per-connection `Dictionary<string, HeapTable> TempTables` on `SimulatedDbConnect
519
519
-**No Msg 111 batch-first enforcement** (same as scalar UDFs).
520
520
-**Multi-statement TVFs** (`RETURNS @t TABLE ... BEGIN ... END`, `type='TF'`) not modeled.
521
521
522
+
### Views
523
+
`CREATE VIEW schema.name [(col_list)] [WITH SCHEMABINDING | ENCRYPTION | VIEW_METADATA] AS <SELECT> [WITH CHECK OPTION]`, referenced from FROM as `FROM schema.view [alias]` (or unqualified `FROM view`). Stored as `View` in `Schema.Views`. Body re-parsed per call inside a child `BatchContext`, returned as `Selection.ForView` wrapped in a `FromSource.LateralPlan`. Same 32-level recursion cap (Msg 217) as scalar UDFs / inline TVFs. Probed against SQL Server 2025 (2026-05-12).
524
+
525
+
-**Body grammar**: a single SELECT (CTE-prefixed bodies via `WITH cte AS (...) SELECT ...` work — the body parse runs at depth 0). ORDER BY without TOP / OFFSET / FETCH → **Msg 1033** (same factory CTE bodies use).
526
+
-**Column-rename list**: `CREATE VIEW v(a, b) AS SELECT ...` renames the projection. Count mismatch → **Msg 8158** (too few listed) / **Msg 8159** (too many) — shared factories with CTE rename lists.
527
+
-**CREATE-time validation**: body parses once to derive `OutputColumns`. Unnamed projection → **Msg 4511** (distinct from inline TVF's Msg 4514 and SELECT INTO's Msg 1038 — different wording too: `"Create View or Function failed because no column name was specified for column N."`). Duplicate column name → **Msg 4506** (shared with inline TVFs).
528
+
-**WITH-clause options**: `SCHEMABINDING` / `ENCRYPTION` / `VIEW_METADATA` parse-and-ignore. **`WITH CHECK OPTION`** (trailing the body) parses and records on `View.WithCheckOption` but isn't enforced (only matters for DML, which v1 doesn't model).
529
+
-**Routing in expression position**: a view name used as a scalar value raises **Msg 4104** (`"The multi-part identifier '...' could not be bound."`), NOT Msg 4121 — views look like tables to the expression parser.
530
+
-**Unqualified names work**: `FROM v1` falls back to `dbo.v1` (probe-confirmed real SQL Server accepts both).
-`sys.columns` emits one row per output column (`is_identity=0`, `is_computed=0`; `is_nullable` always True — same fidelity gap as inline TVFs).
535
+
-`INFORMATION_SCHEMA.VIEWS` (full ISO 6-col shape): `VIEW_DEFINITION` surfaces the stored body text; `CHECK_OPTION` is `'CASCADE'` / `'NONE'`; **`IS_UPDATABLE` always `'NO'`** — probe-confirmed real SQL Server hardcodes this regardless of actual updatability.
536
+
-`INFORMATION_SCHEMA.TABLES` includes views with `TABLE_TYPE='VIEW'`.
537
+
-`OBJECT_ID(name, 'V')` resolves views only; no-filter form falls through both functions and views before tables.
538
+
-**DROP VIEW [IF EXISTS] schema.name[, ...]**: same shape as DROP TABLE / DROP FUNCTION; missing target → **Msg 3701** with `view` wording variant.
539
+
-**EF Core integration**: `ToView()` mapping works end-to-end. Keyless entities (`HasNoKey().ToView("name")`) project rows from CREATE VIEW-produced views; the simulator's per-call body re-parse handles correlated LINQ-emitted WHERE clauses against the view's projection.
540
+
541
+
**Fidelity gaps**:
542
+
-**No updatable views** — INSERT / UPDATE / DELETE on a view raises `NotSupportedException` regardless of view shape. Real SQL Server supports DML against single-source views (with column-name rebinding to the backing table and Msg 4406 rejection for aggregate / derived-field views). Apps target the underlying table directly. Deferred to a follow-up bundle.
543
+
-**No SCHEMABINDING enforcement** — DROP TABLE on a view-referenced table succeeds (real SQL Server raises Msg 3729); the view later fails at call time when re-parsing against the missing name.
544
+
-**`VIEW_DEFINITION` always surfaces body text** even for WITH ENCRYPTION views (real SQL Server returns NULL for ENCRYPTION views).
545
+
-**`is_nullable` always True** in `sys.columns` for view output — same gap as inline TVFs.
546
+
522
547
### `text` / `ntext` / `image` restrictions
523
548
Comparison (Msg 402), ORDER BY/DISTINCT (Msg 306), and aggregates (Msg 8117 from MAX/MIN) all enforced.
524
549
@@ -551,7 +576,7 @@ Full `DbDataReader` contract. Typed accessors read `SqlValue` directly via the c
551
576
-**CREATE SCHEMA's `<schema_element>` greedy form** — real SQL Server consumes trailing CREATE TABLE / VIEW / GRANT as part of the same CREATE SCHEMA statement (and requires CREATE SCHEMA to be the first statement in the batch as a result). The simulator instead dispatches the trailing tokens as their own statements — same end state for the common idiom, but mismatched-grammar trailers (e.g. anything that isn't a recognized statement start) raise `NotSupportedException`.
552
577
-**`CREATE SCHEMA sys` / `INFORMATION_SCHEMA`** — raises Msg 2760 (matching real SQL Server). The schemas themselves exist as catalog-view hosts (`select * from sys.tables` / `select * from INFORMATION_SCHEMA.COLUMNS` work); legacy bare 1-part system-table access (`select * from systypes`) also still works.
-`RAISERROR`, stored procedures, multi-statement table-valued functions (`RETURNS @t TABLE (...) AS BEGIN ... END`), CLR functions, triggers, views, sequences. Scalar UDFs and inline TVFsship — see the "Scalar user-defined functions" and "Inline table-valued functions" sections. `BEGIN ATOMIC` / `BEGIN DISTRIBUTED TRANSACTION` raise `NotSupportedException` at dispatch. Value-form `RETURN N` is legal inside a scalar-UDF body and raises Msg 178 elsewhere (stored-proc scope where it would also be legal isn't modeled). TRY/CATCH + THROW + live `@@ERROR` + `ERROR_*()` functions ship — see the TRY/CATCH section.
579
+
-`RAISERROR`, stored procedures, multi-statement table-valued functions (`RETURNS @t TABLE (...) AS BEGIN ... END`), CLR functions, triggers, sequences. Scalar UDFs, inline TVFs, and views ship — see the matching sections above. **DML through views** (INSERT / UPDATE / DELETE) isn't modeled; the parser raises `NotSupportedException`, deferred to a follow-up bundle. `BEGIN ATOMIC` / `BEGIN DISTRIBUTED TRANSACTION` raise `NotSupportedException` at dispatch. Value-form `RETURN N` is legal inside a scalar-UDF body and raises Msg 178 elsewhere (stored-proc scope where it would also be legal isn't modeled). TRY/CATCH + THROW + live `@@ERROR` + `ERROR_*()` functions ship — see the TRY/CATCH section.
555
580
-**`PRINT` message capture** — the statement parses + evaluates the operand (so operand-side errors like Msg 245 surface), but the message is discarded. `DbConnection` has no `InfoMessage` event (that's a `SqlConnection` extension), so adding a public observability surface would mean a new event on `SimulatedDbConnection`. Defer until an application needs it.
0 commit comments