summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/access/transam/multixact.c5
-rw-r--r--src/bin/pg_upgrade/multixact_read_v18.c44
2 files changed, 38 insertions, 11 deletions
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index f4fab7edfee..34956a5a663 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -1262,6 +1262,11 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("MultiXact %u has invalid next offset", multi)));
+ if (nextMXOffset == offset)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg("MultiXact %u with offset (%" PRIu64 ") has zero members",
+ multi, offset)));
if (nextMXOffset < offset)
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
diff --git a/src/bin/pg_upgrade/multixact_read_v18.c b/src/bin/pg_upgrade/multixact_read_v18.c
index f7ed97f702b..dc4ed1b3ae6 100644
--- a/src/bin/pg_upgrade/multixact_read_v18.c
+++ b/src/bin/pg_upgrade/multixact_read_v18.c
@@ -146,11 +146,14 @@ AllocOldMultiXactRead(char *pgdata, MultiXactId nextMulti,
* - Because there's no concurrent activity, we don't need to worry about
* locking and some corner cases.
*
- * - Don't bail out on invalid entries. If the server crashes, it can leave
- * invalid or half-written entries on disk. Such multixids won't appear
- * anywhere else on disk, so the server will never try to read them. During
- * upgrade, however, we scan through all multixids in order, and will
- * encounter such invalid but unreferenced multixids too.
+ * - Don't bail out on invalid entries that could've been left behind after a
+ * server crash. Such multixids won't appear anywhere else on disk, so the
+ * server will never try to read them. During upgrade, however, we scan
+ * through all multixids in order, and will encounter such invalid but
+ * unreferenced multixids too. We try to distinguish between entries that
+ * are invalid because of missed disk writes, like entries with zeros in
+ * offsets or members, and entries that look corrupt in other ways that
+ * should not happen even on a server crash.
*
* Returns true on success, false if the multixact was invalid.
*/
@@ -211,7 +214,7 @@ GetOldMultiXactIdSingleMember(OldMultiXactReader *state, MultiXactId multi,
if (offset == 0)
{
- /* Invalid entry */
+ /* Invalid entry. These can be left behind on a server crash. */
return false;
}
@@ -247,11 +250,29 @@ GetOldMultiXactIdSingleMember(OldMultiXactReader *state, MultiXactId multi,
if (nextMXOffset == 0)
{
- /* Invalid entry */
+ /* Invalid entry. These can be left behind on a server crash. */
return false;
}
length = nextMXOffset - offset;
+ if (length < 0)
+ {
+ /*
+ * This entry is corrupt. We should not see these even after a server
+ * crash.
+ */
+ pg_fatal("multixact %u has an invalid length (%d)", multi, length);
+ }
+ if (length == 0)
+ {
+ /*
+ * Invalid entry. The server never writes multixids with zero
+ * members, but it's not clear if a server crash or using pg_resetwal
+ * could leave them behind. Seems best to accept them.
+ */
+ return false;
+ }
+
/* read the members */
prev_pageno = -1;
for (int i = 0; i < length; i++, offset++)
@@ -284,10 +305,11 @@ GetOldMultiXactIdSingleMember(OldMultiXactReader *state, MultiXactId multi,
/*
* Otherwise this is an invalid entry that should not be
- * referenced from anywhere in the heap. We could return 'false'
- * here, but we prefer to continue reading the members and
- * converting them the best we can, to preserve evidence in case
- * this is corruption that should not happen.
+ * referenced from anywhere in the heap. These can be left behind
+ * on a server crash. We could return 'false' here, but we prefer
+ * to continue reading the members and converting them the best we
+ * can, to preserve evidence in case this is corruption that
+ * should not have happened.
*/
}