mirror of
https://github.com/block/buzz.git
synced 2026-10-28 21:11:48 +00:00
fix(db): audit nested leaf extra triggers
Audit child-only row triggers on every routable descendant leaf, qualifying nested leaves the same way missing triggers are qualified. Add a Postgres regression test that fails when extra-trigger parity only looks at the immediate root child. Co-authored-by: coder 0 <d97ebdbb198c7237c94f84ea8bb8a73583ea067407eebd0062abbb3962527fb1@buzz.block.builderlab.xyz> Signed-off-by: coder 0 <d97ebdbb198c7237c94f84ea8bb8a73583ea067407eebd0062abbb3962527fb1@buzz.block.builderlab.xyz> Signed-off-by: Ian Oberst <ioberst@squareup.com> Co-authored-by: Codex <noreply@openai.com> Ai-assisted: true
This commit is contained in:
@@ -77,7 +77,8 @@ pub struct PartitionChildAudit {
|
||||
/// Parent trigger names absent from this child or a routable descendant leaf.
|
||||
/// Nested-leaf entries are qualified as `{leaf}:{trigger}`.
|
||||
pub missing_triggers: Vec<String>,
|
||||
/// Child trigger names absent from the parent.
|
||||
/// Child-only row trigger names absent from the parent.
|
||||
/// Nested-leaf entries are qualified as `{leaf}:{trigger}`.
|
||||
pub extra_triggers: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -513,9 +514,6 @@ async fn audit_table_on(
|
||||
let descendant_triggers = trigger_metadata_for_descendants(connection, table).await?;
|
||||
let mut child_audits = Vec::with_capacity(children.len());
|
||||
for child in children {
|
||||
let triggers = descendant_triggers
|
||||
.get(&child.name)
|
||||
.map(|relation| &relation.triggers);
|
||||
let mut missing_triggers = Vec::new();
|
||||
let routable_leaves: Vec<_> = descendant_triggers
|
||||
.values()
|
||||
@@ -527,7 +525,7 @@ async fn audit_table_on(
|
||||
&& relation.relation_kind != "p"))
|
||||
})
|
||||
.collect();
|
||||
for leaf in routable_leaves {
|
||||
for leaf in &routable_leaves {
|
||||
for (name, parent_oid) in &parent_triggers {
|
||||
let present =
|
||||
trigger_lineage_reaches_parent(leaf, name, *parent_oid, &descendant_triggers)
|
||||
@@ -545,15 +543,20 @@ async fn audit_table_on(
|
||||
}
|
||||
}
|
||||
missing_triggers.sort();
|
||||
let mut extra_triggers: Vec<_> = triggers
|
||||
.map(|triggers| {
|
||||
triggers
|
||||
.keys()
|
||||
.filter(|name| !parent_triggers.contains_key(*name))
|
||||
.cloned()
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let mut extra_triggers = Vec::new();
|
||||
for leaf in routable_leaves {
|
||||
for name in leaf
|
||||
.triggers
|
||||
.keys()
|
||||
.filter(|name| !parent_triggers.contains_key(*name))
|
||||
{
|
||||
if leaf.depth == 0 {
|
||||
extra_triggers.push(name.clone());
|
||||
} else {
|
||||
extra_triggers.push(format!("{}:{name}", leaf.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
extra_triggers.sort();
|
||||
child_audits.push(PartitionChildAudit {
|
||||
name: child.name,
|
||||
@@ -1618,6 +1621,60 @@ mod tests {
|
||||
drop_schema(&admin, &schema).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "requires Postgres"]
|
||||
async fn child_only_nested_leaf_trigger_degrades_parity() {
|
||||
let (pool, admin, schema) = scratch_pool().await;
|
||||
seed_parents(&pool).await;
|
||||
create_nested_child(
|
||||
&pool,
|
||||
"events",
|
||||
"events_nested",
|
||||
"created_at",
|
||||
"'2026-09-01'",
|
||||
"'2026-10-01'",
|
||||
)
|
||||
.await;
|
||||
create_child(
|
||||
&pool,
|
||||
"events_nested",
|
||||
"events_nested_first",
|
||||
"'2026-09-01'",
|
||||
"'2026-09-15'",
|
||||
)
|
||||
.await;
|
||||
create_child(
|
||||
&pool,
|
||||
"events_nested",
|
||||
"events_nested_second",
|
||||
"'2026-09-15'",
|
||||
"'2026-10-01'",
|
||||
)
|
||||
.await;
|
||||
sqlx::query(
|
||||
"CREATE TRIGGER child_only_probe BEFORE INSERT ON events_nested_first \
|
||||
FOR EACH ROW EXECUTE FUNCTION partition_test_trigger()",
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.expect("create nested child-only trigger");
|
||||
|
||||
let audit = audit_partition_catalog_at(&pool, 0, fixed_now())
|
||||
.await
|
||||
.expect("audit");
|
||||
let events = audit
|
||||
.tables
|
||||
.iter()
|
||||
.find(|table| table.table == "events")
|
||||
.expect("events audit");
|
||||
assert_eq!(events.extra_trigger_count(), 1);
|
||||
assert!(events.children.iter().any(|child| {
|
||||
child.name == "events_nested"
|
||||
&& child.extra_triggers == ["events_nested_first:child_only_probe"]
|
||||
}));
|
||||
drop_schema(&admin, &schema).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "requires Postgres"]
|
||||
async fn catch_all_skips_create_without_catalog_mutation() {
|
||||
|
||||
Reference in New Issue
Block a user