Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PostgreSQL 'Deferrable Delete' still hits constraint on Delete

I want to delete rows from two tables which have a dependence upon each other through a set of deferrable constraints. To simplify this post, I've mocked up a simple DB schema.

I'm hoping to remove entries from some table, 'delete_from_me', inside a SQL transaction/DB Patch. The catch is, I want to delete based on a select from a second table 'constraining_table' before I loose the link itself.

Here's a description of the two tables:

tab-quarantine=> \d delete_from_me
       Table "public.delete_from_me"
  Column   |       Type        | Modifiers 
-----------+-------------------+-----------
 id        | character varying | not null
 extension | character varying | not null
Indexes:
    "delete_from_me_pkey" PRIMARY KEY, btree (id)

tab-quarantine=> \d constraining_table 
   Table "public.constraining_table"
 Column |       Type        | Modifiers 
--------+-------------------+-----------
 image  | character varying | not null
 type   | character varying | not null
Foreign-key constraints:
    "constraining_table_image_fkey" FOREIGN KEY (image) REFERENCES delete_from_me(id)
         ON UPDATE CASCADE
         ON DELETE RESTRICT DEFERRABLE

Here's some sample data I just blatted in there:

tab-quarantine=> SELECT * FROM delete_from_me;
     id     | extension 
------------+-----------
 12345abcde | png
(1 row)

tab-quarantine=> SELECT * FROM constraining_table;
   image    |   type   
------------+----------
 12345abcde | select_me
(1 row)

And here goes my transaction:

BEGIN;
\set ON_ERROR_STOP 1
SET CONSTRAINTS ALL DEFERRED;
DELETE FROM delete_from_me WHERE id IN (
    SELECT image FROM constraining_table WHERE type = 'select_me'
);
DELETE FROM constraining_table WHERE type = 'select_me';
COMMIT;

This transaction fails. When I step through and do this manually, I'm presented with the following error message:

ERROR:  update or delete on table "delete_from_me" violates foreign key constraint "constraining_table_image_fkey" on table "constraining_table"
DETAIL:  Key (id)=(12345abcde) is still referenced from table "constraining_table".

This seems like a good candidate for a temporary table, however I'd like to know why it is that I can't delete in this order given the constraints should not be effective till the end of the transaction?

like image 735
heymatthew Avatar asked Jul 19 '10 06:07

heymatthew


People also ask

How do I delete a constraint in PostgreSQL?

The syntax for dropping a unique constraint in PostgreSQL is: ALTER TABLE table_name DROP CONSTRAINT constraint_name; table_name.

What is a deferrable constraint in Postgres?

If a constraint is deferrable, this clause specifies the default time to check the constraint. If the constraint is INITIALLY IMMEDIATE, it is checked after each statement, which is the default. If the constraint is INITIALLY DEFERRED, it is checked only at the end of the transaction.

What is on delete cascade in PostgreSQL?

ON DELETE CASCADE option is to specify whether you want rows deleted in a child table when corresponding rows are deleted in the parent table. If you do not specify cascading deletes, the default behaviour of the database server prevents you from deleting data in a table if other tables reference it.

What is deferred constraint?

DEFERRED constraints are not checked until transaction commit. Each constraint has its own IMMEDIATE or DEFERRED mode. Upon creation, a constraint is given one of three characteristics: DEFERRABLE INITIALLY DEFERRED , DEFERRABLE INITIALLY IMMEDIATE , or NOT DEFERRABLE .


1 Answers

Use ON DELETE NO ACTION DEFERRABLE instead of ON DELETE RESTRICT DEFERRABLE. Using RESTRICT instead of NO ACTION forces the constraint to be non-deferrable, regardless of whether you apply the DEFERRABLE modifier.

This is in the fine print of the manual page for CREATE TABLE:

Referential actions other than the NO ACTION check cannot be deferred, even if the constraint is declared deferrable.

Obviously, the above caveat includes RESTRICT.

Following shortly after this sentence are the definitions of NO ACTION and RESTRICT:

NO ACTION

Produce an error indicating that the deletion or update would create a foreign key constraint violation. If the constraint is deferred, this error will be produced at constraint check time if there still exist any referencing rows. This is the default action.

RESTRICT

Produce an error indicating that the deletion or update would create a foreign key constraint violation. This is the same as NO ACTION except that the check is not deferrable.

As you can see, NO ACTION will behave identically to RESTRICT, except NO ACTION is deferrable. This is why I recommended it -- I think it's just what you're asking for.

like image 196
Dan LaRocque Avatar answered Nov 13 '22 01:11

Dan LaRocque