开发者

PostgreSQL delete fails with ON DELETE rule on inherited table

In my PostgreSQL 9.1 database I've defined RULEs that delete rows from child tables whenever a parent table row is deleted. This all worked OK, until I introduced inheritance. If the parent (referencing) table INHERITS from another table and I delete from the base table then the DELETE succeeds, but the RULE doesn't appear to fire at all - the referenced row is not deleted. If I try to delete from the derived table I get an error:

update or delete on table "referenced" violates foreign key constraint "fk_derived_referenced" on table "derived"

There is no other row in the parent table that would violate the foreign key: it's being referenced by the row that's being deleted! How do I fix this?

The following script reproduces the problem:

-- Schema

CREATE TABLE base
(
  id serial NOT NULL,
  name character varying(100),
  CONSTRAINT pk_base PRIMARY KEY (id)
);

CREATE TABLE referenced
(
  id serial NOT NULL,
  value character varying(100),
  CONSTRAINT pk_referenced PRIMARY KEY (id)
);

CREATE TABLE derived
(
  referenced_id integer,
  CONSTRAINT pk_derived PRIMARY KEY (id),
  CONSTRAINT fk_derived_referenced FOREIGN KEY (referenced_id) REFERENCES referenced (id)
)
INHERITS (base);

-- The rule

CREATE OR REPLACE RULE rl_derived_delete_referenced
AS ON DELETE TO derived DO ALSO
DELETE FROM referenced r WHERE r.id = old.referenced_id;

-- Some test data

INSERT INTO referenced (id, value)
VALUES (1, 'referenced 1');

INSERT INTO derived (id, name, referenced_id)
VALUES (2, 'derived 2', 1);

-- Delete from base - 开发者_如何学Cdeletes the "base" and "derived" rows, but not "referenced"
--DELETE FROM base
--WHERE id = 2;

-- Delete from derived - fails with:
-- update or delete on table "referenced" violates foreign key constraint "fk_derived_referenced" on table "derived"
DELETE FROM derived
WHERE id = 2


As I said in my comment, this seems an unusual way to do things. But you can make it work with a deferred constraint.

CREATE TABLE derived
(
  referenced_id integer,
  CONSTRAINT pk_derived PRIMARY KEY (id),
  CONSTRAINT fk_derived_referenced FOREIGN KEY (referenced_id) 
    REFERENCES referenced (id) DEFERRABLE INITIALLY DEFERRED
)
INHERITS (base);

The PostgreSQL docs, Rules vs. Triggers, say

Many things that can be done using triggers can also be implemented using the PostgreSQL rule system. One of the things that cannot be implemented by rules are some kinds of constraints, especially foreign keys.

But it's not clear to me that this specific limitation is what you're running into.


Also, you need to check if other records are still referencing the to-be-deleted rows. I added a test derived record#3, which points to the same #1 reference record.

-- The rule    
CREATE OR REPLACE RULE rl_derived_delete_referenced
AS ON DELETE TO tmp.derived DO ALSO (
    DELETE FROM tmp.referenced re_del
    WHERE re_del.id = OLD.referenced_id
    AND NOT EXISTS ( SELECT * FROM tmp.derived other
        WHERE other.referenced_id = re_del.id
        AND other.id <> OLD.id )
        ;
    );

-- Some test data

INSERT INTO tmp.referenced (id, value)
VALUES (1, 'referenced 1');

-- EXPLAIN ANALYZE
INSERT INTO tmp.derived (id, name, referenced_id)
VALUES (2, 'derived 2', 1); 

INSERT INTO tmp.derived (id, name, referenced_id)
VALUES (3, 'derived 3', 1);

-- Delete from base - deletes the "base" and "derived" rows, but not "referenced"
--DELETE FROM base
--WHERE id = 2;

-- Delete from derived - fails with:
-- update or delete on table "referenced" violates foreign key constraint "fk_derived_referenced" on table "derived"

EXPLAIN ANALYZE
DELETE FROM tmp.derived
WHERE id = 2
    ;

SELECT * FROM tmp.base;
SELECT * FROM tmp.derived;
SELECT * FROM tmp.referenced;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜