Description
DEFERRABLE INITIALLY DEFERRED on UNIQUE constraints is parsed (it's in the syntax docs) but silently dropped at every stage:
dump — strips DEFERRABLE INITIALLY DEFERRED from output
plan — does not detect the difference between a deferrable and non-deferrable constraint
apply — strips DEFERRABLE INITIALLY DEFERRED when creating a new table
Why it matters
We use DEFERRABLE INITIALLY DEFERRED on unique constraints so that batch UPDATE ... SET col = CASE WHEN ... statements can swap unique values between rows without triggering a duplicate key error. Without it, PostgreSQL checks the constraint row-by-row and fails even though the final state is valid.
Reproduction
I've attached a self-contained script (pgschema_deferrable_bug.sh) that demonstrates all three issues. It creates a test schema, runs dump, plan, and apply, and reports the results.
To run:
PGUSER=postgres PGPASSWORD=postgres PGDATABASE=postgres ./pgschema_deferrable_bug.sh
Output on pgschema v1.9.0, PostgreSQL 16.3:
Full output
════════════════════════════════════════════════════════
pgschema DEFERRABLE bug reproduction
════════════════════════════════════════════════════════
▶ Step 1: Create schema and table WITHOUT DEFERRABLE
✓ Table created: deferrable_test.items with non-deferrable UNIQUE constraint
▶ Step 2: pgschema dump (baseline — no DEFERRABLE expected)
Output:
--
-- pgschema database dump
--
-- Dumped from database version PostgreSQL 18.3
-- Dumped by pgschema version 1.9.0
--
-- Name: items; Type: TABLE; Schema: -; Owner: -
--
CREATE TABLE IF NOT EXISTS items (
id varchar(6),
code varchar(32) NOT NULL,
CONSTRAINT items_pkey PRIMARY KEY (id),
CONSTRAINT uq_code UNIQUE (code)
);
▶ Step 3: Create target SQL file WITH DEFERRABLE INITIALLY DEFERRED
Target SQL:
CREATE TABLE IF NOT EXISTS items (
id VARCHAR(6),
code VARCHAR(32) NOT NULL,
CONSTRAINT items_pkey PRIMARY KEY (id),
CONSTRAINT uq_code UNIQUE (code) DEFERRABLE INITIALLY DEFERRED
);
▶ Step 4: pgschema plan — should detect DEFERRABLE change
Output:
No changes detected.
⚠ BUG: pgschema reports 'No changes detected' even though the live DB
has a non-deferrable constraint and the target SQL has DEFERRABLE.
▶ Step 5: Drop table, then apply target SQL (new table creation)
✓ Table dropped
Applying target SQL via pgschema apply...
Plan: 1 to add.
Summary by type:
tables: 1 to add
Tables:
+ items
DDL to be executed:
--------------------------------------------------
CREATE TABLE IF NOT EXISTS items (
id varchar(6),
code varchar(32) NOT NULL,
CONSTRAINT items_pkey PRIMARY KEY (id),
CONSTRAINT uq_code UNIQUE (code)
);
Applying changes...
Set search_path to: deferrable_test, public
Executing group 1/1...
Executing 1 statements in implicit transaction
Changes applied successfully!
▶ Step 6: Check if DEFERRABLE was applied in the database
Query: pg_constraint.condeferrable for uq_code
condeferrable = f
✗ BUG: DEFERRABLE was NOT applied — constraint is NOT deferrable
▶ Step 7: pgschema dump after apply
Output:
--
-- pgschema database dump
--
-- Dumped from database version PostgreSQL 18.3
-- Dumped by pgschema version 1.9.0
--
-- Name: items; Type: TABLE; Schema: -; Owner: -
--
CREATE TABLE IF NOT EXISTS items (
id varchar(6),
code varchar(32) NOT NULL,
CONSTRAINT items_pkey PRIMARY KEY (id),
CONSTRAINT uq_code UNIQUE (code)
);
⚠ Note: Check if DEFERRABLE INITIALLY DEFERRED appears in dump output
but is missing.
════════════════════════════════════════════════════════
Summary
════════════════════════════════════════════════════════
- pgschema dump strips DEFERRABLE from UNIQUE constraints
- pgschema plan does not detect DEFERRABLE differences
- pgschema apply does not preserve DEFERRABLE on new tables
Expected: DEFERRABLE INITIALLY DEFERRED should be tracked,
diffed, and preserved — matching the syntax docs.
════════════════════════════════════════════════════════
Expected behavior
dump should include DEFERRABLE INITIALLY DEFERRED in the constraint definition
plan should detect when a constraint's deferrable property differs between the target SQL and the live database
apply should preserve DEFERRABLE INITIALLY DEFERRED when creating or altering tables
pgschema_deferrable_bug.sh
Description
DEFERRABLE INITIALLY DEFERREDon UNIQUE constraints is parsed (it's in the syntax docs) but silently dropped at every stage:dump— stripsDEFERRABLE INITIALLY DEFERREDfrom outputplan— does not detect the difference between a deferrable and non-deferrable constraintapply— stripsDEFERRABLE INITIALLY DEFERREDwhen creating a new tableWhy it matters
We use
DEFERRABLE INITIALLY DEFERREDon unique constraints so that batchUPDATE ... SET col = CASE WHEN ...statements can swap unique values between rows without triggering a duplicate key error. Without it, PostgreSQL checks the constraint row-by-row and fails even though the final state is valid.Reproduction
I've attached a self-contained script (
pgschema_deferrable_bug.sh) that demonstrates all three issues. It creates a test schema, runsdump,plan, andapply, and reports the results.To run:
Output on pgschema v1.9.0, PostgreSQL 16.3:
Full output
════════════════════════════════════════════════════════
pgschema DEFERRABLE bug reproduction
════════════════════════════════════════════════════════
▶ Step 1: Create schema and table WITHOUT DEFERRABLE
✓ Table created: deferrable_test.items with non-deferrable UNIQUE constraint
▶ Step 2: pgschema dump (baseline — no DEFERRABLE expected)
Output:
--
-- pgschema database dump
--
▶ Step 3: Create target SQL file WITH DEFERRABLE INITIALLY DEFERRED
Target SQL:
CREATE TABLE IF NOT EXISTS items (
id VARCHAR(6),
code VARCHAR(32) NOT NULL,
CONSTRAINT items_pkey PRIMARY KEY (id),
CONSTRAINT uq_code UNIQUE (code) DEFERRABLE INITIALLY DEFERRED
);
▶ Step 4: pgschema plan — should detect DEFERRABLE change
Output:
No changes detected.
⚠ BUG: pgschema reports 'No changes detected' even though the live DB
has a non-deferrable constraint and the target SQL has DEFERRABLE.
▶ Step 5: Drop table, then apply target SQL (new table creation)
✓ Table dropped
Applying target SQL via pgschema apply...
Plan: 1 to add.
▶ Step 6: Check if DEFERRABLE was applied in the database
Query: pg_constraint.condeferrable for uq_code
condeferrable = f
✗ BUG: DEFERRABLE was NOT applied — constraint is NOT deferrable
▶ Step 7: pgschema dump after apply
Output:
--
-- pgschema database dump
--
⚠ Note: Check if DEFERRABLE INITIALLY DEFERRED appears in dump output
but is missing.
════════════════════════════════════════════════════════
Summary
════════════════════════════════════════════════════════
Expected: DEFERRABLE INITIALLY DEFERRED should be tracked,
diffed, and preserved — matching the syntax docs.
════════════════════════════════════════════════════════
Expected behavior
dumpshould includeDEFERRABLE INITIALLY DEFERREDin the constraint definitionplanshould detect when a constraint's deferrable property differs between the target SQL and the live databaseapplyshould preserveDEFERRABLE INITIALLY DEFERREDwhen creating or altering tablespgschema_deferrable_bug.sh