mirror of
https://github.com/block/buzz.git
synced 2026-10-28 13:01:10 +00:00
### Why?
This is a cleanup that brings the schema bootstrap in line with how the
codebase works today, as asked for by
[`docs/database.md`](dc25198d53/docs/database.md (L103-L124))
(#8273). Writes are now blocked during community deletion by application
admission (`begin_community_write_transaction`), and that doc's Rule 1
says new tables must not add triggers. But
`schema/community_write_fence.sql` still looped over every table with a
`community_id` column and attached the old fence trigger to it. That
meant a new table got a trigger on a fresh database and none on a
migrated one, so the two schema paths disagreed and new tenant tables
were blocked. The doc says this bootstrap change must land "before the
feature, or as the feature's first PR."
### What?
Deletes the blanket fence loop and its `\i` from `schema/schema.sql`.
Existing legacy tables keep their fences through their explicit
`schema/triggers/` files, so no current table loses its fence and
runtime behavior is unchanged. Adds a test that a new `community_id`
table stays unfenced while `events` and `users` stay fenced, and updates
`docs/database.md` and `ARCHITECTURE.md` to match. It stands on its own;
the media-sharing PRs (#8451, #8444) are the first to rely on it.
buzz-review-completed
---------
Signed-off-by: Nikolaj Goodger <ngoodger@squareup.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
46 lines
1.6 KiB
PL/PgSQL
46 lines
1.6 KiB
PL/PgSQL
-- Attach the legacy universal fence to one community-scoped relation. Only
|
|
-- tables fenced before fence retirement use it; new tenant tables rely on
|
|
-- application admission and must not call it (docs/database.md Rule 1).
|
|
CREATE FUNCTION attach_community_write_fence(target REGCLASS) RETURNS VOID
|
|
LANGUAGE plpgsql AS $$
|
|
DECLARE
|
|
relation_name NAME;
|
|
BEGIN
|
|
SELECT c.relname
|
|
INTO relation_name
|
|
FROM pg_class c
|
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
WHERE c.oid = target
|
|
AND n.nspname = current_schema()
|
|
AND c.relkind IN ('r', 'p')
|
|
AND NOT c.relispartition;
|
|
IF NOT FOUND THEN
|
|
RAISE EXCEPTION 'community write fence target % is not a table in the current schema', target
|
|
USING ERRCODE = 'wrong_object_type';
|
|
END IF;
|
|
IF community_write_fence_excluded_table(relation_name) THEN
|
|
RETURN;
|
|
END IF;
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_attribute
|
|
WHERE attrelid = target AND attname = 'community_id' AND NOT attisdropped
|
|
) THEN
|
|
RAISE EXCEPTION 'community write fence target % has no community_id', target
|
|
USING ERRCODE = 'undefined_column';
|
|
END IF;
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_trigger
|
|
WHERE tgrelid = target
|
|
AND tgname = 'community_write_fence_' || relation_name
|
|
AND NOT tgisinternal
|
|
) THEN
|
|
EXECUTE format(
|
|
'CREATE TRIGGER %I BEFORE INSERT OR UPDATE OR DELETE ON %s '
|
|
'FOR EACH ROW EXECUTE FUNCTION enforce_community_write_fence()',
|
|
'community_write_fence_' || relation_name,
|
|
target
|
|
);
|
|
END IF;
|
|
END
|
|
$$;
|