1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

Reject COPY TO of unlogged tables during recovery.

Unlogged tables have no data on a standby, so reading one there must
fail. SELECT and COPY (SELECT ...) TO already report a clear error,
but COPY with a table name read the table directly and failed with a
low-level error instead:

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

Check for this case in COPY TO and report the same error as SELECT. On
v19 and later, also check each partition when copying from a
partitioned table, so that an unlogged partition is reported too.

Backpatch to all supported versions, since the low-level error looks
like data corruption.

Author: Jim Jones <jim.jones@uni-muenster.de>
Reviewed-by: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Discussion: https://postgr.es/m/b6d71815-d362-4579-bf07-73eca01aee6d@uni-muenster.de
Backpatch-through: 14
This commit is contained in:
Masahiko Sawada
2026-10-21 10:44:03 -07:00
parent dd568b047a
commit 1e707d3256
2 changed files with 46 additions and 0 deletions

View File

@@ -21,6 +21,7 @@
#include "access/table.h"
#include "access/tableam.h"
#include "access/tupconvert.h"
#include "access/xlog.h"
#include "catalog/pg_inherits.h"
#include "commands/copyapi.h"
#include "commands/progress.h"
@@ -877,6 +878,19 @@ BeginCopyTo(ParseState *pstate,
errhint("Try the COPY (SELECT ...) TO variant."));
}
/* Unlogged partitions are inaccessible during recovery. */
if (RecoveryInProgress() &&
get_rel_persistence(child) == RELPERSISTENCE_UNLOGGED)
{
char *relation_name = get_rel_name(child);
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot access temporary or unlogged relations during recovery"),
errdetail("Partition \"%s\" is an unlogged table in partitioned table \"%s\".",
relation_name, RelationGetRelationName(rel)));
}
/* Exclude tables with no data */
if (RELKIND_HAS_PARTITIONS(relkind))
children = foreach_delete_current(children, child);
@@ -889,6 +903,11 @@ BeginCopyTo(ParseState *pstate,
RelationGetRelationName(rel))));
}
/* Temporary and unlogged relations are inaccessible during recovery. */
if (rel != NULL && !RelationIsPermanent(rel) && RecoveryInProgress())
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot access temporary or unlogged relations during recovery")));
/* Allocate workspace and zero all fields */
cstate = palloc0_object(CopyToStateData);

View File

@@ -113,6 +113,33 @@ is( $node_standby_1->safe_psql(
't',
'pg_sequence_last_value() on unlogged sequence on standby 1');
# Check that COPY TO rejects unlogged tables on standby, including when
# reached through a partitioned table, but still copies permanent ones
$node_primary->safe_psql(
'postgres', q{
CREATE UNLOGGED TABLE ultab (a int);
CREATE TABLE ulpart (a int) PARTITION BY RANGE (a);
CREATE UNLOGGED TABLE ulpart_1 PARTITION OF ulpart FOR VALUES FROM (0) TO (10);
CREATE TABLE lpart (a int) PARTITION BY RANGE (a);
CREATE TABLE lpart_1 PARTITION OF lpart FOR VALUES FROM (0) TO (10);
INSERT INTO lpart VALUES (1);
});
$node_primary->wait_for_replay_catchup($node_standby_1);
my ($copy_ret, $copy_stdout, $copy_stderr) =
$node_standby_1->psql('postgres', 'COPY ultab TO STDOUT');
like(
$copy_stderr,
qr/cannot access temporary or unlogged relations during recovery/,
'COPY TO of unlogged table on standby 1');
($copy_ret, $copy_stdout, $copy_stderr) =
$node_standby_1->psql('postgres', 'COPY ulpart TO STDOUT');
like(
$copy_stderr,
qr/cannot access temporary or unlogged relations during recovery.*\n.*DETAIL: Partition "ulpart_1" is an unlogged table in partitioned table "ulpart"/,
'COPY TO of partitioned table with unlogged partition on standby 1');
is($node_standby_1->safe_psql('postgres', 'COPY lpart TO STDOUT'),
'1', 'COPY TO of partitioned table on standby 1');
# Check that only READ-only queries can run on standbys
is($node_standby_1->psql('postgres', 'INSERT INTO tab_int VALUES (1)'),
3, 'read-only queries on standby 1');