- 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
80 lines
5.1 KiB
SQL
80 lines
5.1 KiB
SQL
-- 07_triggers_meta_grants.sql: updated_at triggers everywhere, register core tables in meta, grants.
|
|
SET ROLE personal;
|
|
|
|
-- 1) updated_at trigger on every table that has the column but no trigger (27 pre-existing + new ones)
|
|
DO $$
|
|
DECLARE r record; n integer := 0;
|
|
BEGIN
|
|
FOR r IN
|
|
SELECT n.nspname AS s, c.relname AS t
|
|
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 IN ('auth','money','house','ref','games','cs','animaldex','site','hardware','energy')
|
|
AND NOT EXISTS (SELECT 1 FROM pg_trigger tg WHERE tg.tgrelid = c.oid AND NOT tg.tgisinternal
|
|
AND tg.tgfoid = 'meta.touch_updated_at()'::regprocedure)
|
|
LOOP
|
|
EXECUTE format('CREATE TRIGGER touch_updated_at BEFORE UPDATE ON %I.%I FOR EACH ROW EXECUTE FUNCTION meta.touch_updated_at()', r.s, r.t);
|
|
n := n + 1;
|
|
END LOOP;
|
|
RAISE NOTICE 'added updated_at triggers: %', n;
|
|
END $$;
|
|
-- the 16 old triggers were all named site_touch_updated_at; normalise the name
|
|
DO $$
|
|
DECLARE r record;
|
|
BEGIN
|
|
FOR r IN SELECT tg.tgname, tg.tgrelid::regclass AS rel FROM pg_trigger tg WHERE tg.tgname = 'site_touch_updated_at' LOOP
|
|
EXECUTE format('ALTER TRIGGER site_touch_updated_at ON %s RENAME TO touch_updated_at', r.rel);
|
|
END LOOP;
|
|
END $$;
|
|
RESET ROLE;
|
|
|
|
-- 2) register core + ref tables in meta (GUI visibility); auth + bookkeeping are not exposed
|
|
INSERT INTO meta.tables (schema_name, table_name, slug, display_name, kind, editable, has_done, sort_order)
|
|
SELECT n.nspname, c.relname,
|
|
replace(n.nspname || '-' || c.relname, '_', '-'),
|
|
initcap(replace(c.relname, '_', ' ')),
|
|
CASE WHEN n.nspname = 'ref' THEN 'ref' ELSE 'core' END,
|
|
n.nspname <> 'ref', false, 1000
|
|
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
WHERE c.relkind IN ('r','v')
|
|
AND n.nspname IN ('money','house','ref','games','cs','animaldex','site','hardware','energy')
|
|
ON CONFLICT DO NOTHING;
|
|
UPDATE meta.tables SET editable = false WHERE kind = 'core' AND (schema_name, table_name) IN
|
|
(SELECT n.nspname, c.relname FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = 'v');
|
|
SELECT meta.sync();
|
|
|
|
-- 3) grants
|
|
GRANT USAGE ON SCHEMA auth, money, house, ref, games, cs, animaldex, site, meta, hardware, energy, lists TO personal_app, personal_ro;
|
|
-- app: full DML on its own data, read-only on ref
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA auth, money, house, games, cs, animaldex, site, hardware, energy, lists TO personal_app;
|
|
GRANT SELECT ON ALL TABLES IN SCHEMA ref, meta TO personal_app;
|
|
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA auth, money, house, games, cs, animaldex, site, hardware, energy, lists TO personal_app;
|
|
-- app may edit presentation fields of the registry directly
|
|
GRANT UPDATE (slug, display_name, description, icon, picture_url, default_sort, sort_order) ON meta.tables TO personal_app; -- slug: URL only, table name stays
|
|
GRANT UPDATE (label, ui_type, options, position, hidden, help_text) ON meta.columns TO personal_app;
|
|
GRANT EXECUTE ON FUNCTION meta.create_list(text, text, text, boolean, jsonb, text, text, text, integer),
|
|
meta.add_column(text, jsonb), meta.rename_column(text, text, text), meta.drop_column(text, text),
|
|
meta.archive_list(text), meta.drop_list(text), meta.sync(integer) TO personal_app;
|
|
-- ro: everything readable
|
|
GRANT SELECT ON ALL TABLES IN SCHEMA auth, money, house, ref, games, cs, animaldex, site, meta, hardware, energy, lists TO personal_ro;
|
|
-- future objects
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE personal IN SCHEMA auth, money, house, games, cs, animaldex, site, hardware, energy
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO personal_app;
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE personal IN SCHEMA auth, money, house, games, cs, animaldex, site, hardware, energy
|
|
GRANT USAGE, SELECT ON SEQUENCES TO personal_app;
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE personal IN SCHEMA ref, meta GRANT SELECT ON TABLES TO personal_app;
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE personal IN SCHEMA auth, money, house, ref, games, cs, animaldex, site, meta, hardware, energy
|
|
GRANT SELECT ON TABLES TO personal_ro;
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE personal_ddl IN SCHEMA lists GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO personal_app;
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE personal_ddl IN SCHEMA lists GRANT USAGE, SELECT ON SEQUENCES TO personal_app;
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE personal_ddl IN SCHEMA lists GRANT SELECT ON TABLES TO personal_ro;
|
|
-- migration owner can read the runtime lists, so deploy backups (pg_dump as `personal`) cover everything
|
|
GRANT USAGE ON SCHEMA lists TO personal;
|
|
GRANT SELECT ON ALL TABLES IN SCHEMA lists TO personal;
|
|
GRANT SELECT ON ALL SEQUENCES IN SCHEMA lists TO personal;
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE personal_ddl IN SCHEMA lists GRANT SELECT ON TABLES TO personal;
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE personal_ddl IN SCHEMA lists GRANT SELECT ON SEQUENCES TO personal;
|
|
-- nobody creates in public anymore
|
|
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
|