made tiny updates
This commit is contained in:
@@ -1,46 +1,59 @@
|
||||
-- Origin Graph — Artboard Ancestry Materialized View
|
||||
-- Migration: 002
|
||||
-- Pre-computes all ancestor/descendant relationships so the app never needs
|
||||
-- recursive CTEs at query time. Updated automatically on artboards INSERT.
|
||||
-- ── 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.
|
||||
|
||||
CREATE MATERIALIZED VIEW artboard_ancestry AS
|
||||
WITH RECURSIVE ancestry(artboard_id, ancestor_id, depth) AS (
|
||||
-- Base: each artboard is at depth 0 relative to itself
|
||||
SELECT id AS artboard_id, id AS ancestor_id, 0 AS depth
|
||||
FROM artboards
|
||||
-- ── Ensure the ancestry maintenance trigger function exists ───────────────────
|
||||
-- Uses CREATE OR REPLACE so the migration is idempotent; safe to re-run.
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Recurse: walk up the parent chain
|
||||
SELECT a.id AS artboard_id, anc.ancestor_id, anc.depth + 1
|
||||
FROM artboards a
|
||||
JOIN ancestry anc ON a.parent_artboard_id = anc.artboard_id
|
||||
)
|
||||
SELECT artboard_id, ancestor_id, depth
|
||||
FROM ancestry
|
||||
WHERE artboard_id <> ancestor_id -- exclude self-reference
|
||||
ORDER BY artboard_id, depth;
|
||||
|
||||
CREATE UNIQUE INDEX artboard_ancestry_pk
|
||||
ON artboard_ancestry(artboard_id, ancestor_id);
|
||||
|
||||
CREATE INDEX artboard_ancestry_ancestor_idx
|
||||
ON artboard_ancestry(ancestor_id);
|
||||
|
||||
-- ── Refresh trigger ───────────────────────────────────────────────────────────
|
||||
-- Refreshes the materialized view concurrently whenever an artboard is
|
||||
-- inserted. CONCURRENTLY requires the unique index above — it allows reads
|
||||
-- to continue during refresh.
|
||||
|
||||
CREATE OR REPLACE FUNCTION refresh_artboard_ancestry()
|
||||
CREATE OR REPLACE FUNCTION maintain_artboard_ancestry()
|
||||
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
REFRESH MATERIALIZED VIEW CONCURRENTLY artboard_ancestry;
|
||||
RETURN NULL;
|
||||
-- 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;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER trg_artboard_ancestry_refresh
|
||||
AFTER INSERT OR UPDATE OF parent_artboard_id ON artboards
|
||||
FOR EACH STATEMENT
|
||||
EXECUTE FUNCTION refresh_artboard_ancestry();
|
||||
-- ── 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;
|
||||
$$;
|
||||
|
||||
Reference in New Issue
Block a user