>From d97434637b38f9520f46c009798b188009eaea7b Mon Sep 17 00:00:00 2001
From: Oskari Saarenmaa <os@aiven.io>
Date: Wed, 6 Jul 2016 13:19:14 +0300
Subject: [PATCH] Delete current command's TOAST when aborting speculative
 insert

---
 contrib/pgrowlocks/pgrowlocks.c        |  1 +
 src/backend/access/heap/heapam.c       | 29 ++++++++++++++++++++++-------
 src/backend/access/heap/tuptoaster.c   | 20 ++++++++++++++------
 src/backend/executor/nodeModifyTable.c |  1 +
 src/backend/utils/time/tqual.c         |  5 +++--
 src/include/access/heapam.h            |  4 +++-
 src/include/access/tuptoaster.h        |  2 +-
 src/include/utils/tqual.h              |  2 +-
 8 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index e20e7f8..7ebac5a 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -131,6 +131,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
 
 		htsu = HeapTupleSatisfiesUpdate(tuple,
 										GetCurrentCommandId(false),
+										false,
 										scan->rs_cbuf);
 		xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
 		infomask = tuple->t_data->t_infomask;
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 57da57a..77b75dd 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2967,6 +2967,8 @@ xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
  *	tid - TID of tuple to be deleted
  *	cid - delete command ID (used for visibility test, and stored into
  *		cmax if successful)
+ *	delete_current - allow deleting tuples created by current command,
+ *                       used when deleting toast for speculative tuples
  *	crosscheck - if not InvalidSnapshot, also check tuple against this
  *	wait - true if should wait for any conflicting update to commit/abort
  *	hufd - output parameter, filled in failure cases (see below)
@@ -2984,7 +2986,8 @@ xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
  */
 HTSU_Result
 heap_delete(Relation relation, ItemPointer tid,
-			CommandId cid, Snapshot crosscheck, bool wait,
+			CommandId cid, bool delete_current,
+			Snapshot crosscheck, bool wait,
 			HeapUpdateFailureData *hufd)
 {
 	HTSU_Result result;
@@ -3053,7 +3056,7 @@ heap_delete(Relation relation, ItemPointer tid,
 	tp.t_self = *tid;
 
 l1:
-	result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+	result = HeapTupleSatisfiesUpdate(&tp, cid, delete_current, buffer);
 
 	if (result == HeapTupleInvisible)
 	{
@@ -3335,7 +3338,7 @@ l1:
 		Assert(!HeapTupleHasExternal(&tp));
 	}
 	else if (HeapTupleHasExternal(&tp))
-		toast_delete(relation, &tp);
+		toast_delete(relation, &tp, false);
 
 	/*
 	 * Mark tuple for invalidation from system caches at next command
@@ -3376,9 +3379,21 @@ simple_heap_delete(Relation relation, ItemPointer tid)
 	HeapUpdateFailureData hufd;
 
 	result = heap_delete(relation, tid,
-						 GetCurrentCommandId(true), InvalidSnapshot,
+						 GetCurrentCommandId(true), false,
+						 InvalidSnapshot,
 						 true /* wait for commit */ ,
 						 &hufd);
+	heap_delete_check(result);
+}
+
+/*
+ *	heap_delete_check - check status of heap delete operation
+ *
+ * Any failure is reported via ereport().
+ */
+void
+heap_delete_check(HTSU_Result result)
+{
 	switch (result)
 	{
 		case HeapTupleSelfUpdated:
@@ -3599,7 +3614,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 l2:
 	checked_lockers = false;
 	locker_remains = false;
-	result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);
+	result = HeapTupleSatisfiesUpdate(&oldtup, cid, false, buffer);
 
 	/* see below about the "no wait" case */
 	Assert(result != HeapTupleBeingUpdated || wait);
@@ -4526,7 +4541,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
 	tuple->t_tableOid = RelationGetRelid(relation);
 
 l3:
-	result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
+	result = HeapTupleSatisfiesUpdate(tuple, cid, false, *buffer);
 
 	if (result == HeapTupleInvisible)
 	{
@@ -6000,7 +6015,7 @@ heap_abort_speculative(Relation relation, HeapTuple tuple)
 	LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 
 	if (HeapTupleHasExternal(&tp))
-		toast_delete(relation, &tp);
+		toast_delete(relation, &tp, true);
 
 	/*
 	 * Never need to mark tuple for invalidation, since catalogs don't support
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 4cffd21..f228eb0 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -66,7 +66,7 @@ typedef struct toast_compress_header
 #define TOAST_COMPRESS_SET_RAWSIZE(ptr, len) \
 	(((toast_compress_header *) (ptr))->rawsize = (len))
 
-static void toast_delete_datum(Relation rel, Datum value);
+static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
 static Datum toast_save_datum(Relation rel, Datum value,
 				 struct varlena * oldexternal, int options);
 static bool toastrel_valueid_exists(Relation toastrel, Oid valueid);
@@ -459,7 +459,7 @@ toast_datum_size(Datum value)
  * ----------
  */
 void
-toast_delete(Relation rel, HeapTuple oldtup)
+toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
 {
 	TupleDesc	tupleDesc;
 	Form_pg_attribute *att;
@@ -506,7 +506,7 @@ toast_delete(Relation rel, HeapTuple oldtup)
 			if (toast_isnull[i])
 				continue;
 			else if (VARATT_IS_EXTERNAL_ONDISK(PointerGetDatum(value)))
-				toast_delete_datum(rel, value);
+				toast_delete_datum(rel, value, is_speculative);
 		}
 	}
 }
@@ -1062,7 +1062,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 	if (need_delold)
 		for (i = 0; i < numAttrs; i++)
 			if (toast_delold[i])
-				toast_delete_datum(rel, toast_oldvalues[i]);
+				toast_delete_datum(rel, toast_oldvalues[i], false);
 
 	return result_tuple;
 }
@@ -1654,7 +1654,7 @@ toast_save_datum(Relation rel, Datum value,
  * ----------
  */
 static void
-toast_delete_datum(Relation rel, Datum value)
+toast_delete_datum(Relation rel, Datum value, bool is_speculative)
 {
 	struct varlena *attr = (struct varlena *) DatumGetPointer(value);
 	struct varatt_external toast_pointer;
@@ -1703,7 +1703,15 @@ toast_delete_datum(Relation rel, Datum value)
 		/*
 		 * Have a chunk, delete it
 		 */
-		simple_heap_delete(toastrel, &toasttup->t_self);
+		HTSU_Result result;
+		HeapUpdateFailureData hufd;
+
+		result = heap_delete(toastrel, &toasttup->t_self,
+							 GetCurrentCommandId(true), is_speculative,
+							 InvalidSnapshot,
+							 true /* wait for commit */ ,
+							 &hufd);
+		heap_delete_check(result);
 	}
 
 	/*
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..50571ad 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -627,6 +627,7 @@ ExecDelete(ItemPointer tupleid,
 ldelete:;
 		result = heap_delete(resultRelationDesc, tupleid,
 							 estate->es_output_cid,
+							 false,
 							 estate->es_crosscheck_snapshot,
 							 true /* wait for commit */ ,
 							 &hufd);
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index a08dae1..084ae92 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -459,7 +459,7 @@ HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
  */
 HTSU_Result
 HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
-						 Buffer buffer)
+						 bool access_current, Buffer buffer)
 {
 	HeapTupleHeader tuple = htup->t_data;
 
@@ -513,7 +513,8 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
 		else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
 		{
 			if (HeapTupleHeaderGetCmin(tuple) >= curcid)
-				return HeapTupleInvisible;		/* inserted after scan started */
+				if (!access_current || HeapTupleHeaderGetCmin(tuple) != curcid)
+					return HeapTupleInvisible;		/* inserted after scan started */
 
 			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
 				return HeapTupleMayBeUpdated;
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index b3a595c..7ecd115 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -153,8 +153,10 @@ extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
 extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
 				  CommandId cid, int options, BulkInsertState bistate);
 extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
-			CommandId cid, Snapshot crosscheck, bool wait,
+			CommandId cid, bool delete_current,
+			Snapshot crosscheck, bool wait,
 			HeapUpdateFailureData *hufd);
+extern void heap_delete_check(HTSU_Result result);
 extern void heap_finish_speculative(Relation relation, HeapTuple tuple);
 extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
 extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h
index 7b5ae62..011f5c1 100644
--- a/src/include/access/tuptoaster.h
+++ b/src/include/access/tuptoaster.h
@@ -142,7 +142,7 @@ extern HeapTuple toast_insert_or_update(Relation rel,
  *	Called by heap_delete().
  * ----------
  */
-extern void toast_delete(Relation rel, HeapTuple oldtup);
+extern void toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative);
 
 /* ----------
  * heap_tuple_fetch_attr() -
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 2445944..886034d 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -80,7 +80,7 @@ extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup,
 
 /* Special "satisfies" routines with different APIs */
 extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup,
-						 CommandId curcid, Buffer buffer);
+						 CommandId curcid, bool access_current, Buffer buffer);
 extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup,
 						 TransactionId OldestXmin, Buffer buffer);
 extern bool HeapTupleIsSurelyDead(HeapTuple htup,
-- 
2.5.5

