From 247032325d65c314ca9be92aa09a398e978fd154 Mon Sep 17 00:00:00 2001 From: Mark Mennell Date: Thu, 22 Oct 2026 13:21:28 +0800 Subject: [PATCH] Sync DD scripts with fmsgd, fmsgid and fmsg-webapi main fmsg-webapi main now stores each address's trash (fmsg_api_trash and fmsg_api_trash_state), and every message read queries it, so the stack's copy without them answered 500 on drafts, replies, reads and events. fmsgd's copy gains the fmsg.purge escape in its delete guard that trash's delete for good relies on; fmsgid's is a comment change. Generated with scripts/update-dd.sh from each repo's main. Co-Authored-By: Claude Opus 5.5 --- docker/postgres/init/002-fmsgd-dd.sql | 5 +++ docker/postgres/init/002-fmsgid-dd.sql | 2 +- docker/postgres/init/003-fmsg-webapi-dd.sql | 34 +++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/docker/postgres/init/002-fmsgd-dd.sql b/docker/postgres/init/002-fmsgd-dd.sql index f5ded0d..0519f7c 100644 --- a/docker/postgres/init/002-fmsgd-dd.sql +++ b/docker/postgres/init/002-fmsgd-dd.sql @@ -413,6 +413,11 @@ declare message_id bigint; frozen boolean; begin + -- A message is removed from this host only on purpose: a transaction that + -- sets fmsg.purge (SET LOCAL fmsg.purge = 'on') may delete its parts, so + -- the message can then be deleted. Once its row is gone, a reply to it is + -- rejected as parent not found. + if TG_OP='DELETE' and current_setting('fmsg.purge', true) = 'on' then return OLD; end if; -- Delivery and read receipts do not change a protocol recipient. if TG_OP='UPDATE' then if TG_TABLE_NAME='msg_to' then diff --git a/docker/postgres/init/002-fmsgid-dd.sql b/docker/postgres/init/002-fmsgid-dd.sql index 193b8f1..1376c1d 100644 --- a/docker/postgres/init/002-fmsgid-dd.sql +++ b/docker/postgres/init/002-fmsgid-dd.sql @@ -59,7 +59,7 @@ create table if not exists address_tx ( id bigserial primary key, address_lower text not null references address (address_lower) , ts timestamptz not null, - type smallint not null, -- 1 for recv, 2 for send + type smallint not null, -- 1 recv, 2 send, 3 recv released, 4 send released size int not null ); diff --git a/docker/postgres/init/003-fmsg-webapi-dd.sql b/docker/postgres/init/003-fmsg-webapi-dd.sql index aa17f29..eba7b67 100644 --- a/docker/postgres/init/003-fmsg-webapi-dd.sql +++ b/docker/postgres/init/003-fmsg-webapi-dd.sql @@ -74,3 +74,37 @@ CREATE INDEX IF NOT EXISTS fmsg_api_sub_account_sub_idx CREATE UNIQUE INDEX IF NOT EXISTS fmsg_api_sub_account_owner_sub_unique ON fmsg_api_sub_account ((lower(owner_addr)), (lower(sub_addr))) WHERE agent <> ''; + +-- Trash: what each address has deleted. A row marks one message for one +-- address (lower-cased). thread marks the whole thread below that message; +-- time_purged means deleted for good: never shown to that address again, +-- nor anything that replies to it. Rows go with their message when it is +-- removed from the host. +CREATE TABLE IF NOT EXISTS fmsg_api_trash ( + addr text NOT NULL, + msg_id bigint NOT NULL REFERENCES msg (id) ON DELETE CASCADE, + thread boolean NOT NULL DEFAULT false, + time_deleted double precision NOT NULL, + time_purged double precision, + PRIMARY KEY (addr, msg_id) +); + +-- fmsg_api_trash_state is where a message stands for an address: 'purged' +-- when it or an ancestor is deleted for good, 'thread' when it or an ancestor +-- carries a thread mark, 'message' when it is deleted itself, NULL when it +-- isn't deleted. addr must be lower-cased. +CREATE OR REPLACE FUNCTION fmsg_api_trash_state(p_addr text, p_msg bigint) RETURNS text +LANGUAGE sql STABLE AS $$ + WITH RECURSIVE up(id, depth) AS ( + SELECT p_msg, 0 + UNION ALL + SELECT m.pid, up.depth + 1 FROM msg m JOIN up ON m.id = up.id + WHERE m.pid IS NOT NULL AND up.depth < 1000 + ) + SELECT CASE + WHEN bool_or(t.time_purged IS NOT NULL) THEN 'purged' + WHEN bool_or(t.thread) THEN 'thread' + WHEN bool_or(up.depth = 0) THEN 'message' + END + FROM up JOIN fmsg_api_trash t ON t.addr = p_addr AND t.msg_id = up.id +$$;