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}
15351535END_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 */
15431547if (HeapTupleHasExtended (& tp ))
15441548heap_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,
15681570ItemId lp ;
15691571HeapTupleData oldtup ;
15701572PageHeader dp ;
1571- Buffer buffer ,newbuf ;
1573+ Buffer buffer ,
1574+ newbuf ;
1575+ bool need_toast ,
1576+ already_marked ;
15721577int result ;
15731578
15741579/* increment access statistics */
@@ -1645,77 +1650,92 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
16451650return result ;
16461651}
16471652
1648- /*XXX order problems if not atomic assignment ??? */
1653+ /*Fill in OID and transaction status data for newtup */
16491654newtup -> t_data -> t_oid = oldtup .t_data -> t_oid ;
16501655TransactionIdStore (GetCurrentTransactionId (),& (newtup -> t_data -> t_xmin ));
16511656newtup -> t_data -> t_cmin = GetCurrentCommandId ();
16521657StoreInvalidTransactionId (& (newtup -> t_data -> t_xmax ));
16531658newtup -> t_data -> t_infomask &= ~(HEAP_XACT_MASK );
16541659newtup -> 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 ;
16911688XactPushRollback (_heap_unlock_tuple , (void * )& _locked_tuple_ );
16921689
1693- TransactionIdStore (GetCurrentTransactionId (),& (oldtup .t_data -> t_xmax ));
1690+ TransactionIdStore (GetCurrentTransactionId (),
1691+ & (oldtup .t_data -> t_xmax ));
16941692oldtup .t_data -> t_cmax = GetCurrentCommandId ();
16951693oldtup .t_data -> t_infomask &= ~(HEAP_XMAX_COMMITTED |
1696- HEAP_XMAX_INVALID |HEAP_MARKED_FOR_UPDATE );
1694+ HEAP_XMAX_INVALID |
1695+ HEAP_MARKED_FOR_UPDATE );
16971696oldtup .t_data -> t_infomask |=HEAP_XMAX_UNLOGGED ;
1697+ already_marked = true;
16981698LockBuffer (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... */
17011712LockBuffer (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 */
17051722START_CRIT_SECTION ();
17061723
17071724RelationPutHeapTuple (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}
17151731else
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{
17261746XLogRecPtr recptr = log_heap_update (relation ,buffer ,oldtup .t_self ,
1727- newbuf ,newtup , false);
1747+ newbuf ,newtup , false);
17281748
17291749if (newbuf != buffer )
17301750{
@@ -1734,6 +1754,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
17341754PageSetLSN (BufferGetPage (buffer ),recptr );
17351755PageSetSUI (BufferGetPage (buffer ),ThisStartUpID );
17361756}
1757+
17371758END_CRIT_SECTION ();
17381759
17391760if (newbuf != buffer )