From 379820592d0d7df18adf7ec18589a828bdefb07b Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 14 Aug 2025 10:57:59 +0900
Subject: [PATCH v5 10/15] Add support for TOAST chunk_id type in binary
 upgrades

This commit adds a new function, which would set the type of a chunk_id
attribute for a TOAST table across upgrades.  This piece currently works
only with chunk_id = OIDOID, but it is required in a follow-up patch
where support for chunk_id = OID8OID is supported on top of the existing
one.
---
 src/include/catalog/binary_upgrade.h          |  1 +
 src/include/catalog/pg_proc.dat               |  4 ++++
 src/backend/catalog/heap.c                    |  1 +
 src/backend/catalog/toasting.c                | 20 ++++++++++++++++++-
 src/backend/utils/adt/pg_upgrade_support.c    | 11 ++++++++++
 src/bin/pg_dump/pg_dump.c                     | 10 +++++++++-
 .../expected/spgist_name_ops.out              |  6 ++++--
 7 files changed, 49 insertions(+), 4 deletions(-)

diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 6fcc59edebd8..3deb0423d795 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -29,6 +29,7 @@ extern PGDLLIMPORT Oid binary_upgrade_next_index_pg_class_oid;
 extern PGDLLIMPORT RelFileNumber binary_upgrade_next_index_pg_class_relfilenumber;
 extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_class_oid;
 extern PGDLLIMPORT RelFileNumber binary_upgrade_next_toast_pg_class_relfilenumber;
+extern PGDLLIMPORT Oid binary_upgrade_next_toast_chunk_id_typoid;
 
 extern PGDLLIMPORT Oid binary_upgrade_next_pg_enum_oid;
 extern PGDLLIMPORT Oid binary_upgrade_next_pg_authid_oid;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 49b830de2099..183ef6fd6ac2 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11759,6 +11759,10 @@
   proname => 'binary_upgrade_set_next_toast_pg_class_oid', provolatile => 'v',
   proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
   prosrc => 'binary_upgrade_set_next_toast_pg_class_oid' },
+{ oid => '8219', descr => 'for use by pg_upgrade',
+  proname => 'binary_upgrade_set_next_toast_chunk_id_typoid', provolatile => 'v',
+  proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+  prosrc => 'binary_upgrade_set_next_toast_chunk_id_typoid' },
 { oid => '3589', descr => 'for use by pg_upgrade',
   proname => 'binary_upgrade_set_next_pg_enum_oid', provolatile => 'v',
   proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index fd6537567ea2..e5cc98937055 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -80,6 +80,7 @@
 /* Potentially set by pg_upgrade_support functions */
 Oid			binary_upgrade_next_heap_pg_class_oid = InvalidOid;
 Oid			binary_upgrade_next_toast_pg_class_oid = InvalidOid;
+Oid			binary_upgrade_next_toast_chunk_id_typoid = InvalidOid;
 RelFileNumber binary_upgrade_next_heap_pg_class_relfilenumber = InvalidRelFileNumber;
 RelFileNumber binary_upgrade_next_toast_pg_class_relfilenumber = InvalidRelFileNumber;
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 874a8fc89adb..f1d76d8acd51 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -145,6 +145,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	int16		coloptions[2];
 	ObjectAddress baseobject,
 				toastobject;
+	Oid			toast_chunkid_typid = OIDOID;
 
 	/*
 	 * Is it already toasted?
@@ -183,6 +184,23 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 		 */
 		if (!OidIsValid(binary_upgrade_next_toast_pg_class_oid))
 			return false;
+
+		/*
+		 * The attribute type for chunk_id should have been set when requesting
+		 * a TOAST table creation.
+		 */
+		if (!OidIsValid(binary_upgrade_next_toast_chunk_id_typoid))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("toast chunk_id type not set while in binary upgrade mode")));
+		if (binary_upgrade_next_toast_chunk_id_typoid != OIDOID)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot support toast chunk_id type %u in binary upgrade mode",
+							binary_upgrade_next_toast_chunk_id_typoid)));
+
+		toast_chunkid_typid = binary_upgrade_next_toast_chunk_id_typoid;
+		binary_upgrade_next_toast_chunk_id_typoid = InvalidOid;
 	}
 
 	/*
@@ -204,7 +222,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	tupdesc = CreateTemplateTupleDesc(3);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1,
 					   "chunk_id",
-					   OIDOID,
+					   toast_chunkid_typid,
 					   -1, 0);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 2,
 					   "chunk_seq",
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index a4f8b4faa90d..200ffcdbab44 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -149,6 +149,17 @@ binary_upgrade_set_next_toast_pg_class_oid(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
+Datum
+binary_upgrade_set_next_toast_chunk_id_typoid(PG_FUNCTION_ARGS)
+{
+	Oid			typoid = PG_GETARG_OID(0);
+
+	CHECK_IS_BINARY_UPGRADE;
+	binary_upgrade_next_toast_chunk_id_typoid = typoid;
+
+	PG_RETURN_VOID();
+}
+
 Datum
 binary_upgrade_set_next_toast_relfilenode(PG_FUNCTION_ARGS)
 {
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index fc7a66391633..ef2f6c6bf332 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -103,6 +103,7 @@ typedef struct
 	RelFileNumber relfilenumber;	/* object filenode */
 	Oid			toast_oid;		/* toast table OID */
 	RelFileNumber toast_relfilenumber;	/* toast table filenode */
+	Oid			toast_chunk_id_typoid;	/* type of chunk_id attribute */
 	Oid			toast_index_oid;	/* toast table index OID */
 	RelFileNumber toast_index_relfilenumber;	/* toast table index filenode */
 } BinaryUpgradeClassOidItem;
@@ -5731,7 +5732,10 @@ collectBinaryUpgradeClassOids(Archive *fout)
 	const char *query;
 
 	query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
-		"ct.relfilenode, i.indexrelid, cti.relfilenode "
+		"ct.relfilenode, i.indexrelid, cti.relfilenode, "
+		"(SELECT a.atttypid FROM pg_attribute AS a "
+		"  WHERE a.attrelid = c.reltoastrelid AND attname = 'chunk_id'::text) "
+		"  AS toastchunktypid "
 		"FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
 		"ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
 		"LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
@@ -5753,6 +5757,7 @@ collectBinaryUpgradeClassOids(Archive *fout)
 		binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
 		binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
 		binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
+		binaryUpgradeClassOids[i].toast_chunk_id_typoid = atooid(PQgetvalue(res, i, 7));
 	}
 
 	PQclear(res);
@@ -5817,6 +5822,9 @@ binary_upgrade_set_pg_class_oids(Archive *fout,
 			appendPQExpBuffer(upgrade_buffer,
 							  "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
 							  entry->toast_relfilenumber);
+			appendPQExpBuffer(upgrade_buffer,
+							  "SELECT pg_catalog.binary_upgrade_set_next_toast_chunk_id_typoid('%u'::pg_catalog.oid);\n",
+							  entry->toast_chunk_id_typoid);
 
 			/* every toast table has an index */
 			appendPQExpBuffer(upgrade_buffer,
diff --git a/src/test/modules/spgist_name_ops/expected/spgist_name_ops.out b/src/test/modules/spgist_name_ops/expected/spgist_name_ops.out
index 1ee65ede2430..35e59d0cd83c 100644
--- a/src/test/modules/spgist_name_ops/expected/spgist_name_ops.out
+++ b/src/test/modules/spgist_name_ops/expected/spgist_name_ops.out
@@ -61,9 +61,10 @@ select * from t
  binary_upgrade_set_next_pg_enum_oid                  |    | binary_upgrade_set_next_pg_enum_oid
  binary_upgrade_set_next_pg_tablespace_oid            |    | binary_upgrade_set_next_pg_tablespace_oid
  binary_upgrade_set_next_pg_type_oid                  |    | binary_upgrade_set_next_pg_type_oid
+ binary_upgrade_set_next_toast_chunk_id_typoid        |    | binary_upgrade_set_next_toast_chunk_id_typoid
  binary_upgrade_set_next_toast_pg_class_oid           |  1 | binary_upgrade_set_next_toast_pg_class_oid
  binary_upgrade_set_next_toast_relfilenode            |    | binary_upgrade_set_next_toast_relfilenode
-(13 rows)
+(14 rows)
 
 -- Verify clean failure when INCLUDE'd columns result in overlength tuple
 -- The error message details are platform-dependent, so show only SQLSTATE
@@ -110,9 +111,10 @@ select * from t
  binary_upgrade_set_next_pg_enum_oid                  |    | binary_upgrade_set_next_pg_enum_oid
  binary_upgrade_set_next_pg_tablespace_oid            |    | binary_upgrade_set_next_pg_tablespace_oid
  binary_upgrade_set_next_pg_type_oid                  |    | binary_upgrade_set_next_pg_type_oid
+ binary_upgrade_set_next_toast_chunk_id_typoid        |    | binary_upgrade_set_next_toast_chunk_id_typoid
  binary_upgrade_set_next_toast_pg_class_oid           |  1 | binary_upgrade_set_next_toast_pg_class_oid
  binary_upgrade_set_next_toast_relfilenode            |    | binary_upgrade_set_next_toast_relfilenode
-(13 rows)
+(14 rows)
 
 \set VERBOSITY sqlstate
 insert into t values(repeat('xyzzy', 12), 42, repeat('xyzzy', 4000));
-- 
2.50.0

