From 6f05254aaec5d8d048480874989546c27a564730 Mon Sep 17 00:00:00 2001 From: dullbananas Date: Tue, 21 Jan 2025 07:52:18 -0700 Subject: [PATCH] Prevent incorrectly using delete instead of uplete (#5331) * implement check and test it by modifying PostLike::remove * revert change in PostLike::remove --- .../db_schema/replaceable_schema/triggers.sql | 38 +++++++++++++++++++ crates/db_schema/src/utils/uplete.rs | 5 ++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/crates/db_schema/replaceable_schema/triggers.sql b/crates/db_schema/replaceable_schema/triggers.sql index c0cbfab34..ad61f0485 100644 --- a/crates/db_schema/replaceable_schema/triggers.sql +++ b/crates/db_schema/replaceable_schema/triggers.sql @@ -889,3 +889,41 @@ CALL r.create_inbox_combined_trigger ('person_post_mention'); CALL r.create_inbox_combined_trigger ('private_message'); +-- Prevent using delete instead of uplete on action tables +CREATE FUNCTION r.require_uplete () + RETURNS TRIGGER + LANGUAGE plpgsql + AS $$ +BEGIN + IF pg_trigger_depth() = 1 AND NOT starts_with (current_query(), '/**/') THEN + RAISE 'using delete instead of uplete is not allowed for this table'; + END IF; + RETURN NULL; +END +$$; + +CREATE TRIGGER require_uplete + BEFORE DELETE ON comment_actions + FOR EACH STATEMENT + EXECUTE FUNCTION r.require_uplete (); + +CREATE TRIGGER require_uplete + BEFORE DELETE ON community_actions + FOR EACH STATEMENT + EXECUTE FUNCTION r.require_uplete (); + +CREATE TRIGGER require_uplete + BEFORE DELETE ON instance_actions + FOR EACH STATEMENT + EXECUTE FUNCTION r.require_uplete (); + +CREATE TRIGGER require_uplete + BEFORE DELETE ON person_actions + FOR EACH STATEMENT + EXECUTE FUNCTION r.require_uplete (); + +CREATE TRIGGER require_uplete + BEFORE DELETE ON post_actions + FOR EACH STATEMENT + EXECUTE FUNCTION r.require_uplete (); + diff --git a/crates/db_schema/src/utils/uplete.rs b/crates/db_schema/src/utils/uplete.rs index 8c5262b90..dddbfe8ea 100644 --- a/crates/db_schema/src/utils/uplete.rs +++ b/crates/db_schema/src/utils/uplete.rs @@ -121,6 +121,9 @@ impl QueryFragment for UpleteQuery { fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> Result<(), Error> { assert_ne!(self.set_null_columns.len(), 0, "`set_null` was not called"); + // This is checked by require_uplete triggers + out.push_sql("/**/"); + // Declare `update_keys` and `delete_keys` CTEs, which select primary keys for (prefix, subquery) in [ ("WITH update_keys", &self.update_subquery), @@ -357,7 +360,7 @@ mod tests { let update_count = "SELECT count(*) FROM update_result"; let delete_count = "SELECT count(*) FROM delete_result"; - format!(r#"WITH {with_queries} SELECT ({update_count}), ({delete_count}) -- binds: []"#) + format!(r#"/**/WITH {with_queries} SELECT ({update_count}), ({delete_count}) -- binds: []"#) } #[test]