2010-10-18 10 views
20

He creado algunas tablas en postgres, he añadido una clave externa de una tabla a otra y configuré ON DELETE en CASCADE. Por extraño que parezca, tengo algunos campos que parecen estar violando esta restricción.Las claves externas en postgresql pueden ser violadas por el disparador

¿Es este comportamiento normal? Y si es así, ¿hay alguna manera de obtener el comportamiento que quiero (no es posible contravenir)?

Editar:

I orginaly creado la clave externa como parte de CREATE TABLE, simplemente usando

... REFERENCES product (id) ON UPDATE CASCADE ON DELETE CASCADE 

El pgadmin3 código actual da es

ALTER TABLE cultivar 
    ADD CONSTRAINT cultivar_id_fkey FOREIGN KEY (id) 
     REFERENCES product (id) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE CASCADE; 

Edición 2:

Para aclarar, tengo la sospecha de que las restricciones solo se verifican cuando se actualiza s/insertos suceden pero nunca se vuelven a mirar. Lamentablemente, no sé lo suficiente sobre postgres para saber si esto es cierto o cómo los campos pueden terminar en la base de datos sin que se ejecuten esos controles.

Si este es el caso, ¿hay alguna manera de verificar todas las claves foráneas y solucionar esos problemas?

Datos 3:

Una violación de la restricción puede ser causada por un gatillo defectuoso, ver más abajo

+0

Sólo para una verificación de cordura ¿puede publicar la declaración que utilizó para crear el FK? –

+0

¿En qué versión de PG estás? – Kuberchaun

+0

Estoy usando la versión 8.3 –

Respuesta

5

Todo lo que he leído hasta ahora parece sugerir que las restricciones sólo se verifican cuando se han insertado los datos. (O cuando se crea la restricción) Por ejemplo the manual on set constraints.

Esto tiene sentido y, si la base de datos funciona correctamente, debería ser lo suficientemente bueno. Todavía tengo curiosidad de cómo pude eludir esto o si acabo de leer la situación equivocada y nunca hubo una verdadera violación de restricción para empezar.

De cualquier manera, caso cerrado: -/

------- -------- ACTUALIZACIÓN

Definitivamente había una violación de restricción, causada por un gatillo defectuoso. Aquí hay una secuencia de comandos para replicar:

-- Create master table 
CREATE TABLE product 
(
    id INT NOT NULL PRIMARY KEY 
); 

-- Create second table, referencing the first 
CREATE TABLE example 
(
    id int PRIMARY KEY REFERENCES product (id) ON DELETE CASCADE 
); 

-- Create a (broken) trigger function 
--CREATE LANGUAGE plpgsql; 
CREATE OR REPLACE FUNCTION delete_product() 
    RETURNS trigger AS 
$BODY$ 
    BEGIN 
     DELETE FROM product WHERE product.id = OLD.id; 
     -- This is an error! 
     RETURN null; 
    END; 
$BODY$ 
    LANGUAGE plpgsql; 

-- Add it to the second table 
CREATE TRIGGER example_delete 
    BEFORE DELETE 
    ON example 
    FOR EACH ROW 
    EXECUTE PROCEDURE delete_product(); 

-- Now lets add a row 
INSERT INTO product (id) VALUES (1); 
INSERT INTO example (id) VALUES (1); 

-- And now lets delete the row 
DELETE FROM example WHERE id = 1; 

/* 
Now if everything is working, this should return two columns: 
(pid,eid)=(1,1). However, it returns only the example id, so 
(pid,eid)=(0,1). This means the foreign key constraint on the 
example table is violated. 
*/ 
SELECT product.id AS pid, example.id AS eid FROM product FULL JOIN example ON product.id = example.id; 
+1

Nunca hubo una violación de restricción real. – Unreason

+0

Me di cuenta de que manejaba esta situación un poco extrañamente, así que hice un esfuerzo extra y repliqué la situación que causó mis problemas iniciales. –

24

Intenté crear un ejemplo simple que muestra la restricción de clave foránea que se aplica. Con este ejemplo demuestro que no puedo ingresar datos que violen el fk y demuestro que si el fk no está en su lugar durante el inserto, y habilito el fk, la restricción fk arroja un error diciéndome que los datos violan el fk. Así que no veo cómo tienes datos en la tabla que violen un fk que está en su lugar. Estoy en 9.0, pero esto no debería ser diferente en 8.3. Si puede mostrar un ejemplo de trabajo que demuestre su problema que podría ayudar.

--CREATE TABLES-- 
CREATE TABLE parent 
(
    parent_id integer NOT NULL, 
    first_name character varying(50) NOT NULL, 
    CONSTRAINT pk_parent PRIMARY KEY (parent_id) 
) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE parent OWNER TO postgres; 

CREATE TABLE child 
(
    child_id integer NOT NULL, 
    parent_id integer NOT NULL, 
    first_name character varying(50) NOT NULL, 
    CONSTRAINT pk_child PRIMARY KEY (child_id), 
    CONSTRAINT fk1_child FOREIGN KEY (parent_id) 
     REFERENCES parent (parent_id) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE CASCADE 
) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE child OWNER TO postgres; 
--CREATE TABLES-- 

--INSERT TEST DATA-- 
INSERT INTO parent(parent_id,first_name) 
SELECT 1,'Daddy' 
UNION 
SELECT 2,'Mommy'; 

INSERT INTO child(child_id,parent_id,first_name) 
SELECT 1,1,'Billy' 
UNION 
SELECT 2,1,'Jenny' 
UNION 
SELECT 3,1,'Kimmy' 
UNION 
SELECT 4,2,'Billy' 
UNION 
SELECT 5,2,'Jenny' 
UNION 
SELECT 6,2,'Kimmy'; 
--INSERT TEST DATA-- 

--SHOW THE DATA WE HAVE-- 
select parent.first_name, 
     child.first_name 
from parent 
inner join child 
     on child.parent_id = parent.parent_id 
order by parent.first_name, child.first_name asc; 
--SHOW THE DATA WE HAVE-- 

--DELETE PARENT WHO HAS CHILDREN-- 
BEGIN TRANSACTION; 
delete from parent 
where parent_id = 1; 

--Check to see if any children that were linked to Daddy are still there? 
--None there so the cascade delete worked. 
select parent.first_name, 
     child.first_name 
from parent 
right outer join child 
     on child.parent_id = parent.parent_id 
order by parent.first_name, child.first_name asc; 
ROLLBACK TRANSACTION; 


--TRY ALLOW NO REFERENTIAL DATA IN-- 
BEGIN TRANSACTION; 

--Get rid of fk constraint so we can insert red headed step child 
ALTER TABLE child DROP CONSTRAINT fk1_child; 

INSERT INTO child(child_id,parent_id,first_name) 
SELECT 7,99999,'Red Headed Step Child'; 

select parent.first_name, 
     child.first_name 
from parent 
right outer join child 
     on child.parent_id = parent.parent_id 
order by parent.first_name, child.first_name asc; 

--Will throw FK check violation because parent 99999 doesn't exist in parent table 
ALTER TABLE child 
    ADD CONSTRAINT fk1_child FOREIGN KEY (parent_id) 
     REFERENCES parent (parent_id) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE CASCADE; 

ROLLBACK TRANSACTION; 
--TRY ALLOW NO REFERENTIAL DATA IN-- 

--DROP TABLE parent; 
--DROP TABLE child; 
Cuestiones relacionadas