pg_toast_corruption_v3.patch
application/octet-stream
Filename: pg_toast_corruption_v3.patch
Type: application/octet-stream
Part: 0
Patch
Format: unified
Series: patch v3
| File | + | − |
|---|---|---|
| src/backend/access/heap/tuptoaster.c | 16 | 5 |
| src/backend/access/transam/xlog.c | 15 | 5 |
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 546f80f05c..01c392d546 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -1569,11 +1569,22 @@ toast_save_datum(Relation rel, Datum value,
*/
if (!OidIsValid(rel->rd_toastoid))
{
- /* normal case: just choose an unused OID */
- toast_pointer.va_valueid =
- GetNewOidWithIndex(toastrel,
- RelationGetRelid(toastidxs[validIndex]),
- (AttrNumber) 1);
+ /*
+ * Normal case: just choose an unused OID. But we must scan the TOAST
+ * table using SnapshotToast to ensure that the value does not exists.
+ * Note that GetNewOidWithIndex() scans the table with SnapshotDirty
+ * and that may not see a RECENTLY_DEAD tuple. But such tuples are
+ * visible to SnapshotToast and hence we must refrain from using such
+ * OIDs. Otherwise toast_fetch_datum() or friends might see duplicate
+ * OIDs, leading to all kinds of errors.
+ */
+ do
+ {
+ toast_pointer.va_valueid =
+ GetNewOidWithIndex(toastrel,
+ RelationGetRelid(toastidxs[validIndex]),
+ (AttrNumber) 1);
+ } while (toastrel_valueid_exists(toastrel, toast_pointer.va_valueid));
}
else
{
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b4fd8395b7..ba8b969cc2 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -9785,11 +9785,21 @@ xlog_redo(XLogReaderState *record)
checkPoint.nextXid))
ShmemVariableCache->nextXid = checkPoint.nextXid;
LWLockRelease(XidGenLock);
- /* ... but still treat OID counter as exact */
- LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
- ShmemVariableCache->nextOid = checkPoint.nextOid;
- ShmemVariableCache->oidCount = 0;
- LWLockRelease(OidGenLock);
+
+ /*
+ * In an ONLINE checkpoint, don't update the nextOid counter. If there
+ * was no XLOG_NEXTOID record seen since the starting checkpoint, then
+ * we continue to believe that the current value is correct. If we saw
+ * a XLOG_NEXTOID record, then we should rather use that and not
+ * overwrite with a potentially old value, thus issuing duplicate
+ * OIDs when we are fully up.
+ *
+ * If you're still curious why that can happen, note that the online
+ * checkpoint may have started before our original redo position and
+ * hence it may have value later made stale by an explicit
+ * XLOG_NEXTOID.
+ */
+
MultiXactAdvanceNextMXact(checkPoint.nextMulti,
checkPoint.nextMultiOffset);