Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cmd/dump/dump_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ func TestDumpCommand_Issue409PartitionedFK(t *testing.T) {
runExactMatchTest(t, "issue_409_partitioned_fk")
}

func TestDumpCommand_Issue472PartitionCloneTrigger(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
runExactMatchTest(t, "issue_472_partition_clone_trigger")
}

func TestDumpCommand_Issue421QuotedFKColumns(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
Expand Down
5 changes: 5 additions & 0 deletions ir/queries/queries.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,11 @@ LEFT JOIN LATERAL (
) def ON true
WHERE n.nspname = $1
AND NOT t.tgisinternal -- Exclude internal triggers
-- Skip partition-clone child triggers (tgparentid != 0) that PostgreSQL
-- automatically creates on every partition when a FOR EACH ROW trigger is
-- defined on a partitioned parent; pg_dump emits only the top-level trigger
-- on the parent (tgparentid = 0).
AND t.tgparentid = 0
ORDER BY n.nspname, c.relname, t.tgname;

-- GetTypesForSchema retrieves all user-defined types for a specific schema
Expand Down
5 changes: 5 additions & 0 deletions ir/queries/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions testdata/dump/issue_472_partition_clone_trigger/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "issue_472_partition_clone_trigger",
"description": "pgschema dump emits partition-clone child triggers (tgparentid != 0) that PostgreSQL creates automatically on every partition",
"source": "https://github.com/pgplex/pgschema/issues/472",
"notes": [
"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.",
"The trigger query in ir/queries now filters rows with t.tgparentid = 0 to match pg_dump, mirroring the #409/#460 treatment of internal per-partition FK constraints.",
"Without the fix, dump produced a bogus CREATE OR REPLACE TRIGGER trg_rollup ... ON ledger_2026_06, which also left a dangling reference when the partition was excluded via .pgschemaignore."
]
}
51 changes: 51 additions & 0 deletions testdata/dump/issue_472_partition_clone_trigger/pgdump.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
--
-- PostgreSQL database dump
--

SET statement_timeout = 0;
SET lock_timeout = 0;
-- SET transaction_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;
SET row_security = off;

--
-- Name: ledger; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.ledger (
id uuid NOT NULL,
amount bigint NOT NULL,
ts timestamp with time zone NOT NULL
)
PARTITION BY RANGE (ts);

ALTER TABLE ONLY public.ledger
ADD CONSTRAINT ledger_pkey PRIMARY KEY (ts, id);

--
-- Name: ledger_2026_06; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.ledger_2026_06 PARTITION OF public.ledger
FOR VALUES FROM ('2026-06-01') TO ('2026-07-01');

--
-- Name: tg_noop(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.tg_noop() RETURNS trigger
LANGUAGE plpgsql
AS $$ BEGIN RETURN NEW; END $$;

--
-- Name: ledger trg_rollup; Type: TRIGGER; Schema: public; Owner: -
--

CREATE TRIGGER trg_rollup AFTER INSERT ON public.ledger FOR EACH ROW EXECUTE FUNCTION public.tg_noop();

--
-- PostgreSQL database dump complete
--
50 changes: 50 additions & 0 deletions testdata/dump/issue_472_partition_clone_trigger/pgschema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--
-- pgschema database dump
--

-- Dumped from database version PostgreSQL 18.0
-- Dumped by pgschema version 1.11.0


--
-- Name: ledger; Type: TABLE; Schema: -; Owner: -
--

CREATE TABLE IF NOT EXISTS ledger (
id uuid,
amount bigint NOT NULL,
ts timestamptz,
CONSTRAINT ledger_pkey PRIMARY KEY (ts, id)
) PARTITION BY RANGE (ts);

--
-- Name: ledger_2026_06; Type: TABLE; Schema: -; Owner: -
--

CREATE TABLE IF NOT EXISTS ledger_2026_06 (
id uuid,
amount bigint NOT NULL,
ts timestamptz,
CONSTRAINT ledger_2026_06_pkey PRIMARY KEY (ts, id)
);

--
-- Name: tg_noop(); Type: FUNCTION; Schema: -; Owner: -
--

CREATE OR REPLACE FUNCTION tg_noop()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
AS $$ BEGIN RETURN NEW; END
$$;

--
-- Name: trg_rollup; Type: TRIGGER; Schema: -; Owner: -
--

CREATE OR REPLACE TRIGGER trg_rollup
AFTER INSERT ON ledger
FOR EACH ROW
EXECUTE FUNCTION tg_noop();

28 changes: 28 additions & 0 deletions testdata/dump/issue_472_partition_clone_trigger/raw.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--
-- Test case for GitHub issue #472: partition-clone child triggers dumped
--
-- When a FOR EACH ROW trigger is defined on a partitioned table, PostgreSQL
-- automatically clones it onto every partition child, creating one extra
-- pg_trigger row per partition with tgparentid != 0 (and tgisinternal = false).
-- These are internal artifacts: pg_dump never emits them, only the top-level
-- trigger on the partitioned parent (tgparentid = 0).
--
-- pgschema dump used to emit all of them, producing a bogus
-- CREATE OR REPLACE TRIGGER trg_rollup ... ON ledger_2026_06 that also left a
-- dangling reference when the partition was excluded via .pgschemaignore.
--

CREATE TABLE ledger (
id uuid NOT NULL,
amount bigint NOT NULL,
ts timestamptz NOT NULL,
PRIMARY KEY (ts, id)
) PARTITION BY RANGE (ts);

CREATE TABLE ledger_2026_06 PARTITION OF ledger
FOR VALUES FROM ('2026-06-01') TO ('2026-07-01');

CREATE FUNCTION tg_noop() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END $$;

CREATE TRIGGER trg_rollup AFTER INSERT ON ledger
FOR EACH ROW EXECUTE FUNCTION tg_noop();