Skip to content

Commit 42b37b8

Browse files
committed
xml data type + XML schema collections + XML indexes — seventh bacpac prerequisite bundle, skip-with-diagnostic stance. xml columns + xml(schema_collection) bindings + CREATE/DROP XML SCHEMA COLLECTION + CREATE [PRIMARY] XML INDEX (with FOR PATH|VALUE|PROPERTY secondary form) + sys.xml_schema_collections / sys.xml_indexes ship. XPath/XQuery methods (.value() / .nodes() / .query() / .exist() / .modify()) parse cleanly so CREATE VIEW / CREATE PROCEDURE bodies that reference them store verbatim, then raise NotSupportedException at execute. AW's 9 xml columns + 6 SqlXmlSchemaCollection + 8 SqlXmlIndex elements all round-trip end-to-end.
**Storage**: new `XmlSqlType : SqlType` singleton (`SqlServerSimulator/Storage/XmlType.cs`) — SqlServerName="xml", SystemTypeId=241, IsLob=true. Payload encoding identical to nvarchar(MAX) (raw UTF-16 LE); xml-typed values share one type identity (no per-column schema-collection binding at the type level — the binding lives on the column). `SqlValue.FromXml` / round-tripping via `SqlType.Xml`. Three SqlType edits: `Precedence` / `SystemTypeId` / `ResolveSimpleKeyword` length-3 arm pick up `XmlSqlType` / `xml`. New `XmlSchemaCollection` class (id + name + schema_id + nullable principal_id + xsdText + create_date / modify_date); `Schema.XmlSchemaCollections` is the per-schema dict (Msg 219 on type-namespace collision with TableTypes / AliasTypes). `Database.AllocateXmlCollectionId` returns 65536 first (probe-confirmed against SQL Server 2025 on 2026-05-15). `HeapColumn.XmlSchemaCollection` is a nullable ref linking xml columns to their collection — metadata only, no XSD validation. `HeapTable.XmlIndexes` is a List<XmlIndex>; `XmlIndex` carries name + columnOrdinal + isPrimary + UsingPrimaryIndexName + nullable SecondaryType (PATH / VALUE / PROPERTY) + ObjectId. `XmlSecondaryIndexType` enum maps to char(1) codes P / V / R. **Parsers** (`Simulation/Simulation.Xml.cs`): - `CREATE XML SCHEMA COLLECTION [schema.]name AS 'xsd-text'` — XSD stored verbatim, no parsing or validation. Allocates id via AllocateXmlCollectionId, lands in `Schema.XmlSchemaCollections`. Type-namespace collision → Msg 219. - `DROP XML SCHEMA COLLECTION [schema.]name` — removes from dict; missing → Msg 208. - `CREATE PRIMARY XML INDEX name ON table(col) [WITH (…)]` — primary form. Allocates ObjectId, stored on `HeapTable.XmlIndexes`. Trailing `WITH (…)` parse-and-discards via `SkipBalancedParens`. - `CREATE XML INDEX name ON table(col) USING XML INDEX primary_name FOR {PATH | VALUE | PROPERTY} [WITH (…)]` — secondary form. Same storage; SecondaryType populated. - xml column-type position: `xml`, `xml(name)`, `xml(CONTENT name)`, `xml(DOCUMENT name)`. Detected in `ParseOneColumnIntoLists` via `PeekIsXmlSchemaArgument` (matched only when the bare 1-part type name is "xml" and the token after `(` is a Name — disambiguates from a length/precision spec). `ParseXmlSchemaCollectionArgument` consumes the inner ref, parses-and-discards the CONTENT/DOCUMENT discriminator, resolves against the schema's XmlSchemaCollections dict (Msg 208 on miss). - Statement dispatch: new `Xml` ContextualKeyword; CREATE / DROP routes match `UnquotedString { ContextualKeyword: ContextualKeyword.Xml }` and `ReservedKeyword { Keyword: Keyword.Primary }` (the latter for `CREATE PRIMARY XML INDEX`). SCHEMA is reserved (Keyword.Schema); COLLECTION is a bare identifier. **XML method execution rejection** (`Parser/Expressions/XmlMethodCall.cs`): closed accept-list of `value` / `nodes` / `query` / `exist` / `modify` matched in `Expression.cs`'s dotted-name dispatch (only when followed by `(`). Arguments parse fully (so name resolution surfaces eagerly per the simulator's idiom) but they're discarded — runtime evaluation raises `NotSupportedException` with `"XML instance method '.NAME()' is not modeled."`. Static result-type inference returns SqlType.Bit for `.exist()`, NVarcharSqlType.MaxForm for `.value()`, SqlType.Xml for others — so projection-schema resolution works at parser level even though the methods can never be evaluated. **Catalog views** in `BuiltInResources.cs`: - `sys.xml_schema_collections` (6-col, probe-confirmed): xml_collection_id / schema_id / principal_id (NULL — AUTHORIZATION clause not modeled) / name / create_date / modify_date. - `sys.xml_indexes` (9-col shipped subset; real surface 26 cols): object_id / name / index_id / type (=3, "XML") / type_desc / using_xml_index_id (NULL for primary, points at primary's index_id for secondary) / secondary_type (char(1)) / secondary_type_desc / is_primary_key (always false). `sys.columns` enumerator extended — `GetSysColumnMetadata` arm for `XmlSqlType` returns max_length=-1, prec=0, scale=0 (matches real SQL Server's reporting of xml columns through `sys.columns`). 21 new tests in `XmlTests.cs` cover: xml column round-trip (raw text through INSERT / SELECT); NULL storage; `sys.columns` reports xml type identity; CREATE XML SCHEMA COLLECTION happy + qualified-schema + duplicate Msg 219; xml(name) column binding + CONTENT/DOCUMENT discriminators + unknown-collection Msg 208; DROP collection removes; CREATE PRIMARY XML INDEX + three secondary forms (PATH/VALUE/PROPERTY) + using_xml_index_id linkage; duplicate-name Msg 2714; XML method NotSupportedException at execute (`.value` / `.query` / `.exist`); CREATE VIEW with XML method succeeds at CREATE and fails at execute (verifies the late-binding stance); xml_collection_id starts at 65536; CAST(xml AS nvarchar(MAX)) round-trip. Test counts: 4162 main (+21 new) / 227 internal / 328 EFCore / 58 analyzers — all green Debug + Release. `docs/claude/bacpac-prerequisites.md` flips the xml checklist item to [x] with the full implementation walkthrough; the order-of-operations sequence absorbs the merged item. `CLAUDE.md` shrinks the BACPAC prerequisite list to just spatial (`geography`/`geometry`) and gains a new Feature-reference index entry for the xml + schema-collection + xml-index + XmlMethodCall surface; the "Working on BACPAC import support" entry's shipped-prerequisites paragraph now lists xml alongside the prior six landed items. **Deferred** (real feature work after the loader baseline ships): XPath/XQuery evaluation pipeline (`.value` / `.nodes` / `.query` / `.exist` / `.modify` body semantics), XSD validation against `xml(schema_collection)` bindings, `FOR XML` query-output clause, `ALTER XML SCHEMA COLLECTION ADD` for incremental schema additions, `SELECTIVE XML INDEX` variant (SQL Server 2014+).
1 parent 71929d1 commit 42b37b8

18 files changed

Lines changed: 1064 additions & 36 deletions

CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,9 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
161161
- **Touching `CREATE TRIGGER … ON DATABASE` / `DROP TRIGGER … ON DATABASE`, the `DdlTrigger` class + `Database.DdlTriggers` dict, the `sys.triggers` row shape for DDL triggers (parent_class=0, parent_class_desc='DATABASE', parent_id=0), or the no-fire deferral (DDL events aren't dispatched to a trigger loop)**[`docs/claude/bacpac-prerequisites.md`](docs/claude/bacpac-prerequisites.md) (the "DDL trigger" section carries the implementation detail).
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).
164+
- **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).
164165
- **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).
165-
- **Working on BACPAC import support (the eventual `Simulation.FromBacpac` / `FromBacPac` entry point), or knocking off any of its remaining prerequisite features (`xml` + XML schema collections + XML methods + XML indexes, `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`).
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).
166167

167168
## Not modeled
168169

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
2+
3+
namespace SqlServerSimulator;
4+
5+
/// <summary>
6+
/// Behavioral tests for the parse-and-store-but-no-search xml surface
7+
/// (<c>xml</c> as a column type, <c>xml(schema_collection)</c> binding,
8+
/// <c>CREATE/DROP XML SCHEMA COLLECTION</c>, <c>CREATE [PRIMARY] XML
9+
/// INDEX</c>, plus <c>sys.xml_schema_collections</c> / <c>sys.xml_indexes</c>).
10+
/// XPath / XQuery methods (<c>.value()</c> / <c>.nodes()</c> / <c>.query()</c>
11+
/// / <c>.exist()</c> / <c>.modify()</c>) raise <see cref="NotSupportedException"/>
12+
/// at execute time.
13+
/// </summary>
14+
[TestClass]
15+
public sealed class XmlTests
16+
{
17+
[TestMethod]
18+
public void XmlColumn_AcceptsAndRoundTrips()
19+
{
20+
var sim = new Simulation();
21+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body xml)");
22+
_ = sim.ExecuteNonQuery("insert into dbo.doc values (1, N'<root><child>hi</child></root>')");
23+
var read = sim.ExecuteScalar("select body from dbo.doc where id = 1");
24+
AreEqual("<root><child>hi</child></root>", read);
25+
}
26+
27+
[TestMethod]
28+
public void XmlColumn_NullStoresAsNull()
29+
{
30+
var sim = new Simulation();
31+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body xml null)");
32+
_ = sim.ExecuteNonQuery("insert into dbo.doc values (1, null)");
33+
AreEqual(DBNull.Value, sim.ExecuteScalar("select body from dbo.doc"));
34+
}
35+
36+
[TestMethod]
37+
public void SysColumns_ReportsXmlTypeIdentity()
38+
{
39+
var sim = new Simulation();
40+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body xml)");
41+
AreEqual("xml", sim.ExecuteScalar(@"
42+
select t.name from sys.columns c
43+
join sys.types t on t.user_type_id = c.user_type_id
44+
where c.object_id = object_id('dbo.doc') and c.name = 'body'"));
45+
}
46+
47+
[TestMethod]
48+
public void CreateXmlSchemaCollection_Succeeds()
49+
{
50+
var sim = new Simulation();
51+
_ = sim.ExecuteNonQuery(@"create xml schema collection xsc1 as N'<xsd:schema xmlns:xsd=""http://www.w3.org/2001/XMLSchema""><xsd:element name=""root"" type=""xsd:string"" /></xsd:schema>'");
52+
AreEqual(1, sim.ExecuteScalar("select count(*) from sys.xml_schema_collections where name = 'xsc1'"));
53+
}
54+
55+
[TestMethod]
56+
public void CreateXmlSchemaCollection_QualifiedSchema_Succeeds()
57+
{
58+
var sim = new Simulation();
59+
_ = sim.ExecuteNonQuery("create schema audit");
60+
_ = sim.ExecuteNonQuery("create xml schema collection audit.xsc1 as N'<xsd:schema/>'");
61+
var schemaId = sim.ExecuteScalar("select schema_id from sys.xml_schema_collections where name = 'xsc1'");
62+
var auditId = sim.ExecuteScalar("select schema_id from sys.schemas where name = 'audit'");
63+
AreEqual(auditId, schemaId);
64+
}
65+
66+
[TestMethod]
67+
public void CreateXmlSchemaCollection_DuplicateName_Raises219()
68+
{
69+
var sim = new Simulation();
70+
_ = sim.ExecuteNonQuery("create xml schema collection xsc1 as N'<xsd:schema/>'");
71+
_ = sim.AssertSqlError("create xml schema collection xsc1 as N'<xsd:schema/>'", 219);
72+
}
73+
74+
[TestMethod]
75+
public void XmlColumn_WithSchemaCollection_Binds()
76+
{
77+
var sim = new Simulation();
78+
_ = sim.ExecuteNonQuery("create xml schema collection xsc1 as N'<xsd:schema/>'");
79+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body xml(xsc1))");
80+
// The binding round-trips through INSERT/SELECT — payload still stores
81+
// as raw text since no XSD validation runs.
82+
_ = sim.ExecuteNonQuery("insert into dbo.doc values (1, N'<hi/>')");
83+
AreEqual("<hi/>", sim.ExecuteScalar("select body from dbo.doc"));
84+
}
85+
86+
[TestMethod]
87+
public void XmlColumn_WithContentDiscriminator_Parses()
88+
{
89+
var sim = new Simulation();
90+
_ = sim.ExecuteNonQuery("create xml schema collection xsc1 as N'<xsd:schema/>'");
91+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body xml(content xsc1))");
92+
AreEqual(1, sim.ExecuteScalar("select count(*) from sys.tables where name = 'doc'"));
93+
}
94+
95+
[TestMethod]
96+
public void XmlColumn_WithDocumentDiscriminator_Parses()
97+
{
98+
var sim = new Simulation();
99+
_ = sim.ExecuteNonQuery("create xml schema collection xsc1 as N'<xsd:schema/>'");
100+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body xml(document xsc1))");
101+
AreEqual(1, sim.ExecuteScalar("select count(*) from sys.tables where name = 'doc'"));
102+
}
103+
104+
[TestMethod]
105+
public void XmlColumn_WithUnknownCollection_Raises208()
106+
{
107+
var sim = new Simulation();
108+
_ = sim.AssertSqlError("create table dbo.doc (id int, body xml(no_such_collection))", 208);
109+
}
110+
111+
[TestMethod]
112+
public void DropXmlSchemaCollection_Removes()
113+
{
114+
var sim = new Simulation();
115+
_ = sim.ExecuteNonQuery("create xml schema collection xsc1 as N'<xsd:schema/>'");
116+
_ = sim.ExecuteNonQuery("drop xml schema collection xsc1");
117+
AreEqual(0, sim.ExecuteScalar("select count(*) from sys.xml_schema_collections"));
118+
}
119+
120+
[TestMethod]
121+
public void CreatePrimaryXmlIndex_Succeeds()
122+
{
123+
var sim = new Simulation();
124+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int not null primary key, body xml)");
125+
_ = sim.ExecuteNonQuery("create primary xml index pxml_doc on dbo.doc(body)");
126+
AreEqual(1, sim.ExecuteScalar("select count(*) from sys.xml_indexes"));
127+
AreEqual("XML", sim.ExecuteScalar("select type_desc from sys.xml_indexes where name = 'pxml_doc'"));
128+
}
129+
130+
[TestMethod]
131+
public void CreateSecondaryXmlIndex_PathValueProperty_Stores()
132+
{
133+
var sim = new Simulation();
134+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int not null primary key, body xml)");
135+
_ = sim.ExecuteNonQuery("create primary xml index pxml_doc on dbo.doc(body)");
136+
_ = sim.ExecuteNonQuery("create xml index sxml_path on dbo.doc(body) using xml index pxml_doc for path");
137+
_ = sim.ExecuteNonQuery("create xml index sxml_value on dbo.doc(body) using xml index pxml_doc for value");
138+
_ = sim.ExecuteNonQuery("create xml index sxml_property on dbo.doc(body) using xml index pxml_doc for property");
139+
AreEqual(4, sim.ExecuteScalar("select count(*) from sys.xml_indexes"));
140+
AreEqual("PATH", sim.ExecuteScalar("select secondary_type_desc from sys.xml_indexes where name = 'sxml_path'"));
141+
AreEqual("VALUE", sim.ExecuteScalar("select secondary_type_desc from sys.xml_indexes where name = 'sxml_value'"));
142+
AreEqual("PROPERTY", sim.ExecuteScalar("select secondary_type_desc from sys.xml_indexes where name = 'sxml_property'"));
143+
}
144+
145+
[TestMethod]
146+
public void SecondaryXmlIndex_UsingId_LinksToPrimary()
147+
{
148+
var sim = new Simulation();
149+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int not null primary key, body xml)");
150+
_ = sim.ExecuteNonQuery("create primary xml index pxml_doc on dbo.doc(body)");
151+
_ = sim.ExecuteNonQuery("create xml index sxml_path on dbo.doc(body) using xml index pxml_doc for path");
152+
var primaryId = sim.ExecuteScalar("select index_id from sys.xml_indexes where name = 'pxml_doc'");
153+
var secondaryUsingId = sim.ExecuteScalar("select using_xml_index_id from sys.xml_indexes where name = 'sxml_path'");
154+
AreEqual(primaryId, secondaryUsingId);
155+
}
156+
157+
[TestMethod]
158+
public void CreateXmlIndex_DuplicateName_Raises2714()
159+
{
160+
var sim = new Simulation();
161+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int not null primary key, body xml)");
162+
_ = sim.ExecuteNonQuery("create primary xml index pxml on dbo.doc(body)");
163+
_ = sim.AssertSqlError("create primary xml index pxml on dbo.doc(body)", 2714);
164+
}
165+
166+
[TestMethod]
167+
public void XmlValue_Method_RaisesNotSupportedAtExecute()
168+
{
169+
var sim = new Simulation();
170+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body xml)");
171+
_ = sim.ExecuteNonQuery("insert into dbo.doc values (1, N'<r><c>hi</c></r>')");
172+
var ex = ThrowsExactly<NotSupportedException>(() =>
173+
sim.ExecuteScalar("select body.value('(/r/c)[1]', 'nvarchar(50)') from dbo.doc"));
174+
Contains(".value()", ex.Message);
175+
}
176+
177+
[TestMethod]
178+
public void XmlQuery_Method_RaisesNotSupportedAtExecute()
179+
{
180+
var sim = new Simulation();
181+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body xml)");
182+
_ = sim.ExecuteNonQuery("insert into dbo.doc values (1, N'<r/>')");
183+
var ex = ThrowsExactly<NotSupportedException>(() =>
184+
sim.ExecuteScalar("select body.query('/r') from dbo.doc"));
185+
Contains(".query()", ex.Message);
186+
}
187+
188+
[TestMethod]
189+
public void XmlExist_Method_RaisesNotSupportedAtExecute()
190+
{
191+
var sim = new Simulation();
192+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body xml)");
193+
_ = sim.ExecuteNonQuery("insert into dbo.doc values (1, N'<r/>')");
194+
var ex = ThrowsExactly<NotSupportedException>(() =>
195+
sim.ExecuteScalar("select body.exist('/r') from dbo.doc"));
196+
Contains(".exist()", ex.Message);
197+
}
198+
199+
[TestMethod]
200+
public void CreateViewWithXmlMethod_Succeeds_FailsAtExecute()
201+
{
202+
// CREATE VIEW body parses cleanly (proc/view bodies parse for name
203+
// resolution but XML methods defer their NotSupportedException to
204+
// run-time). The query against the view fails on first row.
205+
var sim = new Simulation();
206+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body xml)");
207+
_ = sim.ExecuteNonQuery("insert into dbo.doc values (1, N'<r/>')");
208+
_ = sim.ExecuteNonQuery("create view dbo.v_doc as select body.value('(/r)[1]', 'nvarchar(50)') as v from dbo.doc");
209+
_ = ThrowsExactly<NotSupportedException>(() =>
210+
sim.ExecuteScalar("select v from dbo.v_doc"));
211+
}
212+
213+
[TestMethod]
214+
public void XmlSchemaCollections_StartsAt65536()
215+
{
216+
// Probe-confirmed: SQL Server's first user xml_collection_id is 65536.
217+
var sim = new Simulation();
218+
_ = sim.ExecuteNonQuery("create xml schema collection xsc1 as N'<xsd:schema/>'");
219+
AreEqual(65536, sim.ExecuteScalar("select xml_collection_id from sys.xml_schema_collections where name = 'xsc1'"));
220+
}
221+
222+
[TestMethod]
223+
public void CastXmlToNvarchar_Succeeds()
224+
{
225+
var sim = new Simulation();
226+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body xml)");
227+
_ = sim.ExecuteNonQuery("insert into dbo.doc values (1, N'<r/>')");
228+
AreEqual("<r/>", sim.ExecuteScalar("select cast(body as nvarchar(max)) from dbo.doc"));
229+
}
230+
}

0 commit comments

Comments
 (0)