|
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,
|
@@ -3242,7 +3243,49 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
|
3242 | 3243 | LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
3243 | 3244 |
|
3244 | 3245 | lp = PageGetItemId(page, ItemPointerGetOffsetNumber(otid));
|
3245 |
| - Assert(ItemIdIsNormal(lp)); |
| 3246 | + |
| 3247 | + /* |
| 3248 | + * Usually, a buffer pin and/or snapshot blocks pruning of otid, ensuring |
| 3249 | + * we see LP_NORMAL here. When the otid origin is a syscache, we may have |
| 3250 | + * neither a pin nor a snapshot. Hence, we may see other LP_ states, each |
| 3251 | + * of which indicates concurrent pruning. |
| 3252 | + * |
| 3253 | + * Failing with TM_Updated would be most accurate. However, unlike other |
| 3254 | + * TM_Updated scenarios, we don't know the successor ctid in LP_UNUSED and |
| 3255 | + * LP_DEAD cases. While the distinction between TM_Updated and TM_Deleted |
| 3256 | + * does matter to SQL statements UPDATE and MERGE, those SQL statements |
| 3257 | + * hold a snapshot that ensures LP_NORMAL. Hence, the choice between |
| 3258 | + * TM_Updated and TM_Deleted affects only the wording of error messages. |
| 3259 | + * Settle on TM_Deleted, for two reasons. First, it avoids complicating |
| 3260 | + * the specification of when tmfd->ctid is valid. Second, it creates |
| 3261 | + * error log evidence that we took this branch. |
| 3262 | + * |
| 3263 | + * Since it's possible to see LP_UNUSED at otid, it's also possible to see |
| 3264 | + * LP_NORMAL for a tuple that replaced LP_UNUSED. If it's a tuple for an |
| 3265 | + * unrelated row, we'll fail with "duplicate key value violates unique". |
| 3266 | + * XXX if otid is the live, newer version of the newtup row, we'll discard |
| 3267 | + * changes originating in versions of this catalog row after the version |
| 3268 | + * the caller got from syscache. See syscache-update-pruned.spec. |
| 3269 | + */ |
| 3270 | + if (!ItemIdIsNormal(lp)) |
| 3271 | + { |
| 3272 | + Assert(RelationSupportsSysCache(RelationGetRelid(relation))); |
| 3273 | + |
| 3274 | + UnlockReleaseBuffer(buffer); |
| 3275 | + Assert(!have_tuple_lock); |
| 3276 | + if (vmbuffer != InvalidBuffer) |
| 3277 | + ReleaseBuffer(vmbuffer); |
| 3278 | + tmfd->ctid = *otid; |
| 3279 | + tmfd->xmax = InvalidTransactionId; |
| 3280 | + tmfd->cmax = InvalidCommandId; |
| 3281 | + |
| 3282 | + bms_free(hot_attrs); |
| 3283 | + bms_free(key_attrs); |
| 3284 | + bms_free(id_attrs); |
| 3285 | + /* modified_attrs not yet initialized */ |
| 3286 | + bms_free(interesting_attrs); |
| 3287 | + return TM_Deleted; |
| 3288 | + } |
3246 | 3289 |
|
3247 | 3290 | /*
|
3248 | 3291 | * Fill in enough data in oldtup for HeapDetermineColumnsInfo to work
|
|
0 commit comments