Hi Surya

On 04/11/2026 00:37, surya poondla wrote:
> I tested v3 on a primary/standby pair built on top of eaa5961efc6.
> All five patches apply cleanly and build without warnings.  
> On the standby, every new check I tried reports the new error for all
> the functions you reported.
> get_raw_page(..., 'init', 0) on an unlogged index still works, as intended.


Thank you very much for the thorough review!

> 
> A few comments:
> 1) In 0001, the detoast guard checks the relation passed by the caller,
> but detoasting follows va_toastrelid from the pointer itself, so the two
> can disagree.  In addition to Kirill's u_toast/u_snapshot, I
> created on the primary:
> 
>     CREATE TABLE logged_text (b text);
>     CREATE TABLE l_toast (b text);
>     ALTER TABLE l_toast ALTER COLUMN b SET STORAGE EXTERNAL;
>     INSERT INTO l_toast VALUES (repeat('abc', 3000));
>     CREATE TABLE l_snapshot AS SELECT get_raw_page('l_toast', 0) AS raw;
> 
> Then on the standby:
> 
>     -- page from an unlogged table, permanent table passed in
>     SELECT octet_length(t_attrs[1]) FROM u_snapshot,
>       LATERAL heap_page_item_attrs(raw, 'logged_text'::regclass, true);
>     ERROR:  could not open file "base/5/16468": No such file or directory
> 
>     -- page from a permanent table, unlogged table passed in
>     SELECT octet_length(t_attrs[1]) FROM l_snapshot,
>       LATERAL heap_page_item_attrs(raw, 'u_toast'::regclass, true);
>     ERROR:  cannot access temporary or unlogged relations during recovery
> 
> The second value is readable on the standby.  Checking the TOAST
> relation from the pointer fixes both, and still catches Kirill's case:
> 
>     if (VARATT_IS_EXTERNAL_ONDISK(tupdata + off) &&
>           RecoveryInProgress())
>       {
>           toast_external_data toast_ext_data;
> 
>           toast_external_info_get((varlena *) (tupdata + off),
>                                   &toast_ext_data);
>           /* If the relation is gone, let detoasting complain. */
>           if (SearchSysCacheExists1(RELOID,
>                                    
> ObjectIdGetDatum(toast_ext_data.toastrelid)) &&
>               get_rel_persistence(toast_ext_data.toastrelid) !=
>               RELPERSISTENCE_PERMANENT)
>               ereport(ERROR, ...);
>       }
> 
> toast_external_info_get() handles both the OID and OID8 pointer formats.
> The existence check matters for a saved page whose TOAST
> table has since been dropped, without it get_rel_persistence() fails
> with "cache lookup failed for relation", while with it the error stays
> "could not open relation with OID", as in v3.


Nice catch.
v4 now fixes this gap.


> 
>   2) pg_freespacemap has the same problem as pg_visibility_map*.  On the
> standby, pg_freespace('ut', 0) returns 0 while the primary returns
> 3968.  For another session's temp table, it returns 0 if the table has
> no FSM fork yet, but fails with "cannot access temporary tables of other
> sessions" once it has one.  Attached is 0006, which adds both
> checks to pg_freespace(), as 0004 and 0005 do for pg_visibility; the
> module's regression test passes with it.


Yeah, this module also has the same issue.
v4 has your 0006.


> 3) Regarding Michael's point about duplication, pg_visibility now has a
> single check, but pgstattuple still carries six copies and pageinspect
> three (plus the variants in get_raw_page and the detoast path), and core
> has three more of the same shape (plancat.c, copyto.c,
> tid.c).  Would a small core helper make sense here, something like
> CheckRelationReadableDuringRecovery(Relation), so all of these share one
> place and one message?


I'm not so sure about this one. The idea itself is quite reasonable, but
I suspect it would add more complexity to core for very little gain.
We'd also need to change copyto.c, plancat.c, and tid.c, which share the
same message. If we agree that it should be done nevertheless, I'd say
it belongs to another refactoring patch. WDYT?


> A minor comment, pg_prewarm on an unlogged table on a standby reports
> 'fork "main" does not exist for this relation', which is not wrong but
> could use the same check and message as the other modules.


Since the message is not wrong, I'd vote to keep it as is. This patch
set keeps growing and I'd not like to take too much time from the
committers for yet another cosmetic change.

PFA v4. 0002-0005 are unchanged.

Thank you!

Best, Jim

From 4911e889f0de003a55ea01c2b1c777a1bf93a924 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Sun, 25 Oct 2026 11:06:06 +0100
Subject: [PATCH v4 1/6] pageinspect: Reject unlogged relations during recovery

Unlogged tables and indexes have no data on a standby, so reading one
there must fail.  get_raw_page(), bt_metap(), bt_page_stats(),
bt_multi_page_stats(), bt_page_items() and hash_bitmap_info() read the
relation directly instead of going through the planner, so on a standby
they failed with a confusing low-level message instead of the error
that SELECT reports:

  ERROR:  could not open file "base/5/16384": No such file or directory

Check for this case and report the same error as SELECT:

  ERROR:  cannot access temporary or unlogged relations during recovery

The init fork of an unlogged relation does exist on a standby, so
get_raw_page() can still read it.

heap_page_item_attrs() and tuple_data_split() can also reach unlogged
storage when detoasting values from a saved page image, so check for
this case before detoasting as well.

Author: Jim Jones <[email protected]>
Co-authored-by: Kirill Reshke <[email protected]>
Co-authored-by: Surya Poondla <[email protected]>
Reported-by: Zsolt Parragi <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
---
 contrib/pageinspect/btreefuncs.c | 13 +++++++++++++
 contrib/pageinspect/hashfuncs.c  |  7 +++++++
 contrib/pageinspect/heapfuncs.c  | 25 +++++++++++++++++++++++++
 contrib/pageinspect/rawpage.c    | 11 +++++++++++
 4 files changed, 56 insertions(+)

diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index 9917663593b..676afe51de8 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -30,6 +30,7 @@
 #include "access/htup_details.h"
 #include "access/nbtree.h"
 #include "access/relation.h"
+#include "access/xlog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
@@ -241,6 +242,12 @@ bt_index_block_validate(Relation rel, int64 blkno)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot access temporary tables of other sessions")));
 
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	if (blkno == 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -874,6 +881,12 @@ bt_metap(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot access temporary tables of other sessions")));
 
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	buffer = ReadBuffer(rel, 0);
 	LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
diff --git a/contrib/pageinspect/hashfuncs.c b/contrib/pageinspect/hashfuncs.c
index 30870329cc9..0f728d9add8 100644
--- a/contrib/pageinspect/hashfuncs.c
+++ b/contrib/pageinspect/hashfuncs.c
@@ -13,6 +13,7 @@
 #include "access/hash.h"
 #include "access/htup_details.h"
 #include "access/relation.h"
+#include "access/xlog.h"
 #include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
@@ -431,6 +432,12 @@ hash_bitmap_info(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot access temporary tables of other sessions")));
 
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(indexRel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	if (ovflblkno < 0 || ovflblkno > MaxBlockNumber)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 7be4770dc84..b2a14444dcc 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -25,8 +25,10 @@
 
 #include "postgres.h"
 
+#include "access/detoast.h"
 #include "access/htup_details.h"
 #include "access/relation.h"
+#include "access/xlog.h"
 #include "catalog/pg_am_d.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
@@ -36,7 +38,9 @@
 #include "port/pg_bitutils.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 #include "utils/rel.h"
+#include "utils/syscache.h"
 
 /*
  * It's not supported to create tuples with oids anymore, but when pg_upgrade
@@ -389,7 +393,28 @@ tuple_data_split_internal(Oid relid, char *tupdata,
 						 errmsg("unexpected end of tuple data")));
 
 			if (attr->attlen == -1 && do_detoast)
+			{
+				/* A saved page can still refer to unlogged TOAST storage. */
+				if (VARATT_IS_EXTERNAL_ONDISK(tupdata + off) &&
+					RecoveryInProgress())
+				{
+					toast_external_data toast_ext_data;
+
+					toast_external_info_get((varlena *) (tupdata + off),
+											&toast_ext_data);
+
+					/* If the relation is gone, let detoasting complain. */
+					if (SearchSysCacheExists1(RELOID,
+											  ObjectIdGetDatum(toast_ext_data.toastrelid)) &&
+						get_rel_persistence(toast_ext_data.toastrelid) !=
+						RELPERSISTENCE_PERMANENT)
+						ereport(ERROR,
+								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+								 errmsg("cannot access temporary or unlogged relations during recovery")));
+				}
+
 				attr_data = pg_detoast_datum_copy((varlena *) (tupdata + off));
+			}
 			else
 			{
 				attr_data = (bytea *) palloc(len + VARHDRSZ);
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index d136593edb2..05ecde3c20f 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -17,6 +17,7 @@
 
 #include "access/htup_details.h"
 #include "access/relation.h"
+#include "access/xlog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
@@ -175,6 +176,16 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot access temporary tables of other sessions")));
 
+	/*
+	 * Temporary and unlogged relations are inaccessible during recovery,
+	 * except for the init fork of an unlogged relation, which does exist.
+	 */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress() &&
+		forknum != INIT_FORKNUM)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	if (blkno >= RelationGetNumberOfBlocksInFork(rel, forknum))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-- 
2.56.0

From ada804d8d2031d9167f7a19f427a6a5f16779b4c Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Sun, 18 Oct 2026 14:07:37 +0200
Subject: [PATCH v4 2/6] pgrowlocks: Reject unlogged tables during recovery

Unlogged tables have no data on a standby, so reading one there must
fail.  pgrowlocks() reads the table directly instead of going through
the planner, so on a standby it failed with a confusing low-level
message instead of the error that SELECT reports:

  ERROR:  could not open file "base/5/16384": No such file or directory

Check for this case and report the same error as SELECT:

  ERROR:  cannot access temporary or unlogged relations during recovery

Author: Jim Jones <[email protected]>
Reported-by: Zsolt Parragi <[email protected]>
Reviewed-by: Kirill Reshke <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Surya Poondla <[email protected]>
---
 contrib/pgrowlocks/pgrowlocks.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 87bbde59937..84017271e98 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -29,6 +29,7 @@
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "access/xact.h"
+#include "access/xlog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_am_d.h"
 #include "catalog/pg_authid.h"
@@ -114,6 +115,12 @@ pgrowlocks(PG_FUNCTION_ARGS)
 		aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
 					   RelationGetRelationName(rel));
 
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	/* Scan the relation */
 	scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL, SO_NONE);
 	hscan = (HeapScanDesc) scan;
-- 
2.56.0

From 354f874a17091035dea21380e440a8b782a6b3b5 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Sun, 18 Oct 2026 14:09:33 +0200
Subject: [PATCH v4 3/6] pgstattuple: Reject unlogged relations during recovery

Unlogged tables and indexes have no data on a standby, so reading one
there must fail.  pgstattuple(), pgstattuple_approx(), pg_relpages(),
pgstatindex(), pgstatginindex() and pgstathashindex() read the relation
directly instead of going through the planner, so on a standby they
failed with a confusing low-level message instead of the error that
SELECT reports:

  ERROR:  could not open file "base/5/16384": No such file or directory

Check for this case and report the same error as SELECT:

  ERROR:  cannot access temporary or unlogged relations during recovery

Author: Jim Jones <[email protected]>
Reported-by: Zsolt Parragi <[email protected]>
Reviewed-by: Kirill Reshke <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Surya Poondla <[email protected]>
---
 contrib/pgstattuple/pgstatapprox.c |  7 +++++++
 contrib/pgstattuple/pgstatindex.c  | 25 +++++++++++++++++++++++++
 contrib/pgstattuple/pgstattuple.c  |  7 +++++++
 3 files changed, 39 insertions(+)

diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index 21e0b50fb4b..38b0cc951e2 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -16,6 +16,7 @@
 #include "access/htup_details.h"
 #include "access/relation.h"
 #include "access/visibilitymap.h"
+#include "access/xlog.h"
 #include "catalog/pg_am_d.h"
 #include "commands/vacuum.h"
 #include "funcapi.h"
@@ -332,6 +333,12 @@ pgstattuple_approx_internal(Oid relid, FunctionCallInfo fcinfo)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot access temporary tables of other sessions")));
 
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	/*
 	 * We support only relation kinds with a visibility map and a free space
 	 * map.
diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index 8951ad0aac4..87e79527361 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -32,6 +32,7 @@
 #include "access/htup_details.h"
 #include "access/nbtree.h"
 #include "access/relation.h"
+#include "access/xlog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_am.h"
 #include "funcapi.h"
@@ -238,6 +239,12 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot access temporary tables of other sessions")));
 
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	/*
 	 * A !indisready index could lead to ERRCODE_DATA_CORRUPTED later, so exit
 	 * early.  We're capable of assessing an indisready&&!indisvalid index,
@@ -482,6 +489,12 @@ pg_relpages_impl(Relation rel)
 						RelationGetRelationName(rel)),
 				 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
 
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	/* note: this will work OK on non-local temp tables */
 
 	relpages = RelationGetNumberOfBlocks(rel);
@@ -557,6 +570,12 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot access temporary indexes of other sessions")));
 
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	/* see pgstatindex_impl */
 	if (!rel->rd_index->indisvalid)
 		ereport(ERROR,
@@ -647,6 +666,12 @@ pgstathashindex(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot access temporary indexes of other sessions")));
 
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	/* see pgstatindex_impl */
 	if (!rel->rd_index->indisvalid)
 		ereport(ERROR,
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 6a7f8cb4a7c..f9495390673 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -30,6 +30,7 @@
 #include "access/nbtree.h"
 #include "access/relscan.h"
 #include "access/tableam.h"
+#include "access/xlog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_am_d.h"
 #include "funcapi.h"
@@ -254,6 +255,12 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot access temporary tables of other sessions")));
 
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind) ||
 		rel->rd_rel->relkind == RELKIND_SEQUENCE)
 	{
-- 
2.56.0

From 19283be498b42fb9a347d40741a11fc1d340d226 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Mon, 2 Nov 2026 10:03:26 +0100
Subject: [PATCH v4 4/6] pg_visibility: Reject unlogged tables during recovery

Unlogged tables have no data on a standby, so reading one there must
fail.  The pg_visibility functions read the table or its visibility map
directly instead of going through the planner.  On a standby, some of
them failed with a confusing low-level message instead of the error
that SELECT reports:

  ERROR:  could not open file "base/5/16384": No such file or directory

while pg_visibility_map(regclass, bigint) and pg_visibility_map_summary()
reported every page as neither all-visible nor all-frozen, because the
visibility map fork does not exist there.

Check for this case in check_relation_relkind(), which every function
calls, and report the same error as SELECT:

  ERROR:  cannot access temporary or unlogged relations during recovery

Author: Jim Jones <[email protected]>
Reported-by: Zsolt Parragi <[email protected]>
Reviewed-by: Kirill Reshke <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Surya Poondla <[email protected]>
---
 contrib/pg_visibility/pg_visibility.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 717c851583f..4195aeacc2a 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -13,6 +13,7 @@
 #include "access/heapam.h"
 #include "access/htup_details.h"
 #include "access/visibilitymap.h"
+#include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "catalog/pg_type.h"
 #include "catalog/storage_xlog.h"
@@ -918,7 +919,7 @@ tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
 
 /*
  * check_relation_relkind - convenience routine to check that relation
- * is of the relkind supported by the callers
+ * is of the relkind supported by the callers and can be accessed
  */
 static void
 check_relation_relkind(Relation rel)
@@ -929,4 +930,10 @@ check_relation_relkind(Relation rel)
 				 errmsg("relation \"%s\" is of wrong relation kind",
 						RelationGetRelationName(rel)),
 				 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
+
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
 }
-- 
2.56.0

From 3982630f7403a12a17e298becb0bf92ac400de1b Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Mon, 2 Nov 2026 10:18:29 +0100
Subject: [PATCH v4 5/6] pg_visibility: Reject temporary tables of other
 sessions

pg_visibility cannot see the pages of another session's temporary
table, but it did not reject such tables, so some of its functions
returned wrong results or modified that session's visibility map.
Report an error instead.

Author: Jim Jones <[email protected]>
Reported-by: Michael Paquier <[email protected]>
Reviewed-by: Surya Poondla <[email protected]>
---
 contrib/pg_visibility/pg_visibility.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 4195aeacc2a..86528cacaa2 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -931,6 +931,16 @@ check_relation_relkind(Relation rel)
 						RelationGetRelationName(rel)),
 				 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
 
+	/*
+	 * Reject attempts to read non-local temporary relations; we would be
+	 * likely to get wrong data since we have no visibility into the owning
+	 * session's local buffers.
+	 */
+	if (RELATION_IS_OTHER_TEMP(rel))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary tables of other sessions")));
+
 	/* Temporary and unlogged relations are inaccessible during recovery. */
 	if (!RelationIsPermanent(rel) && RecoveryInProgress())
 		ereport(ERROR,
-- 
2.56.0

From 9ab2734d2b2246db3ca16e06ec560ad0a33621e9 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Wed, 4 Nov 2026 07:24:12 +0100
Subject: [PATCH v4 6/6] pg_freespacemap: Reject relations whose free space map
 cannot be read

pg_freespace() returned 0 for unlogged relations during recovery and
for temporary tables of other sessions without a free space map fork,
because a missing free space map reads as no free space.  Report an
error instead.

Author: Surya Poondla <[email protected]>
Reviewed-by: Jim Jones <[email protected]>
---
 contrib/pg_freespacemap/pg_freespacemap.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/contrib/pg_freespacemap/pg_freespacemap.c b/contrib/pg_freespacemap/pg_freespacemap.c
index 610fc90b5ba..4952468695d 100644
--- a/contrib/pg_freespacemap/pg_freespacemap.c
+++ b/contrib/pg_freespacemap/pg_freespacemap.c
@@ -9,6 +9,7 @@
 #include "postgres.h"
 
 #include "access/relation.h"
+#include "access/xlog.h"
 #include "fmgr.h"
 #include "storage/freespace.h"
 #include "utils/rel.h"
@@ -41,6 +42,22 @@ pg_freespace(PG_FUNCTION_ARGS)
 						RelationGetRelationName(rel)),
 				 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
 
+	/*
+	 * Reject attempts to read non-local temporary relations; we would be
+	 * likely to get wrong data since we have no visibility into the owning
+	 * session's local buffers.
+	 */
+	if (RELATION_IS_OTHER_TEMP(rel))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary tables of other sessions")));
+
+	/* Temporary and unlogged relations are inaccessible during recovery. */
+	if (!RelationIsPermanent(rel) && RecoveryInProgress())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary or unlogged relations during recovery")));
+
 	if (blkno < 0 || blkno > MaxBlockNumber)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-- 
2.56.0

Reply via email to