newbuf,
vmbuffer = InvalidBuffer,
vmbuffer_new = InvalidBuffer;
- bool need_toast,
- already_marked;
+ bool need_toast;
Size newtupsize,
pagefree;
bool have_tuple_lock = false;
* on the same page as the old, then we need to release the content lock
* (but not the pin!) on the old tuple's buffer while we are off doing
* TOAST and/or table-file-extension work. We must mark the old tuple to
- * show that it's already being updated, else other processes may try to
- * update it themselves.
+ * show that it's locked, else other processes may try to update it
+ * themselves.
*
* We need to invoke the toaster if there are already any out-of-line
* toasted values present, or if the new tuple is over-threshold.
if (need_toast || newtupsize > pagefree)
{
+ TransactionId xmax_lock_old_tuple;
+ uint16 infomask_lock_old_tuple,
+ infomask2_lock_old_tuple;
+
+ /*
+ * To prevent concurrent sessions from updating the tuple, we have to
+ * temporarily mark it locked, while we release the lock.
+ *
+ * To satisfy the rule that any xid potentially appearing in a buffer
+ * written out to disk, we unfortunately have to WAL log this
+ * temporary modification. We can reuse xl_heap_lock for this
+ * purpose. If we crash/error before following through with the
+ * actual update, xmax will be of an aborted transaction, allowing
+ * other sessions to proceed.
+ */
+
+ /*
+ * Compute xmax / infomask appropriate for locking the tuple. This has
+ * to be done separately from the lock, because the potentially
+ * created multixact would otherwise be wrong.
+ */
+ compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(oldtup.t_data),
+ oldtup.t_data->t_infomask,
+ oldtup.t_data->t_infomask2,
+ xid, *lockmode, false,
+ &xmax_lock_old_tuple, &infomask_lock_old_tuple,
+ &infomask2_lock_old_tuple);
+
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(infomask_lock_old_tuple));
+
+ START_CRIT_SECTION();
+
/* Clear obsolete visibility flags ... */
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
oldtup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
HeapTupleClearHotUpdated(&oldtup);
/* ... and store info about transaction updating this tuple */
- Assert(TransactionIdIsValid(xmax_old_tuple));
- HeapTupleHeaderSetXmax(oldtup.t_data, xmax_old_tuple);
- oldtup.t_data->t_infomask |= infomask_old_tuple;
- oldtup.t_data->t_infomask2 |= infomask2_old_tuple;
+ Assert(TransactionIdIsValid(xmax_lock_old_tuple));
+ HeapTupleHeaderSetXmax(oldtup.t_data, xmax_lock_old_tuple);
+ oldtup.t_data->t_infomask |= infomask_lock_old_tuple;
+ oldtup.t_data->t_infomask2 |= infomask2_lock_old_tuple;
HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
- /* temporarily make it look not-updated */
+
+ /* temporarily make it look not-updated, but locked */
oldtup.t_data->t_ctid = oldtup.t_self;
- already_marked = true;
+
+ MarkBufferDirty(buffer);
+
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_lock xlrec;
+ XLogRecPtr recptr;
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&oldtup.t_self);
+ xlrec.locking_xid = xmax_lock_old_tuple;
+ xlrec.infobits_set = compute_infobits(oldtup.t_data->t_infomask,
+ oldtup.t_data->t_infomask2);
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
/*
else
{
/* No TOAST work needed, and it'll fit on same page */
- already_marked = false;
newbuf = buffer;
heaptup = newtup;
}
RelationPutHeapTuple(relation, newbuf, heaptup, false); /* insert new tuple */
- if (!already_marked)
- {
- /* Clear obsolete visibility flags ... */
- oldtup.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
- oldtup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- /* ... and store info about transaction updating this tuple */
- Assert(TransactionIdIsValid(xmax_old_tuple));
- HeapTupleHeaderSetXmax(oldtup.t_data, xmax_old_tuple);
- oldtup.t_data->t_infomask |= infomask_old_tuple;
- oldtup.t_data->t_infomask2 |= infomask2_old_tuple;
- HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
- }
+
+ /* Clear obsolete visibility flags, possibly set by ourselves above... */
+ oldtup.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
+ oldtup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ /* ... and store info about transaction updating this tuple */
+ Assert(TransactionIdIsValid(xmax_old_tuple));
+ HeapTupleHeaderSetXmax(oldtup.t_data, xmax_old_tuple);
+ oldtup.t_data->t_infomask |= infomask_old_tuple;
+ oldtup.t_data->t_infomask2 |= infomask2_old_tuple;
+ HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
/* record address of new tuple in t_ctid of old one */
oldtup.t_data->t_ctid = heaptup->t_self;