From 4cd9ef0f973600f63d3886674d8afa28574e94cc Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <andrew@dunslane.net>
Date: Sat, 15 Aug 2026 16:14:37 -0400
Subject: [PATCH] Fix heapam_relation_toast_am() to return the literal heap AM
 oid.

heapam_relation_toast_am() returned rel->rd_rel->relam instead of the
heap AM oid, assuming the two are always equal since only heap
relations were expected to reach it. That breaks for a table AM that
reuses this callback by copying heap's whole TableAmRoutine:
rel->rd_rel->relam is then the AM's own oid, so
NewRelationCreateToastTable() makes the TOAST table that AM
too. Building its chunk_id/chunk_seq index then fails in
heap_getnext(), which requires rd_tableam to be literally
GetHeapamTableAmRoutine() -- "only heap AM is supported" for any table
with a toastable column.

Return the literal HEAP_TABLE_AM_OID instead: TOAST tables are always
plain heap, which is what the function's own comment already said.

Discovered while investigating a report from Zsolt Parragi.

Backpatch-thru: 14
---
 src/backend/access/heap/heapam_handler.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index bf87430cf01..6b3d5fc46c0 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -31,6 +31,7 @@
 #include "access/xact.h"
 #include "catalog/catalog.h"
 #include "catalog/index.h"
+#include "catalog/pg_am_d.h"
 #include "catalog/storage.h"
 #include "catalog/storage_xlog.h"
 #include "commands/progress.h"
@@ -2048,12 +2049,22 @@ heapam_relation_needs_toast_table(Relation rel)
 }

 /*
- * TOAST tables for heap relations are just heap relations.
+ * TOAST tables are always plain heap relations, regardless of the AM of
+ * the table they belong to.  Return the literal heap AM oid rather than
+ * rel->rd_rel->relam: those are only the same value when rel is itself a
+ * genuine heap relation.  A table AM that reuses this callback (e.g. by
+ * copying the whole heap TableAmRoutine and overriding only a handful of
+ * callbacks) is not itself heap, so returning rel->rd_rel->relam would
+ * create its TOAST table using that AM instead -- and storage-layer code
+ * that still calls heap_getnext() directly (see its comment) rejects any
+ * relation whose rd_tableam is not literally GetHeapamTableAmRoutine(),
+ * which fails as soon as anything scans that TOAST table, e.g. to build
+ * its chunk_id/chunk_seq index.
  */
 static Oid
 heapam_relation_toast_am(Relation rel)
 {
-	return rel->rd_rel->relam;
+	return HEAP_TABLE_AM_OID;
 }


--
2.43.0
