mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Check that oldestXID and oldestMulti are consistent at pg_upgrade
Now that pg_upgrade will rewrite multixid members, starting from oldestMulti, it's important that oldestMulti is valid. Add a sanity check that oldestMulti is not newer than the oldest datminmxid value in pg_database. One case where this could happen is if the cluster was previously upgraded to version 9.3 with a buggy pg_upgrade version that didn't have commita61daa14d5. This new pg_upgrade check is similar to the defence that was added in commit78db307bb2to VACUUM to avoid truncating away multixids if oldestMulti is too new. This pg_upgrade check differs in that we don't try to soldier on with the upgrade if the oldestMultiXID is inconsistent, but rather just abort the upgrade. Reported-by: Noah Misch <noah@leadboat.com> Discussion: https://www.postgresql.org/message-id/20260827231757.78.noahmisch@microsoft.com Backpatch-through: 19
This commit is contained in:
@@ -2860,34 +2860,6 @@ MultiXactMemberIoErrorDetail(const void *opaque_data)
|
||||
context->offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decide which of two MultiXactIds is earlier.
|
||||
*
|
||||
* XXX do we need to do something special for InvalidMultiXactId?
|
||||
* (Doesn't look like it.)
|
||||
*/
|
||||
bool
|
||||
MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
|
||||
{
|
||||
int32 diff = (int32) (multi1 - multi2);
|
||||
|
||||
return (diff < 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* MultiXactIdPrecedesOrEquals -- is multi1 logically <= multi2?
|
||||
*
|
||||
* XXX do we need to do something special for InvalidMultiXactId?
|
||||
* (Doesn't look like it.)
|
||||
*/
|
||||
bool
|
||||
MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
|
||||
{
|
||||
int32 diff = (int32) (multi1 - multi2);
|
||||
|
||||
return (diff <= 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write a TRUNCATE xlog record
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "postgres_fe.h"
|
||||
|
||||
#include "access/multixact.h"
|
||||
#include "access/transam.h"
|
||||
#include "catalog/pg_am_d.h"
|
||||
#include "catalog/pg_authid_d.h"
|
||||
#include "catalog/pg_class_d.h"
|
||||
@@ -37,6 +39,7 @@ static void check_new_cluster_subscription_configuration(void);
|
||||
static void check_old_cluster_for_valid_slots(void);
|
||||
static void check_old_cluster_subscription_state(void);
|
||||
static void check_old_cluster_global_names(ClusterInfo *cluster);
|
||||
static void check_for_oldestxid_consistency(ClusterInfo *cluster);
|
||||
|
||||
/*
|
||||
* DataTypesUsageChecks - definitions of data type checks for the old cluster
|
||||
@@ -632,6 +635,7 @@ check_and_dump_old_cluster(void)
|
||||
*/
|
||||
check_is_install_user(&old_cluster);
|
||||
check_for_prepared_transactions(&old_cluster);
|
||||
check_for_oldestxid_consistency(&old_cluster);
|
||||
check_for_isn_and_int8_passing_mismatch(&old_cluster);
|
||||
|
||||
if (GET_MAJOR_VERSION(old_cluster.major_version) >= 1700)
|
||||
@@ -2723,3 +2727,70 @@ check_old_cluster_global_names(ClusterInfo *cluster)
|
||||
else
|
||||
check_ok();
|
||||
}
|
||||
|
||||
/*
|
||||
* check_for_oldestxid_consistency()
|
||||
*
|
||||
* Check that the oldestXid and oldestMultiXID values in the control file are
|
||||
* consistent with the 'datfrozenxid' and 'datminmxid' values in pg_database.
|
||||
*
|
||||
* The invariant is that all 'datfrozenxid' and 'datminmxid' values must be
|
||||
* greater than or equal to the values in the control file. Otherwise you
|
||||
* might already have truncated away clog or multixids that are still needed.
|
||||
* If that has happened, we refuse the upgrade and require the administrator
|
||||
* to deal with the situation first.
|
||||
*
|
||||
* One scenario where that is known to happen is if the cluster was upgraded
|
||||
* in the past to version 9.3 with a buggy pg_upgrade version that didn't copy
|
||||
* the oldestMulti value from the old cluster. See commit a61daa14d5.
|
||||
* That was a long time ago, though, so you're not very likely to encounter
|
||||
* that bug in the wild anymore. Therefore we don't assume that's the cause
|
||||
* or try to do anything clever here. In any case, it's still good to check
|
||||
* to prevent further damage.
|
||||
*/
|
||||
static void
|
||||
check_for_oldestxid_consistency(ClusterInfo *cluster)
|
||||
{
|
||||
PGconn *conn_template1;
|
||||
PGresult *dbres;
|
||||
int ntups;
|
||||
int i_datname;
|
||||
int i_datfrozenxid;
|
||||
int i_datminmxid;
|
||||
|
||||
prep_status("Checking oldestXID and oldestMultiXid consistency");
|
||||
|
||||
conn_template1 = connectToServer(cluster, "template1");
|
||||
|
||||
dbres = executeQueryOrDie(conn_template1,
|
||||
"SELECT datname, datfrozenxid, datminmxid "
|
||||
"FROM pg_catalog.pg_database");
|
||||
|
||||
i_datname = PQfnumber(dbres, "datname");
|
||||
i_datfrozenxid = PQfnumber(dbres, "datfrozenxid");
|
||||
i_datminmxid = PQfnumber(dbres, "datminmxid");
|
||||
|
||||
ntups = PQntuples(dbres);
|
||||
for (int dbnum = 0; dbnum < ntups; dbnum++)
|
||||
{
|
||||
char *datname = PQgetvalue(dbres, dbnum, i_datname);
|
||||
TransactionId datfrozenxid = (TransactionId) str2uint(PQgetvalue(dbres, dbnum, i_datfrozenxid));
|
||||
MultiXactId datminmxid = (MultiXactId) str2uint(PQgetvalue(dbres, dbnum, i_datminmxid));
|
||||
|
||||
if (TransactionIdPrecedes(datfrozenxid, cluster->controldata.chkpnt_oldstxid))
|
||||
{
|
||||
pg_fatal("oldestXID (%u) in the control file is newer than the datfrozenxid (%u) of database \"%s\"",
|
||||
cluster->controldata.chkpnt_oldstxid, datfrozenxid, datname);
|
||||
}
|
||||
if (MultiXactIdPrecedes(datminmxid, cluster->controldata.chkpnt_oldstMulti))
|
||||
{
|
||||
pg_fatal("oldestMultiXid (%u) in control file is newer than the datminmxid (%u) of database \"%s\"",
|
||||
cluster->controldata.chkpnt_oldstMulti, datminmxid, datname);
|
||||
}
|
||||
}
|
||||
|
||||
PQclear(dbres);
|
||||
PQfinish(conn_template1);
|
||||
|
||||
check_ok();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,34 @@
|
||||
|
||||
#define MultiXactIdIsValid(multi) ((multi) != InvalidMultiXactId)
|
||||
|
||||
/*
|
||||
* Decide which of two MultiXactIds is earlier.
|
||||
*
|
||||
* XXX do we need to do something special for InvalidMultiXactId?
|
||||
* (Doesn't look like it.)
|
||||
*/
|
||||
static inline bool
|
||||
MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
|
||||
{
|
||||
int32 diff = (int32) (multi1 - multi2);
|
||||
|
||||
return (diff < 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* MultiXactIdPrecedesOrEquals -- is multi1 logically <= multi2?
|
||||
*
|
||||
* XXX do we need to do something special for InvalidMultiXactId?
|
||||
* (Doesn't look like it.)
|
||||
*/
|
||||
static inline bool
|
||||
MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
|
||||
{
|
||||
int32 diff = (int32) (multi1 - multi2);
|
||||
|
||||
return (diff <= 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Possible multixact lock modes ("status"). The first four modes are for
|
||||
* tuple locks (FOR KEY SHARE, FOR SHARE, FOR NO KEY UPDATE, FOR UPDATE); the
|
||||
@@ -110,9 +138,6 @@ extern int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
|
||||
extern void GetMultiXactInfo(uint32 *multixacts, MultiXactOffset *nextOffset,
|
||||
MultiXactId *oldestMultiXactId,
|
||||
MultiXactOffset *oldestOffset);
|
||||
extern bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2);
|
||||
extern bool MultiXactIdPrecedesOrEquals(MultiXactId multi1,
|
||||
MultiXactId multi2);
|
||||
|
||||
extern int multixactoffsetssyncfiletag(const FileTag *ftag, char *path);
|
||||
extern int multixactmemberssyncfiletag(const FileTag *ftag, char *path);
|
||||
|
||||
Reference in New Issue
Block a user