From 3c993d1f59e89a1b0e537b4bc02e62d482bfe028 Mon Sep 17 00:00:00 2001
From: Hari <haribabuk@fast.au.fujitsu.com>
Date: Fri, 9 Jun 2017 11:07:32 +1000
Subject: [PATCH 06/10] HeapTuple replace with StorageTuple

Replace most of the executor flow of HeapTuple
with StorageTuple and the necessary code around it.
---
 src/backend/commands/trigger.c            |  39 +++---
 src/backend/executor/execAmi.c            |   2 +-
 src/backend/executor/execExprInterp.c     |  16 +--
 src/backend/executor/execIndexing.c       |  10 +-
 src/backend/executor/execMain.c           | 113 ++++++++---------
 src/backend/executor/execReplication.c    |  50 ++++----
 src/backend/executor/functions.c          |   4 +-
 src/backend/executor/nodeAgg.c            |   4 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 ++-
 src/backend/executor/nodeForeignscan.c    |   7 +-
 src/backend/executor/nodeGather.c         |   8 +-
 src/backend/executor/nodeGatherMerge.c    |  12 +-
 src/backend/executor/nodeIndexonlyscan.c  |   4 +-
 src/backend/executor/nodeIndexscan.c      |  16 +--
 src/backend/executor/nodeLockRows.c       |  22 ++--
 src/backend/executor/nodeModifyTable.c    | 193 ++++++++++++++++--------------
 src/backend/executor/nodeSamplescan.c     |  27 +++--
 src/backend/executor/nodeSeqscan.c        |  13 +-
 src/backend/executor/nodeTidscan.c        |  16 +--
 src/backend/executor/nodeWindowAgg.c      |   4 +-
 src/backend/executor/spi.c                |  20 ++--
 src/backend/executor/tqueue.c             |  22 ++--
 src/include/access/htup_details.h         |   2 +-
 src/include/access/relscan.h              |   3 +-
 src/include/commands/trigger.h            |   2 +-
 src/include/executor/executor.h           |   7 +-
 src/include/executor/functions.h          |   2 +-
 src/include/executor/spi.h                |  10 +-
 src/include/executor/tqueue.h             |   2 +-
 src/include/executor/tuptable.h           |   6 +-
 src/include/funcapi.h                     |   2 +-
 src/include/nodes/execnodes.h             |   4 +-
 32 files changed, 337 insertions(+), 323 deletions(-)

diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 0271788..8fa42d5 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -17,6 +17,7 @@
 #include "access/heapam.h"
 #include "access/sysattr.h"
 #include "access/htup_details.h"
+#include "access/storageamapi.h"
 #include "access/xact.h"
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
@@ -2200,7 +2201,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 					 TupleTableSlot *slot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-	HeapTuple	slottuple = ExecMaterializeSlot(slot);
+	HeapTuple	slottuple = ExecHeapifySlot(slot);
 	HeapTuple	newtuple = slottuple;
 	HeapTuple	oldtuple;
 	TriggerData LocTriggerData;
@@ -2263,14 +2264,18 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 
 void
 ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
-					 HeapTuple trigtuple, List *recheckIndexes)
+					 TupleTableSlot *slot, List *recheckIndexes)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
 
 	if (trigdesc &&
 		(trigdesc->trig_insert_after_row || trigdesc->trig_insert_new_table))
+	{
+		HeapTuple	trigtuple = ExecHeapifySlot(slot);
+
 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
 							  true, NULL, trigtuple, recheckIndexes, NULL);
+	}
 }
 
 TupleTableSlot *
@@ -2278,7 +2283,7 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 					 TupleTableSlot *slot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-	HeapTuple	slottuple = ExecMaterializeSlot(slot);
+	HeapTuple	slottuple = ExecHeapifySlot(slot);
 	HeapTuple	newtuple = slottuple;
 	HeapTuple	oldtuple;
 	TriggerData LocTriggerData;
@@ -2621,7 +2626,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 					 TupleTableSlot *slot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-	HeapTuple	slottuple = ExecMaterializeSlot(slot);
+	HeapTuple	slottuple = ExecHeapifySlot(slot);
 	HeapTuple	newtuple = slottuple;
 	TriggerData LocTriggerData;
 	HeapTuple	trigtuple;
@@ -2663,7 +2668,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 	if (newSlot != NULL)
 	{
 		slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
-		slottuple = ExecMaterializeSlot(slot);
+		slottuple = ExecHeapifySlot(slot);
 		newtuple = slottuple;
 	}
 
@@ -2768,7 +2773,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
 					 HeapTuple trigtuple, TupleTableSlot *slot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-	HeapTuple	slottuple = ExecMaterializeSlot(slot);
+	HeapTuple	slottuple = ExecHeapifySlot(slot);
 	HeapTuple	newtuple = slottuple;
 	TriggerData LocTriggerData;
 	HeapTuple	oldtuple;
@@ -2920,7 +2925,8 @@ GetTupleForTrigger(EState *estate,
 		 */
 ltrmark:;
 		tuple.t_self = *tid;
-		test = heap_lock_tuple(relation, &tuple,
+		test = relation->rd_stamroutine->tuple_lock(relation, tid,
+							   &tuple,
 							   estate->es_output_cid,
 							   lockmode, LockWaitBlock,
 							   false, &buffer, &hufd);
@@ -3742,8 +3748,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;
@@ -3804,14 +3810,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
 			 * because we start with a minimal tuple that ExecFetchSlotTuple()
 			 * must materialize anyway.
 			 */
-			LocTriggerData.tg_trigtuple =
-				ExecMaterializeSlot(trig_tuple_slot1);
+			LocTriggerData.tg_trigtuple = ExecHeapifySlot(trig_tuple_slot1);
 			LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
 
 			LocTriggerData.tg_newtuple =
 				((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
 				 TRIGGER_EVENT_UPDATE) ?
-				ExecMaterializeSlot(trig_tuple_slot2) : NULL;
+						 ExecHeapifySlot(trig_tuple_slot2) : NULL;
 			LocTriggerData.tg_newtuplebuf = InvalidBuffer;
 
 			break;
@@ -3819,10 +3824,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 (!rel->rd_stamroutine->tuple_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
@@ -3836,10 +3840,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 (!rel->rd_stamroutine->tuple_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/execAmi.c b/src/backend/executor/execAmi.c
index 7337d21..953284c 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -539,7 +539,7 @@ static bool
 IndexSupportsBackwardScan(Oid indexid)
 {
 	bool		result;
-	HeapTuple	ht_idxrel;
+	StorageTuple	ht_idxrel;
 	Form_pg_class idxrelrec;
 	IndexAmRoutine *amroutine;
 
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index fed0052..1217304 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2057,7 +2057,7 @@ ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
 								 econtext);
 
 	/*
-	 * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
+	 * heap_attisnull needs a StorageTuple not a bare HeapTupleHeader.
 	 */
 	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
 	tmptup.t_data = tuple;
@@ -2368,7 +2368,7 @@ ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
 void
 ExecEvalRow(ExprState *state, ExprEvalStep *op)
 {
-	HeapTuple	tuple;
+	HeapTuple	tuple; //hari
 
 	/* build tuple from evaluated field values */
 	tuple = heap_form_tuple(op->d.row.tupdesc,
@@ -2497,7 +2497,7 @@ ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 						   format_type_be(attr->atttypid),
 						   format_type_be(op->d.fieldselect.resulttype))));
 
-	/* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
+	/* heap_getattr needs a StorageTuple not a bare HeapTupleHeader */
 	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
 	tmptup.t_data = tuple;
 
@@ -2540,7 +2540,7 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
 	else
 	{
 		/*
-		 * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
+		 * heap_deform_tuple needs a StorageTuple not a bare HeapTupleHeader. We
 		 * set all the fields in the struct just in case.
 		 */
 		Datum		tupDatum = *op->resvalue;
@@ -2566,7 +2566,7 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
 void
 ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 {
-	HeapTuple	tuple;
+	HeapTuple	tuple; //hari
 
 	/* argdesc should already be valid from the DeForm step */
 	tuple = heap_form_tuple(*op->d.fieldstore.argdesc,
@@ -2781,7 +2781,7 @@ void
 ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 {
 	ConvertRowtypeExpr *convert = op->d.convert_rowtype.convert;
-	HeapTuple	result;
+	HeapTuple	result; //hari
 	Datum		tupDatum;
 	HeapTupleHeader tuple;
 	HeapTupleData tmptup;
@@ -2840,7 +2840,7 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 		MemoryContextSwitchTo(old_cxt);
 	}
 
-	/* Following steps need a HeapTuple not a bare HeapTupleHeader */
+	/* Following steps need a StorageTuple not a bare HeapTupleHeader */
 	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
 	tmptup.t_data = tuple;
 
@@ -3345,7 +3345,7 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 	TupleDesc	output_tupdesc;
 	MemoryContext oldcontext;
 	HeapTupleHeader dtuple;
-	HeapTuple	tuple;
+	HeapTuple	tuple; //hari
 
 	/* This was checked by ExecInitExpr */
 	Assert(variable->varattno == InvalidAttrNumber);
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 108060a..fe4c873 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -650,7 +650,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
 	Oid		   *index_collations = index->rd_indcollation;
 	int			index_natts = index->rd_index->indnatts;
 	IndexScanDesc index_scan;
-	HeapTuple	tup;
+	StorageTuple	tup;
 	ScanKeyData scankeys[INDEX_MAX_KEYS];
 	SnapshotData DirtySnapshot;
 	int			i;
@@ -659,6 +659,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
 	ExprContext *econtext;
 	TupleTableSlot *existing_slot;
 	TupleTableSlot *save_scantuple;
+	StorageAmRoutine *method;
 
 	if (indexInfo->ii_ExclusionOps)
 	{
@@ -711,6 +712,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
 	econtext = GetPerTupleExprContext(estate);
 	save_scantuple = econtext->ecxt_scantuple;
 	econtext->ecxt_scantuple = existing_slot;
+	method = heap->rd_stamroutine;
 
 	/*
 	 * May have to restart scan from this point if a potential conflict is
@@ -737,7 +739,7 @@ retry:
 		 * Ignore the entry for the tuple we're trying to check.
 		 */
 		if (ItemPointerIsValid(tupleid) &&
-			ItemPointerEquals(tupleid, &tup->t_self))
+			ItemPointerEquals(tupleid, method->tuple_get_itempointer(tup)))
 		{
 			if (found_self)		/* should not happen */
 				elog(ERROR, "found self tuple multiple times in index \"%s\"",
@@ -785,7 +787,7 @@ retry:
 			  DirtySnapshot.speculativeToken &&
 			  TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
 		{
-			ctid_wait = tup->t_data->t_ctid;
+			ctid_wait = method->tuple_get_ctid(tup);
 			reason_wait = indexInfo->ii_ExclusionOps ?
 				XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
 			index_endscan(index_scan);
@@ -805,7 +807,7 @@ retry:
 		{
 			conflict = true;
 			if (conflictTid)
-				*conflictTid = tup->t_self;
+				*conflictTid = *(method->tuple_get_itempointer(tup));
 			break;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 4a899f1..3614513 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1918,7 +1918,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 				 */
 				if (resultRelInfo->ri_PartitionRoot)
 				{
-					HeapTuple	tuple = ExecFetchSlotTuple(slot);
+					StorageTuple	tuple = ExecFetchSlotTuple(slot);
 					TupleConversionMap *map;
 
 					rel = resultRelInfo->ri_PartitionRoot;
@@ -1964,7 +1964,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;
 
@@ -2006,7 +2006,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;
 
@@ -2437,7 +2437,8 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
 			 ItemPointer tid, TransactionId priorXmax)
 {
 	TupleTableSlot *slot;
-	HeapTuple	copyTuple;
+	StorageTuple copyTuple;
+	StorageAmRoutine *method;
 
 	Assert(rti > 0);
 
@@ -2450,11 +2451,14 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
 	if (copyTuple == NULL)
 		return NULL;
 
+	method = relation->rd_stamroutine;
+
 	/*
 	 * For UPDATE/DELETE we have to return tid of actual row we're executing
 	 * PQ for.
 	 */
-	*tid = copyTuple->t_self;
+
+	*tid = *(method->tuple_get_itempointer(copyTuple));
 
 	/*
 	 * Need to run a recheck subquery.  Initialize or reinitialize EPQ state.
@@ -2485,7 +2489,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
@@ -2518,13 +2522,13 @@ 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;
+	StorageAmRoutine *method = relation->rd_stamroutine;
+	StorageTuple tuple = NULL;
 	SnapshotData SnapshotDirty;
 
 	/*
@@ -2533,12 +2537,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 (method->tuple_fetch(relation, tid, &SnapshotDirty, &tuple, &buffer, true, NULL))
 		{
 			HTSU_Result test;
 			HeapUpdateFailureData hufd;
@@ -2552,8 +2556,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),
-									 priorXmax))
+			if (!TransactionIdEquals(method->tuple_get_xmin(tuple), priorXmax))
 			{
 				ReleaseBuffer(buffer);
 				return NULL;
@@ -2574,7 +2577,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 				{
 					case LockWaitBlock:
 						XactLockTableWait(SnapshotDirty.xmax,
-										  relation, &tuple.t_self,
+										  relation,
+										  tid,
 										  XLTW_FetchUpdated);
 						break;
 					case LockWaitSkip:
@@ -2604,7 +2608,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 			 * doing HeapTupleHeaderGetXmin again.
 			 */
 			if (TransactionIdIsCurrentTransactionId(priorXmax) &&
-				HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
+				method->tuple_get_cmin(tuple) >= estate->es_output_cid)
 			{
 				ReleaseBuffer(buffer);
 				return NULL;
@@ -2613,10 +2617,10 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 			/*
 			 * 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 = method->tuple_lock(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);
 
@@ -2652,12 +2656,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
+					if (!ItemPointerEquals(&hufd.ctid, method->tuple_get_itempointer(tuple)))
 					{
 						/* 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;
@@ -2679,10 +2686,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;
 		}
@@ -2691,7 +2694,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;
@@ -2700,7 +2703,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(method->tuple_get_xmin(tuple),
 								 priorXmax))
 		{
 			ReleaseBuffer(buffer);
@@ -2719,7 +2722,8 @@ 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))
+		ctid = method->tuple_get_ctid(tuple);
+		if (ItemPointerEquals(tid, &ctid))
 		{
 			/* deleted, so forget about it */
 			ReleaseBuffer(buffer);
@@ -2727,17 +2731,18 @@ 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);
+		priorXmax = method->tuple_get_updated_xid(tuple);
 		ReleaseBuffer(buffer);
 		/* loop back to fetch next in chain */
 	}
 
 	/*
-	 * Return the copied tuple
+	 * Return the tuple
 	 */
-	return copyTuple;
+	return tuple;
 }
 
 /*
@@ -2783,7 +2788,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;
 
@@ -2802,7 +2807,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;
@@ -2830,7 +2835,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");
@@ -2861,8 +2866,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
 
 		if (erm->markType == ROW_MARK_REFERENCE)
 		{
-			HeapTuple	copyTuple;
-
 			Assert(erm->relation != NULL);
 
 			/* fetch the tuple's ctid */
@@ -2886,11 +2889,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,
+				tuple = fdwroutine->RefetchForeignRow(epqstate->estate,
 														  erm,
 														  datum,
 														  &updated);
-				if (copyTuple == NULL)
+				if (tuple == NULL)
 					elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
 
 				/*
@@ -2904,23 +2907,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,
+				if (!erm->relation->rd_stamroutine->tuple_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 */
@@ -2930,19 +2928,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 = erm->relation->rd_stamroutine->tuple_from_datum(datum, erm->relid);
+
+			/* store tuple */
+			EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
+
 		}
 	}
 }
@@ -3108,8 +3099,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 c6a66b6..206238a 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -116,7 +116,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
 							 TupleTableSlot *searchslot,
 							 TupleTableSlot *outslot)
 {
-	HeapTuple	scantuple;
+	StorageTuple	scantuple;
 	ScanKeyData skey[INDEX_MAX_KEYS];
 	IndexScanDesc scan;
 	SnapshotData snap;
@@ -169,12 +169,14 @@ retry:
 		HeapUpdateFailureData hufd;
 		HTSU_Result res;
 		HeapTupleData locktup;
+		ItemPointer tid;
 
-		ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+		tid = outslot->tts_storageam->tuple_get_itempointer(outslot->tts_storage);
+		ItemPointerCopy(tid, &locktup.t_self);
 
 		PushActiveSnapshot(GetLatestSnapshot());
 
-		res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+		res = rel->rd_stamroutine->tuple_lock(rel, tid, &locktup, GetCurrentCommandId(false),
 							  lockmode,
 							  LockWaitBlock,
 							  false /* don't follow updates */ ,
@@ -219,7 +221,7 @@ retry:
  * to use.
  */
 static bool
-tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
+tuple_equals_slot(TupleDesc desc, StorageTuple tup, TupleTableSlot *slot)
 {
 	Datum		values[MaxTupleAttributeNumber];
 	bool		isnull[MaxTupleAttributeNumber];
@@ -267,7 +269,7 @@ bool
 RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
 						 TupleTableSlot *searchslot, TupleTableSlot *outslot)
 {
-	HeapTuple	scantuple;
+    StorageTuple            scantuple;
 	HeapScanDesc scan;
 	SnapshotData snap;
 	TransactionId xwait;
@@ -316,12 +318,14 @@ retry:
 		HeapUpdateFailureData hufd;
 		HTSU_Result res;
 		HeapTupleData locktup;
+		ItemPointer tid;
 
-		ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+		tid = outslot->tts_storageam->tuple_get_itempointer(outslot->tts_storage);
+		ItemPointerCopy(tid, &locktup.t_self);
 
 		PushActiveSnapshot(GetLatestSnapshot());
 
-		res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+		res = rel->rd_stamroutine->tuple_lock(rel, tid, &locktup, GetCurrentCommandId(false),
 							  lockmode,
 							  LockWaitBlock,
 							  false /* don't follow updates */ ,
@@ -364,7 +368,7 @@ void
 ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
 {
 	bool		skip_tuple = false;
-	HeapTuple	tuple;
+	StorageTuple	tuple;
 	ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
 	Relation	rel = resultRelInfo->ri_RelationDesc;
 
@@ -392,18 +396,18 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
 			ExecConstraints(resultRelInfo, slot, estate);
 
 		/* Store the slot into tuple that we can inspect. */
-		tuple = ExecMaterializeSlot(slot);
+		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),
+			recheckIndexes = ExecInsertIndexTuples(slot, rel->rd_stamroutine->tuple_get_itempointer(tuple),
 												   estate, false, NULL,
 												   NIL);
 
 		/* AFTER ROW INSERT Triggers */
-		ExecARInsertTriggers(estate, resultRelInfo, tuple,
+		ExecARInsertTriggers(estate, resultRelInfo, slot,
 							 recheckIndexes);
 
 		list_free(recheckIndexes);
@@ -421,9 +425,10 @@ 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_storageam->tuple_get_itempointer(searchslot->tts_storage);
 
 	/* For now we support only tables. */
 	Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -435,7 +440,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
 		resultRelInfo->ri_TrigDesc->trig_update_before_row)
 	{
 		slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
-									&searchslot->tts_tuple->t_self,
+									tid,
 									NULL, slot);
 
 		if (slot == NULL)		/* "do nothing" */
@@ -451,21 +456,19 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
 			ExecConstraints(resultRelInfo, slot, estate);
 
 		/* Store the slot into tuple that we can write. */
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecHeapifySlot(slot);
 
 		/* OK, update the tuple and index entries for it */
-		simple_heap_update(rel, &searchslot->tts_tuple->t_self,
-						   slot->tts_tuple);
+		simple_heap_update(rel, tid, tuple);
 
-		if (resultRelInfo->ri_NumIndices > 0 &&
-			!HeapTupleIsHeapOnly(slot->tts_tuple))
-			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+		if ((resultRelInfo->ri_NumIndices > 0) && rel->rd_stamroutine->tuple_is_heaponly(tuple))
+			recheckIndexes = ExecInsertIndexTuples(slot, rel->rd_stamroutine->tuple_get_itempointer(tuple),
 												   estate, false, NULL,
 												   NIL);
 
 		/* AFTER ROW UPDATE Triggers */
 		ExecARUpdateTriggers(estate, resultRelInfo,
-							 &searchslot->tts_tuple->t_self,
+							 tid,
 							 NULL, tuple, recheckIndexes);
 
 		list_free(recheckIndexes);
@@ -485,6 +488,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
 	bool		skip_tuple = false;
 	ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
 	Relation	rel = resultRelInfo->ri_RelationDesc;
+	ItemPointer tid = searchslot->tts_storageam->tuple_get_itempointer(searchslot->tts_storage);
 
 	/* For now we support only tables. */
 	Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -496,7 +500,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
 		resultRelInfo->ri_TrigDesc->trig_update_before_row)
 	{
 		skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
-										   &searchslot->tts_tuple->t_self,
+											tid,
 										   NULL);
 	}
 
@@ -505,11 +509,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
 		List	   *recheckIndexes = NIL;
 
 		/* OK, delete the tuple */
-		simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
+		simple_heap_delete(rel, tid);
 
 		/* AFTER ROW DELETE Triggers */
 		ExecARDeleteTriggers(estate, resultRelInfo,
-							 &searchslot->tts_tuple->t_self, NULL);
+				tid, NULL);
 
 		list_free(recheckIndexes);
 	}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index a35ba32..e2ab776 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -181,7 +181,7 @@ static void sqlfunction_destroy(DestReceiver *self);
  * polymorphic arguments.
  */
 SQLFunctionParseInfoPtr
-prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+prepare_sql_fn_parse_info(StorageTuple procedureTuple,
 						  Node *call_expr,
 						  Oid inputCollation)
 {
@@ -596,7 +596,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
 	MemoryContext fcontext;
 	MemoryContext oldcontext;
 	Oid			rettype;
-	HeapTuple	procedureTuple;
+	StorageTuple	procedureTuple;
 	Form_pg_proc procedureStruct;
 	SQLFunctionCachePtr fcache;
 	List	   *raw_parsetree_list;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 7eeda95..a412402 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -3084,7 +3084,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 		Oid			inputTypes[FUNC_MAX_ARGS];
 		int			numArguments;
 		int			numDirectArgs;
-		HeapTuple	aggTuple;
+		StorageTuple	aggTuple;
 		Form_pg_aggregate aggform;
 		AclResult	aclresult;
 		Oid			transfn_oid,
@@ -3199,7 +3199,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 
 		/* Check that aggregate owner has permission to call component fns */
 		{
-			HeapTuple	procTuple;
+			StorageTuple	procTuple;
 			Oid			aggOwner;
 
 			procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index c453362..f8a4e7d 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -215,6 +215,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			 * least AccessShareLock on the table before performing any of the
 			 * indexscans, but let's be safe.)
 			 */
+			//hari --> need a fix to change the scan
 			if (tbmres->blockno >= scan->rs_nblocks)
 			{
 				node->tbmres = tbmres = NULL;
@@ -234,6 +235,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			/*
 			 * Set rs_cindex to first slot to examine
 			 */
+			//hari
 			scan->rs_cindex = 0;
 
 			/* Adjust the prefetch target */
@@ -244,6 +246,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			/*
 			 * Continuing in previously obtained page; advance rs_cindex
 			 */
+			//hari
 			scan->rs_cindex++;
 
 #ifdef USE_PREFETCH
@@ -271,6 +274,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 		/*
 		 * Out of range?  If so, nothing more to look at on this page
 		 */
+		//hari
 		if (scan->rs_cindex < 0 || scan->rs_cindex >= scan->rs_ntuples)
 		{
 			node->tbmres = tbmres = NULL;
@@ -289,6 +293,8 @@ BitmapHeapNext(BitmapHeapScanState *node)
 		/*
 		 * Okay to fetch the tuple
 		 */
+		//hari
+#if 1
 		targoffset = scan->rs_vistuples[scan->rs_cindex];
 		dp = (Page) BufferGetPage(scan->rs_cbuf);
 		lp = PageGetItemId(dp, targoffset);
@@ -309,7 +315,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 					   slot,
 					   scan->rs_cbuf,
 					   false);
-
+#endif
 		/*
 		 * If we are using lossy info, we have to recheck the qual conditions
 		 * at every tuple.
@@ -352,7 +358,8 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
 	Buffer		buffer;
 	Snapshot	snapshot;
 	int			ntup;
-
+	//hari
+#if 1
 	/*
 	 * Acquire pin on the target heap page, trading in any pin we held before.
 	 */
@@ -394,11 +401,11 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
 		{
 			OffsetNumber offnum = tbmres->offsets[curslot];
 			ItemPointerData tid;
-			HeapTupleData heapTuple;
+			HeapTupleData StorageTuple;
 
 			ItemPointerSet(&tid, page, offnum);
 			if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
-									   &heapTuple, NULL, true))
+									   &StorageTuple, NULL, true))
 				scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
 		}
 	}
@@ -440,6 +447,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
 
 	Assert(ntup <= MaxHeapTuplesPerPage);
 	scan->rs_ntuples = ntup;
+#endif
 }
 
 /*
@@ -588,6 +596,7 @@ BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
 					break;
 				}
 				node->prefetch_pages++;
+				//hari
 				PrefetchBuffer(scan->rs_rd, MAIN_FORKNUM, tbmpre->blockno);
 			}
 		}
@@ -630,6 +639,7 @@ BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
 					break;
 				}
 
+				//hari
 				PrefetchBuffer(scan->rs_rd, MAIN_FORKNUM, tbmpre->blockno);
 			}
 		}
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 9ae1561..424acac 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -61,11 +61,8 @@ ForeignNext(ForeignScanState *node)
 	 * tableoid, which is the only actually-useful system column.
 	 */
 	if (plan->fsSystemCol && !TupIsNull(slot))
-	{
-		HeapTuple	tup = ExecMaterializeSlot(slot);
-
-		tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
-	}
+		slot->tts_storageam->slot_update_tableoid(slot,
+							RelationGetRelid(node->ss.ss_currentRelation));
 
 	return slot;
 }
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index c1db2e2..cae003b 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -44,7 +44,7 @@
 
 
 static TupleTableSlot *gather_getnext(GatherState *gatherstate);
-static HeapTuple gather_readnext(GatherState *gatherstate);
+static StorageTuple gather_readnext(GatherState *gatherstate);
 static void ExecShutdownGatherWorkers(GatherState *node);
 
 
@@ -243,7 +243,7 @@ gather_getnext(GatherState *gatherstate)
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *fslot = gatherstate->funnel_slot;
 	MemoryContext tupleContext = gatherstate->ps.ps_ExprContext->ecxt_per_tuple_memory;
-	HeapTuple	tup;
+	StorageTuple	tup;
 
 	while (gatherstate->reader != NULL || gatherstate->need_to_scan_locally)
 	{
@@ -284,7 +284,7 @@ gather_getnext(GatherState *gatherstate)
 /*
  * Attempt to read a tuple from one of our parallel workers.
  */
-static HeapTuple
+static StorageTuple
 gather_readnext(GatherState *gatherstate)
 {
 	int			nvisited = 0;
@@ -292,7 +292,7 @@ gather_readnext(GatherState *gatherstate)
 	for (;;)
 	{
 		TupleQueueReader *reader;
-		HeapTuple	tup;
+		StorageTuple	tup;
 		bool		readerdone;
 
 		/* Check for async events, particularly messages from workers. */
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index e066574..1f98939 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -31,7 +31,7 @@
  */
 typedef struct GMReaderTupleBuffer
 {
-	HeapTuple  *tuple;
+	StorageTuple  *tuple;
 	int			readCounter;
 	int			nTuples;
 	bool		done;
@@ -46,7 +46,7 @@ typedef struct GMReaderTupleBuffer
 
 static int32 heap_compare_slots(Datum a, Datum b, void *arg);
 static TupleTableSlot *gather_merge_getnext(GatherMergeState *gm_state);
-static HeapTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
+static StorageTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
 				  bool nowait, bool *done);
 static void gather_merge_init(GatherMergeState *gm_state);
 static void ExecShutdownGatherMergeWorkers(GatherMergeState *node);
@@ -369,7 +369,7 @@ gather_merge_init(GatherMergeState *gm_state)
 	{
 		/* Allocate the tuple array with MAX_TUPLE_STORE size */
 		gm_state->gm_tuple_buffers[i].tuple =
-			(HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE);
+			(StorageTuple *) palloc0(sizeof(StorageTuple) * MAX_TUPLE_STORE);
 
 		/* Initialize slot for worker */
 		gm_state->gm_slots[i] = ExecInitExtraTupleSlot(gm_state->ps.state);
@@ -531,7 +531,7 @@ static bool
 gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
 {
 	GMReaderTupleBuffer *tuple_buffer;
-	HeapTuple	tup = NULL;
+	StorageTuple	tup = NULL;
 
 	/*
 	 * If we're being asked to generate a tuple from the leader, then we just
@@ -606,12 +606,12 @@ gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
 /*
  * Attempt to read a tuple from given reader.
  */
-static HeapTuple
+static StorageTuple
 gm_readnext_tuple(GatherMergeState *gm_state, int nreader, bool nowait,
 				  bool *done)
 {
 	TupleQueueReader *reader;
-	HeapTuple	tup = NULL;
+	StorageTuple	tup = NULL;
 	MemoryContext oldContext;
 	MemoryContext tupleContext;
 
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 5550f6c..a5052de 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -115,7 +115,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 	 */
 	while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
 	{
-		HeapTuple	tuple = NULL;
+		StorageTuple	tuple = NULL;
 
 		/*
 		 * We can skip the heap fetch if the TID references a heap page on
@@ -182,7 +182,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 
 		/*
 		 * Fill the scan tuple slot with data from the index.  This might be
-		 * provided in either HeapTuple or IndexTuple format.  Conceivably an
+		 * provided in either StorageTuple or IndexTuple format.  Conceivably an
 		 * index AM might fill both fields, in which case we prefer the heap
 		 * format, since it's probably a bit cheaper to fill a slot from.
 		 */
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 5afd02e..b936c13 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -49,7 +49,7 @@
 typedef struct
 {
 	pairingheap_node ph_node;
-	HeapTuple	htup;
+	StorageTuple	htup;
 	Datum	   *orderbyvals;
 	bool	   *orderbynulls;
 } ReorderTuple;
@@ -63,9 +63,9 @@ static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
 				IndexScanState *node);
 static int reorderqueue_cmp(const pairingheap_node *a,
 				 const pairingheap_node *b, void *arg);
-static void reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+static void reorderqueue_push(IndexScanState *node, StorageTuple tuple,
 				  Datum *orderbyvals, bool *orderbynulls);
-static HeapTuple reorderqueue_pop(IndexScanState *node);
+static StorageTuple reorderqueue_pop(IndexScanState *node);
 
 
 /* ----------------------------------------------------------------
@@ -82,7 +82,7 @@ IndexNext(IndexScanState *node)
 	ExprContext *econtext;
 	ScanDirection direction;
 	IndexScanDesc scandesc;
-	HeapTuple	tuple;
+	StorageTuple	tuple;
 	TupleTableSlot *slot;
 
 	/*
@@ -181,7 +181,7 @@ IndexNextWithReorder(IndexScanState *node)
 	EState	   *estate;
 	ExprContext *econtext;
 	IndexScanDesc scandesc;
-	HeapTuple	tuple;
+	StorageTuple	tuple;
 	TupleTableSlot *slot;
 	ReorderTuple *topmost = NULL;
 	bool		was_exact;
@@ -475,7 +475,7 @@ reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
  * Helper function to push a tuple to the reorder queue.
  */
 static void
-reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+reorderqueue_push(IndexScanState *node, StorageTuple tuple,
 				  Datum *orderbyvals, bool *orderbynulls)
 {
 	IndexScanDesc scandesc = node->iss_ScanDesc;
@@ -508,10 +508,10 @@ reorderqueue_push(IndexScanState *node, HeapTuple tuple,
 /*
  * Helper function to pop the next tuple from the reorder queue.
  */
-static HeapTuple
+static StorageTuple
 reorderqueue_pop(IndexScanState *node)
 {
-	HeapTuple	result;
+	StorageTuple	result;
 	ReorderTuple *topmost;
 	int			i;
 
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 5630eae..d628a40 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -70,7 +70,7 @@ lnext:
 	{
 		ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
 		ExecRowMark *erm = aerm->rowmark;
-		HeapTuple  *testTuple;
+		StorageTuple  *testTuple;
 		Datum		datum;
 		bool		isNull;
 		HeapTupleData tuple;
@@ -78,10 +78,10 @@ lnext:
 		HeapUpdateFailureData hufd;
 		LockTupleMode lockmode;
 		HTSU_Result test;
-		HeapTuple	copyTuple;
+		StorageTuple	copyTuple;
 
 		/* 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;
@@ -176,7 +176,7 @@ lnext:
 				break;
 		}
 
-		test = heap_lock_tuple(erm->relation, &tuple,
+		test = erm->relation->rd_stamroutine->tuple_lock(erm->relation, &tuple.t_self, &tuple,
 							   estate->es_output_cid,
 							   lockmode, erm->waitPolicy, true,
 							   &buffer, &hufd);
@@ -234,7 +234,7 @@ lnext:
 					goto lnext;
 				}
 				/* remember the actually locked tuple's TID */
-				tuple.t_self = copyTuple->t_self;
+				tuple.t_self = *(erm->relation->rd_stamroutine->tuple_get_itempointer(copyTuple));
 
 				/* Save locked tuple for EvalPlanQual testing below */
 				*testTuple = copyTuple;
@@ -276,7 +276,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 */
@@ -304,14 +304,12 @@ lnext:
 			Assert(ItemPointerIsValid(&(erm->curCtid)));
 
 			/* okay, fetch the tuple */
-			tuple.t_self = erm->curCtid;
-			if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+			if (!erm->relation->rd_stamroutine->tuple_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);
 		}
 
@@ -389,8 +387,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 cf555fe..58827e0 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,6 +38,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/storageamapi.h"
 #include "access/xact.h"
 #include "commands/trigger.h"
 #include "executor/executor.h"
@@ -163,15 +164,14 @@ 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 = ExecMaterializeSlot(econtext->ecxt_scantuple);
-		tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+		econtext->ecxt_scantuple->tts_storageam->slot_update_tableoid(
+								econtext->ecxt_scantuple,
+								RelationGetRelid(resultRelInfo->ri_RelationDesc));
 	}
 	econtext->ecxt_outertuple = planSlot;
 
@@ -189,7 +189,8 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
  */
 static void
 ExecCheckHeapTupleVisible(EState *estate,
-						  HeapTuple tuple,
+						  Relation rel,
+						  StorageTuple tuple,
 						  Buffer buffer)
 {
 	if (!IsolationUsesXactSnapshot())
@@ -208,7 +209,7 @@ ExecCheckHeapTupleVisible(EState *estate,
 		 * 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(rel->rd_stamroutine->tuple_get_xmin(tuple)))
 			ereport(ERROR,
 					(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
 			 errmsg("could not serialize access due to concurrent update")));
@@ -226,16 +227,15 @@ ExecCheckTIDVisible(EState *estate,
 {
 	Relation	rel = relinfo->ri_RelationDesc;
 	Buffer		buffer;
-	HeapTupleData tuple;
+	StorageTuple tuple;
 
 	/* Redundantly check isolation level */
 	if (!IsolationUsesXactSnapshot())
 		return;
 
-	tuple.t_self = *tid;
-	if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
+	if (!rel->rd_stamroutine->tuple_fetch(rel, tid, SnapshotAny, &tuple, &buffer, false, NULL))
 		elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
-	ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+	ExecCheckHeapTupleVisible(estate, rel, tuple, buffer);
 	ReleaseBuffer(buffer);
 }
 
@@ -257,21 +257,16 @@ ExecInsert(ModifyTableState *mtstate,
 		   EState *estate,
 		   bool canSetTag)
 {
-	HeapTuple	tuple;
+	StorageTuple	tuple;
 	ResultRelInfo *resultRelInfo;
 	ResultRelInfo *saved_resultRelInfo = NULL;
 	Relation	resultRelationDesc;
+    StorageAmRoutine *method;
 	Oid			newId;
 	List	   *recheckIndexes = NIL;
 	TupleTableSlot *result = NULL;
 
 	/*
-	 * get the heap tuple out of the tuple table slot, making sure we have a
-	 * writable copy
-	 */
-	tuple = ExecMaterializeSlot(slot);
-
-	/*
 	 * get information on the (current) result relation
 	 */
 	resultRelInfo = estate->es_result_relation_info;
@@ -282,6 +277,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.
@@ -337,21 +334,35 @@ ExecInsert(ModifyTableState *mtstate,
 	}
 
 	resultRelationDesc = resultRelInfo->ri_RelationDesc;
+	method = resultRelationDesc->rd_stamroutine;
 
 	/*
-	 * 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;
+		slot->tts_storageam->slot_set_tuple_oid(slot, InvalidOid);
+	}
 
 	/*
 	 * BEFORE ROW INSERT Triggers.
@@ -369,9 +380,6 @@ ExecInsert(ModifyTableState *mtstate,
 
 		if (slot == NULL)		/* "do nothing" */
 			return NULL;
-
-		/* trigger might have changed tuple */
-		tuple = ExecMaterializeSlot(slot);
 	}
 
 	/* INSTEAD OF ROW INSERT Triggers */
@@ -383,9 +391,6 @@ ExecInsert(ModifyTableState *mtstate,
 		if (slot == NULL)		/* "do nothing" */
 			return NULL;
 
-		/* trigger might have changed tuple */
-		tuple = ExecMaterializeSlot(slot);
-
 		newId = InvalidOid;
 	}
 	else if (resultRelInfo->ri_FdwRoutine)
@@ -401,14 +406,12 @@ ExecInsert(ModifyTableState *mtstate,
 		if (slot == NULL)		/* "do nothing" */
 			return NULL;
 
-		/* FDW might have changed tuple */
-		tuple = ExecMaterializeSlot(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);
+		slot->tts_storageam->slot_update_tableoid(slot, slot->tts_tableOid);
 
 		newId = InvalidOid;
 	}
@@ -418,7 +421,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);
+		slot->tts_storageam->slot_update_tableoid(slot, slot->tts_tableOid);
 
 		/*
 		 * Check any RLS INSERT WITH CHECK policies
@@ -440,6 +444,7 @@ ExecInsert(ModifyTableState *mtstate,
 		{
 			/* Perform a speculative insertion. */
 			uint32		specToken;
+			ItemPointerData newtid;
 			ItemPointerData conflictTid;
 			bool		specConflict;
 
@@ -502,24 +507,24 @@ ExecInsert(ModifyTableState *mtstate,
 			 * waiting for the whole transaction to complete.
 			 */
 			specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
-			HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+			method->tuple_set_speculative_token(slot, specToken);
 
 			/* insert the tuple, with the speculative token */
-			newId = heap_insert(resultRelationDesc, tuple,
-								estate->es_output_cid,
-								HEAP_INSERT_SPECULATIVE,
-								NULL);
+			newId = method->tuple_insert(resultRelationDesc, slot,
+										 estate->es_output_cid,
+										 HEAP_INSERT_SPECULATIVE,
+										 NULL, &newtid);
 
 			/* insert index entries for tuple */
-			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+			recheckIndexes = ExecInsertIndexTuples(slot, &newtid,
 												 estate, true, &specConflict,
 												   arbiterIndexes);
 
 			/* adjust the tuple's state accordingly */
 			if (!specConflict)
-				heap_finish_speculative(resultRelationDesc, tuple);
+				method->speculative_finish(resultRelationDesc, slot);
 			else
-				heap_abort_speculative(resultRelationDesc, tuple);
+				method->speculative_abort(resultRelationDesc, slot);
 
 			/*
 			 * Wake up anyone waiting for our decision.  They will re-check
@@ -545,19 +550,18 @@ ExecInsert(ModifyTableState *mtstate,
 		}
 		else
 		{
+			ItemPointerData		iptr;
+
 			/*
 			 * 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);
+			newId = method->tuple_insert(resultRelationDesc, slot,
+										 estate->es_output_cid,
+										 0, NULL, &iptr);
 
 			/* insert index entries for tuple */
 			if (resultRelInfo->ri_NumIndices > 0)
-				recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+				recheckIndexes = ExecInsertIndexTuples(slot, &iptr,
 													   estate, false, NULL,
 													   arbiterIndexes);
 		}
@@ -567,11 +571,11 @@ ExecInsert(ModifyTableState *mtstate,
 	{
 		(estate->es_processed)++;
 		estate->es_lastoid = newId;
-		setLastTid(&(tuple->t_self));
+		setLastTid(method->tuple_get_itempointer(tuple));
 	}
 
 	/* AFTER ROW INSERT Triggers */
-	ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes);
+	ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes);
 
 	list_free(recheckIndexes);
 
@@ -620,7 +624,7 @@ ExecInsert(ModifyTableState *mtstate,
  */
 static TupleTableSlot *
 ExecDelete(ItemPointer tupleid,
-		   HeapTuple oldtuple,
+		   StorageTuple oldtuple,
 		   TupleTableSlot *planSlot,
 		   EPQState *epqstate,
 		   EState *estate,
@@ -631,12 +635,14 @@ ExecDelete(ItemPointer tupleid,
 	HTSU_Result result;
 	HeapUpdateFailureData hufd;
 	TupleTableSlot *slot = NULL;
+	StorageAmRoutine *method;
 
 	/*
 	 * get information on the (current) result relation
 	 */
 	resultRelInfo = estate->es_result_relation_info;
 	resultRelationDesc = resultRelInfo->ri_RelationDesc;
+	method = resultRelationDesc->rd_stamroutine;
 
 	/* BEFORE ROW DELETE Triggers */
 	if (resultRelInfo->ri_TrigDesc &&
@@ -665,8 +671,6 @@ ExecDelete(ItemPointer tupleid,
 	}
 	else if (resultRelInfo->ri_FdwRoutine)
 	{
-		HeapTuple	tuple;
-
 		/*
 		 * delete from foreign table: let the FDW do it
 		 *
@@ -676,7 +680,8 @@ ExecDelete(ItemPointer tupleid,
 		 */
 		slot = estate->es_trig_tuple_slot;
 		if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
-			ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
+			ExecSetSlotDescriptor(slot,
+								  RelationGetDescr(resultRelationDesc));
 
 		slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
 															   resultRelInfo,
@@ -692,8 +697,10 @@ ExecDelete(ItemPointer tupleid,
 		 */
 		if (slot->tts_isempty)
 			ExecStoreAllNullTuple(slot);
-		tuple = ExecMaterializeSlot(slot);
-		tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
+		ExecMaterializeSlot(slot);
+
+		slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
 	}
 	else
 	{
@@ -707,11 +714,11 @@ ExecDelete(ItemPointer tupleid,
 		 * mode transactions.
 		 */
 ldelete:;
-		result = heap_delete(resultRelationDesc, tupleid,
-							 estate->es_output_cid,
-							 estate->es_crosscheck_snapshot,
-							 true /* wait for commit */ ,
-							 &hufd);
+		result = method->tuple_delete(resultRelationDesc, tupleid,
+									  estate->es_output_cid,
+									  estate->es_crosscheck_snapshot,
+									  true /* wait for commit */ ,
+									  &hufd);
 		switch (result)
 		{
 			case HeapTupleSelfUpdated:
@@ -806,7 +813,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)
@@ -820,20 +827,20 @@ 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 (!method->tuple_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);
+				ExecSetSlotDescriptor(slot,
+									  RelationGetDescr(resultRelationDesc));
+			ExecStoreTuple(deltuple, slot, InvalidBuffer, false);
 		}
 
 		rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -878,16 +885,17 @@ ldelete:;
  */
 static TupleTableSlot *
 ExecUpdate(ItemPointer tupleid,
-		   HeapTuple oldtuple,
+		   StorageTuple oldtuple,
 		   TupleTableSlot *slot,
 		   TupleTableSlot *planSlot,
 		   EPQState *epqstate,
 		   EState *estate,
 		   bool canSetTag)
 {
-	HeapTuple	tuple;
+	StorageTuple	tuple;
 	ResultRelInfo *resultRelInfo;
 	Relation	resultRelationDesc;
+	StorageAmRoutine *method;
 	HTSU_Result result;
 	HeapUpdateFailureData hufd;
 	List	   *recheckIndexes = NIL;
@@ -902,13 +910,14 @@ ExecUpdate(ItemPointer tupleid,
 	 * get the heap tuple out of the tuple table slot, making sure we have a
 	 * writable copy
 	 */
-	tuple = ExecMaterializeSlot(slot);
+	tuple = ExecHeapifySlot(slot);
 
 	/*
 	 * get information on the (current) result relation
 	 */
 	resultRelInfo = estate->es_result_relation_info;
 	resultRelationDesc = resultRelInfo->ri_RelationDesc;
+	method = resultRelationDesc->rd_stamroutine;
 
 	/* BEFORE ROW UPDATE Triggers */
 	if (resultRelInfo->ri_TrigDesc &&
@@ -921,7 +930,7 @@ ExecUpdate(ItemPointer tupleid,
 			return NULL;
 
 		/* trigger might have changed tuple */
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecHeapifySlot(slot);
 	}
 
 	/* INSTEAD OF ROW UPDATE Triggers */
@@ -935,7 +944,7 @@ ExecUpdate(ItemPointer tupleid,
 			return NULL;
 
 		/* trigger might have changed tuple */
-		tuple = ExecMaterializeSlot(slot);
+		tuple = ExecHeapifySlot(slot);
 	}
 	else if (resultRelInfo->ri_FdwRoutine)
 	{
@@ -950,24 +959,25 @@ ExecUpdate(ItemPointer tupleid,
 		if (slot == NULL)		/* "do nothing" */
 			return NULL;
 
-		/* FDW might have changed tuple */
-		tuple = ExecMaterializeSlot(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_storageam->slot_update_tableoid(slot, RelationGetRelid(resultRelationDesc));
+
+		/* FDW might have changed tuple */
+		tuple = ExecHeapifySlot(slot);
 	}
 	else
 	{
 		LockTupleMode lockmode;
+		ItemPointerData newtid;
 
 		/*
 		 * 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
@@ -1003,11 +1013,11 @@ 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 = method->tuple_update(resultRelationDesc, tupleid, slot,
+									  estate->es_output_cid,
+									  estate->es_crosscheck_snapshot,
+									  true /* wait for commit */ ,
+									  &hufd, &lockmode, &newtid);
 		switch (result)
 		{
 			case HeapTupleSelfUpdated:
@@ -1045,6 +1055,7 @@ lreplace:;
 				return NULL;
 
 			case HeapTupleMayBeUpdated:
+				ItemPointerCopy(method->tuple_get_itempointer(tuple), &newtid);
 				break;
 
 			case HeapTupleUpdated:
@@ -1067,7 +1078,7 @@ lreplace:;
 					{
 						*tupleid = hufd.ctid;
 						slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
-						tuple = ExecMaterializeSlot(slot);
+						tuple = ExecHeapifySlot(slot);
 						goto lreplace;
 					}
 				}
@@ -1095,8 +1106,8 @@ lreplace:;
 		 *
 		 * 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),
+		if ((resultRelInfo->ri_NumIndices > 0) && !method->tuple_is_heaponly(tuple))
+			recheckIndexes = ExecInsertIndexTuples(slot, method->tuple_get_itempointer(tuple),
 												   estate, false, NULL, NIL);
 	}
 
@@ -1168,7 +1179,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	 * true anymore.
 	 */
 	tuple.t_self = *conflictTid;
-	test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
+	test = relation->rd_stamroutine->tuple_lock(relation, conflictTid, &tuple, estate->es_output_cid,
 						   lockmode, LockWaitBlock, false, &buffer,
 						   &hufd);
 	switch (test)
@@ -1254,7 +1265,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	 * snapshot.  This is in line with the way UPDATE deals with newer tuple
 	 * versions.
 	 */
-	ExecCheckHeapTupleVisible(estate, &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);
@@ -1416,7 +1427,7 @@ ExecModifyTable(ModifyTableState *node)
 	ItemPointer tupleid = NULL;
 	ItemPointerData tuple_ctid;
 	HeapTupleData oldtupdata;
-	HeapTuple	oldtuple;
+	StorageTuple	oldtuple;
 
 	/*
 	 * This should NOT get called during EvalPlanQual; we should have passed a
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 0247bd2..51fb182 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -29,9 +29,9 @@
 static void InitScanRelation(SampleScanState *node, EState *estate, int eflags);
 static TupleTableSlot *SampleNext(SampleScanState *node);
 static void tablesample_init(SampleScanState *scanstate);
-static HeapTuple tablesample_getnext(SampleScanState *scanstate);
-static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
-				   HeapScanDesc scan);
+static StorageTuple tablesample_getnext(SampleScanState *scanstate);
+static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
+		HeapScanDesc scan); //hari
 
 /* ----------------------------------------------------------------
  *						Scan Support
@@ -47,7 +47,7 @@ static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
 static TupleTableSlot *
 SampleNext(SampleScanState *node)
 {
-	HeapTuple	tuple;
+	StorageTuple	tuple;
 	TupleTableSlot *slot;
 
 	/*
@@ -66,7 +66,8 @@ SampleNext(SampleScanState *node)
 	if (tuple)
 		ExecStoreTuple(tuple,	/* tuple to store */
 					   slot,	/* slot to store in */
-					   node->ss.ss_currentScanDesc->rs_cbuf,	/* tuple's buffer */
+					   //harinode->ss.ss_currentScanDesc->rs_cbuf,	/* tuple's buffer */
+					   InvalidBuffer,
 					   false);	/* don't pfree this pointer */
 	else
 		ExecClearTuple(slot);
@@ -125,7 +126,7 @@ InitScanRelation(SampleScanState *node, EState *estate, int eflags)
 
 	node->ss.ss_currentRelation = currentRelation;
 
-	/* we won't set up the HeapScanDesc till later */
+	/* we won't set up the StorageScanDesc till later */
 	node->ss.ss_currentScanDesc = NULL;
 
 	/* and report the scan tuple slot's rowtype */
@@ -342,7 +343,7 @@ tablesample_init(SampleScanState *scanstate)
 	/* We'll use syncscan if there's no NextSampleBlock function */
 	allow_sync = (tsm->NextSampleBlock == NULL);
 
-	/* Now we can create or reset the HeapScanDesc */
+	/* Now we can create or reset the StorageScanDesc */
 	if (scanstate->ss.ss_currentScanDesc == NULL)
 	{
 		scanstate->ss.ss_currentScanDesc =
@@ -373,12 +374,12 @@ tablesample_init(SampleScanState *scanstate)
  * Note: an awful lot of this is copied-and-pasted from heapam.c.  It would
  * perhaps be better to refactor to share more code.
  */
-static HeapTuple
+static StorageTuple
 tablesample_getnext(SampleScanState *scanstate)
 {
 	TsmRoutine *tsm = scanstate->tsmroutine;
-	HeapScanDesc scan = scanstate->ss.ss_currentScanDesc;
-	HeapTuple	tuple = &(scan->rs_ctup);
+	HeapScanDesc scan = scanstate->ss.ss_currentScanDesc; //hari
+	HeapTuple	tuple = &(scan->rs_ctup); //hari
 	Snapshot	snapshot = scan->rs_snapshot;
 	bool		pagemode = scan->rs_pageatatime;
 	BlockNumber blockno;
@@ -409,7 +410,7 @@ tablesample_getnext(SampleScanState *scanstate)
 		else
 			blockno = scan->rs_startblock;
 		Assert(blockno < scan->rs_nblocks);
-		heapgetpage(scan, blockno);
+		//heapgetpage(scan, blockno); //hari
 		scan->rs_inited = true;
 	}
 	else
@@ -530,7 +531,7 @@ tablesample_getnext(SampleScanState *scanstate)
 		}
 
 		Assert(blockno < scan->rs_nblocks);
-		heapgetpage(scan, blockno);
+		//heapgetpage(scan, blockno); //hari
 
 		/* Re-establish state for new page */
 		if (!pagemode)
@@ -551,7 +552,7 @@ tablesample_getnext(SampleScanState *scanstate)
  * Check visibility of the tuple.
  */
 static bool
-SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
 {
 	if (scan->rs_pageatatime)
 	{
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 5680464..9cba6b3 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -48,8 +48,8 @@ static TupleTableSlot *SeqNext(SeqScanState *node);
 static TupleTableSlot *
 SeqNext(SeqScanState *node)
 {
-	HeapTuple	tuple;
-	HeapScanDesc scandesc;
+	void	   *tuple;
+	StorageScanDesc scandesc;
 	EState	   *estate;
 	ScanDirection direction;
 	TupleTableSlot *slot;
@@ -90,8 +90,9 @@ SeqNext(SeqScanState *node)
 	if (tuple)
 		ExecStoreTuple(tuple,	/* tuple to store */
 					   slot,	/* slot to store in */
-					   scandesc->rs_cbuf,		/* buffer associated with this
-												 * tuple */
+					   //hariscandesc->rs_cbuf,		/* buffer associated with this
+						//						 * tuple */
+					   InvalidBuffer,
 					   false);	/* don't pfree this pointer */
 	else
 		ExecClearTuple(slot);
@@ -221,7 +222,7 @@ void
 ExecEndSeqScan(SeqScanState *node)
 {
 	Relation	relation;
-	HeapScanDesc scanDesc;
+	StorageScanDesc scanDesc;
 
 	/*
 	 * get information from node
@@ -266,7 +267,7 @@ ExecEndSeqScan(SeqScanState *node)
 void
 ExecReScanSeqScan(SeqScanState *node)
 {
-	HeapScanDesc scan;
+	StorageScanDesc scan;
 
 	scan = node->ss.ss_currentScanDesc;
 
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 4860ec0..a97993c 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -305,7 +305,7 @@ TidNext(TidScanState *node)
 	ScanDirection direction;
 	Snapshot	snapshot;
 	Relation	heapRelation;
-	HeapTuple	tuple;
+	StorageTuple	tuple;
 	TupleTableSlot *slot;
 	Buffer		buffer = InvalidBuffer;
 	ItemPointerData *tidList;
@@ -331,12 +331,6 @@ TidNext(TidScanState *node)
 	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.
 	 */
 	bBackward = ScanDirectionIsBackward(direction);
@@ -363,7 +357,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
@@ -371,9 +365,9 @@ TidNext(TidScanState *node)
 		 * current according to our snapshot.
 		 */
 		if (node->tss_isCurrentOf)
-			heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+			heap_get_latest_tid(heapRelation, snapshot, &tid);
 
-		if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
+		if (heapRelation->rd_stamroutine->tuple_fetch(heapRelation, &tid, snapshot, &tuple, &buffer, false, NULL))
 		{
 			/*
 			 * store the scanned tuple in the scan tuple slot of the scan
@@ -385,7 +379,7 @@ TidNext(TidScanState *node)
 			ExecStoreTuple(tuple,		/* tuple to store */
 						   slot,	/* slot to store in */
 						   buffer,		/* buffer associated with tuple  */
-						   false);		/* don't pfree */
+						   true);		/* don't pfree */
 
 			/*
 			 * At this point we have an extra pin on the buffer, because
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 628bc9f..d1d73bf 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -2087,7 +2087,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
 {
 	Oid			inputTypes[FUNC_MAX_ARGS];
 	int			numArguments;
-	HeapTuple	aggTuple;
+	StorageTuple	aggTuple;
 	Form_pg_aggregate aggform;
 	Oid			aggtranstype;
 	AttrNumber	initvalAttNo;
@@ -2155,7 +2155,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
 
 	/* Check that aggregate owner has permission to call component fns */
 	{
-		HeapTuple	procTuple;
+		StorageTuple	procTuple;
 		Oid			aggOwner;
 
 		procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 97c3925..996f129 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -627,11 +627,11 @@ SPI_freeplan(SPIPlanPtr plan)
 	return 0;
 }
 
-HeapTuple
-SPI_copytuple(HeapTuple tuple)
+StorageTuple
+SPI_copytuple(StorageTuple tuple)
 {
 	MemoryContext oldcxt;
-	HeapTuple	ctuple;
+	StorageTuple	ctuple;
 
 	if (tuple == NULL)
 	{
@@ -655,7 +655,7 @@ SPI_copytuple(HeapTuple tuple)
 }
 
 HeapTupleHeader
-SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
+SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc)
 {
 	MemoryContext oldcxt;
 	HeapTupleHeader dtup;
@@ -686,7 +686,7 @@ SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
 	return dtup;
 }
 
-HeapTuple
+StorageTuple
 SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
 				Datum *Values, const char *Nulls)
 {
@@ -852,7 +852,7 @@ char *
 SPI_gettype(TupleDesc tupdesc, int fnumber)
 {
 	Oid			typoid;
-	HeapTuple	typeTuple;
+	StorageTuple	typeTuple;
 	char	   *result;
 
 	SPI_result = 0;
@@ -960,7 +960,7 @@ SPI_datumTransfer(Datum value, bool typByVal, int typLen)
 }
 
 void
-SPI_freetuple(HeapTuple tuple)
+SPI_freetuple(StorageTuple tuple)
 {
 	/* No longer need to worry which context tuple was in... */
 	heap_freetuple(tuple);
@@ -1681,7 +1681,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 
 	/* set up initial allocations */
 	tuptable->alloced = tuptable->free = 128;
-	tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
+	tuptable->vals = (StorageTuple *) palloc(tuptable->alloced * sizeof(StorageTuple));
 	tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
 
 	MemoryContextSwitchTo(oldcxt);
@@ -1712,8 +1712,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
 		/* Double the size of the pointer array */
 		tuptable->free = tuptable->alloced;
 		tuptable->alloced += tuptable->free;
-		tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
-									  tuptable->alloced * sizeof(HeapTuple));
+		tuptable->vals = (StorageTuple *) repalloc_huge(tuptable->vals,
+									  tuptable->alloced * sizeof(StorageTuple));
 	}
 
 	tuptable->vals[tuptable->alloced - tuptable->free] =
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 8d7e711..16adf26 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -192,12 +192,12 @@ static void TQSendRecordInfo(TQueueDestReceiver *tqueue, int32 typmod,
 				 TupleDesc tupledesc);
 static void TupleQueueHandleControlMessage(TupleQueueReader *reader,
 							   Size nbytes, char *data);
-static HeapTuple TupleQueueHandleDataMessage(TupleQueueReader *reader,
+static StorageTuple TupleQueueHandleDataMessage(TupleQueueReader *reader,
 							Size nbytes, HeapTupleHeader data);
-static HeapTuple TQRemapTuple(TupleQueueReader *reader,
+static StorageTuple TQRemapTuple(TupleQueueReader *reader,
 			 TupleDesc tupledesc,
 			 TupleRemapInfo **field_remapinfo,
-			 HeapTuple tuple);
+			 StorageTuple tuple);
 static Datum TQRemap(TupleQueueReader *reader, TupleRemapInfo *remapinfo,
 		Datum value, bool *changed);
 static Datum TQRemapArray(TupleQueueReader *reader, ArrayRemapInfo *remapinfo,
@@ -225,7 +225,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
 {
 	TQueueDestReceiver *tqueue = (TQueueDestReceiver *) self;
 	TupleDesc	tupledesc = slot->tts_tupleDescriptor;
-	HeapTuple	tuple;
+	HeapTuple	tuple; //hari
 	shm_mq_result result;
 
 	/*
@@ -305,7 +305,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
 	}
 
 	/* Send the tuple itself. */
-	tuple = ExecMaterializeSlot(slot);
+	tuple = ExecHeapifySlot(slot);
 	result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false);
 
 	/* Check for failure. */
@@ -675,7 +675,7 @@ DestroyTupleQueueReader(TupleQueueReader *reader)
  * accumulate bytes from a partially-read message, so it's useful to call
  * this with nowait = true even if nothing is returned.
  */
-HeapTuple
+StorageTuple
 TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
 {
 	shm_mq_result result;
@@ -730,7 +730,7 @@ TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
 /*
  * Handle a data message - that is, a tuple - from the remote side.
  */
-static HeapTuple
+static StorageTuple
 TupleQueueHandleDataMessage(TupleQueueReader *reader,
 							Size nbytes,
 							HeapTupleHeader data)
@@ -759,11 +759,11 @@ TupleQueueHandleDataMessage(TupleQueueReader *reader,
 /*
  * Copy the given tuple, remapping any transient typmods contained in it.
  */
-static HeapTuple
+static StorageTuple
 TQRemapTuple(TupleQueueReader *reader,
 			 TupleDesc tupledesc,
 			 TupleRemapInfo **field_remapinfo,
-			 HeapTuple tuple)
+			 StorageTuple tuple)
 {
 	Datum	   *values;
 	bool	   *isnull;
@@ -1011,7 +1011,7 @@ TQRemapRecord(TupleQueueReader *reader, RecordRemapInfo *remapinfo,
 	if (changed_typmod || remapinfo->field_remap != NULL)
 	{
 		HeapTupleData htup;
-		HeapTuple	atup;
+		HeapTuple	atup; //hari
 
 		/* For now, assume we always need to change the tuple in this case. */
 		*changed = true;
@@ -1121,7 +1121,7 @@ TupleQueueHandleControlMessage(TupleQueueReader *reader, Size nbytes,
 static TupleRemapInfo *
 BuildTupleRemapInfo(Oid typid, MemoryContext mycontext)
 {
-	HeapTuple	tup;
+	StorageTuple	tup;
 	Form_pg_type typ;
 
 	/* This is recursive, so it could be driven to stack overflow. */
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index a8ab945..1802a97 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -665,7 +665,7 @@ struct MinimalTupleData
 /*
  * GETSTRUCT - given a HeapTuple pointer, return address of the user data
  */
-#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
+#define GETSTRUCT(TUP) ((char *) (((HeapTuple)(TUP))->t_data) + ((HeapTuple)(TUP))->t_data->t_hoff)
 
 /*
  * Accessor macros to be used with HeapTuple pointers.
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index f4d4f1e..62013dc 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -18,6 +18,7 @@
 #include "access/heapam.h"
 #include "access/htup_details.h"
 #include "access/itup.h"
+#include "access/storageamapi.h"
 #include "access/tupdesc.h"
 #include "storage/spin.h"
 
@@ -75,7 +76,7 @@ typedef struct HeapScanDescData
 	int			rs_cindex;		/* current tuple's index in vistuples */
 	int			rs_ntuples;		/* number of visible tuples on page */
 	OffsetNumber rs_vistuples[MaxHeapTuplesPerPage];	/* their offsets */
-}	HeapScanDescData;
+} HeapScanDescData;
 
 /*
  * We use the same IndexScanDescData structure for both amgettuple-based
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index d73969c..5e79ed0 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -138,7 +138,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
 					 TupleTableSlot *slot);
 extern void ExecARInsertTriggers(EState *estate,
 					 ResultRelInfo *relinfo,
-					 HeapTuple trigtuple,
+					 TupleTableSlot *slot,
 					 List *recheckIndexes);
 extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
 					 ResultRelInfo *relinfo,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 8cc5f3a..0333df2 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -15,6 +15,7 @@
 #define EXECUTOR_H
 
 #include "catalog/partition.h"
+#include "access/storageamapi.h"
 #include "executor/execdesc.h"
 #include "nodes/parsenodes.h"
 
@@ -196,7 +197,7 @@ 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,
+extern StorageTuple EvalPlanQualFetch(EState *estate, Relation relation,
 				  int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
 				  TransactionId priorXmax);
 extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
@@ -204,8 +205,8 @@ extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
 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);
 extern void ExecSetupPartitionTupleRouting(Relation rel,
 							   PartitionDispatch **pd,
 							   ResultRelInfo **partitions,
diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h
index 7821a63..fe0ce3f 100644
--- a/src/include/executor/functions.h
+++ b/src/include/executor/functions.h
@@ -22,7 +22,7 @@ typedef struct SQLFunctionParseInfo *SQLFunctionParseInfoPtr;
 
 extern Datum fmgr_sql(PG_FUNCTION_ARGS);
 
-extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(StorageTuple procedureTuple,
 						  Node *call_expr,
 						  Oid inputCollation);
 
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index ffb4c28..4031344 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -25,7 +25,7 @@ typedef struct SPITupleTable
 	uint64		alloced;		/* # of alloced vals */
 	uint64		free;			/* # of free vals */
 	TupleDesc	tupdesc;		/* tuple descriptor */
-	HeapTuple  *vals;			/* tuples */
+	StorageTuple  *vals;			/* tuples */
 	slist_node	next;			/* link for internal bookkeeping */
 	SubTransactionId subid;		/* subxact in which tuptable was created */
 } SPITupleTable;
@@ -117,9 +117,9 @@ extern const char *SPI_result_code_string(int code);
 extern List *SPI_plan_get_plan_sources(SPIPlanPtr plan);
 extern CachedPlan *SPI_plan_get_cached_plan(SPIPlanPtr plan);
 
-extern HeapTuple SPI_copytuple(HeapTuple tuple);
-extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
-extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
+extern StorageTuple SPI_copytuple(StorageTuple tuple);
+extern HeapTupleHeader SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc);
+extern StorageTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
 				int *attnum, Datum *Values, const char *Nulls);
 extern int	SPI_fnumber(TupleDesc tupdesc, const char *fname);
 extern char *SPI_fname(TupleDesc tupdesc, int fnumber);
@@ -133,7 +133,7 @@ extern void *SPI_palloc(Size size);
 extern void *SPI_repalloc(void *pointer, Size size);
 extern void SPI_pfree(void *pointer);
 extern Datum SPI_datumTransfer(Datum value, bool typByVal, int typLen);
-extern void SPI_freetuple(HeapTuple pointer);
+extern void SPI_freetuple(StorageTuple pointer);
 extern void SPI_freetuptable(SPITupleTable *tuptable);
 
 extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
diff --git a/src/include/executor/tqueue.h b/src/include/executor/tqueue.h
index 892eec8..03282a9 100644
--- a/src/include/executor/tqueue.h
+++ b/src/include/executor/tqueue.h
@@ -27,7 +27,7 @@ extern DestReceiver *CreateTupleQueueDestReceiver(shm_mq_handle *handle);
 extern TupleQueueReader *CreateTupleQueueReader(shm_mq_handle *handle,
 					   TupleDesc tupledesc);
 extern void DestroyTupleQueueReader(TupleQueueReader *reader);
-extern HeapTuple TupleQueueReaderNext(TupleQueueReader *reader,
+extern StorageTuple TupleQueueReaderNext(TupleQueueReader *reader,
 					 bool nowait, bool *done);
 
 #endif   /* TQUEUE_H */
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 7521739..546d5e3 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -164,12 +164,12 @@ extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
 extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
 extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
 extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
-extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
+extern StorageTuple ExecCopySlotTuple(TupleTableSlot *slot);
 extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
+extern StorageTuple ExecFetchSlotTuple(TupleTableSlot *slot);
 extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
 extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern void ExecMaterializeSlot(TupleTableSlot *slot);
 extern StorageTuple ExecHeapifySlot(TupleTableSlot *slot);
 extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
 			 TupleTableSlot *srcslot);
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 30e66b6..b1593b0 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -229,7 +229,7 @@ extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
 /* from execTuples.c */
 extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
 extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
-extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
+extern StorageTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
 extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
 extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index d33392f..b8b2d6a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -480,7 +480,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 */
 
@@ -1967,7 +1967,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.7.4.windows.1

