Skip to content

Commit 25e9948

Browse files
committed
At update of non-LP_NORMAL TID, fail instead of corrupting page header.
The right mix of DDL and VACUUM could corrupt a catalog page header such that PageIsVerified() durably fails, requiring a restore from backup. This affects only catalogs that both have a syscache and have DDL code that uses syscache tuples to construct updates. One of the test permutations shows a variant not yet fixed. This makes !TransactionIdIsValid(TM_FailureData.xmax) possible with TM_Deleted. I think core and PGXN are indifferent to that. Per bug #17821 from Alexander Lakhin. Back-patch to v13 (all supported versions). The test case is v17+, since it uses INJECTION_POINT. Discussion: https://postgr.es/m/17821-dd8c334263399284@postgresql.org
1 parent 7b3259b commit 25e9948

File tree

2 files changed

+46
-2
lines changed

2 files changed

+46
-2
lines changed

src/backend/access/heap/heapam.c

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
#include "utils/relcache.h"
7373
#include "utils/snapmgr.h"
7474
#include "utils/spccache.h"
75+
#include "utils/syscache.h"
7576

7677

7778
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
@@ -3322,7 +3323,49 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
33223323
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
33233324

33243325
lp = PageGetItemId(page, ItemPointerGetOffsetNumber(otid));
3325-
Assert(ItemIdIsNormal(lp));
3326+
3327+
/*
3328+
* Usually, a buffer pin and/or snapshot blocks pruning of otid, ensuring
3329+
* we see LP_NORMAL here. When the otid origin is a syscache, we may have
3330+
* neither a pin nor a snapshot. Hence, we may see other LP_ states, each
3331+
* of which indicates concurrent pruning.
3332+
*
3333+
* Failing with TM_Updated would be most accurate. However, unlike other
3334+
* TM_Updated scenarios, we don't know the successor ctid in LP_UNUSED and
3335+
* LP_DEAD cases. While the distinction between TM_Updated and TM_Deleted
3336+
* does matter to SQL statements UPDATE and MERGE, those SQL statements
3337+
* hold a snapshot that ensures LP_NORMAL. Hence, the choice between
3338+
* TM_Updated and TM_Deleted affects only the wording of error messages.
3339+
* Settle on TM_Deleted, for two reasons. First, it avoids complicating
3340+
* the specification of when tmfd->ctid is valid. Second, it creates
3341+
* error log evidence that we took this branch.
3342+
*
3343+
* Since it's possible to see LP_UNUSED at otid, it's also possible to see
3344+
* LP_NORMAL for a tuple that replaced LP_UNUSED. If it's a tuple for an
3345+
* unrelated row, we'll fail with "duplicate key value violates unique".
3346+
* XXX if otid is the live, newer version of the newtup row, we'll discard
3347+
* changes originating in versions of this catalog row after the version
3348+
* the caller got from syscache. See syscache-update-pruned.spec.
3349+
*/
3350+
if (!ItemIdIsNormal(lp))
3351+
{
3352+
Assert(RelationSupportsSysCache(RelationGetRelid(relation)));
3353+
3354+
UnlockReleaseBuffer(buffer);
3355+
Assert(!have_tuple_lock);
3356+
if (vmbuffer != InvalidBuffer)
3357+
ReleaseBuffer(vmbuffer);
3358+
tmfd->ctid = *otid;
3359+
tmfd->xmax = InvalidTransactionId;
3360+
tmfd->cmax = InvalidCommandId;
3361+
3362+
bms_free(hot_attrs);
3363+
bms_free(key_attrs);
3364+
bms_free(id_attrs);
3365+
/* modified_attrs not yet initialized */
3366+
bms_free(interesting_attrs);
3367+
return TM_Deleted;
3368+
}
33263369

33273370
/*
33283371
* Fill in enough data in oldtup for HeapDetermineColumnsInfo to work

src/include/access/tableam.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ typedef enum TM_Result
113113
*
114114
* xmax is the outdating transaction's XID. If the caller wants to visit the
115115
* replacement tuple, it must check that this matches before believing the
116-
* replacement is really a match.
116+
* replacement is really a match. This is InvalidTransactionId if the target
117+
* was !LP_NORMAL (expected only for a TID retrieved from syscache).
117118
*
118119
* cmax is the outdating command's CID, but only when the failure code is
119120
* TM_SelfModified (i.e., something in the current transaction outdated the

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy