From fef696babcbaf65a97ac0d2bc76717709f4db153 Mon Sep 17 00:00:00 2001
From: William Bernbaum <wbernbaum@dwdev.com>
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 (
