|
70 | 70 | #include "utils/relcache.h"
|
71 | 71 | #include "utils/snapmgr.h"
|
72 | 72 | #include "utils/spccache.h"
|
| 73 | +#include "utils/syscache.h" |
73 | 74 |
|
74 | 75 |
|
75 | 76 | static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
|
@@ -3047,7 +3048,49 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
|
3047 | 3048 | LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
3048 | 3049 |
|
3049 | 3050 | lp = PageGetItemId(page, ItemPointerGetOffsetNumber(otid));
|
3050 |
| - Assert(ItemIdIsNormal(lp)); |
| 3051 | + |
| 3052 | + /* |
| 3053 | + * Usually, a buffer pin and/or snapshot blocks pruning of otid, ensuring |
| 3054 | + * we see LP_NORMAL here. When the otid origin is a syscache, we may have |
| 3055 | + * neither a pin nor a snapshot. Hence, we may see other LP_ states, each |
| 3056 | + * of which indicates concurrent pruning. |
| 3057 | + * |
| 3058 | + * Failing with TM_Updated would be most accurate. However, unlike other |
| 3059 | + * TM_Updated scenarios, we don't know the successor ctid in LP_UNUSED and |
| 3060 | + * LP_DEAD cases. While the distinction between TM_Updated and TM_Deleted |
| 3061 | + * does matter to SQL statements UPDATE and MERGE, those SQL statements |
| 3062 | + * hold a snapshot that ensures LP_NORMAL. Hence, the choice between |
| 3063 | + * TM_Updated and TM_Deleted affects only the wording of error messages. |
| 3064 | + * Settle on TM_Deleted, for two reasons. First, it avoids complicating |
| 3065 | + * the specification of when tmfd->ctid is valid. Second, it creates |
| 3066 | + * error log evidence that we took this branch. |
| 3067 | + * |
| 3068 | + * Since it's possible to see LP_UNUSED at otid, it's also possible to see |
| 3069 | + * LP_NORMAL for a tuple that replaced LP_UNUSED. If it's a tuple for an |
| 3070 | + * unrelated row, we'll fail with "duplicate key value violates unique". |
| 3071 | + * XXX if otid is the live, newer version of the newtup row, we'll discard |
| 3072 | + * changes originating in versions of this catalog row after the version |
| 3073 | + * the caller got from syscache. See syscache-update-pruned.spec. |
| 3074 | + */ |
| 3075 | + if (!ItemIdIsNormal(lp)) |
| 3076 | + { |
| 3077 | + Assert(RelationSupportsSysCache(RelationGetRelid(relation))); |
| 3078 | + |
| 3079 | + UnlockReleaseBuffer(buffer); |
| 3080 | + Assert(!have_tuple_lock); |
| 3081 | + if (vmbuffer != InvalidBuffer) |
| 3082 | + ReleaseBuffer(vmbuffer); |
| 3083 | + tmfd->ctid = *otid; |
| 3084 | + tmfd->xmax = InvalidTransactionId; |
| 3085 | + tmfd->cmax = InvalidCommandId; |
| 3086 | + |
| 3087 | + bms_free(hot_attrs); |
| 3088 | + bms_free(key_attrs); |
| 3089 | + bms_free(id_attrs); |
| 3090 | + /* modified_attrs not yet initialized */ |
| 3091 | + bms_free(interesting_attrs); |
| 3092 | + return TM_Deleted; |
| 3093 | + } |
3051 | 3094 |
|
3052 | 3095 | /*
|
3053 | 3096 | * Fill in enough data in oldtup for HeapDetermineColumnsInfo to work
|
|
0 commit comments