Skip to content

fix: skip partition-clone child triggers in dump (#472)#487

Merged
tianzhou merged 1 commit into
mainfrom
fix/issue-472-partition-clone-triggers
Jun 22, 2026
Merged

fix: skip partition-clone child triggers in dump (#472)#487
tianzhou merged 1 commit into
mainfrom
fix/issue-472-partition-clone-triggers

Conversation

@tianzhou

Copy link
Copy Markdown
Contributor

Summary

A FOR EACH ROW trigger on a partitioned table causes PostgreSQL to clone the trigger onto every partition child, creating one extra pg_trigger row per partition with tgparentid != 0 (and tgisinternal = false). pg_dump never emits these clones — only the top-level trigger on the partitioned parent (tgparentid = 0).

pgschema's trigger inspector query filtered only tgisinternal, so the clones slipped through and were dumped as standalone CREATE OR REPLACE TRIGGER ... ON <child> statements. When the child partitions were excluded via .pgschemaignore, the resulting desired-state file failed pgschema's own plan with relation "<child>" does not exist.

This is the trigger analog of the internal per-partition FK constraint problem fixed in #409/#460. The fix adds AND t.tgparentid = 0 to the trigger query in ir/queries, matching pg_dump and the existing FK-constraint treatment. tgparentid exists since PostgreSQL 13, so all supported versions (14–18) are covered.

Verified against a live PostgreSQL 17/18 catalog: the parent trigger has tgisinternal=f, tgparentid=0; the cloned child trigger has tgisinternal=f, tgparentid!=0.

Fixes #472

Test plan

Added dump regression testdata/dump/issue_472_partition_clone_trigger/ (partitioned ledger with a row-level trigger). Before the fix, dump emitted a duplicate trg_rollup on ledger_2026_06; after, only the parent trigger is emitted.

go test ./cmd/dump -run TestDumpCommand_Issue472PartitionCloneTrigger
PGSCHEMA_TEST_FILTER="create_trigger/" go test ./internal/diff -run TestDiffFromFiles

Both pass; go build ./... clean.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings June 22, 2026 17:12
@greptile-apps

greptile-apps Bot commented Jun 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes two related trigger-inspection bugs: (1) partition-clone child triggers (tgparentid != 0) were leaking into dump output because the query only filtered tgisinternal, causing bogus CREATE TRIGGER statements on child partitions and plan failures when those partitions were ignored; (2) pg_get_triggerdef was called without a forced search_path, so WHEN-clause type casts in non-public schemas rendered schema-qualified in the current state but unqualified in the desired state, producing a perpetual recreate loop.

Confidence Score: 5/5

Safe to merge — both fixes are narrowly scoped catalog filter/rendering changes with no destructive side effects, and regression tests cover both scenarios.

The tgparentid = 0 guard and the set_config LATERAL pattern are direct analogs of identical fixes already merged for FK constraints and RLS policies, so the approach is well-proven. Test coverage is thorough: a dump regression for the partition-clone case and an idempotency round-trip for the search_path case. The sequence-comment sync is a housekeeping correction with no behavioral change. No logic in the query result pipeline was removed or restructured.

No files require special attention.

Important Files Changed

Filename Overview
ir/queries/queries.sql Two fixes: adds AND t.tgparentid = 0 to exclude partition-clone child triggers, and wraps pg_get_triggerdef in a LATERAL with set_config for deterministic search_path. Also syncs a pre-existing sequence_comment column that was already live in the generated .sql.go file. All patterns match existing constraint/RLS-policy precedents.
ir/queries/queries.sql.go Regenerated sqlc output reflecting the trigger query changes; the GetSequencesForSchema section was already correct before this PR (sequence_comment column was present), so only the trigger portion changes in the diff.
cmd/dump/dump_integration_test.go Adds TestDumpCommand_Issue472PartitionCloneTrigger integration test following the standard runExactMatchTest pattern; short-mode skip is correctly guarded.
cmd/issue_449_integration_test.go Adds a third idempotency sub-test (trigger_when_same_schema_enum_cast) covering issue #481 — trigger WHEN clauses with same-schema enum casts that previously caused perpetual recreations. Test is self-contained and follows the existing applyThenReplan pattern.
testdata/dump/issue_472_partition_clone_trigger/manifest.json New regression test manifest for issue #472 — description and notes accurately document the partition-clone trigger scenario.
testdata/dump/issue_472_partition_clone_trigger/pgdump.sql Reference pg_dump output; SET transaction_timeout = 0 is correctly commented out to preserve compatibility with PostgreSQL 14–16, following the documented convention.
testdata/dump/issue_472_partition_clone_trigger/pgschema.sql Expected pgschema dump output — only the parent trigger trg_rollup ON ledger appears; no duplicate on ledger_2026_06, confirming the fix.
testdata/dump/issue_472_partition_clone_trigger/raw.sql Minimal reproduction schema: partitioned ledger with one child partition and one FOR EACH ROW trigger — exactly the setup needed to reproduce the cloned-trigger bug.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[GetTriggersForSchema query] --> B{t.tgisinternal?}
    B -- true --> C[Skip - internal trigger]
    B -- false --> D{t.tgparentid = 0?}
    D -- "false: non-zero parent" --> E[Skip - partition-clone child added by this PR]
    D -- "true: top-level trigger" --> F[LATERAL: set_config search_path to trigger schema added by this PR]
    F --> G[pg_get_triggerdef with deterministic schema context]
    G --> H[Emit trigger row]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[GetTriggersForSchema query] --> B{t.tgisinternal?}
    B -- true --> C[Skip - internal trigger]
    B -- false --> D{t.tgparentid = 0?}
    D -- "false: non-zero parent" --> E[Skip - partition-clone child added by this PR]
    D -- "true: top-level trigger" --> F[LATERAL: set_config search_path to trigger schema added by this PR]
    F --> G[pg_get_triggerdef with deterministic schema context]
    G --> H[Emit trigger row]
Loading

Reviews (1): Last reviewed commit: "fix: skip partition-clone child triggers..." | Re-trigger Greptile

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates pgschema’s trigger inspection to align with pg_dump by skipping partition-cloned child triggers (pg_trigger.tgparentid != 0), preventing bogus CREATE OR REPLACE TRIGGER ... ON <partition_child> output and downstream failures when partitions are ignored. It also changes how trigger definitions are rendered to be deterministic by forcing search_path before calling pg_get_triggerdef.

Changes:

  • Filter trigger inspection to only dump top-level (non-cloned) triggers via t.tgparentid = 0.
  • Render trigger definitions under a forced search_path to avoid schema-qualification drift in WHEN clauses (idempotent plan/apply).
  • Add a dump regression fixture + integration test for issue #472, plus an integration coverage case for trigger WHEN enum casts.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
testdata/dump/issue_472_partition_clone_trigger/raw.sql Adds a minimal repro schema for partition-cloned triggers.
testdata/dump/issue_472_partition_clone_trigger/pgschema.sql Expected pgschema output for the new regression case.
testdata/dump/issue_472_partition_clone_trigger/pgdump.sql pg_dump-produced input used to seed the regression database.
testdata/dump/issue_472_partition_clone_trigger/manifest.json Documents the regression fixture purpose and links issue #472.
ir/queries/queries.sql Updates trigger query to (a) force deterministic rendering and (b) skip tgparentid != 0 clones. Also includes a sequence query adjustment.
ir/queries/queries.sql.go Regenerates sqlc output to match the updated SQL queries.
cmd/issue_449_integration_test.go Extends repeat-plan idempotency coverage to include trigger WHEN enum casts (issue #481).
cmd/dump/dump_integration_test.go Adds an integration test runner for the new issue #472 dump fixture.
Files not reviewed (1)
  • ir/queries/queries.sql.go: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ir/queries/queries.sql
Comment thread ir/queries/queries.sql
A FOR EACH ROW trigger on a partitioned table causes PostgreSQL to clone
the trigger onto every partition child, creating one extra pg_trigger row
per partition with tgparentid != 0 (and tgisinternal = false). pg_dump
never emits these clones; only the top-level trigger on the partitioned
parent (tgparentid = 0) is dumpable.

pgschema dump emitted all of them, producing a bogus
CREATE OR REPLACE TRIGGER ... ON <child> statement. This broke pgschema's
own plan with "relation does not exist" when the child partition was
excluded via .pgschemaignore, mirroring the #409/#460 internal
per-partition FK constraint problem.

Filter trigger rows with t.tgparentid = 0 in the inspector query, matching
pg_dump and the existing FK-constraint treatment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tianzhou tianzhou force-pushed the fix/issue-472-partition-clone-triggers branch from 0e1fca2 to f565e90 Compare June 22, 2026 17:23
@tianzhou tianzhou requested a review from Copilot June 22, 2026 17:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • ir/queries/queries.sql.go: Generated file

@tianzhou tianzhou merged commit 7850fcd into main Jun 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Partition-clone child triggers (tgparentid != 0) are dumped even when child tables are ignored; qualified [triggers] patterns do not match

2 participants