postgres.git / summary / log / commit / refs

commit    0b54a80a38c79e2fda049c1a2f43de6ef7d45d7a
Author:   Michael Paquier <michael@paquier.xyz>
Date:     Wed Dec 23 03:51:51 2020 +0000
First released in: 14.0, 9.6.21, 10.16, 11.11, 12.6, 13.2

    Fix portability issues with parsing of recovery_target_xid
    
    The parsing of this parameter has been using strtoul(), which is not
    portable across platforms.  On most Unix platforms, unsigned long has a
    size of 64 bits, while on Windows it is 32 bits.  It is common in
    recovery scenarios to rely on the output of txid_current() or even the
    newer pg_current_xact_id() to get a transaction ID for setting up
    recovery_target_xid.  The value returned by those functions includes the
    epoch in the computed result, which would cause strtoul() to fail where
    unsigned long has a size of 32 bits once the epoch is incremented.
    
    WAL records and 2PC data include only information about 32-bit XIDs and
    it is not possible to have XIDs across more than one epoch, so
    discarding the high bits from the transaction ID set has no impact on
    recovery.  On the contrary, the use of strtoul() prevents a consistent
    behavior across platforms depending on the size of unsigned long.
    
    This commit changes the parsing of recovery_target_xid to use
    pg_strtouint64() instead, available down to 9.6.  There is one TAP test
    stressing recovery with recovery_target_xid, where a tweak based on
    pg_reset{xlog,wal} is added to bump the XID epoch so as this change gets
    tested, as per an idea from Alexander Lakhin.
    
    Reported-by: Alexander Lakhin
    Discussion: https://postgr.es/m/16780-107fd0c0385b1035@postgresql.org
    Backpatch-through: 9.6


src/backend/access/transam/xlog.c | 2 +- src/test/recovery/t/003_recovery_targets.pl | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 20c797939cb..fe0baf70da7 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -5162,7 +5162,7 @@ readRecoveryCommandFile(void) else if (strcmp(item->name, "recovery_target_xid") == 0) { errno = 0; - recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0); + recoveryTargetXid = (TransactionId) pg_strtouint64(item->value, NULL, 0); if (errno == EINVAL || errno == ERANGE) ereport(FATAL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl index fd759525d38..f21d3412027 100644 --- a/src/test/recovery/t/003_recovery_targets.pl +++ b/src/test/recovery/t/003_recovery_targets.pl @@ -50,6 +50,10 @@ sub test_recovery_standby my $node_master = get_new_node('master'); $node_master->init(has_archiving => 1, allows_streaming => 1); +# Bump the transaction ID epoch. This is useful to stress the portability +# of recovery_target_xid parsing. +system_or_bail('pg_resetxlog', '-e', '1', $node_master->data_dir); + # Start it $node_master->start; [parent: 1d5f3f976b26]