microsoft / microsoft/DacFx

Export ignores /p:VerifyExtraction: bacpac exports cleanly from the same model Extract rejects with SQL71501

Open
#836 1 comment 0 reactions 0 assignees View on GitHub
documentation
Dominant language
C#
Stars
460
Forks
29
Avg merge
4d 9h
Merged PRs (30d)
7

Description

- **SqlPackage or DacFx Version:** SqlPackage 162.1.172.1
- **.NET Framework (Windows-only) or .NET Core:** .NET Core
- **Environment (local platform and source/target platforms):** linux-x64 client; source and target are Azure SQL Database (compatibility level 170)

### Summary

`VerifyExtraction` is documented under **Properties specific to the Export action** as `(BOOLEAN 'True')` — *"Specifies whether the extracted schema model should be verified. If set to true, schema validation rules are run on the dacpac or bacpac."*

In practice it has no observable effect on `/a:Export`. A database whose model contains an unresolved reference exports to a `.bacpac` with exit code 0, while `/a:Extract /p:VerifyExtraction=True` against the **same database** fails with `SQL71501`. The `model.xml` inside the resulting `.bacpac` and `.dacpac` is **byte-identical**, so both actions verify the same model and reach different conclusions.

The documented defaults are also inverted between the two actions — Extract defaults `False`, Export defaults `True` — so the action that defaults verification *on* is the one that appears not to verify.

**Steps to Reproduce:**

1. Create a schema with a procedure reference that is then broken. SQL Server permits this; the procedure survives.

```sql
CREATE TABLE dbo.Processes (
Id INT NOT NULL PRIMARY KEY,
Name NVARCHAR(200) NOT NULL,
HasInvalidTransientData BIT NOT NULL CONSTRAINT DF_P DEFAULT(0)
);
GO
CREATE PROCEDURE dbo.PlanMigration AS
BEGIN
SELECT p.Id, p.Name
FROM dbo.Processes AS p
WHERE p.HasInvalidTransientData = 1;
END
GO
ALTER TABLE dbo.Processes DROP CONSTRAINT DF_P;
ALTER TABLE dbo.Processes DROP COLUMN HasInvalidTransientData;
```

2. Run each of the following against that database:

| Command | Exit | Result |
| --- | --- | --- |
| `/a:Extract /p:VerifyExtraction=True` | 1 | `SQL71501 ... [dbo].[PlanMigration] has an unresolved reference to object [dbo].[Processes].[HasInvalidTransientData]` |
| `/a:Extract /p:VerifyExtraction=False` | 0 | dacpac written |
| `/a:Export` (default, i.e. `True`) | 0 | bacpac written |
| `/a:Export /p:VerifyExtraction=True` | 0 | bacpac written |
| `/a:Export /p:VerifyExtraction=False` | 0 | bacpac written |

3. Unzip all three packages and compare `model.xml`. They are byte-identical (md5 `86b3eb9e3b7cf993674d9dd66aa75506`), and each records the dangling reference the same way — resolved dependencies as ``, the broken one as a `PersistedResolvableAnnotation`:

```xml










```

`ExtractApplicationScopedObjectsOnly=false` makes no difference to the Extract failure, so model scoping is not the variable.

### Why it matters

To be clear about impact: **Import faithfully reproduces the source.** The restored database has the same procedures, the same bodies, the same data, and fails the same way — `Msg 207, Invalid column name` on execution — exactly as the source database already does. Nothing is corrupted, and for a portability artifact that fidelity is arguably the correct behaviour.

The problem is narrower, and is about the contract of the flag. A caller who explicitly sets `/p:VerifyExtraction=True` on Export, reads the documented description ("schema validation rules are run on the dacpac or bacpac"), and gets exit code 0 has no way to know the model still contains unresolved references. The identical model handed to Extract with the identical flag value is rejected. Any tooling that uses Export's exit code as a schema-soundness gate is therefore silently wrong.

For contrast, `Publish` of the equivalent dacpac fails loudly, because it scripts in dependency order and creates the table before the procedure:

```
Error SQL72014: ... Msg 207 ... Procedure PlanMigration, Line 7
Invalid column name 'HasInvalidTransientData'.
Updating database (Failed)
```

(That publish also leaves the target partially applied — table created, zero procedures, zero rows.)

**Did this occur in prior versions? If not - which version(s) did it work in?**

Not investigated — only reproduced on SqlPackage 162.1.172.1. I have not tested earlier versions, so I can't say whether this is a regression or long-standing behaviour.

### Ask

Please clarify which of these is intended:

1. Export **should** run reference-resolution validation and currently does not — a product bug; or
2. Export's verification deliberately excludes reference resolution, in which case the Export property table (and its wording shared with Extract, "schema validation rules are run on the dacpac or bacpac") is misleading and should say what Export actually checks.

Either way, the inverted defaults plus the shared description make it easy to assume a bacpac has been validated for referential integrity when it has not.

Contributor guide

Open the contributing guide

Research direction

Start with the SqlPackage /a:Export and /a:Extract entry points and the VerifyExtraction property behavior described in the issue. Reproduce the unresolved-reference case using the supplied SQL script, then compare the Export and Extract validation paths and their documentation. Done means either Export validates references consistently or its documented scope and defaults clearly describe the behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
sql
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.