Skip to content

Commit 71929d1

Browse files
committed
Full-text catalog + index DDL surface — sixth bacpac prerequisite bundle, skip-with-diagnostic stance. CREATE/DROP FULLTEXT CATALOG + CREATE/DROP FULLTEXT INDEX parse-and-store; sys.fulltext_catalogs / sys.fulltext_indexes / sys.fulltext_index_columns catalog views ship; CONTAINS / FREETEXT / CONTAINSTABLE / FREETEXTTABLE / SEMANTIC* all raise NotSupportedException at parse time. AW model.xml's 1 SqlFullTextCatalog ([AW2025FullTextCatalog]) + 3 SqlFullTextIndex elements round-trip end-to-end; the existing uspSearchCandidateResumes proc body parses through CREATE PROCEDURE verbatim and fails loudly on EXEC — exactly the skip-with-diagnostic stance the prerequisites doc called for.
**Storage**: new `FullTextCatalog` (`SqlServerSimulator/FullTextCatalog.cs`) — id + name + is_default + is_accent_sensitivity_on + principal_id + create_date. New `FullTextIndex` (`SqlServerSimulator/FullTextIndex.cs`) — catalog_id + key_index_name + unique_index_id + List<FullTextIndexColumn>; lives directly on `HeapTable.FullTextIndex` as a single nullable slot (real SQL Server's invariant: at most one FT index per table). `FullTextIndexColumn` readonly struct — column_id (1-based storage ordinal) + language_id + nullable type_column_id. `Database.FullTextCatalogs` is the per-database ConcurrentDictionary<string, FullTextCatalog> (case-insensitive); `Database.AllocateFullTextCatalogId` returns 5 first (Microsoft Learn documents ids 0..4 as reserved). The new `Fulltext` ContextualKeyword routes CREATE/DROP dispatch. **Parsers** in `Simulation/Simulation.FullText.cs`: - `CREATE FULLTEXT CATALOG name [AS DEFAULT] [AUTHORIZATION owner] [WITH ACCENT_SENSITIVITY = {ON|OFF}] [ON FILEGROUP fg] [IN PATH '…']` — filesystem-placement trailers parse-and-discard. AS DEFAULT demotes prior defaults before promoting. AUTHORIZATION resolves against `Database.Principals` (default dbo); unknown principal raises Msg 15151. Duplicate name raises Msg 2714. - `CREATE FULLTEXT INDEX ON table (col [TYPE COLUMN typeCol] [LANGUAGE n] [STATISTICAL_SEMANTICS] [, …]) [KEY INDEX name] [ON catalog | ON (catalog [, FILEGROUP fg])] [WITH (option [, …])]` — full multi-column form, the AW Production.Document `body TYPE COLUMN file_ext` nested reference, LANGUAGE integer LCID, both paren and bare ON-catalog forms, and the WITH-options block via the shared SkipBalancedParens helper. Missing table → Msg 208. Twice-on-same-table → Msg 2714. Unknown column → Msg 207. Unknown key-index name → Msg 208. Omitted ON catalog falls back to the default catalog (Msg 208 if no default exists). - `DROP FULLTEXT CATALOG name` / `DROP FULLTEXT INDEX ON table` — dedicated `TryParseDropFullText` ahead of the generic DROP-target switch (the sub-keyword grammar would otherwise misroute through the comma-list path). **Predicate / rowset rejection**: `BooleanExpression.ParseAtom` intercepts the `Contains` / `FreeText` ReservedKeywords ahead of the comparison parse and raises NotSupportedException with `"Full-text search predicates ({CONTAINS|FREETEXT}) are not modeled."`. `Selection.ParseSingleFromSource` intercepts the rowset-function ReservedKeywords (ContainsTable / FreeTextTable / SemanticKeyPhraseTable / SemanticSimilarityTable / SemanticSimilarityDetailsTable) ahead of the syntax-error default with `"Full-text rowset functions ({CONTAINSTABLE|…}) are not modeled."`. **Catalog views** in `BuiltInResources.cs`: - `sys.fulltext_catalogs` (9-col): fulltext_catalog_id / name / path (NULL) / is_default / is_accent_sensitivity_on / data_space_id (NULL) / file_id (NULL) / principal_id / is_importing (false). - `sys.fulltext_indexes` (14-col): object_id / unique_index_id / fulltext_catalog_id / is_enabled (true) / change_tracking_state ('A') / change_tracking_state_desc ('AUTO') / has_crawl_completed (true) / crawl_type ('F') / crawl_type_desc ('FULL') / crawl_start_date (NULL) / crawl_end_date (NULL) / stoplist_id (NULL) / data_space_id (NULL) / property_list_id (NULL). - `sys.fulltext_index_columns` (5-col): object_id / column_id / type_column_id / language_id / statistical_semantics (false). Column subset matches Microsoft Learn's documented surface for SQL Server 2022+ (the reference SQL Server 2025 instance doesn't have Full-Text installed — the catalog views aren't registered without the FT service, so probe-confirmation isn't available; column types are from learn.microsoft.com/sql/relational-databases/system-catalog-views/). 21 new tests across `FullTextDdlTests.cs` (17) + `FullTextPredicateTests.cs` (4): catalog AS DEFAULT semantics + accent-sensitivity flag / duplicate-name Msg 2714 / drop happy + missing Msg 208 / default-promotion demotion; index single-col / multi-col with TYPE COLUMN matching AW Production.Document / default-catalog fallback resolution / missing table Msg 208 / duplicate-on-table Msg 2714 / unknown column Msg 207 / unknown key-index Msg 208 / row shape (is_enabled / has_crawl_completed / change_tracking_state_desc / crawl_type_desc) / principal_id pre-seed round-trip; predicate + rowset NotSupportedException verification for all four predicate / rowset forms. Test counts: 4141 main (+21 new) / 227 internal / 328 EFCore / 58 analyzers — all green Debug + Release. `docs/claude/bacpac-prerequisites.md` flips the Full-text 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 xml + spatial and gains a new Feature-reference index entry for the full-text DDL + catalog-view surface; the "Working on BACPAC import support" entry's shipped-prerequisites paragraph now lists full-text alongside the other six landed items. **Deferred**: query-time text search (the tokenizer/stemmer/inverted-index/relevance-rank pipeline) — out of scope as the prereq doc called for. ALTER FULLTEXT CATALOG / INDEX (REORGANIZE / REBUILD / START/STOP POPULATION / ADD/DROP column) — currently raises a syntax error since no ALTER path exists. The legacy `sys.fulltext_languages` / `sys.fulltext_document_types` / `sys.fulltext_stoplists` lookup catalogs aren't shipped — apps that introspect the language enum hit a missing-view error.
1 parent 8e1b7f3 commit 71929d1

15 files changed

Lines changed: 1130 additions & 15 deletions

CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,9 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
160160
- **Touching `hierarchyid` (the `HierarchyIdSqlType` storage + segment-array `int[][]` internal representation in `Storage/HierarchyIdType.cs`, the `SqlValue.FromHierarchyId` / `AsHierarchyId` accessors, the `hierarchyid::Parse` / `hierarchyid::GetRoot` static-call dispatch in `HierarchyIdStaticCall`, the `.GetLevel` / `.GetAncestor` / `.GetDescendant` / `.IsDescendantOf` / `.ToString` instance-method dispatch in `HierarchyIdMethodCall`, the Msg 6522 verbatim wording in `SimulatedSqlException.TypeErrors.cs`, the `:` / `.<method>(` parser cases in `Expression.cs`, or the `sys.types` row shape — system_type_id 240, user_type_id 128, max_length 892)**[`docs/claude/bacpac-prerequisites.md`](docs/claude/bacpac-prerequisites.md) (the `hierarchyid` section carries the implementation detail; the byte-identical-CAST gap is deferred to the BACPAC loader bundle).
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).
163+
- **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).
163164
- **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).
164-
- **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, full-text catalog/index + `CONTAINS`/`FREETEXT`)**[`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`.
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`).
165166

166167
## Not modeled
167168

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
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 full-text surface
7+
/// (<c>CREATE/DROP FULLTEXT CATALOG</c> + <c>CREATE/DROP FULLTEXT INDEX</c>
8+
/// + <c>sys.fulltext_catalogs</c> / <c>sys.fulltext_indexes</c> /
9+
/// <c>sys.fulltext_index_columns</c>). The simulator stores full-text
10+
/// metadata for AW model.xml round-trip but does not execute text search;
11+
/// CONTAINS / FREETEXT / CONTAINSTABLE / FREETEXTTABLE raise
12+
/// <see cref="NotSupportedException"/>.
13+
/// </summary>
14+
[TestClass]
15+
public sealed class FullTextDdlTests
16+
{
17+
private const string AwCatalog =
18+
"create fulltext catalog [AW2025FullTextCatalog] as default";
19+
20+
private const string DocTable = """
21+
create table dbo.doc (
22+
id int identity(1,1) not null constraint pk_doc primary key,
23+
body nvarchar(max) null
24+
)
25+
""";
26+
27+
private static Simulation BuildSimWithDoc()
28+
{
29+
var sim = new Simulation();
30+
_ = sim.ExecuteNonQuery(AwCatalog);
31+
_ = sim.ExecuteNonQuery(DocTable);
32+
return sim;
33+
}
34+
35+
[TestMethod]
36+
public void CreateFullTextCatalog_AsDefault_Succeeds()
37+
{
38+
var sim = new Simulation();
39+
_ = sim.ExecuteNonQuery(AwCatalog);
40+
AreEqual(1, sim.ExecuteScalar("select count(*) from sys.fulltext_catalogs where name = 'AW2025FullTextCatalog'"));
41+
IsTrue((bool)sim.ExecuteScalar("select is_default from sys.fulltext_catalogs where name = 'AW2025FullTextCatalog'")!);
42+
}
43+
44+
[TestMethod]
45+
public void CreateFullTextCatalog_DefaultsAccentSensitiveTrue()
46+
{
47+
var sim = new Simulation();
48+
_ = sim.ExecuteNonQuery("create fulltext catalog mycat");
49+
IsTrue((bool)sim.ExecuteScalar("select is_accent_sensitivity_on from sys.fulltext_catalogs where name = 'mycat'")!);
50+
}
51+
52+
[TestMethod]
53+
public void CreateFullTextCatalog_WithAccentSensitivityOff_Stores()
54+
{
55+
var sim = new Simulation();
56+
_ = sim.ExecuteNonQuery("create fulltext catalog mycat with accent_sensitivity = off");
57+
IsFalse((bool)sim.ExecuteScalar("select is_accent_sensitivity_on from sys.fulltext_catalogs where name = 'mycat'")!);
58+
}
59+
60+
[TestMethod]
61+
public void CreateFullTextCatalog_DuplicateName_Raises2714()
62+
{
63+
var sim = new Simulation();
64+
_ = sim.ExecuteNonQuery("create fulltext catalog mycat");
65+
_ = sim.AssertSqlError("create fulltext catalog mycat", 2714);
66+
}
67+
68+
[TestMethod]
69+
public void CreateFullTextCatalog_DemotesPriorDefault()
70+
{
71+
var sim = new Simulation();
72+
_ = sim.ExecuteNonQuery("create fulltext catalog cat1 as default");
73+
_ = sim.ExecuteNonQuery("create fulltext catalog cat2 as default");
74+
IsFalse((bool)sim.ExecuteScalar("select is_default from sys.fulltext_catalogs where name = 'cat1'")!);
75+
IsTrue((bool)sim.ExecuteScalar("select is_default from sys.fulltext_catalogs where name = 'cat2'")!);
76+
}
77+
78+
[TestMethod]
79+
public void DropFullTextCatalog_Removes()
80+
{
81+
var sim = new Simulation();
82+
_ = sim.ExecuteNonQuery(AwCatalog);
83+
_ = sim.ExecuteNonQuery("drop fulltext catalog [AW2025FullTextCatalog]");
84+
AreEqual(0, sim.ExecuteScalar("select count(*) from sys.fulltext_catalogs"));
85+
}
86+
87+
[TestMethod]
88+
public void DropFullTextCatalog_Missing_Raises208()
89+
=> new Simulation().AssertSqlError("drop fulltext catalog missing_cat", 208);
90+
91+
[TestMethod]
92+
public void CreateFullTextIndex_SingleColumn_Succeeds()
93+
{
94+
var sim = BuildSimWithDoc();
95+
_ = sim.ExecuteNonQuery("create fulltext index on dbo.doc (body language 1033) key index pk_doc on [AW2025FullTextCatalog]");
96+
AreEqual(1, sim.ExecuteScalar("select count(*) from sys.fulltext_indexes"));
97+
AreEqual(1033, sim.ExecuteScalar("select language_id from sys.fulltext_index_columns"));
98+
}
99+
100+
[TestMethod]
101+
public void CreateFullTextIndex_PicksDefaultCatalog_WhenNoOnClause()
102+
{
103+
var sim = BuildSimWithDoc();
104+
_ = sim.ExecuteNonQuery("create fulltext index on dbo.doc (body language 1033) key index pk_doc");
105+
var catName = sim.ExecuteScalar(@"
106+
select c.name
107+
from sys.fulltext_indexes i
108+
join sys.fulltext_catalogs c on c.fulltext_catalog_id = i.fulltext_catalog_id");
109+
AreEqual("AW2025FullTextCatalog", catName);
110+
}
111+
112+
[TestMethod]
113+
public void CreateFullTextIndex_MultiColumn_WithTypeColumn_Succeeds()
114+
{
115+
// AW's [Production].[Document] shape — body column + extension column.
116+
var sim = new Simulation();
117+
_ = sim.ExecuteNonQuery(AwCatalog);
118+
_ = sim.ExecuteNonQuery("""
119+
create table dbo.doc (
120+
id int identity(1,1) not null constraint pk_doc primary key,
121+
file_ext nvarchar(8) null,
122+
body varbinary(max) null,
123+
summary nvarchar(max) null
124+
)
125+
""");
126+
_ = sim.ExecuteNonQuery("""
127+
create fulltext index on dbo.doc (
128+
summary language 1033,
129+
body type column file_ext language 1033
130+
) key index pk_doc on [AW2025FullTextCatalog]
131+
""");
132+
AreEqual(2, sim.ExecuteScalar("select count(*) from sys.fulltext_index_columns"));
133+
// The body column (storage ordinal 3) has type_column_id pointing to
134+
// file_ext (storage ordinal 2).
135+
AreEqual(2, sim.ExecuteScalar("select type_column_id from sys.fulltext_index_columns where column_id = 3"));
136+
}
137+
138+
[TestMethod]
139+
public void CreateFullTextIndex_OnMissingTable_Raises208()
140+
{
141+
var sim = new Simulation();
142+
_ = sim.ExecuteNonQuery(AwCatalog);
143+
_ = sim.AssertSqlError(
144+
"create fulltext index on dbo.missing (body language 1033) key index pk_doc on [AW2025FullTextCatalog]",
145+
208);
146+
}
147+
148+
[TestMethod]
149+
public void CreateFullTextIndex_OnTableTwice_Raises2714()
150+
{
151+
var sim = BuildSimWithDoc();
152+
_ = sim.ExecuteNonQuery("create fulltext index on dbo.doc (body language 1033) key index pk_doc");
153+
_ = sim.AssertSqlError(
154+
"create fulltext index on dbo.doc (body language 1033) key index pk_doc",
155+
2714);
156+
}
157+
158+
[TestMethod]
159+
public void CreateFullTextIndex_UnknownColumn_Raises207()
160+
{
161+
var sim = BuildSimWithDoc();
162+
_ = sim.AssertSqlError(
163+
"create fulltext index on dbo.doc (no_such_col language 1033) key index pk_doc",
164+
207);
165+
}
166+
167+
[TestMethod]
168+
public void CreateFullTextIndex_UnknownKeyIndex_Raises208()
169+
{
170+
var sim = BuildSimWithDoc();
171+
_ = sim.AssertSqlError(
172+
"create fulltext index on dbo.doc (body language 1033) key index no_such_index",
173+
208);
174+
}
175+
176+
[TestMethod]
177+
public void DropFullTextIndex_RemovesFromCatalog()
178+
{
179+
var sim = BuildSimWithDoc();
180+
_ = sim.ExecuteNonQuery("create fulltext index on dbo.doc (body language 1033) key index pk_doc");
181+
_ = sim.ExecuteNonQuery("drop fulltext index on dbo.doc");
182+
AreEqual(0, sim.ExecuteScalar("select count(*) from sys.fulltext_indexes"));
183+
AreEqual(0, sim.ExecuteScalar("select count(*) from sys.fulltext_index_columns"));
184+
}
185+
186+
[TestMethod]
187+
public void SysFullTextIndexes_RowShape()
188+
{
189+
var sim = BuildSimWithDoc();
190+
_ = sim.ExecuteNonQuery("create fulltext index on dbo.doc (body language 1033) key index pk_doc on [AW2025FullTextCatalog]");
191+
IsTrue((bool)sim.ExecuteScalar("select is_enabled from sys.fulltext_indexes")!);
192+
IsTrue((bool)sim.ExecuteScalar("select has_crawl_completed from sys.fulltext_indexes")!);
193+
AreEqual("AUTO", sim.ExecuteScalar("select change_tracking_state_desc from sys.fulltext_indexes"));
194+
AreEqual("FULL", sim.ExecuteScalar("select crawl_type_desc from sys.fulltext_indexes"));
195+
}
196+
197+
[TestMethod]
198+
public void SysFullTextCatalogs_HasDboAsOwner()
199+
{
200+
var sim = new Simulation();
201+
_ = sim.ExecuteNonQuery(AwCatalog);
202+
// dbo is principal_id=1 (probe-confirmed pre-seed).
203+
AreEqual(1, sim.ExecuteScalar("select principal_id from sys.fulltext_catalogs where name = 'AW2025FullTextCatalog'"));
204+
}
205+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
2+
3+
namespace SqlServerSimulator;
4+
5+
/// <summary>
6+
/// The simulator doesn't model full-text search — apps that issue
7+
/// CONTAINS / FREETEXT predicates or CONTAINSTABLE / FREETEXTTABLE rowset
8+
/// functions get an explicit <see cref="NotSupportedException"/> at parse
9+
/// time rather than a silent miss-as-match. These tests pin that
10+
/// loud-failure behavior.
11+
/// </summary>
12+
[TestClass]
13+
public sealed class FullTextPredicateTests
14+
{
15+
private static Simulation BuildSim()
16+
{
17+
var sim = new Simulation();
18+
_ = sim.ExecuteNonQuery("create table dbo.doc (id int, body nvarchar(max))");
19+
return sim;
20+
}
21+
22+
[TestMethod]
23+
public void Contains_Predicate_Raises_NotSupported()
24+
{
25+
var ex = ThrowsExactly<NotSupportedException>(() =>
26+
BuildSim().ExecuteScalar("select count(*) from dbo.doc where contains(body, 'hello')"));
27+
Contains("CONTAINS", ex.Message);
28+
}
29+
30+
[TestMethod]
31+
public void FreeText_Predicate_Raises_NotSupported()
32+
{
33+
var ex = ThrowsExactly<NotSupportedException>(() =>
34+
BuildSim().ExecuteScalar("select count(*) from dbo.doc where freetext(body, 'hello')"));
35+
Contains("FREETEXT", ex.Message);
36+
}
37+
38+
[TestMethod]
39+
public void ContainsTable_Rowset_Raises_NotSupported()
40+
{
41+
var ex = ThrowsExactly<NotSupportedException>(() =>
42+
BuildSim().ExecuteScalar("select count(*) from containstable(dbo.doc, body, 'hello') as t"));
43+
Contains("CONTAINSTABLE", ex.Message);
44+
}
45+
46+
[TestMethod]
47+
public void FreeTextTable_Rowset_Raises_NotSupported()
48+
{
49+
var ex = ThrowsExactly<NotSupportedException>(() =>
50+
BuildSim().ExecuteScalar("select count(*) from freetexttable(dbo.doc, body, 'hello') as t"));
51+
Contains("FREETEXTTABLE", ex.Message);
52+
}
53+
}

0 commit comments

Comments
 (0)