*** ./src/backend/access/heap/heapam.c.orig	Sat Oct 15 11:49:08 2005
--- ./src/backend/access/heap/heapam.c	Mon Nov 21 15:57:23 2005
***************
*** 1085,1096 ****
--- 1085,1103 ----
   *
   * use_fsm is passed directly to RelationGetBufferForTuple, which see for
   * more info.
+  *
+  * The return value is the OID assigned to the tuple (either here or by the
+  * caller), or InvalidOid if no OID.  The header fields of *tup are updated
+  * to match the stored tuple; in particular tup->t_self receives the actual
+  * TID where the tuple was stored.  But note that any toasting of fields
+  * within the tuple data is NOT reflected into *tup.
   */
  Oid
  heap_insert(Relation relation, HeapTuple tup, CommandId cid,
  			bool use_wal, bool use_fsm)
  {
  	TransactionId xid = GetCurrentTransactionId();
+ 	HeapTuple	heaptup;
  	Buffer		buffer;
  
  	if (relation->rd_rel->relhasoids)
***************
*** 1128,1146 ****
  	/*
  	 * If the new tuple is too big for storage or contains already toasted
  	 * out-of-line attributes from some other relation, invoke the toaster.
  	 */
  	if (HeapTupleHasExternal(tup) ||
  		(MAXALIGN(tup->t_len) > TOAST_TUPLE_THRESHOLD))
! 		heap_tuple_toast_attrs(relation, tup, NULL);
  
  	/* Find buffer to insert this tuple into */
! 	buffer = RelationGetBufferForTuple(relation, tup->t_len,
  									   InvalidBuffer, use_fsm);
  
  	/* NO EREPORT(ERROR) from here till changes are logged */
  	START_CRIT_SECTION();
  
! 	RelationPutHeapTuple(relation, buffer, tup);
  
  	/* XLOG stuff */
  	if (relation->rd_istemp)
--- 1135,1158 ----
  	/*
  	 * If the new tuple is too big for storage or contains already toasted
  	 * out-of-line attributes from some other relation, invoke the toaster.
+ 	 *
+ 	 * Note: below this point, heaptup is the data we actually intend to
+ 	 * store into the relation; tup is the caller's original untoasted data.
  	 */
  	if (HeapTupleHasExternal(tup) ||
  		(MAXALIGN(tup->t_len) > TOAST_TUPLE_THRESHOLD))
! 		heaptup = toast_insert_or_update(relation, tup, NULL);
! 	else
! 		heaptup = tup;
  
  	/* Find buffer to insert this tuple into */
! 	buffer = RelationGetBufferForTuple(relation, heaptup->t_len,
  									   InvalidBuffer, use_fsm);
  
  	/* NO EREPORT(ERROR) from here till changes are logged */
  	START_CRIT_SECTION();
  
! 	RelationPutHeapTuple(relation, buffer, heaptup);
  
  	/* XLOG stuff */
  	if (relation->rd_istemp)
***************
*** 1158,1172 ****
  		uint8		info = XLOG_HEAP_INSERT;
  
  		xlrec.target.node = relation->rd_node;
! 		xlrec.target.tid = tup->t_self;
  		rdata[0].data = (char *) &xlrec;
  		rdata[0].len = SizeOfHeapInsert;
  		rdata[0].buffer = InvalidBuffer;
  		rdata[0].next = &(rdata[1]);
  
! 		xlhdr.t_natts = tup->t_data->t_natts;
! 		xlhdr.t_infomask = tup->t_data->t_infomask;
! 		xlhdr.t_hoff = tup->t_data->t_hoff;
  
  		/*
  		 * note we mark rdata[1] as belonging to buffer; if XLogInsert decides
--- 1170,1184 ----
  		uint8		info = XLOG_HEAP_INSERT;
  
  		xlrec.target.node = relation->rd_node;
! 		xlrec.target.tid = heaptup->t_self;
  		rdata[0].data = (char *) &xlrec;
  		rdata[0].len = SizeOfHeapInsert;
  		rdata[0].buffer = InvalidBuffer;
  		rdata[0].next = &(rdata[1]);
  
! 		xlhdr.t_natts = heaptup->t_data->t_natts;
! 		xlhdr.t_infomask = heaptup->t_data->t_infomask;
! 		xlhdr.t_hoff = heaptup->t_data->t_hoff;
  
  		/*
  		 * note we mark rdata[1] as belonging to buffer; if XLogInsert decides
***************
*** 1180,1187 ****
  		rdata[1].next = &(rdata[2]);
  
  		/* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */
! 		rdata[2].data = (char *) tup->t_data + offsetof(HeapTupleHeaderData, t_bits);
! 		rdata[2].len = tup->t_len - offsetof(HeapTupleHeaderData, t_bits);
  		rdata[2].buffer = buffer;
  		rdata[2].buffer_std = true;
  		rdata[2].next = NULL;
--- 1192,1199 ----
  		rdata[1].next = &(rdata[2]);
  
  		/* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */
! 		rdata[2].data = (char *) heaptup->t_data + offsetof(HeapTupleHeaderData, t_bits);
! 		rdata[2].len = heaptup->t_len - offsetof(HeapTupleHeaderData, t_bits);
  		rdata[2].buffer = buffer;
  		rdata[2].buffer_std = true;
  		rdata[2].next = NULL;
***************
*** 1191,1197 ****
  		 * page instead of restoring the whole thing.  Set flag, and hide
  		 * buffer references from XLogInsert.
  		 */
! 		if (ItemPointerGetOffsetNumber(&(tup->t_self)) == FirstOffsetNumber &&
  			PageGetMaxOffsetNumber(page) == FirstOffsetNumber)
  		{
  			info |= XLOG_HEAP_INIT_PAGE;
--- 1203,1209 ----
  		 * page instead of restoring the whole thing.  Set flag, and hide
  		 * buffer references from XLogInsert.
  		 */
! 		if (ItemPointerGetOffsetNumber(&(heaptup->t_self)) == FirstOffsetNumber &&
  			PageGetMaxOffsetNumber(page) == FirstOffsetNumber)
  		{
  			info |= XLOG_HEAP_INIT_PAGE;
***************
*** 1212,1224 ****
  	/*
  	 * If tuple is cachable, mark it for invalidation from the caches in case
  	 * we abort.  Note it is OK to do this after WriteBuffer releases the
! 	 * buffer, because the "tup" data structure is all in local memory, not in
! 	 * the shared buffer.
  	 */
! 	CacheInvalidateHeapTuple(relation, tup);
  
  	pgstat_count_heap_insert(&relation->pgstat_info);
  
  	return HeapTupleGetOid(tup);
  }
  
--- 1224,1246 ----
  	/*
  	 * If tuple is cachable, mark it for invalidation from the caches in case
  	 * we abort.  Note it is OK to do this after WriteBuffer releases the
! 	 * buffer, because the heaptup data structure is all in local memory,
! 	 * not in the shared buffer.
  	 */
! 	CacheInvalidateHeapTuple(relation, heaptup);
  
  	pgstat_count_heap_insert(&relation->pgstat_info);
  
+ 	/*
+ 	 * If heaptup is a private copy, release it.  Don't forget to copy t_self
+ 	 * back to the caller's image, too.
+ 	 */
+ 	if (heaptup != tup)
+ 	{
+ 		tup->t_self = heaptup->t_self;
+ 		heap_freetuple(heaptup);
+ 	}
+ 
  	return HeapTupleGetOid(tup);
  }
  
***************
*** 1469,1475 ****
  	 * context lock on the buffer first.
  	 */
  	if (HeapTupleHasExternal(&tp))
! 		heap_tuple_toast_attrs(relation, NULL, &tp);
  
  	/*
  	 * Mark tuple for invalidation from system caches at next command
--- 1491,1497 ----
  	 * context lock on the buffer first.
  	 */
  	if (HeapTupleHasExternal(&tp))
! 		toast_delete(relation, &tp);
  
  	/*
  	 * Mark tuple for invalidation from system caches at next command
***************
*** 1553,1560 ****
   * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
   * (the last only possible if wait == false).
   *
!  * On success, newtup->t_self is set to the TID where the new tuple
!  * was inserted.
   *
   * In the failure cases, the routine returns the tuple's t_ctid and t_xmax.
   * If t_ctid is the same as otid, the tuple was deleted; if different, the
--- 1575,1584 ----
   * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
   * (the last only possible if wait == false).
   *
!  * On success, the header fields of *newtup are updated to match the new
!  * stored tuple; in particular, newtup->t_self is set to the TID where the
!  * new tuple was inserted.  However, any TOAST changes in the new tuple's
!  * data are not reflected into *newtup.
   *
   * In the failure cases, the routine returns the tuple's t_ctid and t_xmax.
   * If t_ctid is the same as otid, the tuple was deleted; if different, the
***************
*** 1570,1575 ****
--- 1594,1600 ----
  	TransactionId xid = GetCurrentTransactionId();
  	ItemId		lp;
  	HeapTupleData oldtup;
+ 	HeapTuple	heaptup;
  	PageHeader	dp;
  	Buffer		buffer,
  				newbuf;
***************
*** 1760,1770 ****
  	 * 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.
  	 */
  	need_toast = (HeapTupleHasExternal(&oldtup) ||
  				  HeapTupleHasExternal(newtup) ||
! 				  (MAXALIGN(newtup->t_len) > TOAST_TUPLE_THRESHOLD));
  
- 	newtupsize = MAXALIGN(newtup->t_len);
  	pagefree = PageGetFreeSpace((Page) dp);
  
  	if (need_toast || newtupsize > pagefree)
--- 1785,1796 ----
  	 * 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.
  	 */
+ 	newtupsize = MAXALIGN(newtup->t_len);
+ 
  	need_toast = (HeapTupleHasExternal(&oldtup) ||
  				  HeapTupleHasExternal(newtup) ||
! 				  newtupsize > TOAST_TUPLE_THRESHOLD);
  
  	pagefree = PageGetFreeSpace((Page) dp);
  
  	if (need_toast || newtupsize > pagefree)
***************
*** 1776,1790 ****
  									   HEAP_MOVED);
  		HeapTupleHeaderSetXmax(oldtup.t_data, xid);
  		HeapTupleHeaderSetCmax(oldtup.t_data, cid);
  		already_marked = true;
  		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
  
! 		/* Let the toaster do its thing */
  		if (need_toast)
  		{
! 			heap_tuple_toast_attrs(relation, newtup, &oldtup);
! 			newtupsize = MAXALIGN(newtup->t_len);
  		}
  
  		/*
  		 * Now, do we need a new page for the tuple, or not?  This is a bit
--- 1802,1826 ----
  									   HEAP_MOVED);
  		HeapTupleHeaderSetXmax(oldtup.t_data, xid);
  		HeapTupleHeaderSetCmax(oldtup.t_data, cid);
+ 		/* temporarily make it look not-updated */
+ 		oldtup.t_data->t_ctid = oldtup.t_self;
  		already_marked = true;
  		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
  
! 		/*
! 		 * Let the toaster do its thing, if needed.
! 		 *
! 		 * Note: below this point, heaptup is the data we actually intend to
! 		 * store into the relation; newtup is the caller's original untoasted
! 		 * data.
! 		 */
  		if (need_toast)
  		{
! 			heaptup = toast_insert_or_update(relation, newtup, &oldtup);
! 			newtupsize = MAXALIGN(heaptup->t_len);
  		}
+ 		else
+ 			heaptup = newtup;
  
  		/*
  		 * Now, do we need a new page for the tuple, or not?  This is a bit
***************
*** 1805,1812 ****
  		 */
  		if (newtupsize > pagefree)
  		{
! 			/* Assume there's no chance to put newtup on same page. */
! 			newbuf = RelationGetBufferForTuple(relation, newtup->t_len,
  											   buffer, true);
  		}
  		else
--- 1841,1848 ----
  		 */
  		if (newtupsize > pagefree)
  		{
! 			/* Assume there's no chance to put heaptup on same page. */
! 			newbuf = RelationGetBufferForTuple(relation, heaptup->t_len,
  											   buffer, true);
  		}
  		else
***************
*** 1823,1829 ****
  				 * seldom be taken.
  				 */
  				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
! 				newbuf = RelationGetBufferForTuple(relation, newtup->t_len,
  												   buffer, true);
  			}
  			else
--- 1859,1865 ----
  				 * seldom be taken.
  				 */
  				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
! 				newbuf = RelationGetBufferForTuple(relation, heaptup->t_len,
  												   buffer, true);
  			}
  			else
***************
*** 1838,1843 ****
--- 1874,1880 ----
  		/* No TOAST work needed, and it'll fit on same page */
  		already_marked = false;
  		newbuf = buffer;
+ 		heaptup = newtup;
  	}
  
  	/*
***************
*** 1849,1855 ****
  	/* NO EREPORT(ERROR) from here till changes are logged */
  	START_CRIT_SECTION();
  
! 	RelationPutHeapTuple(relation, newbuf, newtup);		/* insert new tuple */
  
  	if (!already_marked)
  	{
--- 1886,1892 ----
  	/* NO EREPORT(ERROR) from here till changes are logged */
  	START_CRIT_SECTION();
  
! 	RelationPutHeapTuple(relation, newbuf, heaptup); /* insert new tuple */
  
  	if (!already_marked)
  	{
***************
*** 1863,1875 ****
  	}
  
  	/* record address of new tuple in t_ctid of old one */
! 	oldtup.t_data->t_ctid = newtup->t_self;
  
  	/* XLOG stuff */
  	if (!relation->rd_istemp)
  	{
  		XLogRecPtr	recptr = log_heap_update(relation, buffer, oldtup.t_self,
! 											 newbuf, newtup, false);
  
  		if (newbuf != buffer)
  		{
--- 1900,1912 ----
  	}
  
  	/* record address of new tuple in t_ctid of old one */
! 	oldtup.t_data->t_ctid = heaptup->t_self;
  
  	/* XLOG stuff */
  	if (!relation->rd_istemp)
  	{
  		XLogRecPtr	recptr = log_heap_update(relation, buffer, oldtup.t_self,
! 											 newbuf, heaptup, false);
  
  		if (newbuf != buffer)
  		{
***************
*** 1905,1914 ****
  	/*
  	 * If new tuple is cachable, mark it for invalidation from the caches in
  	 * case we abort.  Note it is OK to do this after WriteBuffer releases the
! 	 * buffer, because the "newtup" data structure is all in local memory, not
  	 * in the shared buffer.
  	 */
! 	CacheInvalidateHeapTuple(relation, newtup);
  
  	/*
  	 * Release the lmgr tuple lock, if we had it.
--- 1942,1951 ----
  	/*
  	 * If new tuple is cachable, mark it for invalidation from the caches in
  	 * case we abort.  Note it is OK to do this after WriteBuffer releases the
! 	 * buffer, because the heaptup data structure is all in local memory, not
  	 * in the shared buffer.
  	 */
! 	CacheInvalidateHeapTuple(relation, heaptup);
  
  	/*
  	 * Release the lmgr tuple lock, if we had it.
***************
*** 1918,1923 ****
--- 1955,1970 ----
  
  	pgstat_count_heap_update(&relation->pgstat_info);
  
+ 	/*
+ 	 * If heaptup is a private copy, release it.  Don't forget to copy t_self
+ 	 * back to the caller's image, too.
+ 	 */
+ 	if (heaptup != newtup)
+ 	{
+ 		newtup->t_self = heaptup->t_self;
+ 		heap_freetuple(heaptup);
+ 	}
+ 
  	return HeapTupleMayBeUpdated;
  }
  
*** ./src/backend/access/heap/tuptoaster.c.orig	Sat Oct 15 11:49:09 2005
--- ./src/backend/access/heap/tuptoaster.c	Mon Nov 21 16:03:46 2005
***************
*** 12,21 ****
   *
   *
   * INTERFACE ROUTINES
!  *		heap_tuple_toast_attrs -
   *			Try to make a given tuple fit into one page by compressing
   *			or moving off attributes
   *
   *		heap_tuple_untoast_attr -
   *			Fetch back a given value from the "secondary" relation
   *
--- 12,24 ----
   *
   *
   * INTERFACE ROUTINES
!  *		toast_insert_or_update -
   *			Try to make a given tuple fit into one page by compressing
   *			or moving off attributes
   *
+  *		toast_delete -
+  *			Reclaim toast storage when a tuple is deleted
+  *
   *		heap_tuple_untoast_attr -
   *			Fetch back a given value from the "secondary" relation
   *
***************
*** 40,49 ****
  
  #undef TOAST_DEBUG
  
- static void toast_delete(Relation rel, HeapTuple oldtup);
  static void toast_delete_datum(Relation rel, Datum value);
- static void toast_insert_or_update(Relation rel, HeapTuple newtup,
- 					   HeapTuple oldtup);
  static Datum toast_save_datum(Relation rel, Datum value);
  static varattrib *toast_fetch_datum(varattrib *attr);
  static varattrib *toast_fetch_datum_slice(varattrib *attr,
--- 43,49 ----
***************
*** 51,74 ****
  
  
  /* ----------
-  * heap_tuple_toast_attrs -
-  *
-  *	This is the central public entry point for toasting from heapam.
-  *
-  *	Calls the appropriate event specific action.
-  * ----------
-  */
- void
- heap_tuple_toast_attrs(Relation rel, HeapTuple newtup, HeapTuple oldtup)
- {
- 	if (newtup == NULL)
- 		toast_delete(rel, oldtup);
- 	else
- 		toast_insert_or_update(rel, newtup, oldtup);
- }
- 
- 
- /* ----------
   * heap_tuple_fetch_attr -
   *
   *	Public entry point to get back a toasted value
--- 51,56 ----
***************
*** 305,311 ****
   *	Cascaded delete toast-entries on DELETE
   * ----------
   */
! static void
  toast_delete(Relation rel, HeapTuple oldtup)
  {
  	TupleDesc	tupleDesc;
--- 287,293 ----
   *	Cascaded delete toast-entries on DELETE
   * ----------
   */
! void
  toast_delete(Relation rel, HeapTuple oldtup)
  {
  	TupleDesc	tupleDesc;
***************
*** 355,365 ****
   *
   *	Delete no-longer-used toast-entries and create new ones to
   *	make the new tuple fit on INSERT or UPDATE
   * ----------
   */
! static void
  toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
  {
  	TupleDesc	tupleDesc;
  	Form_pg_attribute *att;
  	int			numAttrs;
--- 337,358 ----
   *
   *	Delete no-longer-used toast-entries and create new ones to
   *	make the new tuple fit on INSERT or UPDATE
+  *
+  * Inputs:
+  *	newtup: the candidate new tuple to be inserted
+  *	oldtup: the old row version for UPDATE, or NULL for INSERT
+  * Result:
+  *	either newtup if no toasting is needed, or a palloc'd modified tuple
+  *	that is what should actually get stored
+  *
+  * NOTE: neither newtup nor oldtup will be modified.  This is a change
+  * from the pre-8.1 API of this routine.
   * ----------
   */
! HeapTuple
  toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
  {
+ 	HeapTuple	result_tuple;
  	TupleDesc	tupleDesc;
  	Form_pg_attribute *att;
  	int			numAttrs;
***************
*** 757,763 ****
  	if (need_change)
  	{
  		HeapTupleHeader olddata = newtup->t_data;
! 		char	   *new_data;
  		int32		new_len;
  
  		/*
--- 750,756 ----
  	if (need_change)
  	{
  		HeapTupleHeader olddata = newtup->t_data;
! 		HeapTupleHeader new_data;
  		int32		new_len;
  
  		/*
***************
*** 775,788 ****
  										  toast_values, toast_isnull);
  
  		/*
! 		 * Allocate new tuple in same context as old one.
  		 */
! 		new_data = (char *) MemoryContextAlloc(newtup->t_datamcxt, new_len);
! 		newtup->t_data = (HeapTupleHeader) new_data;
! 		newtup->t_len = new_len;
  
  		/*
! 		 * Put the tuple header and the changed values into place
  		 */
  		memcpy(new_data, olddata, olddata->t_hoff);
  
--- 768,785 ----
  										  toast_values, toast_isnull);
  
  		/*
! 		 * Allocate and zero the space needed, and fill HeapTupleData fields.
  		 */
! 		result_tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + new_len);
! 		result_tuple->t_len = new_len;
! 		result_tuple->t_self = newtup->t_self;
! 		result_tuple->t_tableOid = newtup->t_tableOid;
! 		result_tuple->t_datamcxt = CurrentMemoryContext;
! 		new_data = (HeapTupleHeader) ((char *) result_tuple + HEAPTUPLESIZE);
! 		result_tuple->t_data = new_data;
  
  		/*
! 		 * Put the existing tuple header and the changed values into place
  		 */
  		memcpy(new_data, olddata, olddata->t_hoff);
  
***************
*** 790,805 ****
  						toast_values,
  						toast_isnull,
  						(char *) new_data + olddata->t_hoff,
! 						&(newtup->t_data->t_infomask),
! 						has_nulls ? newtup->t_data->t_bits : NULL);
! 
! 		/*
! 		 * In the case we modified a previously modified tuple again, free the
! 		 * memory from the previous run
! 		 */
! 		if ((char *) olddata != ((char *) newtup + HEAPTUPLESIZE))
! 			pfree(olddata);
  	}
  
  	/*
  	 * Free allocated temp values
--- 787,797 ----
  						toast_values,
  						toast_isnull,
  						(char *) new_data + olddata->t_hoff,
! 						&(new_data->t_infomask),
! 						has_nulls ? new_data->t_bits : NULL);
  	}
+ 	else
+ 		result_tuple = newtup;
  
  	/*
  	 * Free allocated temp values
***************
*** 816,821 ****
--- 808,815 ----
  		for (i = 0; i < numAttrs; i++)
  			if (toast_delold[i])
  				toast_delete_datum(rel, toast_oldvalues[i]);
+ 
+ 	return result_tuple;
  }
  
  
*** ./src/include/access/tuptoaster.h.orig	Thu Jul  7 04:02:53 2005
--- ./src/include/access/tuptoaster.h	Mon Nov 21 16:01:52 2005
***************
*** 66,84 ****
  
  
  /* ----------
!  * heap_tuple_toast_attrs() -
   *
!  *		Called by heap_insert(), heap_update() and heap_delete().
!  *		Outdates any no-longer-needed toast entries referenced
!  *		by oldtup and creates new ones until newtup is no more than
!  *		TOAST_TUPLE_TARGET (or we run out of toastable values).
!  *		Possibly modifies newtup by replacing the t_data part!
   *
!  *		oldtup is NULL if insert, newtup is NULL if delete.
   * ----------
   */
! extern void heap_tuple_toast_attrs(Relation rel,
! 					   HeapTuple newtup, HeapTuple oldtup);
  
  /* ----------
   * heap_tuple_fetch_attr() -
--- 66,86 ----
  
  
  /* ----------
!  * toast_insert_or_update -
   *
!  *	Called by heap_insert() and heap_update().
!  * ----------
!  */
! extern HeapTuple toast_insert_or_update(Relation rel,
! 										HeapTuple newtup, HeapTuple oldtup);
! 
! /* ----------
!  * toast_delete -
   *
!  *	Called by heap_delete().
   * ----------
   */
! extern void toast_delete(Relation rel, HeapTuple oldtup);
  
  /* ----------
   * heap_tuple_fetch_attr() -
