60 lines
2.7 KiB
PL/PgSQL
60 lines
2.7 KiB
PL/PgSQL
-- ── Migration 002: Artboard Ancestry Guard ───────────────────────────────────
|
|
-- The artboard_ancestry closure table and its maintenance trigger are already
|
|
-- created in migration 001 (initial schema). This migration is a safe, idempotent
|
|
-- guard that ensures the trigger function and trigger exist as expected.
|
|
--
|
|
-- IMPORTANT: An earlier draft of this file attempted to create a MATERIALIZED
|
|
-- VIEW named artboard_ancestry, which would conflict with the table created in
|
|
-- 001. That approach has been superseded: the trigger-based closure table from
|
|
-- 001 is maintained incrementally (O(depth) per INSERT) which is far cheaper
|
|
-- than a full REFRESH MATERIALIZED VIEW CONCURRENTLY on every artboard insert.
|
|
-- Do NOT re-introduce the materialized view approach.
|
|
|
|
-- ── Ensure the ancestry maintenance trigger function exists ───────────────────
|
|
-- Uses CREATE OR REPLACE so the migration is idempotent; safe to re-run.
|
|
|
|
CREATE OR REPLACE FUNCTION maintain_artboard_ancestry()
|
|
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
-- Self-row: every artboard is its own ancestor at depth 0.
|
|
INSERT INTO artboard_ancestry (artboard_id, ancestor_id, depth)
|
|
VALUES (NEW.id, NEW.id, 0)
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- Inherit all ancestors of the parent at depth + 1.
|
|
IF NEW.parent_artboard_id IS NOT NULL THEN
|
|
INSERT INTO artboard_ancestry (artboard_id, ancestor_id, depth)
|
|
SELECT NEW.id, ancestor_id, depth + 1
|
|
FROM artboard_ancestry
|
|
WHERE artboard_id = NEW.parent_artboard_id
|
|
ON CONFLICT DO NOTHING;
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
-- ── Ensure the trigger is attached ───────────────────────────────────────────
|
|
-- DROP + recreate is the idempotent way to ensure the trigger is attached
|
|
-- exactly once; IF NOT EXISTS for triggers requires PG 17+ so use this pattern.
|
|
|
|
DROP TRIGGER IF EXISTS artboards_ancestry_insert ON artboards;
|
|
|
|
CREATE TRIGGER artboards_ancestry_insert
|
|
AFTER INSERT ON artboards
|
|
FOR EACH ROW EXECUTE FUNCTION maintain_artboard_ancestry();
|
|
|
|
-- ── RPC helper: get all descendants of an artboard ───────────────────────────
|
|
-- Complements getArtboardAncestors() in origin-graph/queries.ts.
|
|
-- Returns direct + transitive descendants, ordered nearest-first.
|
|
|
|
CREATE OR REPLACE FUNCTION get_artboard_descendants(p_artboard_id UUID)
|
|
RETURNS TABLE (artboard_id UUID, depth INTEGER)
|
|
LANGUAGE SQL STABLE AS $$
|
|
SELECT artboard_id, depth
|
|
FROM artboard_ancestry
|
|
WHERE ancestor_id = p_artboard_id
|
|
AND artboard_id <> p_artboard_id -- exclude self
|
|
ORDER BY depth, artboard_id;
|
|
$$;
|