Skip to content

Commit fe8e432

Browse files
committed
ALTER TABLE ADD / DROP CONSTRAINT across all four families: [WITH CHECK | WITH NOCHECK] ADD [CONSTRAINT name] (PRIMARY KEY | UNIQUE | FOREIGN KEY | CHECK | DEFAULT) … routes through a new Simulation.AlterTableConstraint.cs partial — FK reuses ResolveForeignKeys via a single-element pending list (referenced-key + Msg 1785 cascade-cycle validation); PK / UQ resolve column names against the live table and append into HeapTable.KeyConstraints (now List<KeyConstraint>, mutable contents on a readonly reference — same shape applied to CheckConstraints); DEFAULT introduces a new DefaultConstraint storage type alongside HeapColumn.Default (now mutable), with inline DEFAULT at CREATE TABLE auto-allocating a system-named entry so sys.default_constraints reflects both ALTER-added and CREATE-inline forms. WITH-CHECK (default) existing-data scan raises Msg 1505 ("CREATE UNIQUE INDEX statement terminated …") on PK / UQ duplicates and Msg 547 with the "ALTER TABLE statement" verb prefix on FK orphan / CHECK false rows (the only wording variance from runtime INSERT-time conflicts); WITH NOCHECK skips and sets ForeignKey.IsNotTrusted / CheckConstraint.IsNotTrusted (exposed via sys.foreign_keys.is_not_trusted + the new sys.check_constraints view). New SchemaErrors factories: PrimaryKeyAlreadyExists (1779), DuplicateKeyOnCreate (1505), ColumnAlreadyHasDefault (1781), DefaultColumnInvalid (1752), ForeignKeyInvalidColumn (1769), IndexColumnMissing (1911), NotAConstraint (3728), ConstraintReferencedByForeignKey (3725); ConstraintErrors gains AlterCheckConstraintConflict / AlterForeignKeyConflict — the trailing Msg 1750 / 3727 informational pair that real SQL Server emits after each gets dropped (documented quirk, single primary error per failure). DROP shapes — DROP CONSTRAINT [IF EXISTS] name [, …] — walk KeyConstraints / CheckConstraints / OutgoingForeignKeys / per-column DefaultConstraint in order (first hit wins), atomically validated: any Msg 3728 / 3725 against any name in the list leaves the entire table's constraint state unchanged (probe-confirmed). PK / UQ blocked from drop while an incoming FK references them; FK drop detaches from both endpoints; DEFAULT drop clears HeapColumn.Default + HeapColumn.DefaultConstraint in lockstep. Three new catalog views ship — sys.check_constraints (parent_column_id 1-based for inline / 0 for table-level; is_not_trusted + is_system_named; definition NULL since the simulator stores parsed trees not source text), sys.key_constraints (PK / UQ split via type / type_desc, is_system_named inferred from name prefix), sys.default_constraints (one row per named DEFAULT, inline + ALTER both populate). ALTER TABLE dispatch in Simulation.Alter.cs widens beyond SET SYSTEM_VERSIONING — switch over the post-name token routes Set / Add / Drop branches; identifier-leading ADD (the ADD COLUMN sibling) raises NotSupportedException for the consistent unmodeled signal. MoveNextRequired → MoveNextOptional adjustments in ParseReferentialAction handle ALTER's end-of-batch case. AutoCheckName / AutoForeignKeyName / AutoDefaultName consolidated through a new Fnv1a32 accumulator + FormatAutoConstraintName formatter (PK / UQ's 64-bit / 16-hex form stays separate, matching real SQL Server's object-id-derived shape there). 31 new AlterTableConstraintTests cover ADD across all five families, WITH NOCHECK plumbing + is_not_trusted catalog reflection, existing-data validation per family, DROP including IF EXISTS + atomic multi-drop + referenced-by-FK rejection, all three catalog views, and the ADD-COLUMN-still-unmodeled path. New docs/claude/alter-table.md captures the full grammar / validation / catalog surface / fidelity gaps; CLAUDE.md trigger phrase added; ALTER-TABLE "Not modeled" entry rewritten; foreign-keys.md fidelity-gaps section collapsed (ALTER ADD / DROP / WITH NOCHECK paths no longer deferred).
1 parent 8ea005a commit fe8e432

17 files changed

Lines changed: 1767 additions & 76 deletions

CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
152152
- **Touching `CREATE TRIGGER` / `ALTER TRIGGER` / `DROP TRIGGER` / `DISABLE`/`ENABLE TRIGGER`, the `INSERTED` / `DELETED` pseudo-table materialization, the `TriggerFrame` / `FiringTriggerIds` recursion guard, `TRIGGER_NESTLEVEL()`, or `sys.triggers`**[`docs/claude/triggers.md`](docs/claude/triggers.md).
153153
- **Touching `FOREIGN KEY` parsing (`REFERENCES` inline / `FOREIGN KEY (cols) REFERENCES other(cols)` table-level), referential-action wiring (`ON DELETE` / `ON UPDATE` × `NO ACTION` / `CASCADE` / `SET NULL` / `SET DEFAULT`), the FK enforcement loop in `Simulation.ForeignKeys.cs`, the `HeapTable.OutgoingForeignKeys` / `IncomingForeignKeys` lists, cascade-cycle detection (Msg 1785), DROP-TABLE protection (Msg 3726), or `sys.foreign_keys` / `sys.foreign_key_columns`**[`docs/claude/foreign-keys.md`](docs/claude/foreign-keys.md).
154154
- **Touching `PERIOD FOR SYSTEM_TIME` / `GENERATED ALWAYS AS ROW START / END [HIDDEN]` / `WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = …))` DDL, `ALTER TABLE … SET (SYSTEM_VERSIONING = OFF)`, the auto-created history sibling, `FOR SYSTEM_TIME ALL / AS OF` query syntax, or `HeapTable.PeriodColumns` / `HeapTable.SystemVersioning` / `HeapColumn.GeneratedAs` / `HeapColumn.IsHidden`**[`docs/claude/temporal-tables.md`](docs/claude/temporal-tables.md).
155+
- **Touching `ALTER TABLE … ADD CONSTRAINT` (PK / UQ / FK / CHECK / DEFAULT), `ALTER TABLE … DROP CONSTRAINT [IF EXISTS]`, `WITH CHECK` / `WITH NOCHECK`, the existing-data-validation scan, the constraint-name uniqueness check, the atomic multi-drop pipeline, or `sys.check_constraints` / `sys.key_constraints` / `sys.default_constraints`**[`docs/claude/alter-table.md`](docs/claude/alter-table.md).
155156
- **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).
156157

157158
## Not modeled
@@ -176,7 +177,7 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
176177
- 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.
177178
- CLR functions, INSTEAD OF UPDATE / DELETE on *non-updatable* views (INSTEAD OF INSERT on any view ships; INSTEAD OF UPDATE / DELETE on updatable single-base views ships). DDL / logon triggers also unmodeled. Scalar UDFs, inline TVFs, multi-statement TVFs (`RETURNS @r TABLE (cols) AS BEGIN ... END` — EF Core `HasDbFunction` end-to-end), views, DML-through-views (single-source updatable shape), 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), user-defined table types + table-valued parameters (CREATE TYPE … AS TABLE / DROP TYPE / DECLARE @t MyType / READONLY proc params / EXEC with TVP arg / ADO.NET Structured parameter via the `DbParameter.TypeName` C# 14 extension property + DataTable/IDataReader sources), sequence objects (CREATE / ALTER / DROP SEQUENCE / NEXT VALUE FOR / sys.sequences — supports EF Core HiLo identity strategy end-to-end), and DML triggers (CREATE / ALTER / CREATE OR ALTER / DROP TRIGGER, FOR-as-AFTER synonym, AFTER on heap tables + INSTEAD OF on heap tables / views, multi-action INSERT,UPDATE,DELETE, INSERTED/DELETED pseudo-tables, multiple AFTER triggers per parent / one INSTEAD OF per action per target (Msg 2111), DISABLE/ENABLE TRIGGER, TRIGGER_NESTLEVEL(), direct-recursion suppression matching RECURSIVE_TRIGGERS OFF, sys.triggers + sys.objects integration, trigger-error rolls back firing DML, MERGE per-action routing through INSTEAD OF, DROP TABLE / DROP VIEW cascade-drops attached triggers) ship (see `docs/claude/programmable.md`, `docs/claude/table-valued-parameters.md`, `docs/claude/sequences.md`, and `docs/claude/triggers.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 + RAISERROR (printf-style `%s %d/%i %u %o %x %X %% %ld %I64d` with width / precision / left-align / zero-pad; severity routing 0-10 informational vs 11-18 catchable; `WITH SETERROR`/`NOWAIT` ship, `WITH LOG` uniformly raises Msg 2778; numeric `msg_id` raises Msg 2732 for 50000 / `< 13000` and Msg 18054 otherwise — `sys.messages` registry / `sp_addmessage` not modeled) + live `@@ERROR` + `ERROR_*()` functions ship (see `docs/claude/control-flow.md`).
178179
- **`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.
179-
- **`ALTER TABLE` grammar beyond `SET (SYSTEM_VERSIONING = OFF)`**only the SET-versioning-off shape ships (see [`docs/claude/temporal-tables.md`](docs/claude/temporal-tables.md)). ADD / DROP COLUMN, ADD / DROP CONSTRAINT, DROP PERIOD FOR SYSTEM_TIME, REBUILD, SWITCH PARTITION, the SET-versioning-on direction, and every other shape raise `NotSupportedException` at the post-name dispatch point.
180+
- **`ALTER TABLE` grammar beyond SET / ADD CONSTRAINT / DROP CONSTRAINT**`SET (SYSTEM_VERSIONING = OFF)` (see [`docs/claude/temporal-tables.md`](docs/claude/temporal-tables.md)), `[WITH CHECK | WITH NOCHECK] ADD [CONSTRAINT name] (PRIMARY KEY | UNIQUE | FOREIGN KEY | CHECK | DEFAULT) …` and `DROP CONSTRAINT [IF EXISTS] name [, …]` (see [`docs/claude/alter-table.md`](docs/claude/alter-table.md)) all ship. ADD / DROP COLUMN, ALTER COLUMN, DROP PERIOD FOR SYSTEM_TIME, REBUILD, SWITCH PARTITION, the SET-versioning-on direction, `WITH CHECK CHECK CONSTRAINT` (re-trust), and every other shape raise `NotSupportedException`. Multi-constraint ADD (`ADD CONSTRAINT pk1 PK (a), CONSTRAINT fk1 FK …`) also raises.
180181
- `hierarchyid`, `geography`, `geometry`.
181182

182183
## Quirks (modeled, not byte-identical to SQL Server)

0 commit comments

Comments
 (0)