mirror of
https://github.com/postgres/postgres.git
synced 2025-11-15 03:41:20 +03:00
Fix pg_upgrade around multixid and mxoff wraparound
pg_resetwal didn't accept multixid 0 or multixact offset UINT32_MAX, but they are both valid values that can appear in the control file. That caused pg_upgrade to fail if you tried to upgrade a cluster exactly at multixid or offset wraparound, because pg_upgrade calls pg_resetwal to restore multixid/offset on the new cluster to the values from the old cluster. To fix, allow those values in pg_resetwal. Fixes bugs #18863 and #18865 reported by Dmitry Kovalenko. Backpatch down to v15. Version 14 has the same bug, but the patch doesn't apply cleanly there. It could be made to work but it doesn't seem worth the effort given how rare it is to hit this problem with pg_upgrade, and how few people are upgrading to v14 anymore. Author: Maxim Orlov <orlovmg@gmail.com> Discussion: https://www.postgresql.org/message-id/CACG%3DezaApSMTjd%3DM2Sfn5Ucuggd3FG8Z8Qte8Xq9k5-%2BRQis-g@mail.gmail.com Discussion: https://www.postgresql.org/message-id/18863-72f08858855344a2@postgresql.org Discussion: https://www.postgresql.org/message-id/18865-d4c66cf35c2a67af@postgresql.org Backpatch-through: 15
This commit is contained in:
@@ -70,8 +70,10 @@ static TransactionId set_xid = 0;
|
|||||||
static TransactionId set_oldest_commit_ts_xid = 0;
|
static TransactionId set_oldest_commit_ts_xid = 0;
|
||||||
static TransactionId set_newest_commit_ts_xid = 0;
|
static TransactionId set_newest_commit_ts_xid = 0;
|
||||||
static Oid set_oid = 0;
|
static Oid set_oid = 0;
|
||||||
|
static bool mxid_given = false;
|
||||||
static MultiXactId set_mxid = 0;
|
static MultiXactId set_mxid = 0;
|
||||||
static MultiXactOffset set_mxoff = (MultiXactOffset) -1;
|
static bool mxoff_given = false;
|
||||||
|
static MultiXactOffset set_mxoff = 0;
|
||||||
static TimeLineID minXlogTli = 0;
|
static TimeLineID minXlogTli = 0;
|
||||||
static XLogSegNo minXlogSegNo = 0;
|
static XLogSegNo minXlogSegNo = 0;
|
||||||
static int WalSegSz;
|
static int WalSegSz;
|
||||||
@@ -118,6 +120,7 @@ main(int argc, char *argv[])
|
|||||||
MultiXactId set_oldestmxid = 0;
|
MultiXactId set_oldestmxid = 0;
|
||||||
char *endptr;
|
char *endptr;
|
||||||
char *endptr2;
|
char *endptr2;
|
||||||
|
int64 tmpi64;
|
||||||
char *DataDir = NULL;
|
char *DataDir = NULL;
|
||||||
char *log_fname = NULL;
|
char *log_fname = NULL;
|
||||||
int fd;
|
int fd;
|
||||||
@@ -254,8 +257,6 @@ main(int argc, char *argv[])
|
|||||||
pg_log_error_hint("Try \"%s --help\" for more information.", progname);
|
pg_log_error_hint("Try \"%s --help\" for more information.", progname);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (set_mxid == 0)
|
|
||||||
pg_fatal("multitransaction ID (-m) must not be 0");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX It'd be nice to have more sanity checks here, e.g. so
|
* XXX It'd be nice to have more sanity checks here, e.g. so
|
||||||
@@ -263,19 +264,23 @@ main(int argc, char *argv[])
|
|||||||
*/
|
*/
|
||||||
if (set_oldestmxid == 0)
|
if (set_oldestmxid == 0)
|
||||||
pg_fatal("oldest multitransaction ID (-m) must not be 0");
|
pg_fatal("oldest multitransaction ID (-m) must not be 0");
|
||||||
|
mxid_given = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'O':
|
case 'O':
|
||||||
errno = 0;
|
errno = 0;
|
||||||
set_mxoff = strtoul(optarg, &endptr, 0);
|
tmpi64 = strtoi64(optarg, &endptr, 0);
|
||||||
if (endptr == optarg || *endptr != '\0' || errno != 0)
|
if (endptr == optarg || *endptr != '\0' || errno != 0)
|
||||||
{
|
{
|
||||||
pg_log_error("invalid argument for option %s", "-O");
|
pg_log_error("invalid argument for option %s", "-O");
|
||||||
pg_log_error_hint("Try \"%s --help\" for more information.", progname);
|
pg_log_error_hint("Try \"%s --help\" for more information.", progname);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (set_mxoff == -1)
|
if (tmpi64 < 0 || tmpi64 > (int64) MaxMultiXactOffset)
|
||||||
pg_fatal("multitransaction offset (-O) must not be -1");
|
pg_fatal("multitransaction offset (-O) must be between 0 and %u", MaxMultiXactOffset);
|
||||||
|
|
||||||
|
set_mxoff = (MultiXactOffset) tmpi64;
|
||||||
|
mxoff_given = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
@@ -454,7 +459,7 @@ main(int argc, char *argv[])
|
|||||||
if (set_oid != 0)
|
if (set_oid != 0)
|
||||||
ControlFile.checkPointCopy.nextOid = set_oid;
|
ControlFile.checkPointCopy.nextOid = set_oid;
|
||||||
|
|
||||||
if (set_mxid != 0)
|
if (mxid_given)
|
||||||
{
|
{
|
||||||
ControlFile.checkPointCopy.nextMulti = set_mxid;
|
ControlFile.checkPointCopy.nextMulti = set_mxid;
|
||||||
|
|
||||||
@@ -464,7 +469,7 @@ main(int argc, char *argv[])
|
|||||||
ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
|
ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_mxoff != -1)
|
if (mxoff_given)
|
||||||
ControlFile.checkPointCopy.nextMultiOffset = set_mxoff;
|
ControlFile.checkPointCopy.nextMultiOffset = set_mxoff;
|
||||||
|
|
||||||
if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
|
if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
|
||||||
@@ -808,7 +813,7 @@ PrintNewControlValues(void)
|
|||||||
newXlogSegNo, WalSegSz);
|
newXlogSegNo, WalSegSz);
|
||||||
printf(_("First log segment after reset: %s\n"), fname);
|
printf(_("First log segment after reset: %s\n"), fname);
|
||||||
|
|
||||||
if (set_mxid != 0)
|
if (mxid_given)
|
||||||
{
|
{
|
||||||
printf(_("NextMultiXactId: %u\n"),
|
printf(_("NextMultiXactId: %u\n"),
|
||||||
ControlFile.checkPointCopy.nextMulti);
|
ControlFile.checkPointCopy.nextMulti);
|
||||||
@@ -818,7 +823,7 @@ PrintNewControlValues(void)
|
|||||||
ControlFile.checkPointCopy.oldestMultiDB);
|
ControlFile.checkPointCopy.oldestMultiDB);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_mxoff != -1)
|
if (mxoff_given)
|
||||||
{
|
{
|
||||||
printf(_("NextMultiOffset: %u\n"),
|
printf(_("NextMultiOffset: %u\n"),
|
||||||
ControlFile.checkPointCopy.nextMultiOffset);
|
ControlFile.checkPointCopy.nextMultiOffset);
|
||||||
|
|||||||
@@ -119,10 +119,12 @@ command_fails_like(
|
|||||||
[ 'pg_resetwal', '-m' => '10,bar', $node->data_dir ],
|
[ 'pg_resetwal', '-m' => '10,bar', $node->data_dir ],
|
||||||
qr/error: invalid argument for option -m/,
|
qr/error: invalid argument for option -m/,
|
||||||
'fails with incorrect -m option part 2');
|
'fails with incorrect -m option part 2');
|
||||||
command_fails_like(
|
|
||||||
[ 'pg_resetwal', '-m' => '0,10', $node->data_dir ],
|
# This used to be forbidden, but nextMulti can legitimately be 0 after
|
||||||
qr/must not be 0/,
|
# wraparound, so we now accept it in pg_resetwal too.
|
||||||
'fails with -m value 0 part 1');
|
command_ok([ 'pg_resetwal', '-m' => '0,10', $node->data_dir ],
|
||||||
|
'succeeds with -m value 0 part 1');
|
||||||
|
|
||||||
command_fails_like(
|
command_fails_like(
|
||||||
[ 'pg_resetwal', '-m' => '10,0', $node->data_dir ],
|
[ 'pg_resetwal', '-m' => '10,0', $node->data_dir ],
|
||||||
qr/must not be 0/,
|
qr/must not be 0/,
|
||||||
@@ -143,7 +145,7 @@ command_fails_like(
|
|||||||
'fails with incorrect -O option');
|
'fails with incorrect -O option');
|
||||||
command_fails_like(
|
command_fails_like(
|
||||||
[ 'pg_resetwal', '-O' => '-1', $node->data_dir ],
|
[ 'pg_resetwal', '-O' => '-1', $node->data_dir ],
|
||||||
qr/must not be -1/,
|
qr/must be between 0 and 4294967295/,
|
||||||
'fails with -O value -1');
|
'fails with -O value -1');
|
||||||
# --wal-segsize
|
# --wal-segsize
|
||||||
command_fails_like(
|
command_fails_like(
|
||||||
|
|||||||
Reference in New Issue
Block a user