Commit efd6cade authored by Tom Lane's avatar Tom Lane

Tweak heap_update/delete so that we do not hold the buffer context lock

on the old tuple's page while we are doing TOAST pushups.
parent 0b5d194a
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.107 2001/01/12 21:53:54 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.108 2001/01/15 05:29:19 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -1534,18 +1534,20 @@ l1: ...@@ -1534,18 +1534,20 @@ l1:
} }
END_CRIT_SECTION(); END_CRIT_SECTION();
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
#ifdef TUPLE_TOASTER_ACTIVE #ifdef TUPLE_TOASTER_ACTIVE
/* ---------- /* ----------
* If the relation has toastable attributes, we need to delete * If the relation has toastable attributes, we need to delete
* no longer needed items there too. * no longer needed items there too. We have to do this before
* WriteBuffer because we need to look at the contents of the tuple,
* but it's OK to release the context lock on the buffer first.
* ---------- * ----------
*/ */
if (HeapTupleHasExtended(&tp)) if (HeapTupleHasExtended(&tp))
heap_tuple_toast_attrs(relation, NULL, &(tp)); heap_tuple_toast_attrs(relation, NULL, &(tp));
#endif #endif
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
/* /*
* Mark tuple for invalidation from system caches at next command boundary. * Mark tuple for invalidation from system caches at next command boundary.
* We have to do this before WriteBuffer because we need to look at the * We have to do this before WriteBuffer because we need to look at the
...@@ -1568,7 +1570,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, ...@@ -1568,7 +1570,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
ItemId lp; ItemId lp;
HeapTupleData oldtup; HeapTupleData oldtup;
PageHeader dp; PageHeader dp;
Buffer buffer, newbuf; Buffer buffer,
newbuf;
bool need_toast,
already_marked;
int result; int result;
/* increment access statistics */ /* increment access statistics */
...@@ -1645,7 +1650,7 @@ l2: ...@@ -1645,7 +1650,7 @@ l2:
return result; return result;
} }
/* XXX order problems if not atomic assignment ??? */ /* Fill in OID and transaction status data for newtup */
newtup->t_data->t_oid = oldtup.t_data->t_oid; newtup->t_data->t_oid = oldtup.t_data->t_oid;
TransactionIdStore(GetCurrentTransactionId(), &(newtup->t_data->t_xmin)); TransactionIdStore(GetCurrentTransactionId(), &(newtup->t_data->t_xmin));
newtup->t_data->t_cmin = GetCurrentCommandId(); newtup->t_data->t_cmin = GetCurrentCommandId();
...@@ -1653,69 +1658,84 @@ l2: ...@@ -1653,69 +1658,84 @@ l2:
newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK); newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED); newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
#ifdef TUPLE_TOASTER_ACTIVE /*
/* ---------- * If the toaster needs to be activated, OR if the new tuple will not
* If this relation is enabled for toasting, let the toaster * fit on the same page as the old, then we need to release the context
* delete any no-longer-needed entries and create new ones to * lock (but not the pin!) on the old tuple's buffer while we are off
* make the new tuple fit again. Also, if there are already- * doing TOAST and/or table-file-extension work. We must mark the old
* toasted values from some other relation, the toaster must * tuple to show that it's already being updated, else other processes
* fix them. * may try to update it themselves. To avoid second XLOG log record,
* ---------- * we use xact mgr hook to unlock old tuple without reading log if xact
* will abort before update is logged. In the event of crash prio logging,
* TQUAL routines will see HEAP_XMAX_UNLOGGED flag...
*
* NOTE: this trick is useless currently but saved for future
* when we'll implement UNDO and will re-use transaction IDs
* after postmaster startup.
*
* We need to invoke the toaster if there are already any toasted values
* present, or if the new tuple is over-threshold.
*/ */
if (HeapTupleHasExtended(&oldtup) || need_toast = (HeapTupleHasExtended(&oldtup) ||
HeapTupleHasExtended(newtup) || HeapTupleHasExtended(newtup) ||
(MAXALIGN(newtup->t_len) > TOAST_TUPLE_THRESHOLD)) (MAXALIGN(newtup->t_len) > TOAST_TUPLE_THRESHOLD));
heap_tuple_toast_attrs(relation, newtup, &oldtup);
#endif
/* Find buffer for new tuple */ if (need_toast ||
(unsigned) MAXALIGN(newtup->t_len) > PageGetFreeSpace((Page) dp))
if ((unsigned) MAXALIGN(newtup->t_len) <= PageGetFreeSpace((Page) dp))
newbuf = buffer;
else
{ {
/*
* We have to unlock old tuple buffer before extending table
* file but have to keep lock on the old tuple. To avoid second
* XLOG log record we use xact mngr hook to unlock old tuple
* without reading log if xact will abort before update is logged.
* In the event of crash prio logging, TQUAL routines will see
* HEAP_XMAX_UNLOGGED flag...
*
* NOTE: this trick is useless currently but saved for future
* when we'll implement UNDO and will re-use transaction IDs
* after postmaster startup.
*/
_locked_tuple_.node = relation->rd_node; _locked_tuple_.node = relation->rd_node;
_locked_tuple_.tid = oldtup.t_self; _locked_tuple_.tid = oldtup.t_self;
XactPushRollback(_heap_unlock_tuple, (void*) &_locked_tuple_); XactPushRollback(_heap_unlock_tuple, (void*) &_locked_tuple_);
TransactionIdStore(GetCurrentTransactionId(), &(oldtup.t_data->t_xmax)); TransactionIdStore(GetCurrentTransactionId(),
&(oldtup.t_data->t_xmax));
oldtup.t_data->t_cmax = GetCurrentCommandId(); oldtup.t_data->t_cmax = GetCurrentCommandId();
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID | HEAP_MARKED_FOR_UPDATE); HEAP_XMAX_INVALID |
HEAP_MARKED_FOR_UPDATE);
oldtup.t_data->t_infomask |= HEAP_XMAX_UNLOGGED; oldtup.t_data->t_infomask |= HEAP_XMAX_UNLOGGED;
already_marked = true;
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
newbuf = RelationGetBufferForTuple(relation, newtup->t_len);
/* Let the toaster do its thing */
if (need_toast)
heap_tuple_toast_attrs(relation, newtup, &oldtup);
/* Now, do we need a new page for the tuple, or not? */
if ((unsigned) MAXALIGN(newtup->t_len) <= PageGetFreeSpace((Page) dp))
newbuf = buffer;
else
newbuf = RelationGetBufferForTuple(relation, newtup->t_len);
/* Re-acquire the lock on the old tuple's page. */
/* this seems to be deadlock free... */ /* this seems to be deadlock free... */
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
} }
else
{
/* No TOAST work needed, and it'll fit on same page */
already_marked = false;
newbuf = buffer;
}
/* NO ELOG(ERROR) from here till changes are logged */ /* NO ELOG(ERROR) from here till changes are logged */
START_CRIT_SECTION(); START_CRIT_SECTION();
RelationPutHeapTuple(relation, newbuf, newtup); /* insert new tuple */ RelationPutHeapTuple(relation, newbuf, newtup); /* insert new tuple */
if (buffer == newbuf)
if (already_marked)
{ {
TransactionIdStore(GetCurrentTransactionId(), &(oldtup.t_data->t_xmax)); oldtup.t_data->t_infomask &= ~HEAP_XMAX_UNLOGGED;
oldtup.t_data->t_cmax = GetCurrentCommandId(); XactPopRollback();
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID | HEAP_MARKED_FOR_UPDATE);
} }
else else
{ {
oldtup.t_data->t_infomask &= ~HEAP_XMAX_UNLOGGED; TransactionIdStore(GetCurrentTransactionId(),
XactPopRollback(); &(oldtup.t_data->t_xmax));
oldtup.t_data->t_cmax = GetCurrentCommandId();
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
HEAP_MARKED_FOR_UPDATE);
} }
/* record address of new tuple in t_ctid of old one */ /* record address of new tuple in t_ctid of old one */
...@@ -1724,7 +1744,7 @@ l2: ...@@ -1724,7 +1744,7 @@ l2:
/* XLOG stuff */ /* XLOG stuff */
{ {
XLogRecPtr recptr = log_heap_update(relation, buffer, oldtup.t_self, XLogRecPtr recptr = log_heap_update(relation, buffer, oldtup.t_self,
newbuf, newtup, false); newbuf, newtup, false);
if (newbuf != buffer) if (newbuf != buffer)
{ {
...@@ -1734,6 +1754,7 @@ l2: ...@@ -1734,6 +1754,7 @@ l2:
PageSetLSN(BufferGetPage(buffer), recptr); PageSetLSN(BufferGetPage(buffer), recptr);
PageSetSUI(BufferGetPage(buffer), ThisStartUpID); PageSetSUI(BufferGetPage(buffer), ThisStartUpID);
} }
END_CRIT_SECTION(); END_CRIT_SECTION();
if (newbuf != buffer) if (newbuf != buffer)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.13 2000/10/23 23:42:04 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.14 2001/01/15 05:29:19 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -197,10 +197,12 @@ toast_delete(Relation rel, HeapTuple oldtup) ...@@ -197,10 +197,12 @@ toast_delete(Relation rel, HeapTuple oldtup)
*/ */
for (i = 0; i < numAttrs; i++) for (i = 0; i < numAttrs; i++)
{ {
value = heap_getattr(oldtup, i + 1, tupleDesc, &isnull); if (att[i]->attlen == -1)
if (!isnull && att[i]->attlen == -1) {
if (VARATT_IS_EXTERNAL(value)) value = heap_getattr(oldtup, i + 1, tupleDesc, &isnull);
if (!isnull && VARATT_IS_EXTERNAL(value))
toast_delete_datum(rel, value); toast_delete_datum(rel, value);
}
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment