BEGIN; ALTER TABLE app_user ADD COLUMN IF NOT EXISTS is_super_admin boolean NOT NULL DEFAULT false; CREATE INDEX IF NOT EXISTS idx_app_user_is_super_admin ON app_user (is_super_admin); CREATE TABLE IF NOT EXISTS admin_audit_log ( id bigserial PRIMARY KEY, ts timestamptz NOT NULL DEFAULT now(), actor_user_hash text NOT NULL, target_user_hash text NOT NULL, target_username_hash text, action text NOT NULL, meta jsonb ); CREATE OR REPLACE FUNCTION prevent_super_admin_delete() RETURNS trigger AS $$ BEGIN IF OLD.is_super_admin THEN RAISE EXCEPTION 'cannot delete super admin user'; END IF; RETURN OLD; END; $$ LANGUAGE plpgsql; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_prevent_super_admin_delete') THEN CREATE TRIGGER trg_prevent_super_admin_delete BEFORE DELETE ON app_user FOR EACH ROW EXECUTE FUNCTION prevent_super_admin_delete(); END IF; END $$; DO $$ DECLARE tbl text; c record; fk_name text; BEGIN FOREACH tbl IN ARRAY ARRAY[ 'app_session', 'user_broker', 'zerodha_session', 'zerodha_request_token', 'strategy_run', 'strategy_config', 'strategy_log', 'engine_status', 'engine_state', 'engine_state_paper', 'engine_event', 'paper_broker_account', 'paper_position', 'paper_order', 'paper_trade', 'paper_equity_curve', 'mtm_ledger', 'event_ledger' ] LOOP IF to_regclass(tbl) IS NOT NULL THEN FOR c IN SELECT conname FROM pg_constraint WHERE contype = 'f' AND conrelid = tbl::regclass AND confrelid = 'app_user'::regclass LOOP EXECUTE format('ALTER TABLE %I DROP CONSTRAINT %I', tbl, c.conname); END LOOP; fk_name := format('fk_%s_user', tbl); IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = fk_name) THEN EXECUTE format( 'ALTER TABLE %I ADD CONSTRAINT %I FOREIGN KEY (user_id) REFERENCES app_user(id) ON DELETE CASCADE', tbl, fk_name ); END IF; END IF; END LOOP; END $$; COMMIT;