これを確認するには、トリガーを挿入します。
次のテーブル構造を想定
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)
);
次のINSERTトリガーが必要になります
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();
親Aと子Bの間に新しいリンクが追加されると、AWITHRECURSIVEクエリを使用してAのすべての祖先が検索されます。Bはそれらの1つであってはなりません。
古いリンクに対してトリガーが実行されたときにまだそこにあるため、UPDATEトリガーを使用すると、INSERTトリガーからのテストで誤検知が発生する可能性があるため、UPDATEトリガーはより困難になります。
したがって、UPDATEの場合、古いデータを非表示にするための条件を追加する必要があります。
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) -- hide old row
UNION
SELECT parent
FROM p, event_deps d
WHERE p.id = d.child
AND NOT (child = OLD.child AND parent = OLD.parent) -- hide old row
)
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_update_event_deps_trg BEFORE UPDATE ON event_deps
FOR EACH ROW
EXECUTE PROCEDURE deps_update_trigger_func();