From 4abb8b578a22b3ee46d608af41ec52b50bf072af Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Mon, 11 Dec 2017 21:36:49 +1100
Subject: [PATCH 6/8] Tuple Insert API is added to Storage AM

heap_insert, heap_delete, heap_fetch, heap_update,
heap_get_latest_oid, heap_lock_tuple and heap_multi_insert
functions are added to storage AM. Move the index insertion
logic into storage AM, The Index insert still outside for
the case of multi_insert (Yet to change).

Replaced the usage of HeapTuple with storageTuple in
some places, increased the use of slot.
---
 src/backend/access/common/heaptuple.c       |  24 +++
 src/backend/access/heap/heapam.c            | 136 +++++++++-------
 src/backend/access/heap/heapam_storage.c    | 227 ++++++++++++++++++++++++++
 src/backend/access/heap/rewriteheap.c       |   5 +-
 src/backend/access/heap/tuptoaster.c        |   9 +-
 src/backend/access/storage/storage_common.c |  16 +-
 src/backend/access/storage/storageam.c      | 126 ++++++++++++++
 src/backend/commands/copy.c                 |  39 ++---
 src/backend/commands/createas.c             |  24 +--
 src/backend/commands/matview.c              |  22 ++-
 src/backend/commands/tablecmds.c            |   6 +-
 src/backend/commands/trigger.c              |  50 +++---
 src/backend/executor/execIndexing.c         |   2 +-
 src/backend/executor/execMain.c             | 131 ++++++++-------
 src/backend/executor/execReplication.c      |  72 ++++----
 src/backend/executor/nodeLockRows.c         |  47 +++---
 src/backend/executor/nodeModifyTable.c      | 244 +++++++++++++---------------
 src/backend/executor/nodeTidscan.c          |  23 ++-
 src/backend/utils/adt/tid.c                 |   5 +-
 src/include/access/heapam.h                 |  15 +-
 src/include/access/htup_details.h           |   1 +
 src/include/access/storage_common.h         |   5 -
 src/include/access/storageam.h              |  63 +++++++
 src/include/access/storageamapi.h           | 102 ++++++++++++
 src/include/commands/trigger.h              |   2 +-
 src/include/executor/executor.h             |  15 +-
 src/include/executor/tuptable.h             |   1 +
 src/include/nodes/execnodes.h               |   8 +-
 28 files changed, 981 insertions(+), 439 deletions(-)
 create mode 100644 src/include/access/storageam.h

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c29935b91a..a003df1c28 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -685,6 +685,30 @@ heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 	return PointerGetDatum(td);
 }
 
+/*
+ * heap_form_tuple_by_datum
+ *		construct a tuple from the given dataum
+ *
+ * The result is allocated in the current memory context.
+ */
+HeapTuple
+heap_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+	HeapTuple	newTuple;
+	HeapTupleHeader td;
+
+	td = DatumGetHeapTupleHeader(data);
+
+	newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + HeapTupleHeaderGetDatumLength(td));
+	newTuple->t_len = HeapTupleHeaderGetDatumLength(td);
+	newTuple->t_self = td->t_ctid;
+	newTuple->t_tableOid = tableoid;
+	newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
+	memcpy((char *) newTuple->t_data, (char *) td, newTuple->t_len);
+
+	return newTuple;
+}
+
 /*
  * heap_form_tuple
  *		construct a tuple from the given values[] and isnull[] arrays,
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index d63afbaf69..84358748bd 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -1893,13 +1893,13 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
  */
 bool
 heap_fetch(Relation relation,
+		   ItemPointer tid,
 		   Snapshot snapshot,
 		   HeapTuple tuple,
 		   Buffer *userbuf,
 		   bool keep_buf,
 		   Relation stats_relation)
 {
-	ItemPointer tid = &(tuple->t_self);
 	ItemId		lp;
 	Buffer		buffer;
 	Page		page;
@@ -1933,7 +1933,6 @@ heap_fetch(Relation relation,
 			ReleaseBuffer(buffer);
 			*userbuf = InvalidBuffer;
 		}
-		tuple->t_data = NULL;
 		return false;
 	}
 
@@ -1955,13 +1954,13 @@ heap_fetch(Relation relation,
 			ReleaseBuffer(buffer);
 			*userbuf = InvalidBuffer;
 		}
-		tuple->t_data = NULL;
 		return false;
 	}
 
 	/*
-	 * fill in *tuple fields
+	 * fill in tuple fields and place it in stuple
 	 */
+	ItemPointerCopy(tid, &(tuple->t_self));
 	tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
 	tuple->t_len = ItemIdGetLength(lp);
 	tuple->t_tableOid = RelationGetRelid(relation);
@@ -2312,6 +2311,18 @@ heap_get_latest_tid(Relation relation,
 	}							/* end of loop */
 }
 
+/*
+ * HeapTupleSetHintBits --- exported version of SetHintBits()
+ *
+ * This must be separate because of C99's brain-dead notions about how to
+ * implement inline functions.
+ */
+static void
+HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+					 uint16 infomask, TransactionId xid)
+{
+	SetHintBits(tuple, buffer, infomask, xid);
+}
 
 /*
  * UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -4576,13 +4587,12 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
  * See README.tuplock for a thorough explanation of this mechanism.
  */
 HTSU_Result
-heap_lock_tuple(Relation relation, HeapTuple tuple,
+heap_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
 				CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
 				bool follow_updates,
 				Buffer *buffer, HeapUpdateFailureData *hufd)
 {
 	HTSU_Result result;
-	ItemPointer tid = &(tuple->t_self);
 	ItemId		lp;
 	Page		page;
 	Buffer		vmbuffer = InvalidBuffer;
@@ -4595,6 +4605,9 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
 	bool		first_time = true;
 	bool		have_tuple_lock = false;
 	bool		cleared_all_frozen = false;
+	HeapTupleData tuple;
+
+	Assert(stuple != NULL);
 
 	*buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
 	block = ItemPointerGetBlockNumber(tid);
@@ -4614,12 +4627,13 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
 	lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
 	Assert(ItemIdIsNormal(lp));
 
-	tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
-	tuple->t_len = ItemIdGetLength(lp);
-	tuple->t_tableOid = RelationGetRelid(relation);
+	tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+	tuple.t_len = ItemIdGetLength(lp);
+	tuple.t_tableOid = RelationGetRelid(relation);
+	ItemPointerCopy(tid, &tuple.t_self);
 
 l3:
-	result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
+	result = HeapTupleSatisfiesUpdate(&tuple, cid, *buffer);
 
 	if (result == HeapTupleInvisible)
 	{
@@ -4641,10 +4655,10 @@ l3:
 		ItemPointerData t_ctid;
 
 		/* must copy state data before unlocking buffer */
-		xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
-		infomask = tuple->t_data->t_infomask;
-		infomask2 = tuple->t_data->t_infomask2;
-		ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
+		xwait = HeapTupleHeaderGetRawXmax(tuple.t_data);
+		infomask = tuple.t_data->t_infomask;
+		infomask2 = tuple.t_data->t_infomask2;
+		ItemPointerCopy(&tuple.t_data->t_ctid, &t_ctid);
 
 		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
 
@@ -4776,7 +4790,7 @@ l3:
 				{
 					HTSU_Result res;
 
-					res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
+					res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
 												  GetCurrentTransactionId(),
 												  mode);
 					if (res != HeapTupleMayBeUpdated)
@@ -4797,8 +4811,8 @@ l3:
 				 * now need to follow the update chain to lock the new
 				 * versions.
 				 */
-				if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
-					((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
+				if (!HeapTupleHeaderIsOnlyLocked(tuple.t_data) &&
+					((tuple.t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
 					 !updated))
 					goto l3;
 
@@ -4829,8 +4843,8 @@ l3:
 				 * Make sure it's still an appropriate lock, else start over.
 				 * See above about allowing xmax to change.
 				 */
-				if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
-					HEAP_XMAX_IS_EXCL_LOCKED(tuple->t_data->t_infomask))
+				if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+					HEAP_XMAX_IS_EXCL_LOCKED(tuple.t_data->t_infomask))
 					goto l3;
 				require_sleep = false;
 			}
@@ -4852,8 +4866,8 @@ l3:
 					 * meantime, start over.
 					 */
 					LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-					if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
-						!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+					if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+						!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
 											 xwait))
 						goto l3;
 
@@ -4866,9 +4880,9 @@ l3:
 				LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
 
 				/* if the xmax changed in the meantime, start over */
-				if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
+				if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
 					!TransactionIdEquals(
-										 HeapTupleHeaderGetRawXmax(tuple->t_data),
+										 HeapTupleHeaderGetRawXmax(tuple.t_data),
 										 xwait))
 					goto l3;
 				/* otherwise, we're good */
@@ -4893,11 +4907,11 @@ l3:
 		{
 			/* ... but if the xmax changed in the meantime, start over */
 			LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-			if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
-				!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+			if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+				!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
 									 xwait))
 				goto l3;
-			Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask));
+			Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask));
 			require_sleep = false;
 		}
 
@@ -4954,7 +4968,7 @@ l3:
 				{
 					case LockWaitBlock:
 						MultiXactIdWait((MultiXactId) xwait, status, infomask,
-										relation, &tuple->t_self, XLTW_Lock, NULL);
+										relation, &tuple.t_self, XLTW_Lock, NULL);
 						break;
 					case LockWaitSkip:
 						if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
@@ -4995,7 +5009,7 @@ l3:
 				switch (wait_policy)
 				{
 					case LockWaitBlock:
-						XactLockTableWait(xwait, relation, &tuple->t_self,
+						XactLockTableWait(xwait, relation, &tuple.t_self,
 										  XLTW_Lock);
 						break;
 					case LockWaitSkip:
@@ -5022,7 +5036,7 @@ l3:
 			{
 				HTSU_Result res;
 
-				res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
+				res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
 											  GetCurrentTransactionId(),
 											  mode);
 				if (res != HeapTupleMayBeUpdated)
@@ -5041,8 +5055,8 @@ l3:
 			 * other xact could update this tuple before we get to this point.
 			 * Check for xmax change, and start over if so.
 			 */
-			if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
-				!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+			if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+				!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
 									 xwait))
 				goto l3;
 
@@ -5056,7 +5070,7 @@ l3:
 				 * don't check for this in the multixact case, because some
 				 * locker transactions might still be running.
 				 */
-				UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
+				UpdateXmaxHintBits(tuple.t_data, *buffer, xwait);
 			}
 		}
 
@@ -5068,9 +5082,9 @@ l3:
 		 * at all for whatever reason.
 		 */
 		if (!require_sleep ||
-			(tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
-			HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
-			HeapTupleHeaderIsOnlyLocked(tuple->t_data))
+			(tuple.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+			HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+			HeapTupleHeaderIsOnlyLocked(tuple.t_data))
 			result = HeapTupleMayBeUpdated;
 		else
 			result = HeapTupleUpdated;
@@ -5081,11 +5095,11 @@ failed:
 	{
 		Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
 			   result == HeapTupleWouldBlock);
-		Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
-		hufd->ctid = tuple->t_data->t_ctid;
-		hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
+		Assert(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID));
+		hufd->ctid = tuple.t_data->t_ctid;
+		hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
 		if (result == HeapTupleSelfUpdated)
-			hufd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
+			hufd->cmax = HeapTupleHeaderGetCmax(tuple.t_data);
 		else
 			hufd->cmax = InvalidCommandId;
 		goto out_locked;
@@ -5108,8 +5122,8 @@ failed:
 		goto l3;
 	}
 
-	xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
-	old_infomask = tuple->t_data->t_infomask;
+	xmax = HeapTupleHeaderGetRawXmax(tuple.t_data);
+	old_infomask = tuple.t_data->t_infomask;
 
 	/*
 	 * If this is the first possibly-multixact-able operation in the current
@@ -5126,7 +5140,7 @@ failed:
 	 * not modify the tuple just yet, because that would leave it in the wrong
 	 * state if multixact.c elogs.
 	 */
-	compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
+	compute_new_xmax_infomask(xmax, old_infomask, tuple.t_data->t_infomask2,
 							  GetCurrentTransactionId(), mode, false,
 							  &xid, &new_infomask, &new_infomask2);
 
@@ -5142,13 +5156,13 @@ failed:
 	 * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
 	 * we would break the HOT chain.
 	 */
-	tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
-	tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
-	tuple->t_data->t_infomask |= new_infomask;
-	tuple->t_data->t_infomask2 |= new_infomask2;
+	tuple.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+	tuple.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+	tuple.t_data->t_infomask |= new_infomask;
+	tuple.t_data->t_infomask2 |= new_infomask2;
 	if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
-		HeapTupleHeaderClearHotUpdated(tuple->t_data);
-	HeapTupleHeaderSetXmax(tuple->t_data, xid);
+		HeapTupleHeaderClearHotUpdated(tuple.t_data);
+	HeapTupleHeaderSetXmax(tuple.t_data, xid);
 
 	/*
 	 * Make sure there is no forward chain link in t_ctid.  Note that in the
@@ -5158,7 +5172,7 @@ failed:
 	 * the tuple as well.
 	 */
 	if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
-		tuple->t_data->t_ctid = *tid;
+		tuple.t_data->t_ctid = *tid;
 
 	/* Clear only the all-frozen bit on visibility map if needed */
 	if (PageIsAllVisible(page) &&
@@ -5189,10 +5203,10 @@ failed:
 		XLogBeginInsert();
 		XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
 
-		xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
+		xlrec.offnum = ItemPointerGetOffsetNumber(&tuple.t_self);
 		xlrec.locking_xid = xid;
 		xlrec.infobits_set = compute_infobits(new_infomask,
-											  tuple->t_data->t_infomask2);
+											  tuple.t_data->t_infomask2);
 		xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
 		XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
 
@@ -5226,6 +5240,7 @@ out_unlocked:
 	if (have_tuple_lock)
 		UnlockTupleTuplock(relation, tid, mode);
 
+	*stuple = heap_copytuple(&tuple);
 	return result;
 }
 
@@ -5683,9 +5698,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
 		new_infomask = 0;
 		new_xmax = InvalidTransactionId;
 		block = ItemPointerGetBlockNumber(&tupid);
-		ItemPointerCopy(&tupid, &(mytup.t_self));
 
-		if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL))
+		if (!heap_fetch(rel, &tupid, SnapshotAny, &mytup, &buf, false, NULL))
 		{
 			/*
 			 * if we fail to find the updated version of the tuple, it's
@@ -6032,14 +6046,18 @@ heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
  * An explicit confirmation WAL record also makes logical decoding simpler.
  */
 void
-heap_finish_speculative(Relation relation, HeapTuple tuple)
+heap_finish_speculative(Relation relation, TupleTableSlot *slot)
 {
+	HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+	HeapTuple	tuple = stuple->hst_heaptuple;
 	Buffer		buffer;
 	Page		page;
 	OffsetNumber offnum;
 	ItemId		lp = NULL;
 	HeapTupleHeader htup;
 
+	Assert(slot->tts_speculativeToken != 0);
+
 	buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 	page = (Page) BufferGetPage(buffer);
@@ -6094,6 +6112,7 @@ heap_finish_speculative(Relation relation, HeapTuple tuple)
 	END_CRIT_SECTION();
 
 	UnlockReleaseBuffer(buffer);
+	slot->tts_speculativeToken = 0;
 }
 
 /*
@@ -6123,8 +6142,10 @@ heap_finish_speculative(Relation relation, HeapTuple tuple)
  * confirmation records.
  */
 void
-heap_abort_speculative(Relation relation, HeapTuple tuple)
+heap_abort_speculative(Relation relation, TupleTableSlot *slot)
 {
+	HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+	HeapTuple	tuple = stuple->hst_heaptuple;
 	TransactionId xid = GetCurrentTransactionId();
 	ItemPointer tid = &(tuple->t_self);
 	ItemId		lp;
@@ -6133,6 +6154,10 @@ heap_abort_speculative(Relation relation, HeapTuple tuple)
 	BlockNumber block;
 	Buffer		buffer;
 
+	/*
+	 * Assert(slot->tts_speculativeToken != 0); This needs some update in
+	 * toast
+	 */
 	Assert(ItemPointerIsValid(tid));
 
 	block = ItemPointerGetBlockNumber(tid);
@@ -6246,6 +6271,7 @@ heap_abort_speculative(Relation relation, HeapTuple tuple)
 
 	/* count deletion, as we counted the insertion too */
 	pgstat_count_heap_delete(relation);
+	slot->tts_speculativeToken = 0;
 }
 
 /*
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 8cccf927e9..77fdf7f91a 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -22,6 +22,7 @@
 
 #include "access/storageamapi.h"
 #include "utils/builtins.h"
+#include "utils/rel.h"
 
 extern bool HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer);
 extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
@@ -29,6 +30,219 @@ extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
 extern HTSV_Result HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin,
 						 Buffer buffer);
 
+/* ----------------------------------------------------------------
+ *				storage AM support routines for heapam
+ * ----------------------------------------------------------------
+ */
+
+static bool
+heapam_fetch(Relation relation,
+			 ItemPointer tid,
+			 Snapshot snapshot,
+			 StorageTuple * stuple,
+			 Buffer *userbuf,
+			 bool keep_buf,
+			 Relation stats_relation)
+{
+	HeapTupleData tuple;
+
+	*stuple = NULL;
+	if (heap_fetch(relation, tid, snapshot, &tuple, userbuf, keep_buf, stats_relation))
+	{
+		*stuple = heap_copytuple(&tuple);
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * Insert a heap tuple from a slot, which may contain an OID and speculative
+ * insertion token.
+ */
+static Oid
+heapam_heap_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+				   int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+				   EState *estate, List *arbiterIndexes, List **recheckIndexes)
+{
+	Oid			oid;
+	HeapTuple	tuple = NULL;
+
+	if (slot->tts_storage)
+	{
+		HeapamTuple *htuple = slot->tts_storage;
+
+		tuple = htuple->hst_heaptuple;
+
+		if (relation->rd_rel->relhasoids)
+			HeapTupleSetOid(tuple, InvalidOid);
+	}
+	else
+	{
+		/*
+		 * Obtain the physical tuple to insert, building from the slot values.
+		 * XXX: maybe the slot already contains a physical tuple in the right
+		 * format?  In fact, if the slot isn't fully deformed, this is
+		 * completely bogus ...
+		 */
+		tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+								slot->tts_values,
+								slot->tts_isnull);
+	}
+
+	/* Set the OID, if the slot has one */
+	if (slot->tts_tupleOid != InvalidOid)
+		HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+	/* Update the tuple with table oid */
+	if (slot->tts_tableOid != InvalidOid)
+		tuple->t_tableOid = slot->tts_tableOid;
+
+	/* Set the speculative insertion token, if the slot has one */
+	if ((options & HEAP_INSERT_SPECULATIVE) && slot->tts_speculativeToken)
+		HeapTupleHeaderSetSpeculativeToken(tuple->t_data, slot->tts_speculativeToken);
+
+	/* Perform the insertion, and copy the resulting ItemPointer */
+	oid = heap_insert(relation, tuple, cid, options, bistate);
+	ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+	if (slot->tts_storage == NULL)
+		ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+	if ((estate != NULL) && (estate->es_result_relation_info->ri_NumIndices > 0))
+	{
+		Assert(IndexFunc != NULL);
+
+		if (options & HEAP_INSERT_SPECULATIVE)
+		{
+			bool		specConflict = false;
+
+			*recheckIndexes = (IndexFunc) (slot, estate, true,
+										   &specConflict,
+										   arbiterIndexes);
+
+			/* adjust the tuple's state accordingly */
+			if (!specConflict)
+				heap_finish_speculative(relation, slot);
+			else
+			{
+				heap_abort_speculative(relation, slot);
+				slot->tts_specConflict = true;
+			}
+		}
+		else
+		{
+			*recheckIndexes = (IndexFunc) (slot, estate, false,
+										   NULL, arbiterIndexes);
+		}
+	}
+
+	return oid;
+}
+
+static HTSU_Result
+heapam_heap_delete(Relation relation, ItemPointer tid, CommandId cid,
+				   Snapshot crosscheck, bool wait,
+				   HeapUpdateFailureData *hufd)
+{
+	return heap_delete(relation, tid, cid, crosscheck, wait, hufd);
+}
+
+
+
+static HTSU_Result
+heapam_heap_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+				   EState *estate, CommandId cid, Snapshot crosscheck,
+				   bool wait, HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+				   InsertIndexTuples IndexFunc, List **recheckIndexes)
+{
+	HeapTuple	tuple;
+	HTSU_Result result;
+
+	if (slot->tts_storage)
+	{
+		HeapamTuple *htuple = slot->tts_storage;
+
+		tuple = htuple->hst_heaptuple;
+	}
+	else
+	{
+		tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+								slot->tts_values,
+								slot->tts_isnull);
+	}
+
+	/* Set the OID, if the slot has one */
+	if (slot->tts_tupleOid != InvalidOid)
+		HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+	/* Update the tuple with table oid */
+	if (slot->tts_tableOid != InvalidOid)
+		tuple->t_tableOid = slot->tts_tableOid;
+
+	result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
+						 hufd, lockmode);
+	ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+	if (slot->tts_storage == NULL)
+		ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+	/*
+	 * Note: instead of having to update the old index tuples associated with
+	 * the heap tuple, all we do is form and insert new index tuples. This is
+	 * because UPDATEs are actually DELETEs and INSERTs, and index tuple
+	 * deletion is done later by VACUUM (see notes in ExecDelete). All we do
+	 * here is insert new index tuples.  -cim 9/27/89
+	 */
+
+	/*
+	 * insert index entries for tuple
+	 *
+	 * Note: heap_update returns the tid (location) of the new tuple in the
+	 * t_self field.
+	 *
+	 * If it's a HOT update, we mustn't insert new index entries.
+	 */
+	if ((result == HeapTupleMayBeUpdated) &&
+		((estate != NULL) && (estate->es_result_relation_info->ri_NumIndices > 0)) &&
+		(!HeapTupleIsHeapOnly(tuple)))
+		*recheckIndexes = (IndexFunc) (slot, estate, false, NULL, NIL);
+
+	return result;
+}
+
+static tuple_data
+heapam_get_tuple_data(StorageTuple tuple, tuple_data_flags flags)
+{
+	switch (flags)
+	{
+		case XMIN:
+			return (tuple_data) HeapTupleHeaderGetXmin(((HeapTuple) tuple)->t_data);
+			break;
+		case UPDATED_XID:
+			return (tuple_data) HeapTupleHeaderGetUpdateXid(((HeapTuple) tuple)->t_data);
+			break;
+		case CMIN:
+			return (tuple_data) HeapTupleHeaderGetCmin(((HeapTuple) tuple)->t_data);
+			break;
+		case TID:
+			return (tuple_data) ((HeapTuple) tuple)->t_self;
+			break;
+		case CTID:
+			return (tuple_data) ((HeapTuple) tuple)->t_data->t_ctid;
+			break;
+		default:
+			Assert(0);
+			break;
+	}
+}
+
+static StorageTuple
+heapam_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+	return heap_form_tuple_by_datum(data, tableoid);
+}
+
 Datum
 heapam_storage_handler(PG_FUNCTION_ARGS)
 {
@@ -41,5 +255,18 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
 
 	amroutine->slot_storageam = heapam_storage_slot_handler;
 
+	amroutine->tuple_fetch = heapam_fetch;
+	amroutine->tuple_insert = heapam_heap_insert;
+	amroutine->tuple_delete = heapam_heap_delete;
+	amroutine->tuple_update = heapam_heap_update;
+	amroutine->tuple_lock = heap_lock_tuple;
+	amroutine->multi_insert = heap_multi_insert;
+
+	amroutine->get_tuple_data = heapam_get_tuple_data;
+	amroutine->tuple_from_datum = heapam_form_tuple_by_datum;
+	amroutine->tuple_get_latest_tid = heap_get_latest_tid;
+	amroutine->speculative_abort = heap_abort_speculative;
+	amroutine->relation_sync = heap_sync;
+
 	PG_RETURN_POINTER(amroutine);
 }
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index f93c194e18..9afca702da 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -110,6 +110,7 @@
 #include "access/heapam.h"
 #include "access/heapam_xlog.h"
 #include "access/rewriteheap.h"
+#include "access/storageam.h"
 #include "access/transam.h"
 #include "access/tuptoaster.h"
 #include "access/xact.h"
@@ -126,13 +127,13 @@
 
 #include "storage/bufmgr.h"
 #include "storage/fd.h"
+#include "storage/procarray.h"
 #include "storage/smgr.h"
 
 #include "utils/memutils.h"
 #include "utils/rel.h"
 #include "utils/tqual.h"
 
-#include "storage/procarray.h"
 
 /*
  * State associated with a rewrite operation. This is opaque to the user
@@ -357,7 +358,7 @@ end_heap_rewrite(RewriteState state)
 	 * wrote before the checkpoint.
 	 */
 	if (RelationNeedsWAL(state->rs_new_rel))
-		heap_sync(state->rs_new_rel);
+		storage_sync(state->rs_new_rel);
 
 	logical_end_heap_rewrite(state);
 
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index c74945a52a..1f67ab2af8 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -32,6 +32,7 @@
 
 #include "access/genam.h"
 #include "access/heapam.h"
+#include "access/storageam.h"
 #include "access/tuptoaster.h"
 #include "access/xact.h"
 #include "catalog/catalog.h"
@@ -1777,7 +1778,13 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative)
 		 * Have a chunk, delete it
 		 */
 		if (is_speculative)
-			heap_abort_speculative(toastrel, toasttup);
+		{
+			TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(toastrel));
+
+			ExecStoreTuple(toasttup, slot, InvalidBuffer, false);
+			storage_abort_speculative(toastrel, slot);
+			ExecDropSingleTupleTableSlot(slot);
+		}
 		else
 			simple_heap_delete(toastrel, &toasttup->t_self);
 	}
diff --git a/src/backend/access/storage/storage_common.c b/src/backend/access/storage/storage_common.c
index b65153bb38..30976f6286 100644
--- a/src/backend/access/storage/storage_common.c
+++ b/src/backend/access/storage/storage_common.c
@@ -29,20 +29,6 @@
 SnapshotData SnapshotSelfData = {SELF_VISIBILITY};
 SnapshotData SnapshotAnyData = {ANY_VISIBILITY};
 
-/*
- * HeapTupleSetHintBits --- exported version of SetHintBits()
- *
- * This must be separate because of C99's brain-dead notions about how to
- * implement inline functions.
- */
-void
-HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
-					 uint16 infomask, TransactionId xid)
-{
-	SetHintBits(tuple, buffer, infomask, xid);
-}
-
-
 /*
  * Is the tuple really only locked?  That is, is it not updated?
  *
@@ -352,6 +338,8 @@ heapam_slot_store_tuple(TupleTableSlot *slot, StorageTuple tuple, bool shouldFre
 	MemoryContextSwitchTo(oldcontext);
 
 	slot->tts_tid = ((HeapTuple) tuple)->t_self;
+	if (slot->tts_tupleDescriptor->tdhasoid)
+		slot->tts_tupleOid = HeapTupleGetOid((HeapTuple) tuple);
 	slot->tts_storage = stuple;
 }
 
diff --git a/src/backend/access/storage/storageam.c b/src/backend/access/storage/storageam.c
index 8541c75782..487ffc9746 100644
--- a/src/backend/access/storage/storageam.c
+++ b/src/backend/access/storage/storageam.c
@@ -13,3 +13,129 @@
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
+
+#include "access/storageam.h"
+#include "access/storageamapi.h"
+#include "utils/rel.h"
+
+/*
+ *	storage_fetch		- retrieve tuple with given tid
+ */
+bool
+storage_fetch(Relation relation,
+			  ItemPointer tid,
+			  Snapshot snapshot,
+			  StorageTuple * stuple,
+			  Buffer *userbuf,
+			  bool keep_buf,
+			  Relation stats_relation)
+{
+	return relation->rd_stamroutine->tuple_fetch(relation, tid, snapshot, stuple,
+												 userbuf, keep_buf, stats_relation);
+}
+
+
+/*
+ *	storage_lock_tuple - lock a tuple in shared or exclusive mode
+ */
+HTSU_Result
+storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
+				   CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+				   bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+	return relation->rd_stamroutine->tuple_lock(relation, tid, stuple,
+												cid, mode, wait_policy,
+												follow_updates, buffer, hufd);
+}
+
+/*
+ * Insert a tuple from a slot into storage AM routine
+ */
+Oid
+storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+			   int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+			   EState *estate, List *arbiterIndexes, List **recheckIndexes)
+{
+	return relation->rd_stamroutine->tuple_insert(relation, slot, cid, options,
+												  bistate, IndexFunc, estate,
+												  arbiterIndexes, recheckIndexes);
+}
+
+/*
+ * Delete a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+			   Snapshot crosscheck, bool wait,
+			   HeapUpdateFailureData *hufd)
+{
+	return relation->rd_stamroutine->tuple_delete(relation, tid, cid,
+												  crosscheck, wait, hufd);
+}
+
+/*
+ * update a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+			   EState *estate, CommandId cid, Snapshot crosscheck, bool wait,
+			   HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+			   InsertIndexTuples IndexFunc, List **recheckIndexes)
+{
+	return relation->rd_stamroutine->tuple_update(relation, otid, slot, estate,
+												  cid, crosscheck, wait, hufd,
+												  lockmode, IndexFunc, recheckIndexes);
+}
+
+
+/*
+ *	storage_multi_insert	- insert multiple tuple into a storage
+ */
+void
+storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+					 CommandId cid, int options, BulkInsertState bistate)
+{
+	relation->rd_stamroutine->multi_insert(relation, tuples, ntuples,
+										   cid, options, bistate);
+}
+
+/*
+ *	storage_abort_speculative - kill a speculatively inserted tuple
+ */
+void
+storage_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+	relation->rd_stamroutine->speculative_abort(relation, slot);
+}
+
+tuple_data
+storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags)
+{
+	return relation->rd_stamroutine->get_tuple_data(tuple, flags);
+}
+
+StorageTuple
+storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid)
+{
+	if (relation)
+		return relation->rd_stamroutine->tuple_from_datum(data, tableoid);
+	else
+		return heap_form_tuple_by_datum(data, tableoid);
+}
+
+void
+storage_get_latest_tid(Relation relation,
+					   Snapshot snapshot,
+					   ItemPointer tid)
+{
+	relation->rd_stamroutine->tuple_get_latest_tid(relation, snapshot, tid);
+}
+
+/*
+ *	storage_sync		- sync a heap, for use when no WAL has been written
+ */
+void
+storage_sync(Relation rel)
+{
+	rel->rd_stamroutine->relation_sync(rel);
+}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 0a0eecf509..62d1ac98d5 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -20,6 +20,7 @@
 
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/storageam.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -2719,8 +2720,6 @@ CopyFrom(CopyState cstate)
 
 			if (slot == NULL)	/* "do nothing" */
 				skip_tuple = true;
-			else				/* trigger might have changed tuple */
-				tuple = ExecHeapifySlot(slot);
 		}
 
 		if (!skip_tuple)
@@ -2783,19 +2782,11 @@ CopyFrom(CopyState cstate)
 					List	   *recheckIndexes = NIL;
 
 					/* OK, store the tuple and create index entries for it */
-					heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
-								hi_options, bistate);
-
-					if (resultRelInfo->ri_NumIndices > 0)
-						recheckIndexes = ExecInsertIndexTuples(slot,
-															   &(tuple->t_self),
-															   estate,
-															   false,
-															   NULL,
-															   NIL);
+					storage_insert(resultRelInfo->ri_RelationDesc, slot, mycid, hi_options,
+								   bistate, ExecInsertIndexTuples, estate, NIL, &recheckIndexes);
 
 					/* AFTER ROW INSERT Triggers */
-					ExecARInsertTriggers(estate, resultRelInfo, tuple,
+					ExecARInsertTriggers(estate, resultRelInfo, slot,
 										 recheckIndexes, cstate->transition_capture);
 
 					list_free(recheckIndexes);
@@ -2891,7 +2882,7 @@ CopyFrom(CopyState cstate)
 	 * indexes since those use WAL anyway)
 	 */
 	if (hi_options & HEAP_INSERT_SKIP_WAL)
-		heap_sync(cstate->rel);
+		storage_sync(cstate->rel);
 
 	return processed;
 }
@@ -2924,12 +2915,12 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
 	 * before calling it.
 	 */
 	oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
-	heap_multi_insert(cstate->rel,
-					  bufferedTuples,
-					  nBufferedTuples,
-					  mycid,
-					  hi_options,
-					  bistate);
+	storage_multi_insert(cstate->rel,
+						 bufferedTuples,
+						 nBufferedTuples,
+						 mycid,
+						 hi_options,
+						 bistate);
 	MemoryContextSwitchTo(oldcontext);
 
 	/*
@@ -2945,10 +2936,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
 			cstate->cur_lineno = firstBufferedLineNo + i;
 			ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
 			recheckIndexes =
-				ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
-									  estate, false, NULL, NIL);
+				ExecInsertIndexTuples(myslot, estate, false, NULL, NIL);
 			ExecARInsertTriggers(estate, resultRelInfo,
-								 bufferedTuples[i],
+								 myslot,
 								 recheckIndexes, cstate->transition_capture);
 			list_free(recheckIndexes);
 		}
@@ -2965,8 +2955,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
 		for (i = 0; i < nBufferedTuples; i++)
 		{
 			cstate->cur_lineno = firstBufferedLineNo + i;
+			ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
 			ExecARInsertTriggers(estate, resultRelInfo,
-								 bufferedTuples[i],
+								 myslot,
 								 NIL, cstate->transition_capture);
 		}
 	}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 213a8cccbc..9e6fb8740b 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -26,6 +26,7 @@
 
 #include "access/reloptions.h"
 #include "access/htup_details.h"
+#include "access/storageam.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -582,25 +583,28 @@ static bool
 intorel_receive(TupleTableSlot *slot, DestReceiver *self)
 {
 	DR_intorel *myState = (DR_intorel *) self;
-	HeapTuple	tuple;
 
 	/*
 	 * get the heap tuple out of the tuple table slot, making sure we have a
 	 * writable copy
 	 */
-	tuple = ExecHeapifySlot(slot);
+	ExecMaterializeSlot(slot);
 
 	/*
 	 * force assignment of new OID (see comments in ExecInsert)
 	 */
 	if (myState->rel->rd_rel->relhasoids)
-		HeapTupleSetOid(tuple, InvalidOid);
-
-	heap_insert(myState->rel,
-				tuple,
-				myState->output_cid,
-				myState->hi_options,
-				myState->bistate);
+		slot->tts_tupleOid = InvalidOid;
+
+	storage_insert(myState->rel,
+				   slot,
+				   myState->output_cid,
+				   myState->hi_options,
+				   myState->bistate,
+				   NULL,
+				   NULL,
+				   NIL,
+				   NULL);
 
 	/* We know this is a newly created relation, so there are no indexes */
 
@@ -619,7 +623,7 @@ intorel_shutdown(DestReceiver *self)
 
 	/* If we skipped using WAL, must heap_sync before commit */
 	if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
-		heap_sync(myState->rel);
+		storage_sync(myState->rel);
 
 	/* close rel, but keep lock until commit */
 	heap_close(myState->rel, NoLock);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index b440740e28..936ea9b9e5 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -16,6 +16,7 @@
 
 #include "access/htup_details.h"
 #include "access/multixact.h"
+#include "access/storageam.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -491,19 +492,22 @@ static bool
 transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
 {
 	DR_transientrel *myState = (DR_transientrel *) self;
-	HeapTuple	tuple;
 
 	/*
 	 * get the heap tuple out of the tuple table slot, making sure we have a
 	 * writable copy
 	 */
-	tuple = ExecHeapifySlot(slot);
-
-	heap_insert(myState->transientrel,
-				tuple,
-				myState->output_cid,
-				myState->hi_options,
-				myState->bistate);
+	ExecMaterializeSlot(slot);
+
+	storage_insert(myState->transientrel,
+				   slot,
+				   myState->output_cid,
+				   myState->hi_options,
+				   myState->bistate,
+				   NULL,
+				   NULL,
+				   NIL,
+				   NULL);
 
 	/* We know this is a newly created relation, so there are no indexes */
 
@@ -522,7 +526,7 @@ transientrel_shutdown(DestReceiver *self)
 
 	/* If we skipped using WAL, must heap_sync before commit */
 	if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
-		heap_sync(myState->transientrel);
+		storage_sync(myState->transientrel);
 
 	/* close transientrel, but keep lock until commit */
 	heap_close(myState->transientrel, NoLock);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d979ce266d..73b68826f2 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
 #include "access/multixact.h"
 #include "access/reloptions.h"
 #include "access/relscan.h"
+#include "access/storageam.h"
 #include "access/sysattr.h"
 #include "access/tupconvert.h"
 #include "access/xact.h"
@@ -4663,7 +4664,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 
 			/* Write the tuple out to the new relation */
 			if (newrel)
-				heap_insert(newrel, tuple, mycid, hi_options, bistate);
+				storage_insert(newrel, newslot, mycid, hi_options, bistate,
+							   NULL, NULL, NIL, NULL);
 
 			ResetExprContext(econtext);
 
@@ -4687,7 +4689,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 
 		/* If we skipped writing WAL, then we need to sync the heap. */
 		if (hi_options & HEAP_INSERT_SKIP_WAL)
-			heap_sync(newrel);
+			storage_sync(newrel);
 
 		heap_close(newrel, NoLock);
 	}
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 96c4fe7d43..0a58ea16b6 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -15,6 +15,7 @@
 
 #include "access/genam.h"
 #include "access/heapam.h"
+#include "access/storageam.h"
 #include "access/sysattr.h"
 #include "access/htup_details.h"
 #include "access/xact.h"
@@ -2352,17 +2353,21 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 
 void
 ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
-					 HeapTuple trigtuple, List *recheckIndexes,
+					 TupleTableSlot *slot, List *recheckIndexes,
 					 TransitionCaptureState *transition_capture)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
 
 	if ((trigdesc && trigdesc->trig_insert_after_row) ||
 		(transition_capture && transition_capture->tcs_insert_new_table))
+	{
+		HeapTuple	trigtuple = ExecHeapifySlot(slot);
+
 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
 							  true, NULL, trigtuple,
 							  recheckIndexes, NULL,
 							  transition_capture);
+	}
 }
 
 TupleTableSlot *
@@ -3012,9 +3017,10 @@ GetTupleForTrigger(EState *estate,
 				   TupleTableSlot **newSlot)
 {
 	Relation	relation = relinfo->ri_RelationDesc;
-	HeapTupleData tuple;
+	StorageTuple tuple;
 	HeapTuple	result;
 	Buffer		buffer;
+	tuple_data	t_data;
 
 	if (newSlot != NULL)
 	{
@@ -3030,11 +3036,11 @@ GetTupleForTrigger(EState *estate,
 		 * lock tuple for update
 		 */
 ltrmark:;
-		tuple.t_self = *tid;
-		test = heap_lock_tuple(relation, &tuple,
-							   estate->es_output_cid,
-							   lockmode, LockWaitBlock,
-							   false, &buffer, &hufd);
+		test = storage_lock_tuple(relation, tid, &tuple,
+								  estate->es_output_cid,
+								  lockmode, LockWaitBlock,
+								  false, &buffer, &hufd);
+		result = tuple;
 		switch (test)
 		{
 			case HeapTupleSelfUpdated:
@@ -3066,7 +3072,8 @@ ltrmark:;
 					ereport(ERROR,
 							(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
 							 errmsg("could not serialize access due to concurrent update")));
-				if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+				t_data = relation->rd_stamroutine->get_tuple_data(tuple, TID);
+				if (!ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
 				{
 					/* it was updated, so look at the updated version */
 					TupleTableSlot *epqslot;
@@ -3112,6 +3119,7 @@ ltrmark:;
 	{
 		Page		page;
 		ItemId		lp;
+		HeapTupleData tupledata;
 
 		buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
 
@@ -3130,17 +3138,17 @@ ltrmark:;
 
 		Assert(ItemIdIsNormal(lp));
 
-		tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
-		tuple.t_len = ItemIdGetLength(lp);
-		tuple.t_self = *tid;
-		tuple.t_tableOid = RelationGetRelid(relation);
+		tupledata.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+		tupledata.t_len = ItemIdGetLength(lp);
+		tupledata.t_self = *tid;
+		tupledata.t_tableOid = RelationGetRelid(relation);
 
 		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+		result = heap_copytuple(&tupledata);
 	}
 
-	result = heap_copytuple(&tuple);
 	ReleaseBuffer(buffer);
-
 	return result;
 }
 
@@ -3946,8 +3954,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
 	AfterTriggerShared evtshared = GetTriggerSharedData(event);
 	Oid			tgoid = evtshared->ats_tgoid;
 	TriggerData LocTriggerData;
-	HeapTupleData tuple1;
-	HeapTupleData tuple2;
+	StorageTuple tuple1;
+	StorageTuple tuple2;
 	HeapTuple	rettuple;
 	Buffer		buffer1 = InvalidBuffer;
 	Buffer		buffer2 = InvalidBuffer;
@@ -4020,10 +4028,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
 		default:
 			if (ItemPointerIsValid(&(event->ate_ctid1)))
 			{
-				ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
-				if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
+				if (!storage_fetch(rel, &(event->ate_ctid1), SnapshotAny, &tuple1, &buffer1, false, NULL))
 					elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
-				LocTriggerData.tg_trigtuple = &tuple1;
+				LocTriggerData.tg_trigtuple = tuple1;
 				LocTriggerData.tg_trigtuplebuf = buffer1;
 			}
 			else
@@ -4037,10 +4044,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
 				AFTER_TRIGGER_2CTID &&
 				ItemPointerIsValid(&(event->ate_ctid2)))
 			{
-				ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
-				if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
+				if (!storage_fetch(rel, &(event->ate_ctid2), SnapshotAny, &tuple2, &buffer2, false, NULL))
 					elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
-				LocTriggerData.tg_newtuple = &tuple2;
+				LocTriggerData.tg_newtuple = tuple2;
 				LocTriggerData.tg_newtuplebuf = buffer2;
 			}
 			else
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 89e189fa71..ab533cf9c7 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -269,12 +269,12 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
  */
 List *
 ExecInsertIndexTuples(TupleTableSlot *slot,
-					  ItemPointer tupleid,
 					  EState *estate,
 					  bool noDupErr,
 					  bool *specConflict,
 					  List *arbiterIndexes)
 {
+	ItemPointer tupleid = &slot->tts_tid;
 	List	   *result = NIL;
 	ResultRelInfo *resultRelInfo;
 	int			i;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index dbaa47f2d3..c3c28d0fac 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -38,6 +38,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/storageam.h"
 #include "access/sysattr.h"
 #include "access/transam.h"
 #include "access/xact.h"
@@ -1894,7 +1895,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
 		/* See the comment above. */
 		if (resultRelInfo->ri_PartitionRoot)
 		{
-			HeapTuple	tuple = ExecFetchSlotTuple(slot);
+			StorageTuple tuple = ExecFetchSlotTuple(slot);
 			TupleDesc	old_tupdesc = RelationGetDescr(rel);
 			TupleConversionMap *map;
 
@@ -1974,7 +1975,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 				 */
 				if (resultRelInfo->ri_PartitionRoot)
 				{
-					HeapTuple	tuple = ExecFetchSlotTuple(slot);
+					StorageTuple tuple = ExecFetchSlotTuple(slot);
 					TupleConversionMap *map;
 
 					rel = resultRelInfo->ri_PartitionRoot;
@@ -2021,7 +2022,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 			/* See the comment above. */
 			if (resultRelInfo->ri_PartitionRoot)
 			{
-				HeapTuple	tuple = ExecFetchSlotTuple(slot);
+				StorageTuple tuple = ExecFetchSlotTuple(slot);
 				TupleDesc	old_tupdesc = RelationGetDescr(rel);
 				TupleConversionMap *map;
 
@@ -2480,7 +2481,8 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
 			 ItemPointer tid, TransactionId priorXmax)
 {
 	TupleTableSlot *slot;
-	HeapTuple	copyTuple;
+	StorageTuple copyTuple;
+	tuple_data	t_data;
 
 	Assert(rti > 0);
 
@@ -2497,7 +2499,9 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
 	 * For UPDATE/DELETE we have to return tid of actual row we're executing
 	 * PQ for.
 	 */
-	*tid = copyTuple->t_self;
+
+	t_data = storage_tuple_get_data(relation, copyTuple, TID);
+	*tid = t_data.tid;
 
 	/*
 	 * Need to run a recheck subquery.  Initialize or reinitialize EPQ state.
@@ -2528,7 +2532,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
 	 * is to guard against early re-use of the EPQ query.
 	 */
 	if (!TupIsNull(slot))
-		(void) ExecMaterializeSlot(slot);
+		ExecMaterializeSlot(slot);
 
 	/*
 	 * Clear out the test tuple.  This is needed in case the EPQ query is
@@ -2561,14 +2565,14 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
  * Note: properly, lockmode should be declared as enum LockTupleMode,
  * but we use "int" to avoid having to include heapam.h in executor.h.
  */
-HeapTuple
+StorageTuple
 EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 				  LockWaitPolicy wait_policy,
 				  ItemPointer tid, TransactionId priorXmax)
 {
-	HeapTuple	copyTuple = NULL;
-	HeapTupleData tuple;
+	StorageTuple tuple = NULL;
 	SnapshotData SnapshotDirty;
+	tuple_data	t_data;
 
 	/*
 	 * fetch target tuple
@@ -2576,12 +2580,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 	 * Loop here to deal with updated or busy tuples
 	 */
 	InitDirtySnapshot(SnapshotDirty);
-	tuple.t_self = *tid;
 	for (;;)
 	{
 		Buffer		buffer;
+		ItemPointerData ctid;
 
-		if (heap_fetch(relation, &SnapshotDirty, &tuple, &buffer, true, NULL))
+		if (storage_fetch(relation, tid, &SnapshotDirty, &tuple, &buffer, true, NULL))
 		{
 			HTSU_Result test;
 			HeapUpdateFailureData hufd;
@@ -2595,7 +2599,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 			 * atomic, and Xmin never changes in an existing tuple, except to
 			 * invalid or frozen, and neither of those can match priorXmax.)
 			 */
-			if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
+			if (!TransactionIdEquals(HeapTupleHeaderGetXmin(((HeapTuple) tuple)->t_data),
 									 priorXmax))
 			{
 				ReleaseBuffer(buffer);
@@ -2617,7 +2621,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 				{
 					case LockWaitBlock:
 						XactLockTableWait(SnapshotDirty.xmax,
-										  relation, &tuple.t_self,
+										  relation,
+										  tid,
 										  XLTW_FetchUpdated);
 						break;
 					case LockWaitSkip:
@@ -2646,20 +2651,23 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 			 * that priorXmax == xmin, so we can test that variable instead of
 			 * doing HeapTupleHeaderGetXmin again.
 			 */
-			if (TransactionIdIsCurrentTransactionId(priorXmax) &&
-				HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
+			if (TransactionIdIsCurrentTransactionId(priorXmax))
 			{
-				ReleaseBuffer(buffer);
-				return NULL;
+				t_data = storage_tuple_get_data(relation, tuple, CMIN);
+				if (t_data.cid >= estate->es_output_cid)
+				{
+					ReleaseBuffer(buffer);
+					return NULL;
+				}
 			}
 
 			/*
 			 * This is a live tuple, so now try to lock it.
 			 */
-			test = heap_lock_tuple(relation, &tuple,
-								   estate->es_output_cid,
-								   lockmode, wait_policy,
-								   false, &buffer, &hufd);
+			test = storage_lock_tuple(relation, tid, tuple,
+									  estate->es_output_cid,
+									  lockmode, wait_policy,
+									  false, &buffer, &hufd);
 			/* We now have two pins on the buffer, get rid of one */
 			ReleaseBuffer(buffer);
 
@@ -2695,12 +2703,15 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 								(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
 								 errmsg("could not serialize access due to concurrent update")));
 
+#if 0 //hari
 					/* Should not encounter speculative tuple on recheck */
 					Assert(!HeapTupleHeaderIsSpeculative(tuple.t_data));
-					if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+#endif
+					t_data = storage_tuple_get_data(relation, tuple, TID);
+					if (!ItemPointerEquals(&hufd.ctid, &t_data.tid))
 					{
 						/* it was updated, so look at the updated version */
-						tuple.t_self = hufd.ctid;
+						*tid = hufd.ctid;
 						/* updated row should have xmin matching this xmax */
 						priorXmax = hufd.xmax;
 						continue;
@@ -2722,10 +2733,6 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 					return NULL;	/* keep compiler quiet */
 			}
 
-			/*
-			 * We got tuple - now copy it for use by recheck query.
-			 */
-			copyTuple = heap_copytuple(&tuple);
 			ReleaseBuffer(buffer);
 			break;
 		}
@@ -2734,7 +2741,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 		 * If the referenced slot was actually empty, the latest version of
 		 * the row must have been deleted, so we need do nothing.
 		 */
-		if (tuple.t_data == NULL)
+		if (tuple == NULL)
 		{
 			ReleaseBuffer(buffer);
 			return NULL;
@@ -2743,7 +2750,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 		/*
 		 * As above, if xmin isn't what we're expecting, do nothing.
 		 */
-		if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
+		if (!TransactionIdEquals(HeapTupleHeaderGetXmin(((HeapTuple) tuple)->t_data),
 								 priorXmax))
 		{
 			ReleaseBuffer(buffer);
@@ -2762,7 +2769,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 		 * As above, it should be safe to examine xmax and t_ctid without the
 		 * buffer content lock, because they can't be changing.
 		 */
-		if (ItemPointerEquals(&tuple.t_self, &tuple.t_data->t_ctid))
+		t_data = storage_tuple_get_data(relation, tuple, CTID);
+		ctid = t_data.tid;
+		if (ItemPointerEquals(tid, &ctid))
 		{
 			/* deleted, so forget about it */
 			ReleaseBuffer(buffer);
@@ -2770,17 +2779,19 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 		}
 
 		/* updated, so look at the updated row */
-		tuple.t_self = tuple.t_data->t_ctid;
+		*tid = ctid;
+
 		/* updated row should have xmin matching this xmax */
-		priorXmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+		t_data = storage_tuple_get_data(relation, tuple, UPDATED_XID);
+		priorXmax = t_data.xid;
 		ReleaseBuffer(buffer);
 		/* loop back to fetch next in chain */
 	}
 
 	/*
-	 * Return the copied tuple
+	 * Return the tuple
 	 */
-	return copyTuple;
+	return tuple;
 }
 
 /*
@@ -2826,7 +2837,7 @@ EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
  * NB: passed tuple must be palloc'd; it may get freed later
  */
 void
-EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
+EvalPlanQualSetTuple(EPQState *epqstate, Index rti, StorageTuple tuple)
 {
 	EState	   *estate = epqstate->estate;
 
@@ -2845,7 +2856,7 @@ EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
 /*
  * Fetch back the current test tuple (if any) for the specified RTI
  */
-HeapTuple
+StorageTuple
 EvalPlanQualGetTuple(EPQState *epqstate, Index rti)
 {
 	EState	   *estate = epqstate->estate;
@@ -2873,7 +2884,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
 		ExecRowMark *erm = aerm->rowmark;
 		Datum		datum;
 		bool		isNull;
-		HeapTupleData tuple;
+		StorageTuple tuple;
 
 		if (RowMarkRequiresRowShareLock(erm->markType))
 			elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
@@ -2904,8 +2915,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
 
 		if (erm->markType == ROW_MARK_REFERENCE)
 		{
-			HeapTuple	copyTuple;
-
 			Assert(erm->relation != NULL);
 
 			/* fetch the tuple's ctid */
@@ -2929,11 +2938,11 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 							 errmsg("cannot lock rows in foreign table \"%s\"",
 									RelationGetRelationName(erm->relation))));
-				copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate,
-														  erm,
-														  datum,
-														  &updated);
-				if (copyTuple == NULL)
+				tuple = fdwroutine->RefetchForeignRow(epqstate->estate,
+													  erm,
+													  datum,
+													  &updated);
+				if (tuple == NULL)
 					elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
 
 				/*
@@ -2947,23 +2956,18 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
 				/* ordinary table, fetch the tuple */
 				Buffer		buffer;
 
-				tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
-				if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
-								false, NULL))
+				if (!storage_fetch(erm->relation, (ItemPointer) DatumGetPointer(datum), SnapshotAny, &tuple, &buffer,
+								   false, NULL))
 					elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
 
-				/* successful, copy tuple */
-				copyTuple = heap_copytuple(&tuple);
 				ReleaseBuffer(buffer);
 			}
 
 			/* store tuple */
-			EvalPlanQualSetTuple(epqstate, erm->rti, copyTuple);
+			EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
 		}
 		else
 		{
-			HeapTupleHeader td;
-
 			Assert(erm->markType == ROW_MARK_COPY);
 
 			/* fetch the whole-row Var for the relation */
@@ -2973,19 +2977,12 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
 			/* non-locked rels could be on the inside of outer joins */
 			if (isNull)
 				continue;
-			td = DatumGetHeapTupleHeader(datum);
-
-			/* build a temporary HeapTuple control structure */
-			tuple.t_len = HeapTupleHeaderGetDatumLength(td);
-			tuple.t_data = td;
-			/* relation might be a foreign table, if so provide tableoid */
-			tuple.t_tableOid = erm->relid;
-			/* also copy t_ctid in case there's valid data there */
-			tuple.t_self = td->t_ctid;
-
-			/* copy and store tuple */
-			EvalPlanQualSetTuple(epqstate, erm->rti,
-								 heap_copytuple(&tuple));
+
+			tuple = storage_tuple_by_datum(erm->relation, datum, erm->relid);
+
+			/* store tuple */
+			EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
+
 		}
 	}
 }
@@ -3154,8 +3151,8 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
 	}
 	else
 	{
-		estate->es_epqTuple = (HeapTuple *)
-			palloc0(rtsize * sizeof(HeapTuple));
+		estate->es_epqTuple = (StorageTuple *)
+			palloc0(rtsize * sizeof(StorageTuple));
 		estate->es_epqTupleSet = (bool *)
 			palloc0(rtsize * sizeof(bool));
 	}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 12c15fd6bc..fcb58a0421 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/relscan.h"
+#include "access/storageam.h"
 #include "access/transam.h"
 #include "access/xact.h"
 #include "commands/trigger.h"
@@ -169,19 +170,19 @@ retry:
 		Buffer		buf;
 		HeapUpdateFailureData hufd;
 		HTSU_Result res;
-		HeapTupleData locktup;
-
-		ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+		StorageTuple locktup;
 
 		PushActiveSnapshot(GetLatestSnapshot());
 
-		res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
-							  lockmode,
-							  LockWaitBlock,
-							  false /* don't follow updates */ ,
-							  &buf, &hufd);
+		res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
+								 lockmode,
+								 LockWaitBlock,
+								 false /* don't follow updates */ ,
+								 &buf, &hufd);
 		/* the tuple slot already has the buffer pinned */
-		ReleaseBuffer(buf);
+		if (BufferIsValid(buf))
+			ReleaseBuffer(buf);
+		pfree(locktup);
 
 		PopActiveSnapshot();
 
@@ -277,19 +278,20 @@ retry:
 		Buffer		buf;
 		HeapUpdateFailureData hufd;
 		HTSU_Result res;
-		HeapTupleData locktup;
-
-		ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+		StorageTuple locktup;
 
 		PushActiveSnapshot(GetLatestSnapshot());
 
-		res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
-							  lockmode,
-							  LockWaitBlock,
-							  false /* don't follow updates */ ,
-							  &buf, &hufd);
+		res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
+								 lockmode,
+								 LockWaitBlock,
+								 false /* don't follow updates */ ,
+								 &buf, &hufd);
 		/* the tuple slot already has the buffer pinned */
-		ReleaseBuffer(buf);
+		if (BufferIsValid(buf))
+			ReleaseBuffer(buf);
+
+		pfree(locktup);
 
 		PopActiveSnapshot();
 
@@ -327,7 +329,6 @@ void
 ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
 {
 	bool		skip_tuple = false;
-	HeapTuple	tuple;
 	ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
 	Relation	rel = resultRelInfo->ri_RelationDesc;
 
@@ -354,19 +355,12 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
 		if (rel->rd_att->constr)
 			ExecConstraints(resultRelInfo, slot, estate);
 
-		/* Store the slot into tuple that we can inspect. */
-		tuple = ExecHeapifySlot(slot);
-
-		/* OK, store the tuple and create index entries for it */
-		simple_heap_insert(rel, tuple);
-
-		if (resultRelInfo->ri_NumIndices > 0)
-			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
-												   estate, false, NULL,
-												   NIL);
+		storage_insert(resultRelInfo->ri_RelationDesc, slot,
+					   GetCurrentCommandId(true), 0, NULL,
+					   ExecInsertIndexTuples, estate, NIL, &recheckIndexes);
 
 		/* AFTER ROW INSERT Triggers */
-		ExecARInsertTriggers(estate, resultRelInfo, tuple,
+		ExecARInsertTriggers(estate, resultRelInfo, slot,
 							 recheckIndexes, NULL);
 
 		/*
@@ -390,7 +384,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
 						 TupleTableSlot *searchslot, TupleTableSlot *slot)
 {
 	bool		skip_tuple = false;
-	HeapTuple	tuple;
+	StorageTuple tuple;
 	ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
 	Relation	rel = resultRelInfo->ri_RelationDesc;
 	ItemPointer tid = &(searchslot->tts_tid);
@@ -415,22 +409,18 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
 	if (!skip_tuple)
 	{
 		List	   *recheckIndexes = NIL;
+		HeapUpdateFailureData hufd;
+		LockTupleMode lockmode;
+		InsertIndexTuples IndexFunc = ExecInsertIndexTuples;
 
 		/* Check the constraints of the tuple */
 		if (rel->rd_att->constr)
 			ExecConstraints(resultRelInfo, slot, estate);
 
-		/* Store the slot into tuple that we can write. */
-		tuple = ExecHeapifySlot(slot);
+		storage_update(rel, tid, slot, estate, GetCurrentCommandId(true), InvalidSnapshot,
+					   true, &hufd, &lockmode, IndexFunc, &recheckIndexes);
 
-		/* OK, update the tuple and index entries for it */
-		simple_heap_update(rel, tid, tuple);
-
-		if (resultRelInfo->ri_NumIndices > 0 &&
-			!HeapTupleIsHeapOnly(tuple))
-			recheckIndexes = ExecInsertIndexTuples(slot, tid,
-												   estate, false, NULL,
-												   NIL);
+		tuple = ExecHeapifySlot(slot);
 
 		/* AFTER ROW UPDATE Triggers */
 		ExecARUpdateTriggers(estate, resultRelInfo,
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 93895600a5..2da5240d24 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -22,6 +22,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/storageam.h"
 #include "access/xact.h"
 #include "executor/executor.h"
 #include "executor/nodeLockRows.h"
@@ -74,18 +75,20 @@ lnext:
 	{
 		ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
 		ExecRowMark *erm = aerm->rowmark;
-		HeapTuple  *testTuple;
+		StorageTuple *testTuple;
 		Datum		datum;
 		bool		isNull;
-		HeapTupleData tuple;
+		StorageTuple tuple;
 		Buffer		buffer;
 		HeapUpdateFailureData hufd;
 		LockTupleMode lockmode;
 		HTSU_Result test;
-		HeapTuple	copyTuple;
+		StorageTuple copyTuple;
+		ItemPointerData tid;
+		tuple_data	t_data;
 
 		/* clear any leftover test tuple for this rel */
-		testTuple = &(node->lr_curtuples[erm->rti - 1]);
+		testTuple = (StorageTuple) (&(node->lr_curtuples[erm->rti - 1]));
 		if (*testTuple != NULL)
 			heap_freetuple(*testTuple);
 		*testTuple = NULL;
@@ -159,7 +162,7 @@ lnext:
 		}
 
 		/* okay, try to lock the tuple */
-		tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
+		tid = *((ItemPointer) DatumGetPointer(datum));
 		switch (erm->markType)
 		{
 			case ROW_MARK_EXCLUSIVE:
@@ -180,11 +183,13 @@ lnext:
 				break;
 		}
 
-		test = heap_lock_tuple(erm->relation, &tuple,
-							   estate->es_output_cid,
-							   lockmode, erm->waitPolicy, true,
-							   &buffer, &hufd);
-		ReleaseBuffer(buffer);
+		test = storage_lock_tuple(erm->relation, &tid, &tuple,
+								  estate->es_output_cid,
+								  lockmode, erm->waitPolicy, true,
+								  &buffer, &hufd);
+		if (BufferIsValid(buffer))
+			ReleaseBuffer(buffer);
+
 		switch (test)
 		{
 			case HeapTupleWouldBlock:
@@ -218,7 +223,8 @@ lnext:
 					ereport(ERROR,
 							(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
 							 errmsg("could not serialize access due to concurrent update")));
-				if (ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+				t_data = erm->relation->rd_stamroutine->get_tuple_data(tuple, TID);
+				if (ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
 				{
 					/* Tuple was deleted, so don't return it */
 					goto lnext;
@@ -238,7 +244,8 @@ lnext:
 					goto lnext;
 				}
 				/* remember the actually locked tuple's TID */
-				tuple.t_self = copyTuple->t_self;
+				t_data = erm->relation->rd_stamroutine->get_tuple_data(copyTuple, TID);
+				tid = t_data.tid;
 
 				/* Save locked tuple for EvalPlanQual testing below */
 				*testTuple = copyTuple;
@@ -258,7 +265,7 @@ lnext:
 		}
 
 		/* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
-		erm->curCtid = tuple.t_self;
+		erm->curCtid = tid;
 	}
 
 	/*
@@ -280,7 +287,7 @@ lnext:
 		{
 			ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
 			ExecRowMark *erm = aerm->rowmark;
-			HeapTupleData tuple;
+			StorageTuple tuple;
 			Buffer		buffer;
 
 			/* skip non-active child tables, but clear their test tuples */
@@ -308,14 +315,12 @@ lnext:
 			Assert(ItemPointerIsValid(&(erm->curCtid)));
 
 			/* okay, fetch the tuple */
-			tuple.t_self = erm->curCtid;
-			if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
-							false, NULL))
+			if (!storage_fetch(erm->relation, &erm->curCtid, SnapshotAny, &tuple, &buffer,
+							   false, NULL))
 				elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
 
 			/* successful, copy and store tuple */
-			EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
-								 heap_copytuple(&tuple));
+			EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, tuple);
 			ReleaseBuffer(buffer);
 		}
 
@@ -394,8 +399,8 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
 	 * Create workspace in which we can remember per-RTE locked tuples
 	 */
 	lrstate->lr_ntables = list_length(estate->es_range_table);
-	lrstate->lr_curtuples = (HeapTuple *)
-		palloc0(lrstate->lr_ntables * sizeof(HeapTuple));
+	lrstate->lr_curtuples = (StorageTuple *)
+		palloc0(lrstate->lr_ntables * sizeof(StorageTuple));
 
 	/*
 	 * Locate the ExecRowMark(s) that this node is responsible for, and
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index f0307ba50e..86bf30bd06 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,7 +38,10 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/storageam.h"
 #include "access/xact.h"
+#include "catalog/pg_am.h"
 #include "commands/trigger.h"
 #include "executor/execPartition.h"
 #include "executor/executor.h"
@@ -165,15 +168,13 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
 		econtext->ecxt_scantuple = tupleSlot;
 	else
 	{
-		HeapTuple	tuple;
-
 		/*
 		 * RETURNING expressions might reference the tableoid column, so
 		 * initialize t_tableOid before evaluating them.
 		 */
 		Assert(!TupIsNull(econtext->ecxt_scantuple));
-		tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
-		tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+		ExecSlotUpdateTupleTableoid(econtext->ecxt_scantuple,
+									RelationGetRelid(resultRelInfo->ri_RelationDesc));
 	}
 	econtext->ecxt_outertuple = planSlot;
 
@@ -192,7 +193,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
 static void
 ExecCheckHeapTupleVisible(EState *estate,
 						  Relation rel,
-						  HeapTuple tuple,
+						  StorageTuple tuple,
 						  Buffer buffer)
 {
 	if (!IsolationUsesXactSnapshot())
@@ -205,13 +206,15 @@ ExecCheckHeapTupleVisible(EState *estate,
 	LockBuffer(buffer, BUFFER_LOCK_SHARE);
 	if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
 	{
+		tuple_data	t_data = storage_tuple_get_data(rel, tuple, XMIN);
+
 		/*
 		 * We should not raise a serialization failure if the conflict is
 		 * against a tuple inserted by our own transaction, even if it's not
 		 * visible to our snapshot.  (This would happen, for example, if
 		 * conflicting keys are proposed for insertion in a single command.)
 		 */
-		if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+		if (!TransactionIdIsCurrentTransactionId(t_data.xid))
 			ereport(ERROR,
 					(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
 					 errmsg("could not serialize access due to concurrent update")));
@@ -227,19 +230,20 @@ ExecCheckTIDVisible(EState *estate,
 					ResultRelInfo *relinfo,
 					ItemPointer tid)
 {
-	Relation	rel = relinfo->ri_RelationDesc;
 	Buffer		buffer;
-	HeapTupleData tuple;
+	Relation	rel = relinfo->ri_RelationDesc;
+	StorageTuple tuple;
 
 	/* Redundantly check isolation level */
 	if (!IsolationUsesXactSnapshot())
 		return;
 
-	tuple.t_self = *tid;
-	if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
+	if (!storage_fetch(rel, tid, SnapshotAny, &tuple, &buffer, false, NULL))
 		elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
-	ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
-	ReleaseBuffer(buffer);
+	ExecCheckHeapTupleVisible(estate, rel, tuple, buffer);
+	if (BufferIsValid(buffer))
+		ReleaseBuffer(buffer);
+	pfree(tuple);
 }
 
 /* ----------------------------------------------------------------
@@ -260,7 +264,7 @@ ExecInsert(ModifyTableState *mtstate,
 		   EState *estate,
 		   bool canSetTag)
 {
-	HeapTuple	tuple;
+	StorageTuple tuple;
 	ResultRelInfo *resultRelInfo;
 	ResultRelInfo *saved_resultRelInfo = NULL;
 	Relation	resultRelationDesc;
@@ -268,12 +272,6 @@ ExecInsert(ModifyTableState *mtstate,
 	List	   *recheckIndexes = NIL;
 	TupleTableSlot *result = NULL;
 
-	/*
-	 * get the heap tuple out of the tuple table slot, making sure we have a
-	 * writable copy
-	 */
-	tuple = ExecHeapifySlot(slot);
-
 	/*
 	 * get information on the (current) result relation
 	 */
@@ -285,6 +283,8 @@ ExecInsert(ModifyTableState *mtstate,
 		int			leaf_part_index;
 		TupleConversionMap *map;
 
+		tuple = ExecHeapifySlot(slot);
+
 		/*
 		 * Away we go ... If we end up not finding a partition after all,
 		 * ExecFindPartition() does not return and errors out instead.
@@ -375,19 +375,31 @@ ExecInsert(ModifyTableState *mtstate,
 	resultRelationDesc = resultRelInfo->ri_RelationDesc;
 
 	/*
-	 * If the result relation has OIDs, force the tuple's OID to zero so that
-	 * heap_insert will assign a fresh OID.  Usually the OID already will be
-	 * zero at this point, but there are corner cases where the plan tree can
-	 * return a tuple extracted literally from some table with the same
-	 * rowtype.
+	 * get the heap tuple out of the tuple table slot, making sure we have a
+	 * writable copy  <-- obsolete comment XXX explain what we really do here
+	 *
+	 * Do we really need to do this here?
+	 */
+	ExecMaterializeSlot(slot);
+
+
+	/*
+	 * If the result relation uses heapam and has OIDs, force the tuple's OID
+	 * to zero so that heap_insert will assign a fresh OID.  Usually the OID
+	 * already will be zero at this point, but there are corner cases where
+	 * the plan tree can return a tuple extracted literally from some table
+	 * with the same rowtype.
 	 *
 	 * XXX if we ever wanted to allow users to assign their own OIDs to new
 	 * rows, this'd be the place to do it.  For the moment, we make a point of
 	 * doing this before calling triggers, so that a user-supplied trigger
 	 * could hack the OID if desired.
 	 */
-	if (resultRelationDesc->rd_rel->relhasoids)
-		HeapTupleSetOid(tuple, InvalidOid);
+	if (resultRelationDesc->rd_rel->relam == HEAPAM_STORAGE_AM_OID &&
+		resultRelationDesc->rd_rel->relhasoids)
+	{
+		slot->tts_tupleOid = InvalidOid;
+	}
 
 	/*
 	 * BEFORE ROW INSERT Triggers.
@@ -405,9 +417,6 @@ ExecInsert(ModifyTableState *mtstate,
 
 		if (slot == NULL)		/* "do nothing" */
 			return NULL;
-
-		/* trigger might have changed tuple */
-		tuple = ExecHeapifySlot(slot);
 	}
 
 	/* INSTEAD OF ROW INSERT Triggers */
@@ -419,9 +428,6 @@ ExecInsert(ModifyTableState *mtstate,
 		if (slot == NULL)		/* "do nothing" */
 			return NULL;
 
-		/* trigger might have changed tuple */
-		tuple = ExecHeapifySlot(slot);
-
 		newId = InvalidOid;
 	}
 	else if (resultRelInfo->ri_FdwRoutine)
@@ -437,14 +443,12 @@ ExecInsert(ModifyTableState *mtstate,
 		if (slot == NULL)		/* "do nothing" */
 			return NULL;
 
-		/* FDW might have changed tuple */
-		tuple = ExecHeapifySlot(slot);
-
 		/*
 		 * AFTER ROW Triggers or RETURNING expressions might reference the
 		 * tableoid column, so initialize t_tableOid before evaluating them.
 		 */
-		tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+		slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+		ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
 
 		newId = InvalidOid;
 	}
@@ -464,7 +468,8 @@ ExecInsert(ModifyTableState *mtstate,
 		 * Constraints might reference the tableoid column, so initialize
 		 * t_tableOid before evaluating them.
 		 */
-		tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+		slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+		ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
 
 		/*
 		 * Check any RLS INSERT WITH CHECK policies
@@ -494,7 +499,6 @@ ExecInsert(ModifyTableState *mtstate,
 			/* Perform a speculative insertion. */
 			uint32		specToken;
 			ItemPointerData conflictTid;
-			bool		specConflict;
 
 			/*
 			 * Do a non-conclusive check for conflicts first.
@@ -509,7 +513,7 @@ ExecInsert(ModifyTableState *mtstate,
 			 * speculatively.
 			 */
 	vlock:
-			specConflict = false;
+			slot->tts_specConflict = false;
 			if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
 										   arbiterIndexes))
 			{
@@ -555,24 +559,17 @@ ExecInsert(ModifyTableState *mtstate,
 			 * waiting for the whole transaction to complete.
 			 */
 			specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
-			HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+			slot->tts_speculativeToken = specToken;
 
 			/* insert the tuple, with the speculative token */
-			newId = heap_insert(resultRelationDesc, tuple,
-								estate->es_output_cid,
-								HEAP_INSERT_SPECULATIVE,
-								NULL);
-
-			/* insert index entries for tuple */
-			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
-												   estate, true, &specConflict,
-												   arbiterIndexes);
-
-			/* adjust the tuple's state accordingly */
-			if (!specConflict)
-				heap_finish_speculative(resultRelationDesc, tuple);
-			else
-				heap_abort_speculative(resultRelationDesc, tuple);
+			newId = storage_insert(resultRelationDesc, slot,
+								   estate->es_output_cid,
+								   HEAP_INSERT_SPECULATIVE,
+								   NULL,
+								   ExecInsertIndexTuples,
+								   estate,
+								   arbiterIndexes,
+								   &recheckIndexes);
 
 			/*
 			 * Wake up anyone waiting for our decision.  They will re-check
@@ -588,7 +585,7 @@ ExecInsert(ModifyTableState *mtstate,
 			 * the pre-check again, which will now find the conflicting tuple
 			 * (unless it aborts before we get there).
 			 */
-			if (specConflict)
+			if (slot->tts_specConflict)
 			{
 				list_free(recheckIndexes);
 				goto vlock;
@@ -600,19 +597,14 @@ ExecInsert(ModifyTableState *mtstate,
 		{
 			/*
 			 * insert the tuple normally.
-			 *
-			 * Note: heap_insert returns the tid (location) of the new tuple
-			 * in the t_self field.
 			 */
-			newId = heap_insert(resultRelationDesc, tuple,
-								estate->es_output_cid,
-								0, NULL);
-
-			/* insert index entries for tuple */
-			if (resultRelInfo->ri_NumIndices > 0)
-				recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
-													   estate, false, NULL,
-													   arbiterIndexes);
+			newId = storage_insert(resultRelationDesc, slot,
+								   estate->es_output_cid,
+								   0, NULL,
+								   ExecInsertIndexTuples,
+								   estate,
+								   arbiterIndexes,
+								   &recheckIndexes);
 		}
 	}
 
@@ -620,11 +612,11 @@ ExecInsert(ModifyTableState *mtstate,
 	{
 		(estate->es_processed)++;
 		estate->es_lastoid = newId;
-		setLastTid(&(tuple->t_self));
+		setLastTid(&(slot->tts_tid));
 	}
 
 	/* AFTER ROW INSERT Triggers */
-	ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+	ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
 						 mtstate->mt_transition_capture);
 
 	list_free(recheckIndexes);
@@ -675,7 +667,7 @@ ExecInsert(ModifyTableState *mtstate,
 static TupleTableSlot *
 ExecDelete(ModifyTableState *mtstate,
 		   ItemPointer tupleid,
-		   HeapTuple oldtuple,
+		   StorageTuple oldtuple,
 		   TupleTableSlot *planSlot,
 		   EPQState *epqstate,
 		   EState *estate,
@@ -720,8 +712,6 @@ ExecDelete(ModifyTableState *mtstate,
 	}
 	else if (resultRelInfo->ri_FdwRoutine)
 	{
-		HeapTuple	tuple;
-
 		/*
 		 * delete from foreign table: let the FDW do it
 		 *
@@ -747,8 +737,10 @@ ExecDelete(ModifyTableState *mtstate,
 		 */
 		if (slot->tts_isempty)
 			ExecStoreAllNullTuple(slot);
-		tuple = ExecHeapifySlot(slot);
-		tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
+		ExecMaterializeSlot(slot);
+
+		slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
 	}
 	else
 	{
@@ -762,11 +754,11 @@ ExecDelete(ModifyTableState *mtstate,
 		 * mode transactions.
 		 */
 ldelete:;
-		result = heap_delete(resultRelationDesc, tupleid,
-							 estate->es_output_cid,
-							 estate->es_crosscheck_snapshot,
-							 true /* wait for commit */ ,
-							 &hufd);
+		result = storage_delete(resultRelationDesc, tupleid,
+								estate->es_output_cid,
+								estate->es_crosscheck_snapshot,
+								true /* wait for commit */ ,
+								&hufd);
 		switch (result)
 		{
 			case HeapTupleSelfUpdated:
@@ -862,7 +854,7 @@ ldelete:;
 		 * gotta fetch it.  We can use the trigger tuple slot.
 		 */
 		TupleTableSlot *rslot;
-		HeapTupleData deltuple;
+		StorageTuple deltuple = NULL;
 		Buffer		delbuffer;
 
 		if (resultRelInfo->ri_FdwRoutine)
@@ -876,20 +868,19 @@ ldelete:;
 			slot = estate->es_trig_tuple_slot;
 			if (oldtuple != NULL)
 			{
-				deltuple = *oldtuple;
+				deltuple = heap_copytuple(oldtuple);
 				delbuffer = InvalidBuffer;
 			}
 			else
 			{
-				deltuple.t_self = *tupleid;
-				if (!heap_fetch(resultRelationDesc, SnapshotAny,
-								&deltuple, &delbuffer, false, NULL))
+				if (!storage_fetch(resultRelationDesc, tupleid, SnapshotAny,
+								   &deltuple, &delbuffer, false, NULL))
 					elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
 			}
 
 			if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
 				ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
-			ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+			ExecStoreTuple(deltuple, slot, InvalidBuffer, false);
 		}
 
 		rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -898,7 +889,7 @@ ldelete:;
 		 * Before releasing the target tuple again, make sure rslot has a
 		 * local copy of any pass-by-reference values.
 		 */
-		ExecHeapifySlot(rslot);
+		ExecMaterializeSlot(rslot);
 
 		ExecClearTuple(slot);
 		if (BufferIsValid(delbuffer))
@@ -935,14 +926,14 @@ ldelete:;
 static TupleTableSlot *
 ExecUpdate(ModifyTableState *mtstate,
 		   ItemPointer tupleid,
-		   HeapTuple oldtuple,
+		   StorageTuple oldtuple,
 		   TupleTableSlot *slot,
 		   TupleTableSlot *planSlot,
 		   EPQState *epqstate,
 		   EState *estate,
 		   bool canSetTag)
 {
-	HeapTuple	tuple;
+	StorageTuple tuple;
 	ResultRelInfo *resultRelInfo;
 	Relation	resultRelationDesc;
 	HTSU_Result result;
@@ -1007,14 +998,14 @@ ExecUpdate(ModifyTableState *mtstate,
 		if (slot == NULL)		/* "do nothing" */
 			return NULL;
 
-		/* FDW might have changed tuple */
-		tuple = ExecHeapifySlot(slot);
-
 		/*
 		 * AFTER ROW Triggers or RETURNING expressions might reference the
 		 * tableoid column, so initialize t_tableOid before evaluating them.
 		 */
-		tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+		ExecSlotUpdateTupleTableoid(slot, RelationGetRelid(resultRelationDesc));
+
+		/* FDW might have changed tuple */
+		tuple = ExecHeapifySlot(slot);
 	}
 	else
 	{
@@ -1024,7 +1015,7 @@ ExecUpdate(ModifyTableState *mtstate,
 		 * Constraints might reference the tableoid column, so initialize
 		 * t_tableOid before evaluating them.
 		 */
-		tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+		slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
 
 		/*
 		 * Check any RLS UPDATE WITH CHECK policies
@@ -1060,11 +1051,14 @@ lreplace:;
 		 * needed for referential integrity updates in transaction-snapshot
 		 * mode transactions.
 		 */
-		result = heap_update(resultRelationDesc, tupleid, tuple,
-							 estate->es_output_cid,
-							 estate->es_crosscheck_snapshot,
-							 true /* wait for commit */ ,
-							 &hufd, &lockmode);
+		result = storage_update(resultRelationDesc, tupleid, slot,
+								estate,
+								estate->es_output_cid,
+								estate->es_crosscheck_snapshot,
+								true /* wait for commit */ ,
+								&hufd, &lockmode,
+								ExecInsertIndexTuples,
+								&recheckIndexes);
 		switch (result)
 		{
 			case HeapTupleSelfUpdated:
@@ -1135,26 +1129,6 @@ lreplace:;
 				elog(ERROR, "unrecognized heap_update status: %u", result);
 				return NULL;
 		}
-
-		/*
-		 * Note: instead of having to update the old index tuples associated
-		 * with the heap tuple, all we do is form and insert new index tuples.
-		 * This is because UPDATEs are actually DELETEs and INSERTs, and index
-		 * tuple deletion is done later by VACUUM (see notes in ExecDelete).
-		 * All we do here is insert new index tuples.  -cim 9/27/89
-		 */
-
-		/*
-		 * insert index entries for tuple
-		 *
-		 * Note: heap_update returns the tid (location) of the new tuple in
-		 * the t_self field.
-		 *
-		 * If it's a HOT update, we mustn't insert new index entries.
-		 */
-		if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
-			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
-												   estate, false, NULL, NIL);
 	}
 
 	if (canSetTag)
@@ -1212,11 +1186,12 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
 	ExprState  *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
-	HeapTupleData tuple;
+	StorageTuple tuple = NULL;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
 	HTSU_Result test;
 	Buffer		buffer;
+	tuple_data	t_data;
 
 	/* Determine lock mode to use */
 	lockmode = ExecUpdateLockMode(estate, resultRelInfo);
@@ -1227,10 +1202,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	 * previous conclusion that the tuple is conclusively committed is not
 	 * true anymore.
 	 */
-	tuple.t_self = *conflictTid;
-	test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
-						   lockmode, LockWaitBlock, false, &buffer,
-						   &hufd);
+	test = storage_lock_tuple(relation, conflictTid, &tuple, estate->es_output_cid,
+							  lockmode, LockWaitBlock, false, &buffer, &hufd);
 	switch (test)
 	{
 		case HeapTupleMayBeUpdated:
@@ -1255,7 +1228,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 			 * that for SQL MERGE, an exception must be raised in the event of
 			 * an attempt to update the same row twice.
 			 */
-			if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data)))
+			t_data = storage_tuple_get_data(relation, tuple, XMIN);
+			if (TransactionIdIsCurrentTransactionId(t_data.xid))
 				ereport(ERROR,
 						(errcode(ERRCODE_CARDINALITY_VIOLATION),
 						 errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
@@ -1286,7 +1260,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 			 * loop here, as the new version of the row might not conflict
 			 * anymore, or the conflicting tuple has actually been deleted.
 			 */
-			ReleaseBuffer(buffer);
+			if (BufferIsValid(buffer))
+				ReleaseBuffer(buffer);
+			pfree(tuple);
 			return false;
 
 		default:
@@ -1314,10 +1290,10 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	 * snapshot.  This is in line with the way UPDATE deals with newer tuple
 	 * versions.
 	 */
-	ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
+	ExecCheckHeapTupleVisible(estate, relation, tuple, buffer);
 
 	/* Store target's existing tuple in the state's dedicated slot */
-	ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
+	ExecStoreTuple(tuple, mtstate->mt_existing, buffer, false);
 
 	/*
 	 * Make tuple and any needed join variables available to ExecQual and
@@ -1332,7 +1308,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 
 	if (!ExecQual(onConflictSetWhere, econtext))
 	{
-		ReleaseBuffer(buffer);
+		if (BufferIsValid(buffer))
+			ReleaseBuffer(buffer);
+		pfree(tuple);
 		InstrCountFiltered1(&mtstate->ps, 1);
 		return true;			/* done with the tuple */
 	}
@@ -1372,12 +1350,14 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	 */
 
 	/* Execute UPDATE with projection */
-	*returning = ExecUpdate(mtstate, &tuple.t_self, NULL,
+	*returning = ExecUpdate(mtstate, conflictTid, NULL,
 							mtstate->mt_conflproj, planSlot,
 							&mtstate->mt_epqstate, mtstate->ps.state,
 							canSetTag);
 
-	ReleaseBuffer(buffer);
+	if (BufferIsValid(buffer))
+		ReleaseBuffer(buffer);
+	pfree(tuple);
 	return true;
 }
 
@@ -1580,7 +1560,7 @@ ExecModifyTable(PlanState *pstate)
 	ItemPointer tupleid;
 	ItemPointerData tuple_ctid;
 	HeapTupleData oldtupdata;
-	HeapTuple	oldtuple;
+	StorageTuple oldtuple;
 
 	CHECK_FOR_INTERRUPTS();
 
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 0ee76e7d25..47d8b7b12f 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -22,6 +22,7 @@
  */
 #include "postgres.h"
 
+#include "access/storageam.h"
 #include "access/sysattr.h"
 #include "catalog/pg_type.h"
 #include "executor/execdebug.h"
@@ -306,7 +307,7 @@ TidNext(TidScanState *node)
 	ScanDirection direction;
 	Snapshot	snapshot;
 	Relation	heapRelation;
-	HeapTuple	tuple;
+	StorageTuple tuple;
 	TupleTableSlot *slot;
 	Buffer		buffer = InvalidBuffer;
 	ItemPointerData *tidList;
@@ -331,12 +332,6 @@ TidNext(TidScanState *node)
 	tidList = node->tss_TidList;
 	numTids = node->tss_NumTids;
 
-	/*
-	 * We use node->tss_htup as the tuple pointer; note this can't just be a
-	 * local variable here, as the scan tuple slot will keep a pointer to it.
-	 */
-	tuple = &(node->tss_htup);
-
 	/*
 	 * Initialize or advance scan position, depending on direction.
 	 */
@@ -364,7 +359,7 @@ TidNext(TidScanState *node)
 
 	while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
 	{
-		tuple->t_self = tidList[node->tss_TidPtr];
+		ItemPointerData tid = tidList[node->tss_TidPtr];
 
 		/*
 		 * For WHERE CURRENT OF, the tuple retrieved from the cursor might
@@ -372,9 +367,9 @@ TidNext(TidScanState *node)
 		 * current according to our snapshot.
 		 */
 		if (node->tss_isCurrentOf)
-			heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+			storage_get_latest_tid(heapRelation, snapshot, &tid);
 
-		if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
+		if (storage_fetch(heapRelation, &tid, snapshot, &tuple, &buffer, false, NULL))
 		{
 			/*
 			 * store the scanned tuple in the scan tuple slot of the scan
@@ -385,14 +380,16 @@ TidNext(TidScanState *node)
 			 */
 			ExecStoreTuple(tuple,	/* tuple to store */
 						   slot,	/* slot to store in */
-						   buffer,	/* buffer associated with tuple  */
-						   false);	/* don't pfree */
+						   InvalidBuffer,	/* buffer associated with tuple  */
+						   true);	/* don't pfree */
 
 			/*
 			 * At this point we have an extra pin on the buffer, because
 			 * ExecStoreTuple incremented the pin count. Drop our local pin.
 			 */
-			ReleaseBuffer(buffer);
+			/* hari */
+			if (BufferIsValid(buffer))
+				ReleaseBuffer(buffer);
 
 			return slot;
 		}
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index 854097dd58..c4481ecee9 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -21,6 +21,7 @@
 #include <limits.h>
 
 #include "access/heapam.h"
+#include "access/storageam.h"
 #include "access/sysattr.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_type.h"
@@ -352,7 +353,7 @@ currtid_byreloid(PG_FUNCTION_ARGS)
 	ItemPointerCopy(tid, result);
 
 	snapshot = RegisterSnapshot(GetLatestSnapshot());
-	heap_get_latest_tid(rel, snapshot, result);
+	storage_get_latest_tid(rel, snapshot, result);
 	UnregisterSnapshot(snapshot);
 
 	heap_close(rel, AccessShareLock);
@@ -387,7 +388,7 @@ currtid_byrelname(PG_FUNCTION_ARGS)
 	ItemPointerCopy(tid, result);
 
 	snapshot = RegisterSnapshot(GetLatestSnapshot());
-	heap_get_latest_tid(rel, snapshot, result);
+	storage_get_latest_tid(rel, snapshot, result);
 	UnregisterSnapshot(snapshot);
 
 	heap_close(rel, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4e41024e92..251342b01b 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -16,6 +16,7 @@
 
 #include "access/sdir.h"
 #include "access/skey.h"
+#include "access/storage_common.h"
 #include "nodes/lockoptions.h"
 #include "nodes/primnodes.h"
 #include "storage/bufpage.h"
@@ -133,7 +134,7 @@ extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
 extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
 extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
 
-extern bool heap_fetch(Relation relation, Snapshot snapshot,
+extern bool heap_fetch(Relation relation, ItemPointer tid, Snapshot snapshot,
 		   HeapTuple tuple, Buffer *userbuf, bool keep_buf,
 		   Relation stats_relation);
 extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
@@ -141,7 +142,6 @@ extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
 					   bool *all_dead, bool first_call);
 extern bool heap_hot_search(ItemPointer tid, Relation relation,
 				Snapshot snapshot, bool *all_dead);
-
 extern void heap_get_latest_tid(Relation relation, Snapshot snapshot,
 					ItemPointer tid);
 extern void setLastTid(const ItemPointer tid);
@@ -157,16 +157,17 @@ extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
 extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
 			CommandId cid, Snapshot crosscheck, bool wait,
 			HeapUpdateFailureData *hufd);
-extern void heap_finish_speculative(Relation relation, HeapTuple tuple);
-extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
+extern void heap_finish_speculative(Relation relation, TupleTableSlot *slot);
+extern void heap_abort_speculative(Relation relation, TupleTableSlot *slot);
 extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
 			HeapTuple newtup,
 			CommandId cid, Snapshot crosscheck, bool wait,
 			HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
-extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
+extern HTSU_Result heap_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * tuple,
 				CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
 				bool follow_update,
 				Buffer *buffer, HeapUpdateFailureData *hufd);
+
 extern void heap_inplace_update(Relation relation, HeapTuple tuple);
 extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
 				  TransactionId cutoff_multi);
@@ -182,6 +183,10 @@ extern void simple_heap_update(Relation relation, ItemPointer otid,
 extern void heap_sync(Relation relation);
 extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
 
+/* in heap/heapam_visibility.c */
+extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
+						 Buffer buffer);
+
 /* in heap/pruneheap.c */
 extern void heap_page_prune_opt(Relation relation, Buffer buffer);
 extern int heap_page_prune(Relation relation, Buffer buffer,
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 168edb058d..489aa78731 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -816,6 +816,7 @@ extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
 extern HeapTuple heap_copytuple(HeapTuple tuple);
 extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
 extern Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc);
+extern HeapTuple heap_form_tuple_by_datum(Datum data, Oid relid);
 extern HeapTuple heap_form_tuple(TupleDesc tupleDescriptor,
 				Datum *values, bool *isnull);
 extern HeapTuple heap_modify_tuple(HeapTuple tuple,
diff --git a/src/include/access/storage_common.h b/src/include/access/storage_common.h
index 5f799406fe..ff28b4dfec 100644
--- a/src/include/access/storage_common.h
+++ b/src/include/access/storage_common.h
@@ -45,9 +45,6 @@ typedef MinimalTuple (*SlotGetMinTuple_function) (TupleTableSlot *slot, bool pal
 
 typedef void (*SlotUpdateTableoid_function) (TupleTableSlot *slot, Oid tableoid);
 
-typedef void (*SpeculativeAbort_function) (Relation rel,
-										   TupleTableSlot *slot);
-
 typedef struct StorageSlotAmRoutine
 {
 	/* Operations on TupleTableSlot */
@@ -74,8 +71,6 @@ typedef enum
 
 
 /* in storage/storage_common.c */
-extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
-					 uint16 infomask, TransactionId xid);
 extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
 extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
 extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
new file mode 100644
index 0000000000..e0658a1ddd
--- /dev/null
+++ b/src/include/access/storageam.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.h
+ *	  POSTGRES storage access method definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/storageam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STORAGEAM_H
+#define STORAGEAM_H
+
+#include "access/heapam.h"
+#include "access/storageamapi.h"
+#include "executor/tuptable.h"
+#include "nodes/execnodes.h"
+
+extern bool storage_fetch(Relation relation,
+			  ItemPointer tid,
+			  Snapshot snapshot,
+			  StorageTuple * stuple,
+			  Buffer *userbuf,
+			  bool keep_buf,
+			  Relation stats_relation);
+
+extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
+				   CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+				   bool follow_updates,
+				   Buffer *buffer, HeapUpdateFailureData *hufd);
+
+extern Oid storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+			   int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+			   EState *estate, List *arbiterIndexes, List **recheckIndexes);
+
+extern HTSU_Result storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+			   Snapshot crosscheck, bool wait,
+			   HeapUpdateFailureData *hufd);
+
+extern HTSU_Result storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+			   EState *estate, CommandId cid, Snapshot crosscheck, bool wait,
+			   HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+			   InsertIndexTuples IndexFunc, List **recheckIndexes);
+
+extern void storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+					 CommandId cid, int options, BulkInsertState bistate);
+
+extern void storage_abort_speculative(Relation relation, TupleTableSlot *slot);
+
+extern tuple_data storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags);
+
+extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
+
+extern void storage_get_latest_tid(Relation relation,
+					   Snapshot snapshot,
+					   ItemPointer tid);
+
+extern void storage_sync(Relation rel);
+
+#endif
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 5e807d827e..22bc2493ac 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -11,11 +11,33 @@
 #ifndef STORAGEAMAPI_H
 #define STORAGEAMAPI_H
 
+#include "access/heapam.h"
 #include "access/storage_common.h"
+#include "nodes/execnodes.h"
 #include "nodes/nodes.h"
 #include "utils/snapshot.h"
 #include "fmgr.h"
 
+typedef union tuple_data
+{
+	TransactionId xid;
+	CommandId	cid;
+	ItemPointerData tid;
+}			tuple_data;
+
+typedef enum tuple_data_flags
+{
+	XMIN = 0,
+	UPDATED_XID,
+	CMIN,
+	TID,
+	CTID
+}			tuple_data_flags;
+
+/* Function pointer to let the index tuple insert from storage am */
+typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool noDupErr,
+									bool *specConflict, List *arbiterIndexes);
+
 /*
  * Storage routine functions
  */
@@ -24,6 +46,63 @@ typedef HTSU_Result (*SnapshotSatisfiesUpdate_function) (StorageTuple htup, Comm
 typedef HTSV_Result (*SnapshotSatisfiesVacuum_function) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer);
 
 
+typedef Oid (*TupleInsert_function) (Relation rel, TupleTableSlot *slot, CommandId cid,
+									 int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+									 EState *estate, List *arbiterIndexes, List **recheckIndexes);
+
+typedef HTSU_Result (*TupleDelete_function) (Relation relation,
+											 ItemPointer tid,
+											 CommandId cid,
+											 Snapshot crosscheck,
+											 bool wait,
+											 HeapUpdateFailureData *hufd);
+
+typedef HTSU_Result (*TupleUpdate_function) (Relation relation,
+											 ItemPointer otid,
+											 TupleTableSlot *slot,
+											 EState *estate,
+											 CommandId cid,
+											 Snapshot crosscheck,
+											 bool wait,
+											 HeapUpdateFailureData *hufd,
+											 LockTupleMode *lockmode,
+											 InsertIndexTuples IndexFunc,
+											 List **recheckIndexes);
+
+typedef bool (*TupleFetch_function) (Relation relation,
+									 ItemPointer tid,
+									 Snapshot snapshot,
+									 StorageTuple * tuple,
+									 Buffer *userbuf,
+									 bool keep_buf,
+									 Relation stats_relation);
+
+typedef HTSU_Result (*TupleLock_function) (Relation relation,
+										   ItemPointer tid,
+										   StorageTuple * tuple,
+										   CommandId cid,
+										   LockTupleMode mode,
+										   LockWaitPolicy wait_policy,
+										   bool follow_update,
+										   Buffer *buffer,
+										   HeapUpdateFailureData *hufd);
+
+typedef void (*MultiInsert_function) (Relation relation, HeapTuple *tuples, int ntuples,
+									  CommandId cid, int options, BulkInsertState bistate);
+
+typedef void (*TupleGetLatestTid_function) (Relation relation,
+											Snapshot snapshot,
+											ItemPointer tid);
+
+typedef tuple_data(*GetTupleData_function) (StorageTuple tuple, tuple_data_flags flags);
+
+typedef StorageTuple(*TupleFromDatum_function) (Datum data, Oid tableoid);
+
+typedef void (*SpeculativeAbort_function) (Relation rel,
+										   TupleTableSlot *slot);
+
+typedef void (*RelationSync_function) (Relation relation);
+
 /*
  * API struct for a storage AM.  Note this must be stored in a single palloc'd
  * chunk of memory.
@@ -43,6 +122,29 @@ typedef struct StorageAmRoutine
 
 	slot_storageam_hook slot_storageam;
 
+	/* Operations on physical tuples */
+	TupleInsert_function tuple_insert;	/* heap_insert */
+	TupleUpdate_function tuple_update;	/* heap_update */
+	TupleDelete_function tuple_delete;	/* heap_delete */
+	TupleFetch_function tuple_fetch;	/* heap_fetch */
+	TupleLock_function tuple_lock;	/* heap_lock_tuple */
+	MultiInsert_function multi_insert;	/* heap_multi_insert */
+	TupleGetLatestTid_function tuple_get_latest_tid;	/* heap_get_latest_tid */
+
+	GetTupleData_function get_tuple_data;
+	TupleFromDatum_function tuple_from_datum;
+
+	/*
+	 * Speculative insertion support operations
+	 *
+	 * Setting a tuple's speculative token is a slot-only operation, so no
+	 * need for a storage AM method, but after inserting a tuple containing a
+	 * speculative token, the insertion must be completed by these routines:
+	 */
+	SpeculativeAbort_function speculative_abort;
+
+	RelationSync_function relation_sync;	/* heap_sync */
+
 }			StorageAmRoutine;
 
 extern StorageAmRoutine * GetStorageAmRoutine(Oid amhandler);
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index adbcfa1297..203371148c 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -190,7 +190,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
 					 TupleTableSlot *slot);
 extern void ExecARInsertTriggers(EState *estate,
 					 ResultRelInfo *relinfo,
-					 HeapTuple trigtuple,
+					 TupleTableSlot *slot,
 					 List *recheckIndexes,
 					 TransitionCaptureState *transition_capture);
 extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index b5578f5855..fa04a5ea65 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -198,16 +198,16 @@ extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
 extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
 			 Relation relation, Index rti, int lockmode,
 			 ItemPointer tid, TransactionId priorXmax);
-extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
-				  int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
-				  TransactionId priorXmax);
+extern StorageTuple EvalPlanQualFetch(EState *estate, Relation relation,
+									  int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
+									  TransactionId priorXmax);
 extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
 				 Plan *subplan, List *auxrowmarks, int epqParam);
 extern void EvalPlanQualSetPlan(EPQState *epqstate,
 					Plan *subplan, List *auxrowmarks);
 extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
-					 HeapTuple tuple);
-extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
+					 StorageTuple tuple);
+extern StorageTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
 
 #define EvalPlanQualSetSlot(epqstate, slot)  ((epqstate)->origslot = (slot))
 extern void EvalPlanQualFetchRowMarks(EPQState *epqstate);
@@ -520,9 +520,8 @@ extern int	ExecCleanTargetListLength(List *targetlist);
  */
 extern void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative);
 extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
-extern List *ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
-					  EState *estate, bool noDupErr, bool *specConflict,
-					  List *arbiterIndexes);
+extern List *ExecInsertIndexTuples(TupleTableSlot *slot, EState *estate, bool noDupErr,
+					  bool *specConflict, List *arbiterIndexes);
 extern bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate,
 						  ItemPointer conflictTid, List *arbiterIndexes);
 extern void check_exclusion_constraint(Relation heap, Relation index,
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index e6ff66f14b..7839d5e9b7 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -133,6 +133,7 @@ typedef struct TupleTableSlot
 	Oid			tts_tupleOid;	/* XXX describe */
 	int			tts_nvalid;		/* # of valid values in tts_values */
 	uint32		tts_speculativeToken;	/* XXX describe */
+	bool		tts_specConflict;	/* XXX describe */
 	bool		tts_shouldFree;
 	bool		tts_shouldFreeMin;
 	Datum	   *tts_values;		/* current per-attribute values */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1a35c5c9ad..a956138e4a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -504,7 +504,7 @@ typedef struct EState
 	 * remember if the tuple has been returned already.  Arrays are of size
 	 * list_length(es_range_table) and are indexed by scan node scanrelid - 1.
 	 */
-	HeapTuple  *es_epqTuple;	/* array of EPQ substitute tuples */
+	StorageTuple *es_epqTuple;	/* array of EPQ substitute tuples */
 	bool	   *es_epqTupleSet; /* true if EPQ tuple is provided */
 	bool	   *es_epqScanDone; /* true if EPQ tuple has been fetched */
 
@@ -2001,7 +2001,7 @@ typedef struct HashInstrumentation
 	int			nbatch;			/* number of batches at end of execution */
 	int			nbatch_original;	/* planned number of batches */
 	size_t		space_peak;		/* speak memory usage in bytes */
-} HashInstrumentation;
+}			HashInstrumentation;
 
 /* ----------------
  *	 Shared memory container for per-worker hash information
@@ -2011,7 +2011,7 @@ typedef struct SharedHashInfo
 {
 	int			num_workers;
 	HashInstrumentation hinstrument[FLEXIBLE_ARRAY_MEMBER];
-} SharedHashInfo;
+}			SharedHashInfo;
 
 /* ----------------
  *	 HashState information
@@ -2069,7 +2069,7 @@ typedef struct LockRowsState
 	PlanState	ps;				/* its first field is NodeTag */
 	List	   *lr_arowMarks;	/* List of ExecAuxRowMarks */
 	EPQState	lr_epqstate;	/* for evaluating EvalPlanQual rechecks */
-	HeapTuple  *lr_curtuples;	/* locked tuples (one entry per RT entry) */
+	StorageTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
 	int			lr_ntables;		/* length of lr_curtuples[] array */
 } LockRowsState;
 
-- 
2.15.0.windows.1

