use anyhow::Result;
use sqlx::postgres::PgConnection;
use tracing::info;
use crate::catalog::{DependsOn, id::DbObjectId};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Trigger {
pub schema: String,
pub table_name: String,
pub name: String,
pub function_schema: String,
pub function_name: String,
pub function_args: String,
pub comment: Option<String>,
pub depends_on: Vec<DbObjectId>,
pub definition: String,
}
impl DependsOn for Trigger {
fn id(&self) -> DbObjectId {
DbObjectId::Trigger {
schema: self.schema.clone(),
table: self.table_name.clone(),
name: self.name.clone(),
}
}
fn depends_on(&self) -> &[DbObjectId] {
&self.depends_on
}
}
pub async fn fetch(conn: &mut PgConnection) -> Result<Vec<Trigger>> {
info!("Fetching triggers...");
let triggers = sqlx::query!(
r#"
SELECT
tn.nspname AS table_schema,
c.relname AS table_name,
t.tgname AS trigger_name,
-- Function details
p.proname AS function_name,
fn.nspname AS function_schema,
pg_catalog.pg_get_function_identity_arguments(p.oid) AS "function_args!",
-- Comments
d.description AS "comment?",
-- Complete trigger definition (authoritative source)
pg_get_triggerdef(t.oid) AS "definition!"
FROM pg_trigger t
JOIN pg_class c ON t.tgrelid = c.oid
JOIN pg_namespace tn ON c.relnamespace = tn.oid
JOIN pg_proc p ON t.tgfoid = p.oid
JOIN pg_namespace fn ON p.pronamespace = fn.oid
LEFT JOIN pg_description d ON d.objoid = t.oid AND d.objsubid = 0
WHERE tn.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
AND NOT t.tgisinternal -- Exclude system-generated triggers
AND c.relkind IN ('r', 'v', 'm') -- Regular tables, views, materialized views
-- Exclude triggers on extension-owned relations. Triggers never get
-- their own pg_depend 'e' entry; membership is recorded on the parent.
AND NOT EXISTS (
SELECT 1 FROM pg_depend dep
WHERE dep.objid = c.oid
AND dep.deptype = 'e'
)
ORDER BY tn.nspname, c.relname, t.tgname
"#
)
.fetch_all(&mut *conn)
.await?;
let mut result = Vec::new();
for row in triggers {
let depends_on = vec![
DbObjectId::Table {
schema: row.table_schema.clone(),
name: row.table_name.clone(),
},
DbObjectId::Function {
schema: row.function_schema.clone(),
name: row.function_name.clone(),
arguments: row.function_args.clone(),
},
];
let trigger = Trigger {
schema: row.table_schema,
table_name: row.table_name,
name: row.trigger_name,
function_schema: row.function_schema,
function_name: row.function_name,
function_args: row.function_args,
comment: row.comment,
depends_on,
definition: row.definition,
};
result.push(trigger);
}
Ok(result)
}