Prevent and/or detect cycles in postgres

后端 未结 4 797
刺人心
刺人心 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:25

    CREATE OR REPLACE FUNCTION detect_cycle()
      RETURNS TRIGGER AS
    $func$
    DECLARE
      cycle int[];
    BEGIN
    EXECUTE format('WITH RECURSIVE search_graph(%4$I, path, cycle) AS (
      SELECT g.%4$I, ARRAY[g.%3$I, g.%4$I], (g.%3$I = g.%4$I)
        FROM %1$I.%2$I g
       WHERE g.%3$I = $1.%3$I
      UNION ALL
      SELECT g.%4$I, sg.path || g.%4$I, g.%4$I = ANY(sg.path)
        FROM search_graph  sg
        JOIN %1$I.%2$I g ON g.%3$I = sg.%4$I
       WHERE NOT sg.cycle)
    SELECT path
      FROM search_graph
     WHERE cycle
     LIMIT 1', TG_TABLE_SCHEMA, TG_TABLE_NAME, quote_ident(TG_ARGV[0]), quote_ident(TG_ARGV[1]))
    INTO cycle
    USING NEW;
    IF cycle IS NULL
    THEN
      RETURN NEW;
    ELSE
       RAISE EXCEPTION 'Loop in %.% detected: %', TG_TABLE_SCHEMA, TG_TABLE_NAME, array_to_string(cycle, ' -> ');
    END IF;
    
    END
    $func$ LANGUAGE plpgsql;
    
    CREATE TRIGGER detect_cycle_after_update
     AFTER INSERT OR UPDATE ON node
       FOR EACH ROW EXECUTE PROCEDURE detect_cycle('id', 'parent_id');
    

提交回复
热议问题