Skip to content

Commit b257696

Browse files
committed
Stored procedures: CREATE [OR ALTER] / ALTER / DROP PROCEDURE (BEGIN/END optional, ALTER preserves object_id, DROP→Msg 3701/proc variant), EXEC [@rc=] proc [pos/named/OUTPUT/DEFAULT args] with literals/@vars only (Msg 119/201/8143/8144/2812), ProcFrame gates value-form RETURN (default 0, NULL→0, Msg 245 non-coercible), proc result sets forward to outer caller (UDFs still discard), CommandType.StoredProcedure entrypoint with Output/ReturnValue writeback, dynamic SQL EXEC (@SQL) + sp_executesql (outer @vars invisible, OUTPUT writeback), catalog sys.procedures + INFORMATION_SCHEMA.ROUTINES/PARAMETERS + sys.objects 'P ' + OBJECT_ID 'P' filter, 32-level recursion shared with UDFs.
1 parent 3c33478 commit b257696

23 files changed

Lines changed: 2351 additions & 24 deletions

CLAUDE.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ Five scopes, one home each. **Add new state to whichever class matches its true
6262

6363
- **`Simulation`** = server / instance. Process-shared system tables (`SystemHeapTables`), `NEWSEQUENTIALID` anchor + counter, the `Databases` dictionary. Public surface (`Simulation` ctor + `CreateDbConnection()`) stays on this class.
6464
- **`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`), `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"`).
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), `Procedures` (per-schema stored-procedure dict). Future sequences / triggers land here too. Schema-qualified references (`SELECT * FROM audit.t`, `SELECT audit.fn(x)`, `FROM audit.tvf(x)`, `FROM audit.view1`, `EXEC audit.proc1`) route through `Database.Schemas["audit"]`; unqualified references fall back to `Database.DefaultSchemaName` (`"dbo"`).
6666
- **`SimulatedDbConnection`** = session. `CurrentDatabase` pointer, `CurrentTransaction`, `LastIdentity` (`SCOPE_IDENTITY()` / `@@IDENTITY`), `LastStatementRowCount` (`@@ROWCOUNT`), `LastErrorNumber` (`@@ERROR`), `NestingLevel` (UDF / proc / trigger / view recursion depth, capped at 32), `IdentityInsertTable`, `TraceFlags`, `IsVerboseTruncationActive()`, `TempTables` (per-session `#foo` dictionary, cleared on `Dispose`).
67-
- **`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.
67+
- **`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), `ProcFrame` (non-null when this batch is a stored-procedure body — same gate for value-form `RETURN`, plus a return-code slot the caller reads), 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). `TryResolveProcedure(MultiPartName)` resolves through the same schema-lookup path but accepts 1-part names (probe-confirmed: `EXEC p1` finds `dbo.p1`). `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 and procedure invocation each allocate a child `BatchContext` via the corresponding body constructor: parameters pre-seed `Variables`, the matching frame is set, the body source text (captured at CREATE FUNCTION / CREATE PROCEDURE time) is re-tokenized through a synthesized `SimulatedDbCommand`, and the same dispatch loop runs the body. UDF bodies discard yielded result sets at the call site (Msg 444 territory); procedure bodies forward them through to the outer caller's iterator.
6868
- **`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.
6969

7070
**Don't stack misfit state into these buckets unthinkingly**: when adding fields, ask which scope it actually belongs to. If none fits, that's the signal to introduce the missing scope rather than squat on a neighbor. Multi-database is structurally supported but exercised only by the default entry; `USE <db>` is the trigger to populate the dictionary properly.
@@ -142,7 +142,7 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
142142
- **Touching JSON_VALUE / JSON_MODIFY / OPENJSON**[`docs/claude/json.md`](docs/claude/json.md).
143143
- **Changing name resolution, schema lookup, CREATE SCHEMA, or OBJECT_ID**[`docs/claude/schemas.md`](docs/claude/schemas.md).
144144
- **Adding or changing system metadata surfaces** (sys.* / INFORMATION_SCHEMA.*) → [`docs/claude/catalog-views.md`](docs/claude/catalog-views.md).
145-
- **Extending scalar UDFs, inline TVFs, views, or updatable-view DML routing**[`docs/claude/programmable.md`](docs/claude/programmable.md).
145+
- **Extending scalar UDFs, inline TVFs, views, updatable-view DML routing, stored procedures (CREATE/ALTER/DROP/EXEC), or dynamic SQL (`EXEC (@sql)` / `sp_executesql`)**[`docs/claude/programmable.md`](docs/claude/programmable.md).
146146
- **Touching `#foo` routing, DROP TABLE, TRUNCATE TABLE**[`docs/claude/temp-tables.md`](docs/claude/temp-tables.md).
147147
- **Adding a new top-level statement parser or changing the dispatch loop's statement-separator rules**[`docs/claude/grammar.md`](docs/claude/grammar.md) + [`docs/claude/control-flow.md`](docs/claude/control-flow.md).
148148

@@ -171,8 +171,8 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
171171
- **`CREATE SCHEMA AUTHORIZATION <owner>`**`NotSupportedException` (simulator has no user / principal model).
172172
- **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`.
173173
- **`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.
174-
- T-SQL `GOTO` / labels — `IF` / `BEGIN…END` / `WHILE` / `BREAK` / `CONTINUE` / `RETURN` (bare + UDF-body value form) ship; unconditional jumps don't.
175-
- `RAISERROR`, stored procedures, multi-statement table-valued functions (`RETURNS @t TABLE (...) AS BEGIN ... END`), CLR functions, triggers, sequences. Scalar UDFs, inline TVFs, views, and DML-through-views (single-source updatable shape) ship (see `docs/claude/programmable.md`). JOIN-view single-base-table UPDATE/DELETE, OUTPUT through views, and multi-source alias-form UPDATE/DELETE through views are deferred (Msg 4405 or `NotSupportedException` at the DML site). `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 `docs/claude/control-flow.md`).
174+
- T-SQL `GOTO` / labels — `IF` / `BEGIN…END` / `WHILE` / `BREAK` / `CONTINUE` / `RETURN` (bare + UDF-body and stored-procedure-body value form) ship; unconditional jumps don't.
175+
- `RAISERROR`, multi-statement table-valued functions (`RETURNS @t TABLE (...) AS BEGIN ... END`), CLR functions, triggers, sequences. Scalar UDFs, inline TVFs, views, DML-through-views (single-source updatable shape), and stored procedures (including CREATE / ALTER / DROP, EXEC with input/output/default params, `@rc = EXEC` return-code capture, `CommandType.StoredProcedure`, `EXEC (@sql)` and `sp_executesql` dynamic SQL) ship (see `docs/claude/programmable.md`). JOIN-view single-base-table UPDATE/DELETE, OUTPUT through views, and multi-source alias-form UPDATE/DELETE through views are deferred (Msg 4405 or `NotSupportedException` at the DML site). `BEGIN ATOMIC` / `BEGIN DISTRIBUTED TRANSACTION` raise `NotSupportedException` at dispatch. Value-form `RETURN N` is legal inside a scalar-UDF body and a stored-procedure body; bare batch / dynamic-SQL scope raises Msg 178. TRY/CATCH + THROW + live `@@ERROR` + `ERROR_*()` functions ship (see `docs/claude/control-flow.md`).
176176
- **`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.
177177
- `hierarchyid`, `geography`, `geometry`.
178178

0 commit comments

Comments
 (0)