feat: init
This commit is contained in:
@@ -0,0 +1,667 @@
|
||||
-- ============================================================
|
||||
-- Migration: 002_triggers.sql
|
||||
-- Automated: updated_at, deleted_at, event recording, audit, security, stats
|
||||
--
|
||||
-- Usage:
|
||||
-- Application sets session variable before writes:
|
||||
-- SET LOCAL app.current_user_id = '00000000-0000-0000-0000-000000000001';
|
||||
-- Triggers read it for actor_id in events / audit / security logs.
|
||||
-- ============================================================
|
||||
|
||||
-- ============================================================
|
||||
-- 1. set_updated_at — auto-refresh updated_at on any UPDATE
|
||||
-- Applied to ALL tables with an updated_at column.
|
||||
-- ============================================================
|
||||
CREATE OR REPLACE FUNCTION set_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Tables with updated_at (generated from models/)
|
||||
DROP TRIGGER IF EXISTS trg_agent_updated_at ON agent;
|
||||
CREATE TRIGGER trg_agent_updated_at BEFORE UPDATE ON agent FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_event_subscription_updated_at ON agent_event_subscription;
|
||||
CREATE TRIGGER trg_agent_event_subscription_updated_at BEFORE UPDATE ON agent_event_subscription FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_execution_updated_at ON agent_execution;
|
||||
CREATE TRIGGER trg_agent_execution_updated_at BEFORE UPDATE ON agent_execution FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_feedback_updated_at ON agent_feedback;
|
||||
CREATE TRIGGER trg_agent_feedback_updated_at BEFORE UPDATE ON agent_feedback FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_schedule_updated_at ON agent_schedule;
|
||||
CREATE TRIGGER trg_agent_schedule_updated_at BEFORE UPDATE ON agent_schedule FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_version_updated_at ON agent_version;
|
||||
CREATE TRIGGER trg_agent_version_updated_at BEFORE UPDATE ON agent_version FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_workspace_binding_updated_at ON agent_workspace_binding;
|
||||
CREATE TRIGGER trg_agent_workspace_binding_updated_at BEFORE UPDATE ON agent_workspace_binding FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_ai_model_updated_at ON ai_model;
|
||||
CREATE TRIGGER trg_ai_model_updated_at BEFORE UPDATE ON ai_model FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_ai_model_capability_updated_at ON ai_model_capability;
|
||||
CREATE TRIGGER trg_ai_model_capability_updated_at BEFORE UPDATE ON ai_model_capability FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_ai_model_version_updated_at ON ai_model_version;
|
||||
CREATE TRIGGER trg_ai_model_version_updated_at BEFORE UPDATE ON ai_model_version FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_updated_at ON channel;
|
||||
CREATE TRIGGER trg_channel_updated_at BEFORE UPDATE ON channel FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_member_updated_at ON channel_member;
|
||||
CREATE TRIGGER trg_channel_member_updated_at BEFORE UPDATE ON channel_member FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_member_role_updated_at ON channel_member_role;
|
||||
CREATE TRIGGER trg_channel_member_role_updated_at BEFORE UPDATE ON channel_member_role FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_repo_link_updated_at ON channel_repo_link;
|
||||
CREATE TRIGGER trg_channel_repo_link_updated_at BEFORE UPDATE ON channel_repo_link FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_slash_command_updated_at ON channel_slash_command;
|
||||
CREATE TRIGGER trg_channel_slash_command_updated_at BEFORE UPDATE ON channel_slash_command FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_stats_updated_at ON channel_stats;
|
||||
CREATE TRIGGER trg_channel_stats_updated_at BEFORE UPDATE ON channel_stats FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_webhook_updated_at ON channel_webhook;
|
||||
CREATE TRIGGER trg_channel_webhook_updated_at BEFORE UPDATE ON channel_webhook FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_conversation_updated_at ON conversation;
|
||||
CREATE TRIGGER trg_conversation_updated_at BEFORE UPDATE ON conversation FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_conversation_bookmark_updated_at ON conversation_bookmark;
|
||||
CREATE TRIGGER trg_conversation_bookmark_updated_at BEFORE UPDATE ON conversation_bookmark FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_conversation_message_updated_at ON conversation_message;
|
||||
CREATE TRIGGER trg_conversation_message_updated_at BEFORE UPDATE ON conversation_message FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_conversation_participant_updated_at ON conversation_participant;
|
||||
CREATE TRIGGER trg_conversation_participant_updated_at BEFORE UPDATE ON conversation_participant FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_conversation_summary_updated_at ON conversation_summary;
|
||||
CREATE TRIGGER trg_conversation_summary_updated_at BEFORE UPDATE ON conversation_summary FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_conversation_tool_call_updated_at ON conversation_tool_call;
|
||||
CREATE TRIGGER trg_conversation_tool_call_updated_at BEFORE UPDATE ON conversation_tool_call FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_updated_at ON issue;
|
||||
CREATE TRIGGER trg_issue_updated_at BEFORE UPDATE ON issue FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_comment_updated_at ON issue_comment;
|
||||
CREATE TRIGGER trg_issue_comment_updated_at BEFORE UPDATE ON issue_comment FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_label_updated_at ON issue_label;
|
||||
CREATE TRIGGER trg_issue_label_updated_at BEFORE UPDATE ON issue_label FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_milestone_updated_at ON issue_milestone;
|
||||
CREATE TRIGGER trg_issue_milestone_updated_at BEFORE UPDATE ON issue_milestone FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_reminder_updated_at ON issue_reminder;
|
||||
CREATE TRIGGER trg_issue_reminder_updated_at BEFORE UPDATE ON issue_reminder FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_stats_updated_at ON issue_stats;
|
||||
CREATE TRIGGER trg_issue_stats_updated_at BEFORE UPDATE ON issue_stats FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_subscriber_updated_at ON issue_subscriber;
|
||||
CREATE TRIGGER trg_issue_subscriber_updated_at BEFORE UPDATE ON issue_subscriber FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_template_updated_at ON issue_template;
|
||||
CREATE TRIGGER trg_issue_template_updated_at BEFORE UPDATE ON issue_template FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_message_updated_at ON message;
|
||||
CREATE TRIGGER trg_message_updated_at BEFORE UPDATE ON message FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_message_bookmark_updated_at ON message_bookmark;
|
||||
CREATE TRIGGER trg_message_bookmark_updated_at BEFORE UPDATE ON message_bookmark FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_message_thread_updated_at ON message_thread;
|
||||
CREATE TRIGGER trg_message_thread_updated_at BEFORE UPDATE ON message_thread FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_notification_updated_at ON notification;
|
||||
CREATE TRIGGER trg_notification_updated_at BEFORE UPDATE ON notification FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_notification_block_updated_at ON notification_block;
|
||||
CREATE TRIGGER trg_notification_block_updated_at BEFORE UPDATE ON notification_block FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_notification_delivery_updated_at ON notification_delivery;
|
||||
CREATE TRIGGER trg_notification_delivery_updated_at BEFORE UPDATE ON notification_delivery FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_notification_subscription_updated_at ON notification_subscription;
|
||||
CREATE TRIGGER trg_notification_subscription_updated_at BEFORE UPDATE ON notification_subscription FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_notification_template_updated_at ON notification_template;
|
||||
CREATE TRIGGER trg_notification_template_updated_at BEFORE UPDATE ON notification_template FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_pr_check_run_updated_at ON pr_check_run;
|
||||
CREATE TRIGGER trg_pr_check_run_updated_at BEFORE UPDATE ON pr_check_run FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_pr_file_updated_at ON pr_file;
|
||||
CREATE TRIGGER trg_pr_file_updated_at BEFORE UPDATE ON pr_file FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_pr_label_updated_at ON pr_label;
|
||||
CREATE TRIGGER trg_pr_label_updated_at BEFORE UPDATE ON pr_label FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_pr_merge_strategy_updated_at ON pr_merge_strategy;
|
||||
CREATE TRIGGER trg_pr_merge_strategy_updated_at BEFORE UPDATE ON pr_merge_strategy FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_pr_status_updated_at ON pr_status;
|
||||
CREATE TRIGGER trg_pr_status_updated_at BEFORE UPDATE ON pr_status FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_pr_subscription_updated_at ON pr_subscription;
|
||||
CREATE TRIGGER trg_pr_subscription_updated_at BEFORE UPDATE ON pr_subscription FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_pull_request_updated_at ON pull_request;
|
||||
CREATE TRIGGER trg_pull_request_updated_at BEFORE UPDATE ON pull_request FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_updated_at ON repo;
|
||||
CREATE TRIGGER trg_repo_updated_at BEFORE UPDATE ON repo FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_branch_updated_at ON repo_branch;
|
||||
CREATE TRIGGER trg_repo_branch_updated_at BEFORE UPDATE ON repo_branch FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_commit_comment_updated_at ON repo_commit_comment;
|
||||
CREATE TRIGGER trg_repo_commit_comment_updated_at BEFORE UPDATE ON repo_commit_comment FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_commit_status_updated_at ON repo_commit_status;
|
||||
CREATE TRIGGER trg_repo_commit_status_updated_at BEFORE UPDATE ON repo_commit_status FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_deploy_key_updated_at ON repo_deploy_key;
|
||||
CREATE TRIGGER trg_repo_deploy_key_updated_at BEFORE UPDATE ON repo_deploy_key FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_member_updated_at ON repo_member;
|
||||
CREATE TRIGGER trg_repo_member_updated_at BEFORE UPDATE ON repo_member FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_push_lock_updated_at ON repo_push_lock;
|
||||
CREATE TRIGGER trg_repo_push_lock_updated_at BEFORE UPDATE ON repo_push_lock FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_release_updated_at ON repo_release;
|
||||
CREATE TRIGGER trg_repo_release_updated_at BEFORE UPDATE ON repo_release FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_stats_updated_at ON repo_stats;
|
||||
CREATE TRIGGER trg_repo_stats_updated_at BEFORE UPDATE ON repo_stats FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_watch_updated_at ON repo_watch;
|
||||
CREATE TRIGGER trg_repo_watch_updated_at BEFORE UPDATE ON repo_watch FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_webhook_updated_at ON repo_webhook;
|
||||
CREATE TRIGGER trg_repo_webhook_updated_at BEFORE UPDATE ON repo_webhook FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_updated_at ON "user";
|
||||
CREATE TRIGGER trg_user_updated_at BEFORE UPDATE ON "user" FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_appearance_updated_at ON user_appearance;
|
||||
CREATE TRIGGER trg_user_appearance_updated_at BEFORE UPDATE ON user_appearance FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_device_updated_at ON user_device;
|
||||
CREATE TRIGGER trg_user_device_updated_at BEFORE UPDATE ON user_device FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_gpg_key_updated_at ON user_gpg_key;
|
||||
CREATE TRIGGER trg_user_gpg_key_updated_at BEFORE UPDATE ON user_gpg_key FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_mail_updated_at ON user_mail;
|
||||
CREATE TRIGGER trg_user_mail_updated_at BEFORE UPDATE ON user_mail FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_notify_setting_updated_at ON user_notify_setting;
|
||||
CREATE TRIGGER trg_user_notify_setting_updated_at BEFORE UPDATE ON user_notify_setting FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_password_updated_at ON user_password;
|
||||
CREATE TRIGGER trg_user_password_updated_at BEFORE UPDATE ON user_password FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_personal_access_token_updated_at ON user_personal_access_token;
|
||||
CREATE TRIGGER trg_user_personal_access_token_updated_at BEFORE UPDATE ON user_personal_access_token FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_profile_updated_at ON user_profile;
|
||||
CREATE TRIGGER trg_user_profile_updated_at BEFORE UPDATE ON user_profile FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_ssh_key_updated_at ON user_ssh_key;
|
||||
CREATE TRIGGER trg_user_ssh_key_updated_at BEFORE UPDATE ON user_ssh_key FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_updated_at ON workspace;
|
||||
CREATE TRIGGER trg_workspace_updated_at BEFORE UPDATE ON workspace FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_billing_updated_at ON workspace_billing;
|
||||
CREATE TRIGGER trg_workspace_billing_updated_at BEFORE UPDATE ON workspace_billing FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_custom_branding_updated_at ON workspace_custom_branding;
|
||||
CREATE TRIGGER trg_workspace_custom_branding_updated_at BEFORE UPDATE ON workspace_custom_branding FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_domain_updated_at ON workspace_domain;
|
||||
CREATE TRIGGER trg_workspace_domain_updated_at BEFORE UPDATE ON workspace_domain FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_integration_updated_at ON workspace_integration;
|
||||
CREATE TRIGGER trg_workspace_integration_updated_at BEFORE UPDATE ON workspace_integration FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_member_updated_at ON workspace_member;
|
||||
CREATE TRIGGER trg_workspace_member_updated_at BEFORE UPDATE ON workspace_member FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_pending_approval_updated_at ON workspace_pending_approval;
|
||||
CREATE TRIGGER trg_workspace_pending_approval_updated_at BEFORE UPDATE ON workspace_pending_approval FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_settings_updated_at ON workspace_settings;
|
||||
CREATE TRIGGER trg_workspace_settings_updated_at BEFORE UPDATE ON workspace_settings FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_stats_updated_at ON workspace_stats;
|
||||
CREATE TRIGGER trg_workspace_stats_updated_at BEFORE UPDATE ON workspace_stats FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_webhook_updated_at ON workspace_webhook;
|
||||
CREATE TRIGGER trg_workspace_webhook_updated_at BEFORE UPDATE ON workspace_webhook FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- ============================================================
|
||||
-- 2. set_deleted_at — auto-timestamp soft delete
|
||||
-- Fires when deleted_at transitions NULL → NOT NULL.
|
||||
-- Works for ANY table with a deleted_at column (no status dependency).
|
||||
-- ============================================================
|
||||
CREATE OR REPLACE FUNCTION set_deleted_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.deleted_at IS NOT NULL AND OLD.deleted_at IS NULL THEN
|
||||
NEW.deleted_at = NOW();
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Only tables that contain BOTH status AND deleted_at columns
|
||||
-- Verified from models: user, repo, workspace, issue, pull_request, channel,
|
||||
-- conversation, conversation_message, agent, message, issue_comment,
|
||||
-- repo_commit_comment, repo_release, notification, ai_model, conversation_attachment
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_deleted_at ON "user";
|
||||
CREATE TRIGGER trg_user_deleted_at BEFORE UPDATE ON "user" FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_deleted_at ON repo;
|
||||
CREATE TRIGGER trg_repo_deleted_at BEFORE UPDATE ON repo FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_deleted_at ON workspace;
|
||||
CREATE TRIGGER trg_workspace_deleted_at BEFORE UPDATE ON workspace FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_deleted_at ON issue;
|
||||
CREATE TRIGGER trg_issue_deleted_at BEFORE UPDATE ON issue FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_pull_request_deleted_at ON pull_request;
|
||||
CREATE TRIGGER trg_pull_request_deleted_at BEFORE UPDATE ON pull_request FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_deleted_at ON channel;
|
||||
CREATE TRIGGER trg_channel_deleted_at BEFORE UPDATE ON channel FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_conversation_deleted_at ON conversation;
|
||||
CREATE TRIGGER trg_conversation_deleted_at BEFORE UPDATE ON conversation FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_conversation_message_deleted_at ON conversation_message;
|
||||
CREATE TRIGGER trg_conversation_message_deleted_at BEFORE UPDATE ON conversation_message FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_deleted_at ON agent;
|
||||
CREATE TRIGGER trg_agent_deleted_at BEFORE UPDATE ON agent FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_message_deleted_at ON message;
|
||||
CREATE TRIGGER trg_message_deleted_at BEFORE UPDATE ON message FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_comment_deleted_at ON issue_comment;
|
||||
CREATE TRIGGER trg_issue_comment_deleted_at BEFORE UPDATE ON issue_comment FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_commit_comment_deleted_at ON repo_commit_comment;
|
||||
CREATE TRIGGER trg_repo_commit_comment_deleted_at BEFORE UPDATE ON repo_commit_comment FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_repo_release_deleted_at ON repo_release;
|
||||
CREATE TRIGGER trg_repo_release_deleted_at BEFORE UPDATE ON repo_release FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_notification_deleted_at ON notification;
|
||||
CREATE TRIGGER trg_notification_deleted_at BEFORE UPDATE ON notification FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_ai_model_deleted_at ON ai_model;
|
||||
CREATE TRIGGER trg_ai_model_deleted_at BEFORE UPDATE ON ai_model FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_conversation_attachment_deleted_at ON conversation_attachment;
|
||||
CREATE TRIGGER trg_conversation_attachment_deleted_at BEFORE UPDATE ON conversation_attachment FOR EACH ROW EXECUTE FUNCTION set_deleted_at();
|
||||
|
||||
-- ============================================================
|
||||
-- 3. Helper: resolve current user from session variable
|
||||
-- Application must SET LOCAL app.current_user_id = '...' before writes.
|
||||
-- ============================================================
|
||||
CREATE OR REPLACE FUNCTION app_current_user_id()
|
||||
RETURNS UUID AS $$
|
||||
BEGIN
|
||||
RETURN current_setting('app.current_user_id', true)::UUID;
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql STABLE;
|
||||
|
||||
-- ============================================================
|
||||
-- 4. Event recording — issue / pull_request / channel
|
||||
-- Tracks: created, renamed, state_changed, priority_changed,
|
||||
-- draft_toggled, archived, restored.
|
||||
-- ============================================================
|
||||
|
||||
-- 4a. issue events
|
||||
CREATE OR REPLACE FUNCTION record_issue_event()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
actor UUID := app_current_user_id();
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
INSERT INTO issue_event (issue_id, actor_id, event_type, created_at)
|
||||
VALUES (NEW.id, COALESCE(actor, NEW.author_id), 'created', NOW());
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
IF NEW.title IS DISTINCT FROM OLD.title THEN
|
||||
INSERT INTO issue_event (issue_id, actor_id, event_type, old_value, new_value, created_at)
|
||||
VALUES (NEW.id, actor, 'renamed', OLD.title, NEW.title, NOW());
|
||||
END IF;
|
||||
|
||||
IF NEW.state IS DISTINCT FROM OLD.state THEN
|
||||
INSERT INTO issue_event (issue_id, actor_id, event_type, old_value, new_value, created_at)
|
||||
VALUES (NEW.id, actor, 'state_changed', OLD.state, NEW.state, NOW());
|
||||
END IF;
|
||||
|
||||
IF NEW.priority IS DISTINCT FROM OLD.priority THEN
|
||||
INSERT INTO issue_event (issue_id, actor_id, event_type, old_value, new_value, created_at)
|
||||
VALUES (NEW.id, actor, 'priority_changed', OLD.priority, NEW.priority, NOW());
|
||||
END IF;
|
||||
|
||||
IF NEW.body IS DISTINCT FROM OLD.body THEN
|
||||
INSERT INTO issue_event (issue_id, actor_id, event_type, created_at)
|
||||
VALUES (NEW.id, actor, 'body_updated', NOW());
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_event ON issue;
|
||||
CREATE TRIGGER trg_issue_event
|
||||
AFTER INSERT OR UPDATE ON issue
|
||||
FOR EACH ROW EXECUTE FUNCTION record_issue_event();
|
||||
|
||||
-- 4b. pull_request events
|
||||
CREATE OR REPLACE FUNCTION record_pr_event()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
actor UUID := app_current_user_id();
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
INSERT INTO pr_event (pull_request_id, actor_id, event_type, created_at)
|
||||
VALUES (NEW.id, COALESCE(actor, NEW.author_id), 'created', NOW());
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
IF NEW.title IS DISTINCT FROM OLD.title THEN
|
||||
INSERT INTO pr_event (pull_request_id, actor_id, event_type, old_value, new_value, created_at)
|
||||
VALUES (NEW.id, actor, 'renamed', OLD.title, NEW.title, NOW());
|
||||
END IF;
|
||||
|
||||
IF NEW.state IS DISTINCT FROM OLD.state THEN
|
||||
INSERT INTO pr_event (pull_request_id, actor_id, event_type, old_value, new_value, created_at)
|
||||
VALUES (NEW.id, actor, 'state_changed', OLD.state, NEW.state, NOW());
|
||||
END IF;
|
||||
|
||||
IF NEW.draft IS DISTINCT FROM OLD.draft THEN
|
||||
INSERT INTO pr_event (pull_request_id, actor_id, event_type, old_value, new_value, created_at)
|
||||
VALUES (NEW.id, actor, 'draft_toggled', OLD.draft::text, NEW.draft::text, NOW());
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_pr_event ON pull_request;
|
||||
CREATE TRIGGER trg_pr_event
|
||||
AFTER INSERT OR UPDATE ON pull_request
|
||||
FOR EACH ROW EXECUTE FUNCTION record_pr_event();
|
||||
|
||||
-- 4c. channel events
|
||||
CREATE OR REPLACE FUNCTION record_channel_event()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
actor UUID := app_current_user_id();
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
INSERT INTO channel_event (channel_id, actor_id, event_type, created_at)
|
||||
VALUES (NEW.id, COALESCE(actor, NEW.created_by), 'created', NOW());
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
IF NEW.name IS DISTINCT FROM OLD.name THEN
|
||||
INSERT INTO channel_event (channel_id, actor_id, event_type, old_value, new_value, created_at)
|
||||
VALUES (NEW.id, actor, 'renamed', OLD.name, NEW.name, NOW());
|
||||
END IF;
|
||||
|
||||
IF NEW.archived IS DISTINCT FROM OLD.archived THEN
|
||||
INSERT INTO channel_event (channel_id, actor_id, event_type, old_value, new_value, created_at)
|
||||
VALUES (NEW.id, actor, 'archive_toggled', OLD.archived::text, NEW.archived::text, NOW());
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_event ON channel;
|
||||
CREATE TRIGGER trg_channel_event
|
||||
AFTER INSERT OR UPDATE ON channel
|
||||
FOR EACH ROW EXECUTE FUNCTION record_channel_event();
|
||||
|
||||
-- ============================================================
|
||||
-- 5. Workspace audit log
|
||||
-- Only applied to tables that directly own a workspace_id column.
|
||||
-- actor_id resolved from app.current_user_id session variable.
|
||||
-- ============================================================
|
||||
|
||||
-- Tables with workspace_id (verified from models):
|
||||
-- repo, channel, channel_invitation, channel_slash_command,
|
||||
-- agent, agent_event_subscription, agent_execution, agent_schedule,
|
||||
-- agent_workspace_binding, conversation,
|
||||
-- notification, notification_block, notification_subscription,
|
||||
-- workspace_billing, workspace_custom_branding, workspace_domain,
|
||||
-- workspace_integration, workspace_invitation, workspace_member,
|
||||
-- workspace_pending_approval, workspace_settings, workspace_webhook
|
||||
|
||||
CREATE OR REPLACE FUNCTION record_workspace_audit()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
ws_id UUID;
|
||||
actor UUID := app_current_user_id();
|
||||
action_text TEXT;
|
||||
BEGIN
|
||||
ws_id := COALESCE(NEW.workspace_id, OLD.workspace_id);
|
||||
|
||||
IF ws_id IS NULL THEN
|
||||
RETURN COALESCE(NEW, OLD);
|
||||
END IF;
|
||||
|
||||
action_text := CASE TG_OP
|
||||
WHEN 'INSERT' THEN 'created'
|
||||
WHEN 'UPDATE' THEN 'updated'
|
||||
WHEN 'DELETE' THEN 'deleted'
|
||||
END;
|
||||
|
||||
INSERT INTO workspace_audit_log (workspace_id, actor_id, action, target_type, target_id, created_at)
|
||||
VALUES (ws_id, actor, action_text, TG_TABLE_NAME, COALESCE(NEW.id, OLD.id), NOW());
|
||||
|
||||
RETURN COALESCE(NEW, OLD);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Apply to tables with workspace_id column
|
||||
DROP TRIGGER IF EXISTS trg_repo_audit ON repo;
|
||||
CREATE TRIGGER trg_repo_audit AFTER INSERT OR UPDATE OR DELETE ON repo FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_audit ON channel;
|
||||
CREATE TRIGGER trg_channel_audit AFTER INSERT OR UPDATE OR DELETE ON channel FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_slash_command_audit ON channel_slash_command;
|
||||
CREATE TRIGGER trg_channel_slash_command_audit AFTER INSERT OR UPDATE OR DELETE ON channel_slash_command FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_audit ON agent;
|
||||
CREATE TRIGGER trg_agent_audit AFTER INSERT OR UPDATE OR DELETE ON agent FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_schedule_audit ON agent_schedule;
|
||||
CREATE TRIGGER trg_agent_schedule_audit AFTER INSERT OR UPDATE OR DELETE ON agent_schedule FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_workspace_binding_audit ON agent_workspace_binding;
|
||||
CREATE TRIGGER trg_agent_workspace_binding_audit AFTER INSERT OR UPDATE OR DELETE ON agent_workspace_binding FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_conversation_audit ON conversation;
|
||||
CREATE TRIGGER trg_conversation_audit AFTER INSERT OR UPDATE OR DELETE ON conversation FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_invitation_audit ON channel_invitation;
|
||||
CREATE TRIGGER trg_channel_invitation_audit AFTER INSERT OR UPDATE OR DELETE ON channel_invitation FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_event_subscription_audit ON agent_event_subscription;
|
||||
CREATE TRIGGER trg_agent_event_subscription_audit AFTER INSERT OR UPDATE OR DELETE ON agent_event_subscription FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_agent_execution_audit ON agent_execution;
|
||||
CREATE TRIGGER trg_agent_execution_audit AFTER INSERT OR UPDATE OR DELETE ON agent_execution FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_notification_audit ON notification;
|
||||
CREATE TRIGGER trg_notification_audit AFTER INSERT OR UPDATE OR DELETE ON notification FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_notification_block_audit ON notification_block;
|
||||
CREATE TRIGGER trg_notification_block_audit AFTER INSERT OR UPDATE OR DELETE ON notification_block FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_notification_subscription_audit ON notification_subscription;
|
||||
CREATE TRIGGER trg_notification_subscription_audit AFTER INSERT OR UPDATE OR DELETE ON notification_subscription FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_billing_audit ON workspace_billing;
|
||||
CREATE TRIGGER trg_workspace_billing_audit AFTER INSERT OR UPDATE OR DELETE ON workspace_billing FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_custom_branding_audit ON workspace_custom_branding;
|
||||
CREATE TRIGGER trg_workspace_custom_branding_audit AFTER INSERT OR UPDATE OR DELETE ON workspace_custom_branding FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_domain_audit ON workspace_domain;
|
||||
CREATE TRIGGER trg_workspace_domain_audit AFTER INSERT OR UPDATE OR DELETE ON workspace_domain FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_integration_audit ON workspace_integration;
|
||||
CREATE TRIGGER trg_workspace_integration_audit AFTER INSERT OR UPDATE OR DELETE ON workspace_integration FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_invitation_audit ON workspace_invitation;
|
||||
CREATE TRIGGER trg_workspace_invitation_audit AFTER INSERT OR UPDATE OR DELETE ON workspace_invitation FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_member_audit ON workspace_member;
|
||||
CREATE TRIGGER trg_workspace_member_audit AFTER INSERT OR UPDATE OR DELETE ON workspace_member FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_pending_approval_audit ON workspace_pending_approval;
|
||||
CREATE TRIGGER trg_workspace_pending_approval_audit AFTER INSERT OR UPDATE OR DELETE ON workspace_pending_approval FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_settings_audit ON workspace_settings;
|
||||
CREATE TRIGGER trg_workspace_settings_audit AFTER INSERT OR UPDATE OR DELETE ON workspace_settings FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_workspace_webhook_audit ON workspace_webhook;
|
||||
CREATE TRIGGER trg_workspace_webhook_audit AFTER INSERT OR UPDATE OR DELETE ON workspace_webhook FOR EACH ROW EXECUTE FUNCTION record_workspace_audit();
|
||||
|
||||
-- ============================================================
|
||||
-- 6. User security log
|
||||
-- Password changes, logins, session revocations.
|
||||
-- ============================================================
|
||||
CREATE OR REPLACE FUNCTION record_user_security_event()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
actor UUID := app_current_user_id();
|
||||
BEGIN
|
||||
IF TG_OP = 'UPDATE' THEN
|
||||
IF NEW.password_hash IS DISTINCT FROM OLD.password_hash THEN
|
||||
INSERT INTO user_security_log (user_id, event_type, description, created_at)
|
||||
VALUES (NEW.user_id, 'password_changed', 'Password updated', NOW());
|
||||
END IF;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_password_security ON user_password;
|
||||
CREATE TRIGGER trg_user_password_security
|
||||
AFTER UPDATE ON user_password
|
||||
FOR EACH ROW EXECUTE FUNCTION record_user_security_event();
|
||||
|
||||
CREATE OR REPLACE FUNCTION record_user_session_event()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
actor UUID := app_current_user_id();
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
INSERT INTO user_security_log (user_id, event_type, description, ip_address, user_agent, created_at)
|
||||
VALUES (NEW.user_id, 'login', 'User logged in', NEW.ip_address, NEW.user_agent, NOW());
|
||||
ELSIF TG_OP = 'UPDATE' AND NEW.revoked_at IS NOT NULL AND OLD.revoked_at IS NULL THEN
|
||||
INSERT INTO user_security_log (user_id, event_type, description, created_at)
|
||||
VALUES (NEW.user_id, 'session_revoked', 'Session revoked', NOW());
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_user_session_event ON user_session;
|
||||
CREATE TRIGGER trg_user_session_event
|
||||
AFTER INSERT OR UPDATE ON user_session
|
||||
FOR EACH ROW EXECUTE FUNCTION record_user_session_event();
|
||||
|
||||
-- ============================================================
|
||||
-- 7. Stats auto-maintenance
|
||||
-- issue_stats.comments_count — incremented on comment insert/delete.
|
||||
-- channel_stats.messages_count — incremented on message insert.
|
||||
-- Stats rows MUST be pre-created (no auto-INSERT with zero counts).
|
||||
-- ============================================================
|
||||
|
||||
-- 7a. Issue comment stats (UPDATE only, no INSERT)
|
||||
CREATE OR REPLACE FUNCTION update_issue_stats()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
UPDATE issue_stats
|
||||
SET comments_count = comments_count + 1,
|
||||
updated_at = NOW()
|
||||
WHERE issue_id = NEW.issue_id;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RAISE WARNING 'issue_stats row missing for issue_id %. Insert skipped; seed stats row first.', NEW.issue_id;
|
||||
END IF;
|
||||
|
||||
ELSIF TG_OP = 'DELETE' THEN
|
||||
UPDATE issue_stats
|
||||
SET comments_count = GREATEST(comments_count - 1, 0),
|
||||
updated_at = NOW()
|
||||
WHERE issue_id = OLD.issue_id;
|
||||
END IF;
|
||||
|
||||
RETURN COALESCE(NEW, OLD);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_issue_comment_stats ON issue_comment;
|
||||
CREATE TRIGGER trg_issue_comment_stats
|
||||
AFTER INSERT OR DELETE ON issue_comment
|
||||
FOR EACH ROW EXECUTE FUNCTION update_issue_stats();
|
||||
|
||||
-- 7b. Channel message stats (UPDATE only, no INSERT)
|
||||
CREATE OR REPLACE FUNCTION update_channel_stats()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
UPDATE channel_stats
|
||||
SET messages_count = messages_count + 1,
|
||||
last_activity_at = NOW(),
|
||||
updated_at = NOW()
|
||||
WHERE channel_id = NEW.channel_id;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RAISE WARNING 'channel_stats row missing for channel_id %. Insert skipped; seed stats row first.', NEW.channel_id;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_channel_message_stats ON message;
|
||||
CREATE TRIGGER trg_channel_message_stats
|
||||
AFTER INSERT ON message
|
||||
FOR EACH ROW EXECUTE FUNCTION update_channel_stats();
|
||||
Reference in New Issue
Block a user