Skip to content

Commit 90725c2

Browse files
committed
geography / geometry data types + spatial indexes — eighth and final bacpac prerequisite bundle, skip-with-diagnostic stance. Spatial columns + geography::Parse(wkt) / geometry::STGeomFromText(wkt, srid) / geometry::Point(x, y, srid) construction + CREATE SPATIAL INDEX (with USING <scheme> / BOUNDING_BOX / GRIDS / CELLS_PER_OBJECT) + sys.spatial_indexes / sys.spatial_index_tessellations / sys.spatial_reference_systems ship. OGC + Microsoft-extension instance methods (.STDistance / .STAsText / .STIntersects / etc.) parse cleanly so CREATE VIEW / CREATE PROCEDURE bodies that reference them store verbatim, then raise NotSupportedException at execute — except .ToString() which returns the stored WKT (the only spatial method whose result is recoverable from the degraded in-memory representation). AW's sole spatial column (Person.Address.SpatialLocation, geography) round-trips end-to-end as a first-class spatial-typed column rather than the originally-recommended varbinary(MAX) degradation. All eight bacpac prerequisites now complete.
**Storage**: new `GeographySqlType` + `GeometrySqlType` singletons in `Storage/SpatialType.cs`, both inheriting `SpatialSqlType : SqlType(SqlTypeCategory.String)`. SystemTypeId=240 (CLR-UDT family, shared with hierarchyid via the new arm in `SqlType.SystemTypeId`), UserTypeId=130 (geography) / 129 (geometry) via new arms in `SqlType.UserTypeId`. IsLob=true (routes column declaration through the off-row LOB chain, matching real-server max_length=-1 reporting). Payload encoding: raw UTF-16 LE of the constructed WKT string — the simulator's degraded-mode form. `SqlValue.FromGeography` / `FromGeometry` factories + the corresponding branches in `SqlValue.FromString`. The pre-existing `BuiltInResources.SystypesRowData` rows 33-34 (geography + geometry) already carried the system_type_id / user_type_id pair; the bundle wired them into `ResolveSimpleKeyword`'s 8-char / 9-char arms so `CREATE TABLE (g geography)` actually accepts them, and added the `SpatialSqlType => (-1, 0, 0)` arm to `GetSysColumnMetadata` so `sys.columns` reports the type identity. The byte form on disk is **not** SQL Server's documented binary CLR-UDT representation — same simulator-specific deferral as `hierarchyid`, to be replaced when the BACPAC loader bundle implements wire-format encoding (the prerequisite doc's order-of-operations item 2 calls this out explicitly). **Method-call surface** (`Parser/Expressions/SpatialMethodCall.cs`, ~270 lines): broad closed accept-list (~70 names) covering every OGC predicate / accessor / constructor exposed on both geography and geometry, plus the common Microsoft extensions (`Lat` / `Long` / `MakeValid` / `Reduce` / `Filter` / `BufferWithTolerance` / `BufferWithCurves` / `EnvelopeAngle` / `STSrid` / `STNumGeometries` / `STX` / `STY` / `STZ` / `STM` / `HasZ` / `HasM` / `InstanceOf` / etc.). `IsKnownMethodName` is checked at parse in `Expression.cs`'s dotted-name dispatch loop, immediately after the `XmlMethodCall.IsKnownMethodName` check; on a positive match (followed by `(`) the parse dispatches as `SpatialMethodCall` rather than falling through to multipart-Reference. `Run` raises `NotSupportedException` with `"Spatial instance method '.NAME()' is not modeled."` except `.ToString()` which returns the stored WKT as `nvarchar(MAX)`. Static result-type inference applies the right SqlType per method (`.STDistance` / `.STArea` / `.STLength` / `.Lat` / `.Long` → `float`; `.STContains` / `.STIntersects` / `.STIsValid` / `.HasZ` / `.HasM` → `bit`; `.STAsText` / `.STGeometryType` / `.ToString` / `.AsGml` / `.AsTextZM` → `nvarchar(MAX)`; `.STAsBinary` / `.AsBinaryZM` → `varbinary(MAX)`; `.STSrid` / `.STDimension` / `.STNumGeometries` / etc. → `int`; constructor-style methods → same `SpatialSqlType` as the receiver) so projection-schema resolution works at the parser level even though `Run` never succeeds. **Static-call surface** (`Parser/Expressions/SpatialStaticCall.cs`): `geography::` and `geometry::` type-scope dispatched alongside `hierarchyid::` in `Expression.cs`'s `::` operator handling — the existing single-arm `hierarchyid`-only check generalizes to a 3-way switch over the LHS Reference's leaf name. `Parse(wkt)` / `STGeomFromText(wkt, srid)` accept a single WKT-string argument (the SRID is parsed-and-discarded — the simulator doesn't track per-value SRID). `Point(x, y, srid)` accepts numeric coordinates and synthesizes a `POINT (x y)` WKT. Every other static method raises `NotSupportedException` at `Run`. **ToString collision resolution**: the `.ToString()` name collides with hierarchyid's `.ToString()` (which dispatches via the existing `HierarchyIdMethodCall` path; both classes' `IsKnownMethodName` claim it). Since the dispatch in `Expression.cs` checks Hierarchy before Spatial, a naive integration would route `geography_col.ToString()` through `HierarchyIdMethodCall.Run`, which would raise Msg 6522 with `"receiver is geography, not hierarchyid"`. Fix: `HierarchyIdMethodCall.Run` now detects a `SpatialSqlType` receiver at runtime and returns the WKT through the spatial path (`SqlValue.FromNVarchar(receiver.AsString)` for non-NULL, `SqlValue.Null(NVarcharSqlType.MaxForm)` for NULL). This avoids touching the dispatch-order in `Expression.cs` — the hierarchyid `.ToString()` tests stay green, the spatial `.ToString()` path now works end-to-end. **Spatial index parser** (`Simulation/Simulation.Spatial.cs`): `CREATE SPATIAL INDEX name ON table(col) [USING <i>scheme</i>] [WITH (BOUNDING_BOX = (xmin, ymin, xmax, ymax) | GRIDS = (level [, …]) | CELLS_PER_OBJECT = n | <i>any other index option</i>)]`. Cursor on entry is the `SPATIAL` contextual keyword; the parser walks INDEX → name → ON → object → `(col)` → optional USING → optional WITH. Default `tessellation_scheme` when no USING clause is given: `GEOMETRY_AUTO_GRID` (geometry-typed col) / `GEOGRAPHY_AUTO_GRID` (geography-typed col), matching probed real-server behavior. GRIDS level arguments accept either numeric codes (1/2/3) or named levels (LOW / MEDIUM / HIGH) — both forms surface as 1/2/3 in `sys.spatial_index_tessellations.level_*_grid`. Unknown options inside WITH (FILLFACTOR / PAD_INDEX / IGNORE_DUP_KEY / ONLINE / etc.) skip via balanced-paren consumption until the next top-level `,` or `)`. Non-spatial column → `NotSupportedException`; duplicate index name → Msg 2714. New `SpatialIndex` storage class + `SpatialIndexKind` enum on `HeapTable.SpatialIndexes`; new `Spatial` entry in `ContextualKeyword` enum + dispatch arm in `TryParseCreate`. **Catalog views** in `BuiltInResources.cs`: - `sys.spatial_indexes` (23-col, probe-confirmed against SQL Server 2025 on 2026-05-15): object_id / name / index_id / type (=4) / type_desc (='SPATIAL') / is_unique (=false) / data_space_id (=1) / ignore_dup_key / is_primary_key / is_unique_constraint / fill_factor / is_padded / is_disabled / is_hypothetical / is_ignored_in_optimization / allow_row_locks (=true) / allow_page_locks (=true) / spatial_index_type (3 for geometry / 4 for geography) / spatial_index_type_desc ('GEOMETRY' / 'GEOGRAPHY') / tessellation_scheme / has_filter / filter_definition / auto_created. - `sys.spatial_index_tessellations` (16-col, probe-confirmed): object_id / index_id / tessellation_scheme / bounding_box_xmin/ymin/xmax/ymax / level_1_grid + level_1_grid_desc / ... / level_4_grid + level_4_grid_desc / cells_per_object. Unspecified GRIDS levels surface as NULL; level_*_grid_desc translates the 1/2/3 codes to 'LOW' / 'MEDIUM' / 'HIGH'. - `sys.spatial_reference_systems` (6-col): empty by default (real SQL Server pre-seeds ~390 EPSG/ESRI SRID rows; the simulator surfaces the column shape but skips the WKT-laden seed payload). 23 new tests in `SpatialTypeTests.cs` cover: column round-trip for both geography and geometry; NULL handling; `sys.types` reports user_type_id=130 / 129 for geography / geometry sharing system_type_id=240; `sys.columns` reports spatial type identity + max_length=-1; geography::Parse from nvarchar literal; geometry::Point synthesizes a WKT from coordinates; `.ToString()` returns stored WKT; `.STDistance` / `.STAsText` / `.STIntersects` raise NotSupportedException at execute; CREATE VIEW with spatial method succeeds at CREATE and fails at execute (verifies the late-binding stance); CREATE SPATIAL INDEX populates sys.spatial_indexes (geometry with explicit bounding-box; geography with default GEOGRAPHY_AUTO_GRID); BOUNDING_BOX round-trips through sys.spatial_index_tessellations; GRIDS with LOW / HIGH level names parse to 1 / 3 codes + CELLS_PER_OBJECT; duplicate index name → Msg 2714; sys.spatial_reference_systems empty + column-shape reachable; CAST geography → nvarchar(MAX) round-trip. Total: 4185 main (+23 new) / 227 internal / 328 EFCore / 58 analyzers — all green Debug + Release. `docs/claude/bacpac-prerequisites.md` flips the geography/geometry checklist item to [x] with the full implementation walkthrough; the order-of-operations sequence absorbs the merged item and marks **all prerequisites complete**, updates the loader-baseline item to reflect that xml / spatial now ship first-class (no longer need varbinary(MAX) degradation at the loader), and updates the Status section to flag the loader-baseline as the next bundle. `CLAUDE.md` adds a new Feature-reference index entry for the spatial surface (`GeographySqlType` / `GeometrySqlType` / `SpatialMethodCall` / `SpatialStaticCall` / `SpatialIndex` / `Simulation.Spatial.cs` / catalog views), rewrites the BACPAC working-document trigger line to drop the no-longer-pending spatial prerequisite + note all eight bundles complete, and rewrites the `Not modeled` `geography` / `geometry` bullet to reflect the shipped surface (skip-with-diagnostic stance with `.ToString()` returning WKT, static constructors working, OGC methods parsing+throwing). **Deferred** (real feature work after the loader baseline ships): WKT/WKB parsing for validation (any string is currently accepted as a "WKT" payload); OGC method evaluation pipeline (`.STDistance` / `.STIntersects` / `.STArea` / `.STBuffer` / `.STContains` / `.STIntersection` / `.STUnion` / `.STDifference` / etc. — all the geometric predicates and constructors); SRID tracking + transformation; spatial-index query-planner integration (the index parses cleanly but never accelerates anything); the `sys.spatial_reference_systems` seed data (~390 EPSG/ESRI rows); `ALTER SPATIAL INDEX` (REORGANIZE / REBUILD); and the documented byte-identical CAST encoding for cross-engine binary transfer.
1 parent 42b37b8 commit 90725c2

16 files changed

Lines changed: 1353 additions & 22 deletions

CLAUDE.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,9 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
162162
- **Touching `GRANT` / `REVOKE` / `DENY` parsing (including `WITH GRANT OPTION`, `REVOKE GRANT OPTION FOR`, `CASCADE`, `ON OBJECT::name` / `SCHEMA::name` / `DATABASE::name` / `TYPE::name`), the principal DDL surface (`CREATE USER`, `CREATE ROLE`, `ALTER ROLE … {ADD | DROP} MEMBER`, `DROP USER`, `DROP ROLE`), the `DatabasePrincipal` / `DatabasePermission` classes + `Database.Principals` / `Database.Permissions` / `Database.RoleMembers`, the pre-seeded fixed principals (public=0 / dbo=1 / guest=2 / INFORMATION_SCHEMA=3 / sys=4), the `sys.database_principals` / `sys.database_permissions` / `sys.database_role_members` catalog views, the Msg 15151 unknown-principal verbatim wording, or the Msg 15023 duplicate-principal-name verbatim wording**[`docs/claude/bacpac-prerequisites.md`](docs/claude/bacpac-prerequisites.md) (the "Permission statements" section carries the implementation detail).
163163
- **Touching `CREATE FULLTEXT CATALOG` / `CREATE FULLTEXT INDEX` / `DROP FULLTEXT CATALOG` / `DROP FULLTEXT INDEX`, the `FullTextCatalog` / `FullTextIndex` / `FullTextIndexColumn` storage, `Database.FullTextCatalogs` dict + `AllocateFullTextCatalogId`, the per-table `HeapTable.FullTextIndex` slot, the `Simulation.FullText.cs` partial, the `Fulltext` contextual keyword routing in CREATE/DROP, the `sys.fulltext_catalogs` / `sys.fulltext_indexes` / `sys.fulltext_index_columns` catalog views, or the `NotSupportedException` rejection for CONTAINS / FREETEXT / CONTAINSTABLE / FREETEXTTABLE / SEMANTIC* in `BooleanExpression.ParseAtom` + `Selection.ParseSingleFromSource`**[`docs/claude/bacpac-prerequisites.md`](docs/claude/bacpac-prerequisites.md) (the "Full-text catalog + index" section carries the implementation detail).
164164
- **Touching `XmlSqlType` (singleton in `Storage/XmlType.cs`, SystemTypeId=241, IsLob=true), `XmlSchemaCollection` storage class + `Schema.XmlSchemaCollections` dict + `Database.AllocateXmlCollectionId` (seeds at 65536 per probe), `HeapColumn.XmlSchemaCollection` per-column binding, the `xml`-recognizing arms of `SqlType.ResolveSimpleKeyword` / `Precedence` / `SystemTypeId`, the `xml(name)` / `xml(CONTENT name)` / `xml(DOCUMENT name)` column-type parsing in `ParseOneColumnIntoLists` (via `PeekIsXmlSchemaArgument` / `ParseXmlSchemaCollectionArgument`), `Simulation.Xml.cs` partial (CREATE XML SCHEMA COLLECTION / DROP XML SCHEMA COLLECTION / CREATE [PRIMARY] XML INDEX / FOR PATH|VALUE|PROPERTY secondary form), the `Xml` contextual keyword + `Keyword.Primary` dispatch in `TryParseCreate`, `XmlIndex` / `XmlSecondaryIndexType` storage on `HeapTable.XmlIndexes`, the `XmlMethodCall` expression in `Parser/Expressions/XmlMethodCall.cs` (closed accept-list of `value` / `nodes` / `query` / `exist` / `modify`, parses cleanly, throws `NotSupportedException` at `Run`), or the `sys.xml_schema_collections` / `sys.xml_indexes` catalog views** → [`docs/claude/bacpac-prerequisites.md`](docs/claude/bacpac-prerequisites.md) (the "`xml` data type + XML schema collections + XML methods + XML indexes" section carries the implementation detail).
165+
- **Touching `GeographySqlType` / `GeometrySqlType` (singletons in `Storage/SpatialType.cs`, both inherit `SpatialSqlType : SqlType(SqlTypeCategory.String)`, SystemTypeId=240, UserTypeId=130/129, IsLob=true), `SqlValue.FromGeography` / `FromGeometry` factories + the spatial branches in `FromString`, the `geography` / `geometry`-recognizing arms of `SqlType.ResolveSimpleKeyword` / `Precedence` / `SystemTypeId` / `UserTypeId`, the `SpatialMethodCall` expression in `Parser/Expressions/SpatialMethodCall.cs` (broad ~70-name accept-list of OGC + Microsoft-extension methods; parses cleanly + throws `NotSupportedException` at `Run` except `.ToString()` returns the stored WKT), the `SpatialStaticCall` expression in `Parser/Expressions/SpatialStaticCall.cs` (`geography::Parse(wkt)` / `geography::STGeomFromText(wkt, srid)` / `geometry::Point(x, y, srid)` work; other static methods throw), the `::` dispatch for `geography`/`geometry` in `Expression.cs` alongside `hierarchyid::`, the `.ToString()` polymorphic delegation in `HierarchyIdMethodCall.Run` (spatial receivers return WKT through the spatial path), the `SpatialIndex` storage class + `SpatialIndexKind` enum on `HeapTable.SpatialIndexes`, the `Simulation.Spatial.cs` partial (CREATE SPATIAL INDEX with USING / BOUNDING_BOX / GRIDS / CELLS_PER_OBJECT parsing + default tessellation_scheme), the `Spatial` contextual keyword routing in `TryParseCreate`, or the `sys.spatial_indexes` (23-col probe-confirmed) / `sys.spatial_index_tessellations` (16-col probe-confirmed) / `sys.spatial_reference_systems` (6-col, empty seed) catalog views** → [`docs/claude/bacpac-prerequisites.md`](docs/claude/bacpac-prerequisites.md) (the "`geography` / `geometry` data types" section carries the implementation detail).
165166
- **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).
166-
- **Working on BACPAC import support (the eventual `Simulation.FromBacpac` / `FromBacPac` entry point), or knocking off the sole remaining prerequisite feature (`geography`/`geometry` spatial types)** → [`docs/claude/bacpac-prerequisites.md`](docs/claude/bacpac-prerequisites.md). Working document with a feature checklist + the BCP wire-format type matrix; serves as the running scoping doc until the loader baseline lands. Shipped prerequisites: UDDTs / alias types, `sp_addextendedproperty` + `sys.extended_properties`, ALTER DATABASE options accept-list, hierarchyid AW-minimum-viable surface, DDL trigger `ON DATABASE`, GRANT/REVOKE/DENY + CREATE USER/CREATE ROLE/ALTER ROLE/DROP USER/DROP ROLE + `sys.database_principals` / `sys.database_permissions` / `sys.database_role_members`, full-text catalog + index (DDL + `sys.fulltext_*` catalog views ship, CONTAINS / FREETEXT / CONTAINSTABLE / FREETEXTTABLE / SEMANTIC* raise `NotSupportedException`), `xml` data type + xml(schema_collection) bindings + CREATE [PRIMARY] XML INDEX + `sys.xml_schema_collections` / `sys.xml_indexes` (xml-typed columns round-trip raw UTF-16 text identical to nvarchar(MAX) storage; XPath/XQuery methods `.value()` / `.nodes()` / `.query()` / `.exist()` / `.modify()` raise `NotSupportedException` at execute).
167+
- **Working on BACPAC import support (the eventual `Simulation.FromBacpac` / `FromBacPac` entry point)** → [`docs/claude/bacpac-prerequisites.md`](docs/claude/bacpac-prerequisites.md). Working document with a feature checklist + the BCP wire-format type matrix; serves as the running scoping doc until the loader baseline lands. **All eight prerequisites complete** (shipped 2026-05-14 through 2026-05-15): UDDTs / alias types, `sp_addextendedproperty` + `sys.extended_properties`, ALTER DATABASE options accept-list, hierarchyid AW-minimum-viable surface, DDL trigger `ON DATABASE`, GRANT/REVOKE/DENY + CREATE USER/CREATE ROLE/ALTER ROLE/DROP USER/DROP ROLE + `sys.database_principals` / `sys.database_permissions` / `sys.database_role_members`, full-text catalog + index (DDL + `sys.fulltext_*` catalog views ship, CONTAINS / FREETEXT / CONTAINSTABLE / FREETEXTTABLE / SEMANTIC* raise `NotSupportedException`), `xml` data type + xml(schema_collection) bindings + CREATE [PRIMARY] XML INDEX + `sys.xml_schema_collections` / `sys.xml_indexes`, and `geography` / `geometry` data types + CREATE SPATIAL INDEX + `sys.spatial_indexes` / `sys.spatial_index_tessellations` / `sys.spatial_reference_systems` (spatial columns round-trip the constructed WKT through raw UTF-16 storage; OGC methods `.STDistance` / `.STAsText` / `.STIntersects` / etc. parse cleanly + throw `NotSupportedException` at execute except `.ToString()` returns the stored WKT; static constructors `geography::Parse` / `geography::STGeomFromText` / `geometry::Point` construct values).
167168

168169
## Not modeled
169170

@@ -192,7 +193,7 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
192193
- **TRY/CATCH + THROW + RAISERROR + `@@ERROR` + `ERROR_*()` ship** with printf-style formatter (`%s %d/%i %u %o %x %X %% %ld %I64d`, width/precision/left-align/zero-pad), severity routing (0-10 informational, 11-18 catchable, `WITH SETERROR`/`NOWAIT` honored, `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. See [`docs/claude/control-flow.md`](docs/claude/control-flow.md).
193194
- **Public `InfoMessage` event** — `SimulatedDbConnection.InfoMessage` ships as an `internal` event (with `internal SimulatedInfoMessageEventArgs` carrying `Message`, `LineNumber`, `Source`) and a `BatchContext`-side buffer that coalesces multiple `PRINT`s per command into one firing (joined with `\n`, line number = first contributing `PRINT`'s line). The internal surface is exercised through `SqlServerSimulator.Tests.Internal/PrintInfoMessageTests.cs`. Going *public* — exposing the event on the `SimulatedDbConnection` consumer surface for application use — is deferred pending the public-API shape decision (mirror SqlClient's `SqlInfoMessageEventArgs` exactly? trim to a minimal shape? add a `DbConnection`-compatible extension method instead of a typed event?). Subquery-in-operand still silently evaluates instead of raising Msg 1046 ("Subqueries are not allowed in this context"); non-string-value formatting routes through `SqlValue.CoerceTo(varchar(8000))` rather than SQL Server's PRINT-specific style 0 conventions (datetime → ISO instead of `"May 14 2026 12:00AM"`; money → `F4` instead of 2-decimal). The 8000-byte ANSI / 4000-character Unicode PRINT truncation isn't enforced — long strings pass through verbatim.
194195
- **`ALTER TABLE` modeled shapes**: `SET (SYSTEM_VERSIONING = OFF)`, `[WITH CHECK|NOCHECK] ADD CONSTRAINT … (PK | UNIQUE | FK | CHECK | DEFAULT)`, `DROP CONSTRAINT [IF EXISTS] name [, …]`, trust toggling via `(CHECK|NOCHECK) CONSTRAINT (ALL | name)`, `ADD [COLUMN] col TYPE [, …]` (multi-column, inline DEFAULT/IDENTITY/CHECK/FK/computed), `DROP COLUMN [IF EXISTS] col [, …]`, `ALTER COLUMN col TYPE [NULL|NOT NULL]` (single-column, type widening/narrowing, nullability flip). See [`docs/claude/alter-table.md`](docs/claude/alter-table.md). Out of scope: DROP PERIOD FOR SYSTEM_TIME, REBUILD, SWITCH PARTITION, SET-versioning-on, `ALTER COLUMN col ADD/DROP {PERSISTED|MASKED|ROWGUIDCOL|SPARSE}`, identity-column type change to non-integer, multi-constraint ADD (`ADD CONSTRAINT pk1 PK (a), CONSTRAINT fk1 FK …`) — all raise `NotSupportedException`.
195-
- `geography`, `geometry``hierarchyid` ships the AW-minimum-viable surface (Parse / GetRoot / GetLevel / GetAncestor / GetDescendant / IsDescendantOf / ToString / comparison / `ORDER BY`); see [`docs/claude/bacpac-prerequisites.md`](docs/claude/bacpac-prerequisites.md) for the methods + the deferred byte-identical CAST encoding (currently simulator-native, to be replaced when the BACPAC loader bundle starts).
196+
- `hierarchyid` ships the AW-minimum-viable surface (Parse / GetRoot / GetLevel / GetAncestor / GetDescendant / IsDescendantOf / ToString / comparison / `ORDER BY`); `geography` and `geometry` ship skip-with-diagnostic — WKT round-trip + static constructors + `.ToString()` work, every other OGC/MS-extension method parses cleanly + throws `NotSupportedException` at execute. See [`docs/claude/bacpac-prerequisites.md`](docs/claude/bacpac-prerequisites.md) for the method list + the deferred byte-identical CAST encoding (currently simulator-native for all three, to be replaced when the BACPAC loader bundle starts).
196197
- **Query hints**`WITH (hint, …)` on FROM/JOIN-RHS/INSERT/UPDATE/DELETE/MERGE targets and `OPTION (…)` ship; closed accept-lists raise `Msg 321` (unknown table hint) / `Msg 102` (unknown OPTION hint) verbatim; `Msg 1047`/`1065`/`1069`/`307`/`308` enforced; legacy bare-paren form FROM/JOIN-RHS only. `MAXRECURSION N` retains runtime effect; every other recognized hint parse-and-discards. Remaining gaps: FROM-source `(unknown)` without alias falls through to Msg 102 (real SQL Server raises Msg 207/321); `FORCESEEK(name(cols))` nested-form name validation isn't run. See [`docs/claude/query-hints.md`](docs/claude/query-hints.md).
197198

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

0 commit comments

Comments
 (0)