Prevent and/or detect cycles in postgres

后端 未结 4 796
刺人心
刺人心 2020-12-06 12:28

Assuming a schema like the following:

CREATE TABLE node (
  id       SERIAL PRIMARY KEY,
  name     VARCHAR,
  parentid INT REFERENCES node(id)
);

4条回答
  •  既然无缘
    2020-12-06 13:29

    To answer my own question, I came up with a trigger that prevents this:

    CREATE OR REPLACE FUNCTION detect_cycle() RETURNS TRIGGER AS
    $func$
    DECLARE
      loops INTEGER;
    BEGIN
       EXECUTE 'WITH RECURSIVE search_graph(id, parentid, name, depth, path, cycle) AS (
            SELECT g.id, g.parentid, g.name, 1,
              ARRAY[g.id],
              false
            FROM node g
          UNION ALL
            SELECT g.id, g.parentid, g.name, sg.depth + 1,
              path || g.id,
              g.id = ANY(path)
            FROM node g, search_graph sg
            WHERE g.id = sg.parentid AND NOT cycle
    )
    SELECT count(*) FROM search_graph where cycle = TRUE' INTO loops;
    IF loops > 0 THEN
      RAISE EXCEPTION 'Loop detected!';
    ELSE
      RETURN NEW;
    END IF;
    END
    $func$ LANGUAGE plpgsql;
    
    CREATE TRIGGER detect_cycle_after_update
    AFTER UPDATE ON node
    FOR EACH ROW EXECUTE PROCEDURE detect_cycle();
    

    So, if you try to create a loop, like in the question:

    UPDATE node SET parentid = 2 WHERE id = 1;
    

    You get an EXCEPTION:

    ERROR:  Loop detected!
    

提交回复
热议问题