]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix portability issues with parsing of recovery_target_xid
authorMichael Paquier <michael@paquier.xyz>
Wed, 23 Dec 2020 03:51:51 +0000 (12:51 +0900)
committerMichael Paquier <michael@paquier.xyz>
Wed, 23 Dec 2020 03:51:51 +0000 (12:51 +0900)
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
src/test/recovery/t/003_recovery_targets.pl

index 20c797939cb219c1e0e0e680eb3e8d0335f512e0..fe0baf70da7adb7219f0174d8d274649b58b354a 100644 (file)
@@ -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),
index fd759525d38927d855fdebaf873b6f8e0dfd948f..f21d34120278aeb723ec875c91bf4f37ab72345c 100644 (file)
@@ -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;