Aggregates are a declared limitation. A project declaring CREATE AGGREGATE gets a clear diagnostic — SQL Server aggregates are CLR assemblies the model cannot express — instead of a wrong CREATE FUNCTION.
Changed
datetime and datetime2 are two types, and both are preserved. A user who writes datetime gets datetime; datetime2 round-trips verbatim. The model’s canonical datetime no longer silently upgrades to datetime2.
System types are captured bare. A built-in outside the model’s vocabulary (money, xml, both datetimes) introspects under its own bare name — the engine’s vocabulary is addressed bare — while user-defined types keep their owning schema.
Alias types are domains. A user-defined alias type (CREATE TYPE … FROM base) now introspects as a domain — base type, nullability, and MS_Description comment — and the dialect renders domain actions: create, drop, rename (sp_rename … 'USERDATATYPE'), recreate, and comment. A domain declaring a default or check constraints remains a clear diagnostic — alias types cannot carry them.
Changed
Alias types left the vocabulary. They were previously reported as NativeTypes; a declaration the plan can create and drop is not vocabulary. CLR types remain vocabulary, and table types remain excluded.
Fixed
A view’s introspected body is the bare query. The trailing statement terminator sys.sql_modules preserves from the original CREATE VIEW is now stripped, so the introspected body matches what an author writes.
Introspected comments are trimmed. An MS_Description value with surrounding whitespace cannot be expressed by an NSQL doc comment, so it could never round-trip; values are trimmed on the way in and whitespace-only values are treated as no comment.
Unparenthesized procedure headers split correctly. A procedure declared without parentheses had its parameter list found by searching for a space-padded AS, which missed a newline-delimited header AS and could match one inside the body (a CTE’s, an alias’s). The header AS is now located by a scan that honours strings, bracketed identifiers, comments, parenthesis depth, and a parameter’s own @name AS type.
Options clauses on procedures split correctly. An options clause (WITH EXECUTE AS …, WITH RECOMPILE) ends the header too and stays with the definition.
Comments before a module’s CREATE no longer derail the split.sys.sql_modules stores the batch as written, comments included; the leading trivia is now stepped over before the header is matched, for routines, views, and triggers.
A parameter list ending in a line comment renders intact. The closing parenthesis is printed on a new line rather than being swallowed by the comment.
A parameter-less procedure renders without parentheses. T-SQL rejects CREATE PROCEDURE name(); the empty list is omitted (functions keep theirs).
Clustered indexes round-trip.SupportsClustering is on, so CLUSTERED and NONCLUSTERED are rendered on indexes, primary keys and unique constraints.
XML schema collections round-trip.XML_SCHEMA_NAMESPACE reassembles a collection from sys.xml_schema_* into the single document it was declared as, and a typed xml column carries the collection it is bound to along with whether it is DOCUMENT or CONTENT.
Constraints SQL Server named for itself are reported.is_system_named is read for key, foreign-key and check constraints, and a system-named-constraint warning names them as one grouped finding.
XML indexes round-trip.sys.xml_indexes supplies the kind and, for a secondary, the primary whose node table it reads (i.type identifies an XML index, since secondary_type_desc is null for a primary as well as for a non-XML index).
Indexed views round-trip. Indexes are read from sys.objects rather than sys.tables, so a view’s indexes are introspected onto View.Indexes alongside a table’s.
Schema binding round-trips.WITH SCHEMABINDING is read from sys.sql_modules.is_schema_bound onto View.IsSchemaBound and written back from it.
is_persisted is introspected, and SupportsVirtualGeneratedColumns is declared.
Changed
The build now writes the full dependency closure beside the assembly (CopyLocalLockFileAssemblies), so a dotnet build of this project can be loaded directly by PLUGIN ( path = '...' ) without being packed first. Package contents are unchanged.
Fixed
Computed columns are no longer all PERSISTED. The keyword was emitted unconditionally, so a column computed on read came back written to storage.
ROWGUIDCOL is no longer dropped. It was neither introspected nor rendered, so a column marked as the table’s row identifier came back as an ordinary one.
Default constraint names are preserved. A default was emitted unnamed, so SQL Server generated one.
NOT FOR REPLICATION is no longer dropped from identity columns or triggers.