Latest Results
fix(schema-engine): refuse a shadow database that is the main database (#5851)
Prisma Migrate commands that replay a migrations directory do so by
first resetting a shadow database. Nothing prevented the shadow database
URL from denoting the main database itself: `migrate diff
--from-migrations` with `shadowDatabaseUrl` equal to the datasource URL
dropped the real schema (`DROP SCHEMA … CASCADE` on PostgreSQL) — and
because the *from* target resolves before *to*, it then introspected the
freshly rebuilt schema and reported an empty diff with exit code 0. This
PR makes the engine refuse such configurations before touching any
database, with the new user-facing error **P3025**.
## Changes
- **`sql-schema-connector`**: new `same_database` module exporting
`urls_denote_same_database(flavour, a, b)` — a flavour-aware comparison
built on quaint's URL parsing: host compared case-insensitively,
parser-default ports applied, database names compared; the PostgreSQL
`?schema=` selector is deliberately ignored (dropping *another* schema
of the same database is still data loss); SQLite compares canonicalized
file paths; unparseable URLs fall back to exact string equality so the
guard can never block a working configuration on a parse quirk. The
existing `validate_connection_infos_do_not_match` guard (the `migrate
dev` shape) delegates to it — call sites now pass their statically-known
flavour — and returns the typed error.
- **`schema-engine-core`**: `diff_cli` runs the guard as its literal
first step: when a `Migrations` diff target will use the external shadow
database, the shadow URL is compared against the datasource URL and any
`DiffTarget::Url` on either side before any dialect or connector is
constructed.
- **`user-facing-errors`**: new `P3025` / `ShadowDbSameAsMainDb`;
message text unchanged from the previous untyped error.
- **Tests**: an incident repro asserting both the refusal and that a
`precious_data` table in the target database survives; per-flavour
refusal tests for identity-preserving URL spellings (scheme alias, host
case, `?schema=`); same-server-different-database negative controls;
no-DB wiring tests for the diff-path guard. A new harness helper
provisions a genuinely separate shadow database for tests — notably,
three pre-existing tests had configured the database under test as its
own shadow database and are repointed here; one previously passed only
because both sides of its diff derived from the database the diff had
just wiped.
## Why
Two comparison classes are exempt by design: `prisma+postgres://` URLs
are compared verbatim (their database identity lives in the `api_key`
token, and two distinct local PPg databases share host, port, and path —
normalizing would refuse working `prisma dev` setups), and anonymous
in-memory SQLite never matches anything (each `:memory:` connection is a
private database). Together with DNS aliases, which no static comparison
can catch, these are bounded by the follow-up layer: a stacked PR will
require an explicitly-provided external shadow database to be *empty*
before reset, with explicit consent otherwise.
A note on branch history: the diff-path guard commit briefly broke the
three repointed tests — that is, it refused exactly the misconfiguration
it exists to refuse. They are fixed within this PR; the branch is green
at its tip.
Part of the shadow-db-safety project (Linear: TML-3116); the
emptiness-check/consent layer and the CLI consent UX follow in stacked
PRs.
---------
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net> Latest Branches
0%
dependabot/npm_and_yarn/libs/driver-adapters/executor/undici-6.28.0 0%
0%
© 2026 CodSpeed Technology