88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.107 2001/01/12 21:53:54 tgl Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.108 2001/01/15 05:29:19 tgl Exp $
1212 *
1313 *
1414 * INTERFACE ROUTINES
@@ -1534,18 +1534,20 @@ heap_delete(Relation relation, ItemPointer tid, ItemPointer ctid)
15341534 }
15351535 END_CRIT_SECTION ();
15361536
1537+ LockBuffer (buffer , BUFFER_LOCK_UNLOCK );
1538+
15371539#ifdef TUPLE_TOASTER_ACTIVE
15381540 /* ----------
15391541 * If the relation has toastable attributes, we need to delete
1540- * no longer needed items there too.
1542+ * no longer needed items there too. We have to do this before
1543+ * WriteBuffer because we need to look at the contents of the tuple,
1544+ * but it's OK to release the context lock on the buffer first.
15411545 * ----------
15421546 */
15431547 if (HeapTupleHasExtended (& tp ))
15441548 heap_tuple_toast_attrs (relation , NULL , & (tp ));
15451549#endif
15461550
1547- LockBuffer (buffer , BUFFER_LOCK_UNLOCK );
1548-
15491551 /*
15501552 * Mark tuple for invalidation from system caches at next command boundary.
15511553 * 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,
15681570 ItemId lp ;
15691571 HeapTupleData oldtup ;
15701572 PageHeader dp ;
1571- Buffer buffer , newbuf ;
1573+ Buffer buffer ,
1574+ newbuf ;
1575+ bool need_toast ,
1576+ already_marked ;
15721577 int result ;
15731578
15741579 /* increment access statistics */
@@ -1645,77 +1650,92 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
16451650 return result ;
16461651 }
16471652
1648- /* XXX order problems if not atomic assignment ??? */
1653+ /* Fill in OID and transaction status data for newtup */
16491654 newtup -> t_data -> t_oid = oldtup .t_data -> t_oid ;
16501655 TransactionIdStore (GetCurrentTransactionId (), & (newtup -> t_data -> t_xmin ));
16511656 newtup -> t_data -> t_cmin = GetCurrentCommandId ();
16521657 StoreInvalidTransactionId (& (newtup -> t_data -> t_xmax ));
16531658 newtup -> t_data -> t_infomask &= ~(HEAP_XACT_MASK );
16541659 newtup -> t_data -> t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED );
16551660
1656- #ifdef TUPLE_TOASTER_ACTIVE
1657- /* ----------
1658- * If this relation is enabled for toasting, let the toaster
1659- * delete any no-longer-needed entries and create new ones to
1660- * make the new tuple fit again. Also, if there are already-
1661- * toasted values from some other relation, the toaster must
1662- * fix them.
1663- * ----------
1661+ /*
1662+ * If the toaster needs to be activated, OR if the new tuple will not
1663+ * fit on the same page as the old, then we need to release the context
1664+ * lock (but not the pin!) on the old tuple's buffer while we are off
1665+ * doing TOAST and/or table-file-extension work. We must mark the old
1666+ * tuple to show that it's already being updated, else other processes
1667+ * may try to update it themselves. To avoid second XLOG log record,
1668+ * we use xact mgr hook to unlock old tuple without reading log if xact
1669+ * will abort before update is logged. In the event of crash prio logging,
1670+ * TQUAL routines will see HEAP_XMAX_UNLOGGED flag...
1671+ *
1672+ * NOTE: this trick is useless currently but saved for future
1673+ * when we'll implement UNDO and will re-use transaction IDs
1674+ * after postmaster startup.
1675+ *
1676+ * We need to invoke the toaster if there are already any toasted values
1677+ * present, or if the new tuple is over-threshold.
16641678 */
1665- if (HeapTupleHasExtended (& oldtup ) ||
1666- HeapTupleHasExtended (newtup ) ||
1667- (MAXALIGN (newtup -> t_len ) > TOAST_TUPLE_THRESHOLD ))
1668- heap_tuple_toast_attrs (relation , newtup , & oldtup );
1669- #endif
1679+ need_toast = (HeapTupleHasExtended (& oldtup ) ||
1680+ HeapTupleHasExtended (newtup ) ||
1681+ (MAXALIGN (newtup -> t_len ) > TOAST_TUPLE_THRESHOLD ));
16701682
1671- /* Find buffer for new tuple */
1672-
1673- if ((unsigned ) MAXALIGN (newtup -> t_len ) <= PageGetFreeSpace ((Page ) dp ))
1674- newbuf = buffer ;
1675- else
1683+ if (need_toast ||
1684+ (unsigned ) MAXALIGN (newtup -> t_len ) > PageGetFreeSpace ((Page ) dp ))
16761685 {
1677- /*
1678- * We have to unlock old tuple buffer before extending table
1679- * file but have to keep lock on the old tuple. To avoid second
1680- * XLOG log record we use xact mngr hook to unlock old tuple
1681- * without reading log if xact will abort before update is logged.
1682- * In the event of crash prio logging, TQUAL routines will see
1683- * HEAP_XMAX_UNLOGGED flag...
1684- *
1685- * NOTE: this trick is useless currently but saved for future
1686- * when we'll implement UNDO and will re-use transaction IDs
1687- * after postmaster startup.
1688- */
16891686 _locked_tuple_ .node = relation -> rd_node ;
16901687 _locked_tuple_ .tid = oldtup .t_self ;
16911688 XactPushRollback (_heap_unlock_tuple , (void * ) & _locked_tuple_ );
16921689
1693- TransactionIdStore (GetCurrentTransactionId (), & (oldtup .t_data -> t_xmax ));
1690+ TransactionIdStore (GetCurrentTransactionId (),
1691+ & (oldtup .t_data -> t_xmax ));
16941692 oldtup .t_data -> t_cmax = GetCurrentCommandId ();
16951693 oldtup .t_data -> t_infomask &= ~(HEAP_XMAX_COMMITTED |
1696- HEAP_XMAX_INVALID | HEAP_MARKED_FOR_UPDATE );
1694+ HEAP_XMAX_INVALID |
1695+ HEAP_MARKED_FOR_UPDATE );
16971696 oldtup .t_data -> t_infomask |= HEAP_XMAX_UNLOGGED ;
1697+ already_marked = true;
16981698 LockBuffer (buffer , BUFFER_LOCK_UNLOCK );
1699- newbuf = RelationGetBufferForTuple (relation , newtup -> t_len );
1699+
1700+ /* Let the toaster do its thing */
1701+ if (need_toast )
1702+ heap_tuple_toast_attrs (relation , newtup , & oldtup );
1703+
1704+ /* Now, do we need a new page for the tuple, or not? */
1705+ if ((unsigned ) MAXALIGN (newtup -> t_len ) <= PageGetFreeSpace ((Page ) dp ))
1706+ newbuf = buffer ;
1707+ else
1708+ newbuf = RelationGetBufferForTuple (relation , newtup -> t_len );
1709+
1710+ /* Re-acquire the lock on the old tuple's page. */
17001711 /* this seems to be deadlock free... */
17011712 LockBuffer (buffer , BUFFER_LOCK_EXCLUSIVE );
17021713 }
1714+ else
1715+ {
1716+ /* No TOAST work needed, and it'll fit on same page */
1717+ already_marked = false;
1718+ newbuf = buffer ;
1719+ }
17031720
17041721 /* NO ELOG(ERROR) from here till changes are logged */
17051722 START_CRIT_SECTION ();
17061723
17071724 RelationPutHeapTuple (relation , newbuf , newtup ); /* insert new tuple */
1708- if (buffer == newbuf )
1725+
1726+ if (already_marked )
17091727 {
1710- TransactionIdStore (GetCurrentTransactionId (), & (oldtup .t_data -> t_xmax ));
1711- oldtup .t_data -> t_cmax = GetCurrentCommandId ();
1712- oldtup .t_data -> t_infomask &= ~(HEAP_XMAX_COMMITTED |
1713- HEAP_XMAX_INVALID | HEAP_MARKED_FOR_UPDATE );
1728+ oldtup .t_data -> t_infomask &= ~HEAP_XMAX_UNLOGGED ;
1729+ XactPopRollback ();
17141730 }
17151731 else
17161732 {
1717- oldtup .t_data -> t_infomask &= ~HEAP_XMAX_UNLOGGED ;
1718- XactPopRollback ();
1733+ TransactionIdStore (GetCurrentTransactionId (),
1734+ & (oldtup .t_data -> t_xmax ));
1735+ oldtup .t_data -> t_cmax = GetCurrentCommandId ();
1736+ oldtup .t_data -> t_infomask &= ~(HEAP_XMAX_COMMITTED |
1737+ HEAP_XMAX_INVALID |
1738+ HEAP_MARKED_FOR_UPDATE );
17191739 }
17201740
17211741 /* record address of new tuple in t_ctid of old one */
@@ -1724,7 +1744,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
17241744 /* XLOG stuff */
17251745 {
17261746 XLogRecPtr recptr = log_heap_update (relation , buffer , oldtup .t_self ,
1727- newbuf , newtup , false);
1747+ newbuf , newtup , false);
17281748
17291749 if (newbuf != buffer )
17301750 {
@@ -1734,6 +1754,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
17341754 PageSetLSN (BufferGetPage (buffer ), recptr );
17351755 PageSetSUI (BufferGetPage (buffer ), ThisStartUpID );
17361756 }
1757+
17371758 END_CRIT_SECTION ();
17381759
17391760 if (newbuf != buffer )
0 commit comments