Reject COPY TO of unlogged relations during recovery

Started by Jim Jones8 days ago10 messageshackers
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

won't retrysuccessCI history

This thread has been committed, so CI has stopped here. Anything below is the last result it produced.

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t254219
psql -h localhost -U postgres

Built from patchset v7 (message #7), October 21, 2026 at 02:55 PM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t254219_7 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t254219_7 && git checkout t254219_7

Patchset v7 (message #7) is on t254219_7

Jump to latest
#1Jim Jones
jim.jones@uni-muenster.de

Hi

While testing the unlogged materialized views patch I noticed that copy
with an unlogged table from a standby tries to read a file that does not
exist on disk:

== primary ==

psql (20devel)
Type "help" for help.

postgres=# create unlogged table ut as select 1;
SELECT 1

== standby ==

psql (20devel)
Type "help" for help.

postgres=# copy ut to stdout;
ERROR: could not open file "base/5/16384": No such file or directory

I could reproduce this bug on PG14 as well:

== primary ==

psql (14.23 (Debian 14.23-1.pgdg13+1))
Geben Sie »help« für Hilfe ein.

postgres=# create unlogged table ut1 as select 1;
SELECT 1

== standby ==

psql (14.23 (Debian 14.23-1.pgdg13+1))
Geben Sie »help« für Hilfe ein.

postgres=# copy ut1 to stdout;
ERROR: could not open file "base/13759/772691051": No such file or
directory

I did some digging and found this thread[1] fixing a similar problem in
pg_dump, but I couldn't find anything similar on the server. I managed
to get a proper error message by adding a check in copyto.c (patch
attached), but I'm not sure this is the right place for it.

psql (20devel)
Type "help" for help.

postgres=# copy ut to stdout;
ERROR: cannot access temporary or unlogged relations during recovery

Before I put too much effort on tests I would like to hear your thoughts
on the approach.

Best, Jim

1 -
/messages/by-id/E1TutDO-0001Hb-2v@wrigleys.postgresql.org

Attachments:

t254219_1
v1-0001-Reject-COPY-TO-of-unlogged-relations-during-recov.patchtext/x-patch; charset=UTF-8; name=v1-0001-Reject-COPY-TO-of-unlogged-relations-during-recov.patchDownload+20-1
#2Zsolt Parragi
zsolt.parragi@percona.com
In reply to: Jim Jones (#1)
Re: Reject COPY TO of unlogged relations during recovery

Nice catch!

Claude pointed out that a few contrib modules are also affected (pgstattuple, pg_visibility, pageinspect and pgrowlocks)

I think the location is right for the COPY TO case, but maybe it would be the best to handle all locations in the same patch(set)?

+		foreach_oid(child, children)
+		{
+			if (get_rel_persistence(child) != RELPERSISTENCE_PERMANENT)

Can we put this check in the existing loop above? And maybe the error detail could also name the specific relation?

#3Jim Jones
jim.jones@uni-muenster.de
In reply to: Zsolt Parragi (#2)
Re: Reject COPY TO of unlogged relations during recovery

Hi Zsolt

On 15/10/2026 23:38, Zsolt Parragi wrote:

Claude pointed out that a few contrib modules are also affected
(pgstattuple, pg_visibility, pageinspect and pgrowlocks)

I think the location is right for the COPY TO case, but maybe it would
be the best to handle all locations in the same patch(set)?

The fix itself here shouldn't be too difficult, but I anticipate some
questions on "how" unlogged tables should be treated, for instance
amcheck seems to simply skips it:

postgres=# set client_min_messages to debug1;
SET
postgres=# select * from verify_heapam('ut');
DEBUG: cannot verify unlogged relation "ut" during recovery, skipping
blkno | offnum | attnum | msg
-------+--------+--------+-----
(0 rows)

So I am not sure how the mentioned contrib modules should behave. But I
agree that anything is better than the current error message:

postgres=# select * from pgrowlocks('ut');
ERROR: could not open file "base/5/16410": No such file or directory

That said, I would be in favour of creating a separate patch for the
contrib modules. WDYT?

+		foreach_oid(child, children)
+		{
+			if (get_rel_persistence(child) != RELPERSISTENCE_PERMANENT)

Can we put this check in the existing loop above? And maybe the error
detail could also name the specific relation?

I moved the check to the existing loop and added the relation name to
errdetail on partition tables:

== primary ==

psql (20devel)
Type "help" for help.

postgres=# create unlogged table ut as select 1 ;
SELECT 1
postgres=# create table pt (a int) partition by range (a);
CREATE TABLE
postgres=# create unlogged table pt_p1 partition of pt for values from
(0) to (10);
CREATE TABLE

== standby ==

psql (20devel)
Type "help" for help.

postgres=# copy ut to stdout;
ERROR: cannot access temporary or unlogged relations during recovery
postgres=# copy pt to stdout;
ERROR: cannot access temporary or unlogged relations during recovery
DETAIL: Partition "pt_p1" is an unlogged table in partitioned table "pt".

I didn't add the relation name to non-partitioned unlogged tables to
make it consistent with the errmsg of a select:

postgres=# select * from ut;
ERROR: cannot access temporary or unlogged relations during recovery

PFA v2.

Thanks for the review!

Best, Jim

Attachments:

t254219_3
v2-0001-Reject-COPY-TO-of-unlogged-tables-during-recovery.patchtext/x-patch; charset=UTF-8; name=v2-0001-Reject-COPY-TO-of-unlogged-tables-during-recovery.patchDownload+46-1
#4Jim Jones
jim.jones@uni-muenster.de
In reply to: Jim Jones (#3)
Re: Reject COPY TO of unlogged relations during recovery

On 16/10/2026 09:34, Jim Jones wrote:

So I am not sure how the mentioned contrib modules should behave. But I
agree that anything is better than the current error message:

postgres=# select * from pgrowlocks('ut');
ERROR: could not open file "base/5/16410": No such file or directory

That said, I would be in favour of creating a separate patch for the
contrib modules. WDYT?

I took a look at the contrib modules and the fix is pretty much
identical everywhere. Regarding how to approach it, it I guess where a
function fails today with "could not open file", it's fine to report a
proper error message instead -- this rather unhelpful error message is
sitting there for a long time and didn't seem to bother anybody.

Thoughts?

Best, Jim

#5Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Jim Jones (#4)
Re: Reject COPY TO of unlogged relations during recovery

On Fri, Oct 16, 2026 at 4:06 AM Jim Jones <jim.jones@uni-muenster.de> wrote:

On 16/10/2026 09:34, Jim Jones wrote:

So I am not sure how the mentioned contrib modules should behave. But I
agree that anything is better than the current error message:

postgres=# select * from pgrowlocks('ut');
ERROR: could not open file "base/5/16410": No such file or directory

That said, I would be in favour of creating a separate patch for the
contrib modules. WDYT?

I took a look at the contrib modules and the fix is pretty much
identical everywhere. Regarding how to approach it, it I guess where a
function fails today with "could not open file", it's fine to report a
proper error message instead -- this rather unhelpful error message is
sitting there for a long time and didn't seem to bother anybody.

Thoughts?

While I agree that we can report a proper error message instead at
these places, I think we need to note that pageinspect's check for
init fork should work even on the standby.

Also, currtid2() seems to have the same issue.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

#6Jim Jones
jim.jones@uni-muenster.de
In reply to: Masahiko Sawada (#5)
Re: Reject COPY TO of unlogged relations during recovery

On 16/10/2026 20:11, Masahiko Sawada wrote:

While I agree that we can report a proper error message instead at
these places, I think we need to note that pageinspect's check for
init fork should work even on the standby.

Ok, I will create a followup thread only with the contrib fixes and take
this into account for pageinspect. Thanks.

Also, currtid2() seems to have the same issue.

Yeah, I can reproduce the same issue in currtid2().

psql (20devel)
Type "help" for help.

postgres=# SELECT currtid2('t', '(0,1)'::tid);
ERROR: could not open file "base/5/16384": No such file or directory

0002 attached adds a check to report an error message for it.

Thanks!

Best, Jim

Attachments:

t254219_6
v3-0001-Reject-COPY-TO-of-unlogged-tables-during-recovery.patchtext/x-patch; charset=UTF-8; name=v3-0001-Reject-COPY-TO-of-unlogged-tables-during-recovery.patchDownload+46-1
v3-0002-Reject-currtid2-on-unlogged-tables-during-recover.patchtext/x-patch; charset=UTF-8; name=v3-0002-Reject-currtid2-on-unlogged-tables-during-recover.patchDownload+7-1
#7Jim Jones
jim.jones@uni-muenster.de
In reply to: Jim Jones (#6)
Re: Reject COPY TO of unlogged relations during recovery

On 17/10/2026 13:01, Jim Jones wrote:

Ok, I will create a followup thread only with the contrib fixes and take
this into account for pageinspect. Thanks.

I created another CF entry dedicated to the contrib modules:

https://commitfest.postgresql.org/patch/7478/

I also added a small test into 001_stream_rep.pl to check the currti2 fix.

Thanks!

Best, Jim

Attachments:

t254219_7
v4-0001-Reject-COPY-TO-of-unlogged-tables-during-recovery.patchtext/x-patch; charset=UTF-8; name=v4-0001-Reject-COPY-TO-of-unlogged-tables-during-recovery.patchDownload+46-1
v4-0002-Reject-currtid2-on-unlogged-tables-during-recover.patchtext/x-patch; charset=UTF-8; name=v4-0002-Reject-currtid2-on-unlogged-tables-during-recover.patchDownload+30-1
#8Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Jim Jones (#7)
Re: Reject COPY TO of unlogged relations during recovery

On Sun, Oct 18, 2026 at 7:14 AM Jim Jones <jim.jones@uni-muenster.de> wrote:

On 17/10/2026 13:01, Jim Jones wrote:

Ok, I will create a followup thread only with the contrib fixes and take
this into account for pageinspect. Thanks.

These patches look good to me. Both patches should be backpatched to
v14. I'll push them, barring objections.

I created another CF entry dedicated to the contrib modules:

https://commitfest.postgresql.org/patch/7478/

Thanks. I'll look at it.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

#9Jim Jones
jim.jones@uni-muenster.de
In reply to: Masahiko Sawada (#8)
Re: Reject COPY TO of unlogged relations during recovery

On 10/20/26 10:52 PM, Masahiko Sawada wrote:

These patches look good to me. Both patches should be backpatched to
v14. I'll push them, barring objections.

Thanks!

Best, Jim

#10Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Jim Jones (#9)
Re: Reject COPY TO of unlogged relations during recovery

On Tue, Oct 20, 2026 at 3:12 PM Jim Jones <jim.jones@uni-muenster.de> wrote:

On 10/20/26 10:52 PM, Masahiko Sawada wrote:

These patches look good to me. Both patches should be backpatched to
v14. I'll push them, barring objections.

Thanks!

I realized that currtid2() is an internal undocumented function so we
don't necessarily need to have the regression tests. So I've pushed
the patches after removing the regression test.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com