|
72 | 72 | #include "utils/relcache.h"
|
73 | 73 | #include "utils/snapmgr.h"
|
74 | 74 | #include "utils/spccache.h"
|
| 75 | +#include "utils/syscache.h" |
75 | 76 |
|
76 | 77 |
|
77 | 78 | static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
|
@@ -3322,7 +3323,49 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
|
3322 | 3323 | LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
3323 | 3324 |
|
3324 | 3325 | 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 | + } |
3326 | 3369 |
|
3327 | 3370 | /*
|
3328 | 3371 | * Fill in enough data in oldtup for HeapDetermineColumnsInfo to work
|
|
0 commit comments