From fef696babcbaf65a97ac0d2bc76717709f4db153 Mon Sep 17 00:00:00 2001 From: William Bernbaum Date: Tue, 1 Sep 2026 11:24:37 -0700 Subject: [PATCH v2] pg_dump: Don't emit OVERRIDING SYSTEM VALUE for dropped identity columns ALTER TABLE ... DROP COLUMN marks a column as dropped but leaves pg_attribute.attidentity unchanged. getTableAttrs() processes every attribute with attnum > 0, including dropped ones, and it set the table's needs_override flag without consulting attisdropped. As a result, dumpTableData_insert() emitted a spurious OVERRIDING SYSTEM VALUE clause for a table whose only GENERATED ALWAYS AS IDENTITY column had been dropped. Fix by reading attisdropped before computing needs_override, and ignoring dropped columns there. The adjacent flags do not have the same problem: RemoveAttributeById() clears attgenerated, and atthasdef (which drives hasdefaults) is cleared when the column's default is dropped along with the column. --- src/bin/pg_dump/pg_dump.c | 11 +++++- src/bin/pg_dump/t/002_pg_dump.pl | 67 ++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 36309aa9494..9cbfa190ca0 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -9500,8 +9500,17 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage)); tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity)); tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated)); - tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS); tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't'); + + /* + * ALTER TABLE DROP COLUMN does not clear attidentity, so we must + * ignore dropped columns here or we'd emit a spurious OVERRIDING + * SYSTEM VALUE clause for the table's data. + */ + if (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS && + !tbinfo->attisdropped[j]) + tbinfo->needs_override = true; + tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen)); tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign)); tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't'); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 9258948b583..80b41a04733 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2043,6 +2043,29 @@ my %tests = ( }, }, + 'COPY test_table_identity_dropped' => { + create_order => 54, + create_sql => + 'INSERT INTO dump_test.test_table_identity_dropped (col2) VALUES (\'test\');', + regexp => qr/^ + \QCOPY dump_test.test_table_identity_dropped (col2) FROM stdin;\E + \ntest\n\\\.\n + /xm, + like => { + %full_runs, + %dump_test_schema_runs, + data_only => 1, + no_schema => 1, + section_data => 1, + }, + unlike => { + binary_upgrade => 1, + exclude_dump_test_schema => 1, + schema_only => 1, + only_dump_measurement => 1, + }, + }, + 'INSERT INTO test_table' => { regexp => qr/^ (?:INSERT\ INTO\ dump_test\.test_table\ \(col1,\ col2,\ col3,\ col4\)\ VALUES\ \(\d,\ NULL,\ NULL,\ NULL\);\n){9} @@ -2100,6 +2123,28 @@ my %tests = ( like => { column_inserts => 1, }, }, + 'INSERT INTO test_table_identity_dropped' => { + regexp => + qr/^\QINSERT INTO dump_test.test_table_identity_dropped (col2) VALUES ('test');\E/m, + like => { column_inserts => 1, }, + }, + + 'INSERT INTO test_table_identity_dropped (no column list)' => { + regexp => + qr/^\QINSERT INTO dump_test.test_table_identity_dropped VALUES ('test');\E/m, + like => { inserts => 1, }, + }, + + 'OVERRIDING SYSTEM VALUE for dropped identity column' => { + regexp => qr/^ + \QINSERT INTO dump_test.test_table_identity_dropped\E + .*\QOVERRIDING SYSTEM VALUE\E + /xm, + + # the table has no identity column anymore, so this must never appear + like => {}, + }, + 'CREATE ROLE regress_dump_test_role' => { create_order => 1, create_sql => 'CREATE ROLE regress_dump_test_role;', @@ -3881,6 +3926,28 @@ my %tests = ( }, }, + 'CREATE TABLE test_table_identity_dropped' => { + create_order => 3, + create_sql => 'CREATE TABLE dump_test.test_table_identity_dropped ( + col1 int generated always as identity, + col2 text + ); + ALTER TABLE dump_test.test_table_identity_dropped + DROP COLUMN col1;', + regexp => qr/^ + \QCREATE TABLE dump_test.test_table_identity_dropped (\E\n + \s+\Qcol2 text\E\n + \); + /xm, + like => + { %full_runs, %dump_test_schema_runs, section_pre_data => 1, }, + unlike => { + binary_upgrade => 1, + exclude_dump_test_schema => 1, + only_dump_measurement => 1, + }, + }, + 'CREATE TABLE test_table_generated' => { create_order => 3, create_sql => 'CREATE TABLE dump_test.test_table_generated (