Skip to content

Commit 523b424

Browse files
committed
DROP SCHEMA + ALTER SCHEMA TRANSFER — schema lifecycle completes. DROP SCHEMA [IF EXISTS] routes through Simulation.Drop.cs's extended DropTargetKind switch alongside DROP TABLE/VIEW/FUNCTION/PROCEDURE/SEQUENCE/TRIGGER/TYPE; new DropOneSchema enforces Msg 15150 on reserved schemas (dbo / sys / INFORMATION_SCHEMA — probe-confirmed dbo isn't droppable even when empty, sharing IsReservedSchemaName with CreateSchema), Msg 15151 on missing-without-IF-EXISTS (silent miss when IF EXISTS), and Msg 3729 on non-empty (FirstSchemaResident walks Schema.SchemaObjects() first then falls through to TableTypes — the named object differs from real SQL Server's "first dependency-graph object" which is often an auto-named PK constraint, but the Msg / wording-prefix match probe). ALTER SCHEMA dest TRANSFER [(OBJECT|TYPE)::] source.obj routes through new TryParseAlterSchemaTransfer in Simulation.Alter.cs; OBJECT class walks HeapTables → Views → Functions → Procedures → Sequences first-hit-wins (triggers explicitly fail-fast with Msg 15347 since trigger schemas follow their parent automatically), TYPE class targets the parallel TableTypes namespace, and HeapTable / View transfers co-migrate every attached trigger via ReseatAttachedTriggers (their Trigger.Schema reference and SchemaId update to the destination's). Transfer / Object added as ContextualKeywords; tokenizer recognizes ':' as a single-char operator so the '::' class-prefix separator decomposes naturally into two adjacent tokens. SchemaObject.SchemaId dropped readonly (the TRANSFER path mutates it); every derived type's Schema reference field (View / Procedure / Sequence / TableType / Trigger / UserDefinedFunction) likewise dropped readonly. Probe-confirmed verbatim wording for all five error factories (CannotDropProtectedSchema 15150 / CannotDropSchemaDoesNotExist 15151 / CannotAlterSchemaDoesNotExist 15151 alter-variant / CannotFindObject 15151 find-object-variant / CannotFindType 15151 find-type-variant / CannotDropSchemaBecauseNotEmpty 3729 / ObjectAlreadyExistsInDestination 15530 / CannotTransferObjectOwnedByParent 15347). Missing destination → 15151 alter-variant; missing source object → 15151 find-object; same-schema TRANSFER is a silent no-op (probe-confirmed). 12 new SchemaTests covering all rejection paths plus successful transfers across every object class (table / view / sequence / table-type / trigger-co-migration); obsolete DropSchema_NotSupported test removed. 3344 / 264 tests pass; dotnet format clean. CLAUDE.md "Not modeled" entry collapsed; docs/claude/schemas.md gains full DROP SCHEMA + ALTER SCHEMA TRANSFER sections covering grammar, error paths, trigger co-migration, and the SchemaId-mutability change.
1 parent e3df5b3 commit 523b424

15 files changed

Lines changed: 630 additions & 18 deletions

CLAUDE.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
141141
- **Touching variables (DECLARE/SET/SELECT-assign), control flow (IF/BEGIN/WHILE/BREAK/CONTINUE/RETURN), TRY/CATCH+THROW+ERROR_*, PRINT, WAITFOR DELAY**[`docs/claude/control-flow.md`](docs/claude/control-flow.md).
142142
- **Extending CTE shapes or recursive-CTE error handling**[`docs/claude/ctes.md`](docs/claude/ctes.md).
143143
- **Touching JSON_VALUE / JSON_MODIFY / OPENJSON**[`docs/claude/json.md`](docs/claude/json.md).
144-
- **Changing name resolution, schema lookup, CREATE SCHEMA, or OBJECT_ID**[`docs/claude/schemas.md`](docs/claude/schemas.md).
144+
- **Changing name resolution, schema lookup, CREATE SCHEMA / DROP SCHEMA / ALTER SCHEMA TRANSFER, or OBJECT_ID**[`docs/claude/schemas.md`](docs/claude/schemas.md).
145145
- **Adding or changing system metadata surfaces** (sys.* / INFORMATION_SCHEMA.*) → [`docs/claude/catalog-views.md`](docs/claude/catalog-views.md).
146146
- **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).
147147
- **Touching `#foo` routing, DROP TABLE, TRUNCATE TABLE**[`docs/claude/temp-tables.md`](docs/claude/temp-tables.md).
@@ -170,8 +170,7 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
170170
- **Table-variable named constraints / foreign keys**`DECLARE @t TABLE (...)` shares its column-list parser with CREATE TABLE (see `docs/claude/table-variables.md`); column features (IDENTITY / UNIQUE / inline + table-level CHECK / computed columns / rowversion) all ship, alongside per-statement-atomic mutations and `OUTPUT … INTO <target>` for both `@t` and regular tables. Named constraints (`CONSTRAINT pk1 PRIMARY KEY`) and `FOREIGN KEY` raise Msg 102 (matches probe — real SQL Server's grammar disallows both shapes inside `DECLARE @t TABLE`). Multi-variable DECLARE with a table variable (`DECLARE @t1 TABLE (...), @t2 TABLE (...)`) and mixed scalar+table DECLARE also raise Msg 102/156. `SET IDENTITY_INSERT @t ON` likewise raises Msg 102 (probe-confirmed: there's no way to force a specific value into a table-variable identity column).
171171
- **Global temp tables (`##foo`)**`NotSupportedException` at parse. Local `#foo` works; the lifecycle for global temps (drops when creator session closes, visible across sessions) is the deferred scope.
172172
- **`ALTER TABLE #foo`**, **`OBJECT_ID('tempdb..#foo')`** — none modeled (none of those exist for regular tables either yet). The common `IF OBJECT_ID(...) IS NOT NULL DROP TABLE` cleanup pattern works via `DROP TABLE IF EXISTS #foo` instead.
173-
- **`DROP SCHEMA`**, **`ALTER SCHEMA … TRANSFER`** — deferred. CREATE SCHEMA + schema-qualified resolution ships; lifecycle doesn't yet (catalog views — `sys.schemas`, `INFORMATION_SCHEMA.SCHEMATA` — do ship as of the catalog-view-expansion bundle).
174-
- **`CREATE SCHEMA AUTHORIZATION <owner>`**`NotSupportedException` (simulator has no user / principal model).
173+
- **`CREATE SCHEMA AUTHORIZATION <owner>`**`NotSupportedException` (simulator has no user / principal model). `DROP SCHEMA` + `ALTER SCHEMA … TRANSFER` both ship (see [`docs/claude/schemas.md`](docs/claude/schemas.md)): DROP enforces Msg 15150 / 15151 / 3729 rejections; TRANSFER routes OBJECT-class moves (HeapTable / View / Function / Procedure / Sequence) and TYPE-class moves (TableType) and co-migrates attached triggers with their parent table / view. Direct trigger transfer raises Msg 15347 (probe-confirmed: triggers follow their parent's schema automatically).
175174
- **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`.
176175
- **`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.
177176
- 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.

SqlServerSimulator.Tests/CreateSchemaTests.cs

Lines changed: 234 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,6 @@ public void CrossSchemaJoin_Works()
262262
select count(*) from dbo.users u inner join audit.entries e on u.id = e.user_id where u.id = 1
263263
"""));
264264

265-
[TestMethod]
266-
public void DropSchema_NotSupported()
267-
=> Throws<DbException>(() =>
268-
new Simulation().ExecuteNonQuery("create schema audit; drop schema audit"));
269-
270265
[TestMethod]
271266
public void CreateSchema_PersistsAcrossBatches()
272267
{
@@ -278,4 +273,238 @@ public void CreateSchema_PersistsAcrossBatches()
278273
cmd.CommandText = "create table audit.t (id int); insert audit.t values (1); select count(*) from audit.t";
279274
AreEqual(1, cmd.ExecuteScalar());
280275
}
276+
277+
[TestMethod]
278+
public void DropSchema_Empty_Succeeds()
279+
{
280+
using var conn = new Simulation().CreateDbConnection();
281+
conn.Open();
282+
using var cmd = conn.CreateCommand();
283+
cmd.CommandText = "create schema audit; drop schema audit; select schema_id('audit')";
284+
AreEqual(DBNull.Value, cmd.ExecuteScalar());
285+
}
286+
287+
[TestMethod]
288+
public void DropSchema_Missing_Msg15151()
289+
=> new Simulation().AssertSqlError(
290+
"drop schema nope",
291+
15151,
292+
"Cannot drop the schema 'nope', because it does not exist or you do not have permission.");
293+
294+
[TestMethod]
295+
public void DropSchema_IfExistsMissing_NoOp()
296+
=> AreEqual(1, new Simulation().ExecuteScalar("drop schema if exists nope; select 1"));
297+
298+
[TestMethod]
299+
public void DropSchema_Dbo_Msg15150()
300+
=> new Simulation().AssertSqlError(
301+
"drop schema dbo",
302+
15150,
303+
"Cannot drop the schema 'dbo'.");
304+
305+
[TestMethod]
306+
public void DropSchema_Sys_Msg15150()
307+
=> new Simulation().AssertSqlError(
308+
"drop schema sys",
309+
15150);
310+
311+
[TestMethod]
312+
public void DropSchema_InformationSchema_Msg15150()
313+
=> new Simulation().AssertSqlError(
314+
"drop schema INFORMATION_SCHEMA",
315+
15150);
316+
317+
[TestMethod]
318+
public void DropSchema_NotEmpty_Msg3729()
319+
{
320+
var ex = new Simulation().AssertSqlError(
321+
"create schema audit; create table audit.t (id int); drop schema audit",
322+
3729);
323+
IsTrue(ex.Message.StartsWith("Cannot drop schema 'audit' because it is being referenced by object", StringComparison.Ordinal));
324+
}
325+
326+
[TestMethod]
327+
public void AlterSchema_Transfer_TableMoves()
328+
{
329+
using var conn = new Simulation().CreateDbConnection();
330+
conn.Open();
331+
using var cmd = conn.CreateCommand();
332+
cmd.CommandText = """
333+
create schema src;
334+
create schema dst;
335+
create table src.t (id int);
336+
insert src.t values (1), (2);
337+
alter schema dst transfer src.t;
338+
select s.name from sys.tables t join sys.schemas s on s.schema_id = t.schema_id where t.name = 't'
339+
""";
340+
AreEqual("dst", cmd.ExecuteScalar());
341+
}
342+
343+
[TestMethod]
344+
public void AlterSchema_Transfer_ExplicitObjectPrefix()
345+
{
346+
using var conn = new Simulation().CreateDbConnection();
347+
conn.Open();
348+
using var cmd = conn.CreateCommand();
349+
cmd.CommandText = """
350+
create schema src;
351+
create schema dst;
352+
create table src.t (id int);
353+
alter schema dst transfer object::src.t;
354+
select count(*) from dst.t
355+
""";
356+
AreEqual(0, cmd.ExecuteScalar());
357+
}
358+
359+
[TestMethod]
360+
public void AlterSchema_Transfer_NameCollision_Msg15530()
361+
=> new Simulation().AssertSqlError(
362+
"""
363+
create schema src;
364+
create schema dst;
365+
create table src.t (id int);
366+
create table dst.t (id int);
367+
alter schema dst transfer src.t
368+
""",
369+
15530,
370+
"The object with name \"t\" already exists.");
371+
372+
[TestMethod]
373+
public void AlterSchema_Transfer_MissingSource_Msg15151()
374+
=> new Simulation().AssertSqlError(
375+
"create schema src; create schema dst; alter schema dst transfer src.nope",
376+
15151,
377+
"Cannot find the object 'nope', because it does not exist or you do not have permission.");
378+
379+
[TestMethod]
380+
public void AlterSchema_Transfer_MissingDest_Msg15151()
381+
=> new Simulation().AssertSqlError(
382+
"create schema src; create table src.t (id int); alter schema nope transfer src.t",
383+
15151,
384+
"Cannot alter the schema 'nope', because it does not exist or you do not have permission.");
385+
386+
[TestMethod]
387+
public void AlterSchema_Transfer_TypeMoves()
388+
{
389+
using var conn = new Simulation().CreateDbConnection();
390+
conn.Open();
391+
using var cmd = conn.CreateCommand();
392+
cmd.CommandText = """
393+
create schema src;
394+
create schema dst;
395+
create type src.MyType as table (id int);
396+
alter schema dst transfer type::src.MyType;
397+
select s.name from sys.types tt join sys.schemas s on s.schema_id = tt.schema_id where tt.name = 'MyType'
398+
""";
399+
AreEqual("dst", cmd.ExecuteScalar());
400+
}
401+
402+
[TestMethod]
403+
public void AlterSchema_Transfer_SameSchema_NoOp()
404+
{
405+
using var conn = new Simulation().CreateDbConnection();
406+
conn.Open();
407+
using var cmd = conn.CreateCommand();
408+
cmd.CommandText = """
409+
create schema src;
410+
create table src.t (id int);
411+
alter schema src transfer src.t;
412+
select count(*) from src.t
413+
""";
414+
AreEqual(0, cmd.ExecuteScalar());
415+
}
416+
417+
[TestMethod]
418+
public void AlterSchema_Transfer_TriggerDirect_Msg15347()
419+
{
420+
using var conn = new Simulation().CreateDbConnection();
421+
conn.Open();
422+
using (var setup = conn.CreateCommand())
423+
{
424+
setup.CommandText = "create schema src; create schema dst; create table src.t (id int)";
425+
_ = setup.ExecuteNonQuery();
426+
}
427+
using (var trg = conn.CreateCommand())
428+
{
429+
trg.CommandText = "create trigger src.tr on src.t after insert as select 1";
430+
_ = trg.ExecuteNonQuery();
431+
}
432+
using var cmd = conn.CreateCommand();
433+
cmd.CommandText = "alter schema dst transfer src.tr";
434+
var ex = Throws<DbException>(() => cmd.ExecuteNonQuery());
435+
AreEqual("15347", ex.Data["HelpLink.EvtID"]);
436+
AreEqual("Cannot transfer an object that is owned by a parent object.", ex.Message);
437+
}
438+
439+
[TestMethod]
440+
public void AlterSchema_Transfer_TriggerFollowsParent()
441+
{
442+
using var conn = new Simulation().CreateDbConnection();
443+
conn.Open();
444+
using (var setup = conn.CreateCommand())
445+
{
446+
setup.CommandText = "create schema src; create schema dst; create table src.t (id int)";
447+
_ = setup.ExecuteNonQuery();
448+
}
449+
using (var trg = conn.CreateCommand())
450+
{
451+
trg.CommandText = "create trigger src.tr on src.t after insert as select 1";
452+
_ = trg.ExecuteNonQuery();
453+
}
454+
using var cmd = conn.CreateCommand();
455+
cmd.CommandText = "alter schema dst transfer src.t; select s.name from sys.triggers t join sys.objects o on o.object_id = t.parent_id join sys.schemas s on s.schema_id = o.schema_id where t.name = 'tr'";
456+
AreEqual("dst", cmd.ExecuteScalar());
457+
}
458+
459+
[TestMethod]
460+
public void AlterSchema_Transfer_View_Works()
461+
{
462+
using var conn = new Simulation().CreateDbConnection();
463+
conn.Open();
464+
using var cmd = conn.CreateCommand();
465+
cmd.CommandText = """
466+
create schema src;
467+
create schema dst;
468+
create table dbo.base (id int);
469+
insert dbo.base values (1), (2);
470+
""";
471+
_ = cmd.ExecuteNonQuery();
472+
cmd.CommandText = "create view src.v as select id from dbo.base";
473+
_ = cmd.ExecuteNonQuery();
474+
cmd.CommandText = "alter schema dst transfer src.v; select count(*) from dst.v";
475+
AreEqual(2, cmd.ExecuteScalar());
476+
}
477+
478+
[TestMethod]
479+
public void AlterSchema_Transfer_Sequence_Works()
480+
{
481+
using var conn = new Simulation().CreateDbConnection();
482+
conn.Open();
483+
using var cmd = conn.CreateCommand();
484+
cmd.CommandText = """
485+
create schema src;
486+
create schema dst;
487+
create sequence src.s start with 100;
488+
alter schema dst transfer src.s;
489+
select next value for dst.s
490+
""";
491+
AreEqual(100L, cmd.ExecuteScalar());
492+
}
493+
494+
[TestMethod]
495+
public void DropSchema_AfterTransferringOut_Succeeds()
496+
{
497+
using var conn = new Simulation().CreateDbConnection();
498+
conn.Open();
499+
using var cmd = conn.CreateCommand();
500+
cmd.CommandText = """
501+
create schema src;
502+
create schema dst;
503+
create table src.t (id int);
504+
alter schema dst transfer src.t;
505+
drop schema src;
506+
select schema_id('src')
507+
""";
508+
AreEqual(DBNull.Value, cmd.ExecuteScalar());
509+
}
281510
}

SqlServerSimulator/Errors/SimulatedSqlException.SchemaErrors.cs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,4 +428,84 @@ internal static SimulatedSqlException CannotUpdateTimestampColumn() =>
428428
/// </summary>
429429
internal static SimulatedSqlException MultipleTimestampColumns(string tableName, string secondColumnName) =>
430430
new($"A table can only have one timestamp column. Because table '{tableName}' already has one, the column '{secondColumnName}' cannot be added.", 2738, 16, 2);
431+
432+
/// <summary>
433+
/// Mimics SQL Server error 15150: <c>DROP SCHEMA</c> targeted a built-in /
434+
/// reserved schema (<c>dbo</c>, <c>sys</c>, <c>INFORMATION_SCHEMA</c>,
435+
/// <c>guest</c>, …). Real SQL Server treats these as un-droppable even when
436+
/// empty; the simulator matches that rejection. Probe-confirmed verbatim
437+
/// against SQL Server 2025.
438+
/// </summary>
439+
internal static SimulatedSqlException CannotDropProtectedSchema(string schemaName) =>
440+
new($"Cannot drop the schema '{schemaName}'.", 15150, 16, 1);
441+
442+
/// <summary>
443+
/// Mimics SQL Server error 15151 (drop-schema variant): <c>DROP SCHEMA</c>
444+
/// targeted a name that doesn't exist (suppressed by <c>IF EXISTS</c>).
445+
/// Probe-confirmed verbatim wording against SQL Server 2025; SQL Server
446+
/// reuses Msg 15151 for several "doesn't exist or no permission" lookups
447+
/// — the simulator surfaces the variant matching each statement's noun.
448+
/// </summary>
449+
internal static SimulatedSqlException CannotDropSchemaDoesNotExist(string schemaName) =>
450+
new($"Cannot drop the schema '{schemaName}', because it does not exist or you do not have permission.", 15151, 16, 1);
451+
452+
/// <summary>
453+
/// Mimics SQL Server error 15151 (alter-schema variant): <c>ALTER SCHEMA
454+
/// dest TRANSFER ...</c> named a destination schema that doesn't exist.
455+
/// Probe-confirmed verbatim wording — same Msg / Class / State as the
456+
/// drop-schema-missing variant, distinct only in the "alter" vs "drop"
457+
/// verb in the message body.
458+
/// </summary>
459+
internal static SimulatedSqlException CannotAlterSchemaDoesNotExist(string schemaName) =>
460+
new($"Cannot alter the schema '{schemaName}', because it does not exist or you do not have permission.", 15151, 16, 1);
461+
462+
/// <summary>
463+
/// Mimics SQL Server error 15151 (find-object variant): <c>ALTER SCHEMA
464+
/// ... TRANSFER source.obj</c> named a source object (table / view /
465+
/// function / procedure / sequence) that doesn't resolve. The object's
466+
/// leaf name is reported (probe-confirmed) — the qualifier doesn't appear
467+
/// in the canonical message.
468+
/// </summary>
469+
internal static SimulatedSqlException CannotFindObject(string objectLeafName) =>
470+
new($"Cannot find the object '{objectLeafName}', because it does not exist or you do not have permission.", 15151, 16, 1);
471+
472+
/// <summary>
473+
/// Mimics SQL Server error 15151 (find-type variant): <c>ALTER SCHEMA
474+
/// ... TRANSFER TYPE::source.name</c> named a user-defined type that
475+
/// doesn't resolve. Probe-confirmed wording variant — the canonical
476+
/// "type" noun replaces "object".
477+
/// </summary>
478+
internal static SimulatedSqlException CannotFindType(string typeLeafName) =>
479+
new($"Cannot find the type '{typeLeafName}', because it does not exist or you do not have permission.", 15151, 16, 1);
480+
481+
/// <summary>
482+
/// Mimics SQL Server error 3729: <c>DROP SCHEMA</c> rejected because the
483+
/// schema still contains at least one object. SQL Server names the first
484+
/// object found in the dependency-graph walk (which often happens to be
485+
/// an auto-named PK / UNIQUE / CHECK constraint rather than the table
486+
/// itself); the simulator picks a representative name from the same
487+
/// dict scan. Probe-confirmed verbatim wording against SQL Server 2025.
488+
/// </summary>
489+
internal static SimulatedSqlException CannotDropSchemaBecauseNotEmpty(string schemaName, string objectName) =>
490+
new($"Cannot drop schema '{schemaName}' because it is being referenced by object '{objectName}'.", 3729, 16, 1);
491+
492+
/// <summary>
493+
/// Mimics SQL Server error 15530: <c>ALTER SCHEMA dest TRANSFER source.obj</c>
494+
/// rejected because <paramref name="objectLeafName"/> is already present
495+
/// in the destination schema. The canonical wording surfaces only the
496+
/// object's leaf (the destination schema isn't part of the message).
497+
/// Probe-confirmed verbatim against SQL Server 2025.
498+
/// </summary>
499+
internal static SimulatedSqlException ObjectAlreadyExistsInDestination(string objectLeafName) =>
500+
new($"The object with name \"{objectLeafName}\" already exists.", 15530, 16, 1);
501+
502+
/// <summary>
503+
/// Mimics SQL Server error 15347: <c>ALTER SCHEMA TRANSFER</c> targeted
504+
/// an object that's owned by a parent (the common case is a DML trigger,
505+
/// whose schema follows its parent table / view automatically — the
506+
/// trigger can't be transferred independently). Probe-confirmed verbatim
507+
/// wording.
508+
/// </summary>
509+
internal static SimulatedSqlException CannotTransferObjectOwnedByParent() =>
510+
new("Cannot transfer an object that is owned by a parent object.", 15347, 16, 1);
431511
}

SqlServerSimulator/Parser/ContextualKeyword.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ enum ContextualKeyword
4848
Next,
4949
No,
5050
NoWait,
51+
Object,
5152
Offset,
5253
Only,
5354
Out,
@@ -74,6 +75,7 @@ enum ContextualKeyword
7475
Time,
7576
TraceOff,
7677
TraceOn,
78+
Transfer,
7779
Try,
7880
Type,
7981
Unbounded,

SqlServerSimulator/Parser/Tokenizer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static class Tokenizer
4646
'-' => ParseMinusOrComment(command, ref index),
4747
'/' => ParseForwardSlashOrComment(command, ref index),
4848
'[' => ParseBracketDelimitedString(command, ref index),
49-
'+' or '*' or '%' or '(' or ')' or ',' or '.' or ';' or '=' or '&' or '|' or '^' or '>' or '<' or '!' => new Operator(command, index++),
49+
'+' or '*' or '%' or '(' or ')' or ',' or '.' or ';' or ':' or '=' or '&' or '|' or '^' or '>' or '<' or '!' => new Operator(command, index++),
5050
'$' when IsDollarAction(command, index) => ParseDollarAction(command, ref index),
5151
'$' or '¢' or '£' or '¥' or '฿' or (>= '₠' and <= '₱') => ParseCurrencyLiteral(command, ref index),
5252
var c => throw SimulatedSqlException.SyntaxErrorNear(c) // Might throw on valid-but-unsupported syntax.

0 commit comments

Comments
 (0)