INSERT Trigger to check this out.
Assuming the following table structure
CREATE TABLE event ( id bigserial PRIMARY KEY, foo varchar ); CREATE TABLE event_deps ( parent bigint REFERENCES event(id), child bigint REFERENCES event(id), PRIMARY KEY (parent, child), CHECK (parent <> child) );
The following INSERT trigger is required
CREATE FUNCTION deps_insert_trigger_func() RETURNS trigger AS $BODY$ DECLARE results bigint; BEGIN WITH RECURSIVE p(id) AS ( SELECT parent FROM event_deps WHERE child=NEW.parent UNION SELECT parent FROM p, event_deps d WHERE p.id = d.child ) SELECT * INTO results FROM p WHERE id=NEW.child; IF FOUND THEN RAISE EXCEPTION 'Circular dependencies are not allowed.'; END IF; RETURN NEW; END; $BODY$ LANGUAGE plpgsql; CREATE TRIGGER before_insert_event_deps_trg BEFORE INSERT ON event_deps FOR EACH ROW EXECUTE PROCEDURE deps_insert_trigger_func();
What he does when a new link is added between parent A and child B, he uses the query A WITH RECURSIVE to find all the ancestors of A. B should not be one of them.
The UPDATE trigger is more complicated, because when you start the trigger on the old link, you still have one, so the test from the INSERT trigger can give a false positive value when used for UPDATE.
So, for UPDATE we need to add additional conditions to hide the old data.
CREATE FUNCTION deps_update_trigger_func() RETURNS trigger AS $BODY$ DECLARE results bigint; BEGIN WITH RECURSIVE p(id) AS ( SELECT parent FROM event_deps WHERE child=NEW.parent AND NOT (child = OLD.child AND parent = OLD.parent)
Eelke source share