- pgSchema tables for auth, money, house, ref, games, cs, animaldex, site, meta (lists excluded via schemaFilter; runtime list tables are managed by meta.*) - drizzle/ baseline matches the reorged DB exactly (catalog diff = 0 on rehearsal) - db/reorg: one-time SQL that moves the flat public schema (rehearsed on a prod copy) - scripts/db-migrate.ts: migrator with --init to record the baseline and a guard against running the baseline on a non-empty, unrecorded database
69 lines
4.1 KiB
SQL
69 lines
4.1 KiB
SQL
-- 08_verify.sql: hard assertions; any failure aborts the whole transaction.
|
|
DO $$
|
|
DECLARE r record; a bigint; bad text := '';
|
|
BEGIN
|
|
-- every moved table keeps its exact row count
|
|
-- expected = prod count before + rows imported from dev (cs.* only; prod cs was empty)
|
|
FOR r IN SELECT m.*, b.n + coalesce(i.n, 0) AS n FROM legacy.reorg_table_map m
|
|
JOIN legacy.reorg_counts_before b ON b.table_name = m.old_name
|
|
LEFT JOIN legacy.reorg_imports i ON i.dst = m.new_schema || '.' || m.new_name LOOP
|
|
EXECUTE format('SELECT count(*) FROM %I.%I', r.new_schema, r.new_name) INTO a;
|
|
IF a <> r.n THEN bad := bad || format('%s.%s %s<>%s; ', r.new_schema, r.new_name, a, r.n); END IF;
|
|
END LOOP;
|
|
IF bad <> '' THEN RAISE EXCEPTION 'row count mismatch: %', bad; END IF;
|
|
IF (SELECT count(*) FROM legacy.reorg_counts_before) <> 58 THEN RAISE EXCEPTION 'expected 58 source tables'; END IF;
|
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public') THEN RAISE EXCEPTION 'public not empty'; END IF;
|
|
END $$;
|
|
|
|
-- lists: every legacy item is in exactly one lists.* table, except the 4 merged duplicates; done count preserved
|
|
DO $$
|
|
DECLARE t record; n bigint; total bigint := 0; done bigint := 0; d bigint;
|
|
dropped bigint := (SELECT count(*) FROM legacy.reorg_list_fixes WHERE action = 'dedupe-drop');
|
|
BEGIN
|
|
FOR t IN SELECT * FROM meta.tables WHERE kind = 'list' LOOP
|
|
EXECUTE format('SELECT count(*) FROM lists.%I', t.table_name) INTO n;
|
|
total := total + n;
|
|
IF t.has_done THEN EXECUTE format('SELECT count(*) FROM lists.%I WHERE done_at IS NOT NULL', t.table_name) INTO d; done := done + d; END IF;
|
|
END LOOP;
|
|
IF total + dropped <> (SELECT count(*) FROM legacy.list_items) THEN
|
|
RAISE EXCEPTION 'lists total % + % dropped <> legacy %', total, dropped, (SELECT count(*) FROM legacy.list_items); END IF;
|
|
-- expected done = legacy completions minus dedupe pairs where BOTH copies were done (they collapse to one)
|
|
d := (SELECT count(*) FROM legacy.list_item_completions)
|
|
- (SELECT count(*) FROM dedupe x
|
|
WHERE EXISTS (SELECT 1 FROM legacy.list_item_completions c WHERE c.item_id = x.keep_id)
|
|
AND EXISTS (SELECT 1 FROM legacy.list_item_completions c WHERE c.item_id = x.drop_id));
|
|
IF done <> d THEN RAISE EXCEPTION 'done count % <> expected %', done, d; END IF;
|
|
IF (SELECT count(*) FROM meta.tables WHERE kind = 'list') <> (SELECT count(*) FROM legacy.lists) THEN
|
|
RAISE EXCEPTION 'list count mismatch'; END IF;
|
|
RAISE NOTICE 'lists OK: % rows (+% merged dupes), % done', total, dropped, done;
|
|
END $$;
|
|
|
|
-- every table with updated_at has the trigger
|
|
DO $$
|
|
DECLARE missing text;
|
|
BEGIN
|
|
SELECT string_agg(n.nspname || '.' || c.relname, ', ') INTO missing
|
|
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
JOIN pg_attribute a ON a.attrelid = c.oid AND a.attname = 'updated_at' AND NOT a.attisdropped
|
|
WHERE c.relkind = 'r' AND n.nspname NOT IN ('legacy','pg_catalog','information_schema') AND n.nspname !~ '^pg_'
|
|
AND NOT EXISTS (SELECT 1 FROM pg_trigger tg WHERE tg.tgrelid = c.oid AND tg.tgfoid = 'meta.touch_updated_at()'::regprocedure);
|
|
IF missing IS NOT NULL THEN RAISE EXCEPTION 'missing updated_at trigger: %', missing; END IF;
|
|
-- every table has a PK
|
|
SELECT string_agg(n.nspname || '.' || c.relname, ', ') INTO missing
|
|
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
WHERE c.relkind = 'r' AND n.nspname NOT IN ('legacy','pg_catalog','information_schema') AND n.nspname !~ '^pg_'
|
|
AND NOT EXISTS (SELECT 1 FROM pg_constraint k WHERE k.conrelid = c.oid AND k.contype = 'p');
|
|
IF missing IS NOT NULL THEN RAISE EXCEPTION 'tables without PK: %', missing; END IF;
|
|
END $$;
|
|
|
|
-- list rows keep their original timestamps
|
|
DO $$
|
|
DECLARE t record; n bigint; bad bigint := 0;
|
|
BEGIN
|
|
FOR t IN SELECT table_name FROM meta.tables WHERE kind = 'list' LOOP
|
|
EXECUTE format('SELECT count(*) FROM lists.%I x JOIN legacy.list_items i ON i.id = x.id WHERE x.updated_at <> i.updated_at OR x.created_at <> i.created_at', t.table_name) INTO n;
|
|
bad := bad + n;
|
|
END LOOP;
|
|
IF bad > 0 THEN RAISE EXCEPTION '% list rows lost their original timestamps', bad; END IF;
|
|
END $$;
|