Skip to content

Schema Compare: Fabric Warehouse platform detection + constraint script preservation#2722

Draft
roramsay wants to merge 4 commits into
microsoft:mainfrom
roramsay:roramsay/schema-compare-fabric-platform
Draft

Schema Compare: Fabric Warehouse platform detection + constraint script preservation#2722
roramsay wants to merge 4 commits into
microsoft:mainfrom
roramsay:roramsay/schema-compare-fabric-platform

Conversation

@roramsay

Copy link
Copy Markdown

Three commits on this branch ship the sqltoolsservice changes for Schema Compare
on Microsoft Fabric Warehouse (Feature 1847587):

  1. 30d90abe — surface SourcePlatform / TargetPlatform on the result contract.
  2. 23a8ef56 — read platform from the comparison''s DSP via cached reflection
    (TSqlModel.Version returns Sql150 for SqlDwUnified due to an internal
    DacFx mapping bug — see InternalModelUtils.CalculateVersionsForPlatform).
  3. aa9a0761 — keep Fabric Warehouse constraint scripts under table diffs
    (SqlDwUnified-scoped carve-out in CreateDiffEntry''s starts-with-alter
    filter; behaviour unchanged for every other platform).

Companion PRs:

  • DacFx PR 2143938 (in flight) — cascade exclusion to hierarchical-child
    constraints under SqlDwUnified.
  • vscode-mssql PR (to be filed alongside this one) — platform badges +
    affected-constraints banner.

Spec: https://dev.azure.com/powerbi/Trident/_git/specs-dw/pullrequest/989687

Verified end-to-end against a live Fabric Warehouse + a Fabric .sqlproj:

  • Platform pill shows SqlDwUnified on both endpoints.
  • "Constraints added: Primary Key" banner appears above the diff editor
    for an Add table with a PK constraint.
  • The Monaco diff editor''s right pane shows CREATE TABLE followed by
    the standalone ALTER TABLE ... ADD CONSTRAINT [PK_...] PRIMARY KEY NONCLUSTERED ([col]) NOT ENFORCED; statement.
  • The generated deployment script includes that ALTER statement.

Test gap: a new unit test for CreateDiffEntry_PreservesConstraintAlterScriptUnderSqlDwUnified
is in the spec''s test design but not added in this PR — the SchemaCompare
unit-test project (Microsoft.SqlTools.ServiceLayer.UnitTests) currently
fails to compile against the DacFx 170.5 overlay (TSqlModelBuilder removed in
IntelliSense/SqlProjectIntelliSenseTests.cs). The test will be added once
the DacFx package version is officially bumped (gated on PR 2143938).

roramsay and others added 3 commits June 10, 2026 15:50
For Feature 1847587 (Schema Compare for Fabric Warehouse). The vscode-mssql
client needs to know which DacFx platform a comparison ran under so it can
display the dialect badge in the diff view header (e.g. `SqlDwUnified` for
Fabric Warehouse vs. `Sql160` for Azure SQL Database). Today the result has
no way to convey this without the client re-walking the DacFx model.

Adds `SourcePlatform` and `TargetPlatform` (nullable `string`) to both
`SchemaCompareResult` types (`Microsoft.SqlTools.SqlCore` for SSMS
direct-call consumers; `Microsoft.SqlTools.ServiceLayer` for the JSON-RPC
`schemaCompare/compare` response). Populated from
`ComparisonResult.SourceModel.Version.ToString()` and the target equivalent
after the comparison runs -- pass-through reads, no new I/O. Null-safe when
the comparison didn't produce a model.

Wired through `SchemaCompareOperation`:
- New `SourcePlatform` / `TargetPlatform` properties on the operation
  set by `Execute()` from the DacFx `ComparisonResult` models.
- `SchemaCompareService` reads these into the JSON-RPC
  `SchemaCompareResult` it returns to clients.

Tests: two new unit tests in `SchemaCompareTests.cs` covering the contract
shape (both `SchemaCompareResult` and the operation expose the properties,
default to `null`, and accept writes). End-to-end behaviour against live
comparisons is covered by integration tests for Feature 1847587.

No DacFx API change. No version bump required for this commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The public TSqlModel.Version surface maps SqlPlatforms.SqlDwUnified to
SqlServerVersion.Sql150 inside DacFx (InternalModelUtils.CalculateVersionsForPlatform),
so reading .Version.ToString() always returned 'Sql150' for Fabric Warehouse
models and mislabelled the platform pill in Schema Compare.

Read the comparison's normalized DSP from
SchemaComparisonResult.DataModel.DatabaseSchemaProvider.Platform via reflection.
DatabaseSchemaProvider is internal-abstract in DacFx but its .Platform property
returns the public SqlPlatforms enum, whose values map directly to the DSP-name
strings clients expect ('Sql160', 'SqlAzure', 'SqlDwUnified', etc.). Lookups
are cached statically; any reflection failure is logged and falls back to a
null platform string (the pill silently hides).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…diffs

CreateDiffEntry stripped any child script that started with `alter` to avoid
duplicating constraint DDL that the parent's CREATE TABLE already inlined for
legacy SQL Server. SqlDwUnified (Fabric Warehouse) never inlines PK/FK/UNIQUE/
CHECK/DEFAULT constraints into CREATE TABLE; they are always emitted as
standalone `ALTER TABLE ... ADD CONSTRAINT ...` statements. The filter
therefore discarded the only script that defined the constraint, so the diff
editor showed CREATE TABLE without the PK and the generated script was missing
the PK definition entirely.

Detect constraint children (PK/FK/UNIQUE/CHECK/DEFAULT) under a SqlDwUnified
comparison and preserve their scripts in that case. Behavior is unchanged for
all other platforms.

Move the cached reflection-based platform detector from SchemaCompareOperation
into SchemaCompareUtils so CreateDiffEntry can use it; add a
ConditionalWeakTable cache keyed by SchemaComparisonResult so the recursive
walk pays the reflection cost once per comparison, not once per diff entry.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@roramsay

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="Microsoft"

…e 1847587)

Implements the test design from the Schema Compare for Fabric Warehouse spec (Feature 1847587).

Unit tests (SchemaCompareTests.cs):

Extracts SchemaCompareUtils.ShouldPreserveAlterScriptForConstraint(typeName, platform) as a pure helper so the SqlDwUnified strip-on-alter carve-out is testable without manufacturing a sealed DacFx SchemaComparisonResult graph. Existing IsConstraintChildOnSqlDwUnified is now a thin wrapper. Adds 18 TestCase rows covering every constraint suffix under SqlDwUnified, non-constraint types under SqlDwUnified, every non-Fabric platform string a comparison can emit (Sql160/Sql150/SqlAzure/SqlAzureV12/SqlDw), null/empty type and platform inputs, substring-vs-suffix guard, and GetComparisonPlatform null-result short-circuit.

Integration tests (SchemaCompareFabricTests.cs):

Full 4-endpoint-pair x 7-scenario matrix (28 cells) via [TestCaseSource]. Scenarios: AllSupportedTypes, CompatibleAlterColumn, IncompatibleAlterColumn, PrimaryKey, Unique, ForeignKey, SelectionScope. Endpoint pairs: DacpacToDacpac, DacpacToProject, ProjectToDacpac, ProjectToProject. Fabric dacpacs are built offline at fixture setup via new TSqlModel(SqlServerVersion.SqlDwUnified, ...) + DacPackageExtensions.BuildPackage so the suite requires no live Fabric Warehouse.

Per-cell assertions (per spec lines 309-316): comparison validity + IsEqual=false + Differences.Any(); SourcePlatform == TargetPlatform == SqlDwUnified; constraints folded under parent SqlTable diff (not surfaced as top-level entries); constraint child carries ADD CONSTRAINT NONCLUSTERED NOT ENFORCED script; selection-scope IncludeOnly(OrdersScope) yields a Generate Script output that references zero unrelated tables or constraints (locks in the Prototype 3 empirical regression that motivated DacFx PR 2143938); project-target round-trip converges to IsEqual after publish.

Live Fabric publish (per-cell assertion 7) is intentionally manual-only per the spec; it is exercised by the WSR matrix, not automated here.

Fixtures (test/.../SchemaCompare/Fabric/*.sql): 14 minimal T-SQL scripts confined to the Fabric-supported data type list, with all constraints scripted as NONCLUSTERED NOT ENFORCED. Plus emptyFabricTemplate.sqlproj with DSP=SqlDwUnifiedDatabaseSchemaProvider for the Project endpoint cases.

Requires DacFx >= 170.5.20 (the published build that ships PR 2143938's cascade fix). The sts-version-bump that flips Packages.props onto that DacFx is a separate change and is not included here.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@roramsay

Copy link
Copy Markdown
Author

Added the test scenarios from the spec (Feature 1847587 §Test design) in commit 40f96df:

Unit tests (test/Microsoft.SqlTools.ServiceLayer.UnitTests/SchemaCompare/SchemaCompareTests.cs)
Extracted SchemaCompareUtils.ShouldPreserveAlterScriptForConstraint(typeName, platform) as a pure helper so the SqlDwUnified strip-on-alter carve-out is testable without manufacturing a sealed DacFx SchemaComparisonResult graph. Added 18 TestCase rows covering every constraint suffix under SqlDwUnified, non-constraint types under SqlDwUnified, every non-Fabric platform string a comparison can emit (Sql160 / Sql150 / SqlAzure / SqlAzureV12 / SqlDw), null/empty inputs, substring-vs-suffix guard, and GetComparisonPlatform null-result short-circuit. 26/26 SchemaCompare unit tests pass locally.

Integration tests (test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareFabricTests.cs)
Full 4 endpoint pair × 7 scenario matrix (28 cells) via [TestCaseSource]. Scenarios: AllSupportedTypes, CompatibleAlterColumn, IncompatibleAlterColumn, PrimaryKey, Unique, ForeignKey, SelectionScope. Endpoint pairs: DacpacToDacpac, DacpacToProject, ProjectToDacpac, ProjectToProject.

Fabric dacpacs are built offline at fixture setup via new TSqlModel(SqlServerVersion.SqlDwUnified, …) + DacPackageExtensions.BuildPackage so the suite requires no live Fabric Warehouse. Fixtures (14 minimal Fabric-T-SQL .sql files plus emptyFabricTemplate.sqlproj with <DSP>SqlDwUnifiedDatabaseSchemaProvider</DSP>) live alongside the test.

Per-cell assertions implemented per spec lines 309-316:

  1. IsValid / IsEqual=false / Differences.Any()
  2. SourcePlatform == TargetPlatform == "SqlDwUnified"
  3. Constraints folded under parent SqlTable diff (zero top-level constraint diffs)
  4. Constraint child carries ADD CONSTRAINT … NONCLUSTERED … NOT ENFORCED script preserved by the SqlDwUnified carve-out in CreateDiffEntry
  5. Selection-scope: IncludeOnly(OrdersScope) ⇒ generated script references zero unrelated tables and zero unrelated constraint names (locks in the Prototype 3 empirical regression — the 19 leaked PK statements — that motivated DacFx PR 2143938)
  6. Project-target round-trip: re-compare after publish converges to IsEqual=true
  7. Live Fabric publish (manual/WSR only, intentionally not automated per spec)

Local run: 28/28 Fabric integration tests pass (~17 s) against a locally-built DacFx 170.5.20-local that includes PR 2143938's cascade fix.

⚠️ CI dependency

These tests require the DacFx NuGet that ships PR 2143938 (cascade exclusion to hierarchical-child constraints). On this branch Packages.props still references 170.4.80-preview — that bump is intentionally not part of this commit because it is the same change tracked as the sts-version-bump follow-up. The tests will turn green in CI as soon as DacFx PR 2143938 publishes and we bump the version pin.

Linked vscode-mssql PR with the webview-side tests: microsoft/vscode-mssql#22340.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant