From b98a8b002a0fddbbd665a55abcc820273a88b0f3 Mon Sep 17 00:00:00 2001
From: Hari Babu Kommi <kommih@localhost.localdomain>
Date: Fri, 14 Jul 2017 22:13:09 +1000
Subject: [PATCH] WIP tuple replace with slot

---
 contrib/dblink/dblink.c                  |  38 ++--
 contrib/lo/lo.c                          |  24 +--
 contrib/spi/autoinc.c                    |  16 +-
 contrib/spi/insert_username.c            |  10 +-
 contrib/spi/moddatetime.c                |   9 +-
 contrib/spi/refint.c                     |  40 ++--
 contrib/spi/timetravel.c                 |  40 ++--
 contrib/tablefunc/tablefunc.c            |  34 ++--
 contrib/tcn/tcn.c                        |   6 +-
 contrib/xml2/xpath.c                     |   8 +-
 src/backend/bootstrap/bootstrap.c        |   4 +-
 src/backend/catalog/aclchk.c             |  12 +-
 src/backend/catalog/index.c              |   4 +-
 src/backend/catalog/pg_conversion.c      |   2 +-
 src/backend/catalog/pg_db_role_setting.c |   2 +-
 src/backend/catalog/pg_publication.c     |   2 +-
 src/backend/catalog/pg_subscription.c    |   2 +-
 src/backend/commands/constraint.c        |  14 +-
 src/backend/commands/copy.c              |  27 ++-
 src/backend/commands/dbcommands.c        |   1 +
 src/backend/commands/indexcmds.c         |   2 +-
 src/backend/commands/matview.c           |   2 +-
 src/backend/commands/tablecmds.c         |  14 +-
 src/backend/commands/trigger.c           | 334 +++++++++++++++++--------------
 src/backend/executor/execReplication.c   |   7 +-
 src/backend/executor/execTuples.c        |  60 ++++++
 src/backend/executor/nodeModifyTable.c   |  57 ++++--
 src/backend/executor/spi.c               |  92 +++++++--
 src/backend/replication/logical/worker.c |   1 +
 src/backend/utils/adt/ri_triggers.c      | 230 ++++++++++-----------
 src/backend/utils/adt/ruleutils.c        |  50 +++--
 src/backend/utils/adt/trigfuncs.c        |   4 +-
 src/backend/utils/adt/tsquery_rewrite.c  |   4 +-
 src/backend/utils/adt/tsvector_op.c      |  26 ++-
 src/backend/utils/adt/xml.c              |   2 -
 src/include/access/htup.h                |   1 +
 src/include/commands/trigger.h           |  26 ++-
 src/include/executor/spi.h               |   8 +-
 src/include/executor/tuptable.h          |   6 +
 src/pl/plperl/plperl.c                   |  20 +-
 src/pl/plpgsql/src/pl_comp.c             |   2 +-
 src/pl/plpgsql/src/pl_exec.c             | 102 ++++------
 src/pl/plpgsql/src/plpgsql.h             |   2 +-
 src/pl/plpython/plpy_exec.c              |  14 +-
 src/pl/tcl/pltcl.c                       |  10 +-
 src/test/modules/worker_spi/worker_spi.c |   2 -
 src/test/regress/regress.c               |  55 +++--
 47 files changed, 779 insertions(+), 649 deletions(-)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 81136b1..cad2039 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -107,7 +107,7 @@ static char *get_sql_delete(Relation rel, int *pkattnums, int pknumatts, char **
 static char *get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals);
 static char *quote_ident_cstr(char *rawstr);
 static int	get_attnum_pk_pos(int *pkattnums, int pknumatts, int key);
-static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals);
+static TupleTableSlot* get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals);
 static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode);
 static char *generate_relation_name(Relation rel);
 static void dblink_connstr_check(const char *connstr);
@@ -2144,7 +2144,7 @@ static char *
 get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals)
 {
 	char	   *relname;
-	HeapTuple	tuple;
+	TupleTableSlot *slot;
 	TupleDesc	tupdesc;
 	int			natts;
 	StringInfoData buf;
@@ -2158,15 +2158,15 @@ get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals
 	/* get relation name including any needed schema prefix and quoting */
 	relname = generate_relation_name(rel);
 
-	tupdesc = rel->rd_att;
-	natts = tupdesc->natts;
-
-	tuple = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals);
-	if (!tuple)
+	slot = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals);
+	if (!slot)
 		ereport(ERROR,
 				(errcode(ERRCODE_CARDINALITY_VIOLATION),
 				 errmsg("source row not found")));
 
+	tupdesc = slot->tts_tupleDescriptor;
+	natts = tupdesc->natts;
+
 	appendStringInfo(&buf, "INSERT INTO %s(", relname);
 
 	needComma = false;
@@ -2202,7 +2202,7 @@ get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals
 		if (key >= 0)
 			val = tgt_pkattvals[key] ? pstrdup(tgt_pkattvals[key]) : NULL;
 		else
-			val = SPI_getvalue(tuple, tupdesc, i + 1);
+			val = SPI_getvalue(slot, i + 1);
 
 		if (val != NULL)
 		{
@@ -2258,7 +2258,7 @@ static char *
 get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals)
 {
 	char	   *relname;
-	HeapTuple	tuple;
+	TupleTableSlot *slot;
 	TupleDesc	tupdesc;
 	int			natts;
 	StringInfoData buf;
@@ -2272,15 +2272,15 @@ get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals
 	/* get relation name including any needed schema prefix and quoting */
 	relname = generate_relation_name(rel);
 
-	tupdesc = rel->rd_att;
-	natts = tupdesc->natts;
-
-	tuple = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals);
-	if (!tuple)
+	slot = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals);
+	if (!slot)
 		ereport(ERROR,
 				(errcode(ERRCODE_CARDINALITY_VIOLATION),
 				 errmsg("source row not found")));
 
+	tupdesc = slot->tts_tupleDescriptor;
+	natts = tupdesc->natts;
+
 	appendStringInfo(&buf, "UPDATE %s SET ", relname);
 
 	/*
@@ -2303,7 +2303,7 @@ get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals
 		if (key >= 0)
 			val = tgt_pkattvals[key] ? pstrdup(tgt_pkattvals[key]) : NULL;
 		else
-			val = SPI_getvalue(tuple, tupdesc, i + 1);
+			val = SPI_getvalue(slot, i + 1);
 
 		if (val != NULL)
 		{
@@ -2372,7 +2372,7 @@ get_attnum_pk_pos(int *pkattnums, int pknumatts, int key)
 	return -1;
 }
 
-static HeapTuple
+static TupleTableSlot *
 get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals)
 {
 	char	   *relname;
@@ -2380,7 +2380,7 @@ get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pk
 	int			natts;
 	StringInfoData buf;
 	int			ret;
-	HeapTuple	tuple;
+	TupleTableSlot *slot;
 	int			i;
 
 	/*
@@ -2456,10 +2456,10 @@ get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pk
 	{
 		SPITupleTable *tuptable = SPI_tuptable;
 
-		tuple = SPI_copytuple(tuptable->vals[0]);
+		slot = tuptable->vals[0];
 		SPI_finish();
 
-		return tuple;
+		return slot;
 	}
 	else
 	{
diff --git a/contrib/lo/lo.c b/contrib/lo/lo.c
index 4585923..432bedb 100644
--- a/contrib/lo/lo.c
+++ b/contrib/lo/lo.c
@@ -27,10 +27,10 @@ lo_manage(PG_FUNCTION_ARGS)
 	int			attnum;			/* attribute number to monitor	*/
 	char	  **args;			/* Args containing attr name	*/
 	TupleDesc	tupdesc;		/* Tuple Descriptor				*/
-	HeapTuple	rettuple;		/* Tuple to be returned			*/
+	TupleTableSlot*	retslot;		/* Tuple to be returned			*/
 	bool		isdelete;		/* are we deleting?				*/
-	HeapTuple	newtuple;		/* The new value for tuple		*/
-	HeapTuple	trigtuple;		/* The original value of tuple	*/
+	TupleTableSlot*	newslot;		/* The new value for tuple		*/
+	TupleTableSlot*	trigslot;		/* The original value of tuple	*/
 
 	if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */
 		elog(ERROR, "%s: not fired by trigger manager",
@@ -43,8 +43,8 @@ lo_manage(PG_FUNCTION_ARGS)
 	/*
 	 * Fetch some values from trigdata
 	 */
-	newtuple = trigdata->tg_newtuple;
-	trigtuple = trigdata->tg_trigtuple;
+	newslot = trigdata->tg_newslot;
+	trigslot = trigdata->tg_trigslot;
 	tupdesc = trigdata->tg_relation->rd_att;
 	args = trigdata->tg_trigger->tgargs;
 
@@ -54,9 +54,9 @@ lo_manage(PG_FUNCTION_ARGS)
 
 	/* tuple to return to Executor */
 	if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-		rettuple = newtuple;
+		retslot = newslot;
 	else
-		rettuple = trigtuple;
+		retslot = trigslot;
 
 	/* Are we deleting the row? */
 	isdelete = TRIGGER_FIRED_BY_DELETE(trigdata->tg_event);
@@ -74,10 +74,10 @@ lo_manage(PG_FUNCTION_ARGS)
 	 * Here, if the value of the monitored attribute changes, then the large
 	 * object associated with the original value is unlinked.
 	 */
-	if (newtuple != NULL)
+	if (newslot != NULL)
 	{
-		char	   *orig = SPI_getvalue(trigtuple, tupdesc, attnum);
-		char	   *newv = SPI_getvalue(newtuple, tupdesc, attnum);
+		char	   *orig = SPI_getvalue(trigslot, attnum);
+		char	   *newv = SPI_getvalue(newslot, attnum);
 
 		if (orig != NULL && (newv == NULL || strcmp(orig, newv) != 0))
 			DirectFunctionCall1(be_lo_unlink,
@@ -96,7 +96,7 @@ lo_manage(PG_FUNCTION_ARGS)
 	 */
 	if (isdelete)
 	{
-		char	   *orig = SPI_getvalue(trigtuple, tupdesc, attnum);
+		char	   *orig = SPI_getvalue(trigslot, attnum);
 
 		if (orig != NULL)
 		{
@@ -107,5 +107,5 @@ lo_manage(PG_FUNCTION_ARGS)
 		}
 	}
 
-	return PointerGetDatum(rettuple);
+	return PointerGetDatum(retslot->tts_storageam->slot_get_tuple(retslot));
 }
diff --git a/contrib/spi/autoinc.c b/contrib/spi/autoinc.c
index 8bf7422..b4a207e 100644
--- a/contrib/spi/autoinc.c
+++ b/contrib/spi/autoinc.c
@@ -28,7 +28,7 @@ autoinc(PG_FUNCTION_ARGS)
 	char	  **args;			/* arguments */
 	char	   *relname;		/* triggered relation name */
 	Relation	rel;			/* triggered relation */
-	HeapTuple	rettuple = NULL;
+	TupleTableSlot *retslot = NULL;
 	TupleDesc	tupdesc;		/* tuple description */
 	bool		isnull;
 	int			i;
@@ -44,9 +44,9 @@ autoinc(PG_FUNCTION_ARGS)
 		elog(ERROR, "must be fired before event");
 
 	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
-		rettuple = trigdata->tg_trigtuple;
+		retslot = trigdata->tg_trigslot;
 	else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-		rettuple = trigdata->tg_newtuple;
+		retslot = trigdata->tg_newslot;
 	else
 		/* internal error */
 		elog(ERROR, "cannot process DELETE events");
@@ -86,7 +86,7 @@ autoinc(PG_FUNCTION_ARGS)
 					 errmsg("attribute \"%s\" of \"%s\" must be type INT4",
 							args[i], relname)));
 
-		val = DatumGetInt32(SPI_getbinval(rettuple, tupdesc, attnum, &isnull));
+		val = DatumGetInt32(SPI_getbinval(retslot, attnum, &isnull));
 
 		if (!isnull && val != 0)
 		{
@@ -113,9 +113,9 @@ autoinc(PG_FUNCTION_ARGS)
 
 	if (chnattrs > 0)
 	{
-		rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc,
-											 chnattrs, chattrs,
-											 newvals, newnulls);
+		retslot = heap_modify_slot_by_cols(retslot,
+											chnattrs, chattrs,
+											newvals, newnulls);
 	}
 
 	pfree(relname);
@@ -123,5 +123,5 @@ autoinc(PG_FUNCTION_ARGS)
 	pfree(newvals);
 	pfree(newnulls);
 
-	return PointerGetDatum(rettuple);
+	return PointerGetDatum(retslot->tts_tuple);
 }
diff --git a/contrib/spi/insert_username.c b/contrib/spi/insert_username.c
index a2e1747..f3093ed 100644
--- a/contrib/spi/insert_username.c
+++ b/contrib/spi/insert_username.c
@@ -29,7 +29,7 @@ insert_username(PG_FUNCTION_ARGS)
 	char	  **args;			/* arguments */
 	char	   *relname;		/* triggered relation name */
 	Relation	rel;			/* triggered relation */
-	HeapTuple	rettuple = NULL;
+	TupleTableSlot *retslot;
 	TupleDesc	tupdesc;		/* tuple description */
 	int			attnum;
 
@@ -45,9 +45,9 @@ insert_username(PG_FUNCTION_ARGS)
 		elog(ERROR, "insert_username: must be fired before event");
 
 	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
-		rettuple = trigdata->tg_trigtuple;
+		retslot = trigdata->tg_trigslot;
 	else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-		rettuple = trigdata->tg_newtuple;
+		retslot = trigdata->tg_newslot;
 	else
 		/* internal error */
 		elog(ERROR, "insert_username: cannot process DELETE events");
@@ -83,10 +83,10 @@ insert_username(PG_FUNCTION_ARGS)
 	newnull = false;
 
 	/* construct new tuple */
-	rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc,
+	retslot = heap_modify_slot_by_cols(retslot,
 										 1, &attnum, &newval, &newnull);
 
 	pfree(relname);
 
-	return PointerGetDatum(rettuple);
+	return PointerGetDatum(retslot->tts_tuple);
 }
diff --git a/contrib/spi/moddatetime.c b/contrib/spi/moddatetime.c
index 70476f7..2ab74a7 100644
--- a/contrib/spi/moddatetime.c
+++ b/contrib/spi/moddatetime.c
@@ -39,7 +39,7 @@ moddatetime(PG_FUNCTION_ARGS)
 	char	  **args;			/* arguments */
 	char	   *relname;		/* triggered relation name */
 	Relation	rel;			/* triggered relation */
-	HeapTuple	rettuple = NULL;
+	TupleTableSlot *slot;
 	TupleDesc	tupdesc;		/* tuple description */
 
 	if (!CALLED_AS_TRIGGER(fcinfo))
@@ -58,7 +58,7 @@ moddatetime(PG_FUNCTION_ARGS)
 		/* internal error */
 		elog(ERROR, "moddatetime: cannot process INSERT events");
 	else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-		rettuple = trigdata->tg_newtuple;
+		slot = trigdata->tg_newslot;
 	else
 		/* internal error */
 		elog(ERROR, "moddatetime: cannot process DELETE events");
@@ -120,11 +120,10 @@ moddatetime(PG_FUNCTION_ARGS)
 	newdtnull = false;
 
 	/* Replace the attnum'th column with newdt */
-	rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc,
-										 1, &attnum, &newdt, &newdtnull);
+	slot = heap_modify_slot_by_cols(slot, 1, &attnum, &newdt, &newdtnull);
 
 	/* Clean up */
 	pfree(relname);
 
-	return PointerGetDatum(rettuple);
+	return PointerGetDatum(slot->tts_tuple);
 }
diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c
index 46205c7..0d87f59 100644
--- a/contrib/spi/refint.c
+++ b/contrib/spi/refint.c
@@ -53,7 +53,7 @@ check_primary_key(PG_FUNCTION_ARGS)
 	Datum	   *kvals;			/* key values */
 	char	   *relname;		/* referenced relation name */
 	Relation	rel;			/* triggered relation */
-	HeapTuple	tuple = NULL;	/* tuple to return */
+	TupleTableSlot *slot = NULL; /* tuple to return */
 	TupleDesc	tupdesc;		/* tuple description */
 	EPlan	   *plan;			/* prepared plan */
 	Oid		   *argtypes = NULL;	/* key types to prepare execution plan */
@@ -82,7 +82,7 @@ check_primary_key(PG_FUNCTION_ARGS)
 
 	/* If INSERTion then must check Tuple to being inserted */
 	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
-		tuple = trigdata->tg_trigtuple;
+		slot = trigdata->tg_trigslot;
 
 	/* Not should be called for DELETE */
 	else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
@@ -91,7 +91,7 @@ check_primary_key(PG_FUNCTION_ARGS)
 
 	/* If UPDATE, then must check new Tuple, not old one */
 	else
-		tuple = trigdata->tg_newtuple;
+		slot = trigdata->tg_newslot;
 
 	trigger = trigdata->tg_trigger;
 	nargs = trigger->tgnargs;
@@ -142,7 +142,7 @@ check_primary_key(PG_FUNCTION_ARGS)
 							args[i], SPI_getrelname(rel))));
 
 		/* Well, get binary (in internal format) value of column */
-		kvals[i] = SPI_getbinval(tuple, tupdesc, fnumber, &isnull);
+		kvals[i] = SPI_getbinval(slot, fnumber, &isnull);
 
 		/*
 		 * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
@@ -152,7 +152,7 @@ check_primary_key(PG_FUNCTION_ARGS)
 		if (isnull)
 		{
 			SPI_finish();
-			return PointerGetDatum(tuple);
+			return PointerGetDatum(slot->tts_tuple);
 		}
 
 		if (plan->nplans <= 0)	/* Get typeId of column */
@@ -217,7 +217,7 @@ check_primary_key(PG_FUNCTION_ARGS)
 
 	SPI_finish();
 
-	return PointerGetDatum(tuple);
+	return PointerGetDatum(slot->tts_tuple);
 }
 
 /*
@@ -248,8 +248,8 @@ check_foreign_key(PG_FUNCTION_ARGS)
 	Datum	   *kvals;			/* key values */
 	char	   *relname;		/* referencing relation name */
 	Relation	rel;			/* triggered relation */
-	HeapTuple	trigtuple = NULL;	/* tuple to being changed */
-	HeapTuple	newtuple = NULL;	/* tuple to return */
+	TupleTableSlot*	trigslot = NULL;		/* tuple to being changed */
+	TupleTableSlot*	newslot = NULL;	/* tuple to return */
 	TupleDesc	tupdesc;		/* tuple description */
 	EPlan	   *plan;			/* prepared plan(s) */
 	Oid		   *argtypes = NULL;	/* key types to prepare execution plan */
@@ -285,7 +285,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
 		elog(ERROR, "check_foreign_key: cannot process INSERT events");
 
 	/* Have to check tg_trigtuple - tuple being deleted */
-	trigtuple = trigdata->tg_trigtuple;
+	trigslot = trigdata->tg_trigslot;
 
 	/*
 	 * But if this is UPDATE then we have to return tg_newtuple. Also, if key
@@ -294,7 +294,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
 	is_update = 0;
 	if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
 	{
-		newtuple = trigdata->tg_newtuple;
+		newslot = trigdata->tg_newslot;
 		is_update = 1;
 	}
 	trigger = trigdata->tg_trigger;
@@ -369,7 +369,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
 							args[i], SPI_getrelname(rel))));
 
 		/* Well, get binary (in internal format) value of column */
-		kvals[i] = SPI_getbinval(trigtuple, tupdesc, fnumber, &isnull);
+		kvals[i] = SPI_getbinval(trigslot, fnumber, &isnull);
 
 		/*
 		 * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
@@ -379,7 +379,8 @@ check_foreign_key(PG_FUNCTION_ARGS)
 		if (isnull)
 		{
 			SPI_finish();
-			return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
+			return PointerGetDatum((newslot == NULL) ?
+					trigslot->tts_tuple : newslot->tts_tuple);
 		}
 
 		/*
@@ -387,16 +388,16 @@ check_foreign_key(PG_FUNCTION_ARGS)
 		 * compare is this the same as old one. For the moment we use string
 		 * presentation of values...
 		 */
-		if (newtuple != NULL)
+		if (newslot != NULL)
 		{
-			char	   *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
+			char	   *oldval = SPI_getvalue(trigslot, fnumber);
 			char	   *newval;
 
 			/* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */
 			if (oldval == NULL)
 				/* internal error */
 				elog(ERROR, "check_foreign_key: SPI_getvalue returned %d", SPI_result);
-			newval = SPI_getvalue(newtuple, tupdesc, fnumber);
+			newval = SPI_getvalue(newslot, fnumber);
 			if (newval == NULL || strcmp(oldval, newval) != 0)
 				isequal = false;
 		}
@@ -470,7 +471,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
 
 						fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
 						Assert(fn > 0); /* already checked above */
-						nv = SPI_getvalue(newtuple, tupdesc, fn);
+						nv = SPI_getvalue(newslot, fn);
 						type = SPI_gettype(tupdesc, fn);
 
 						if ((strcmp(type, "text") && strcmp(type, "varchar") &&
@@ -552,10 +553,10 @@ check_foreign_key(PG_FUNCTION_ARGS)
 	/*
 	 * If UPDATE and key is not changed ...
 	 */
-	if (newtuple != NULL && isequal)
+	if (newslot != NULL && isequal)
 	{
 		SPI_finish();
-		return PointerGetDatum(newtuple);
+		return PointerGetDatum(newslot->tts_tuple);
 	}
 
 	/*
@@ -604,7 +605,8 @@ check_foreign_key(PG_FUNCTION_ARGS)
 
 	SPI_finish();
 
-	return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
+	return PointerGetDatum((newslot == NULL) ?
+							trigslot->tts_tuple : newslot->tts_tuple);
 }
 
 static EPlan *
diff --git a/contrib/spi/timetravel.c b/contrib/spi/timetravel.c
index f7905e2..2127cd6 100644
--- a/contrib/spi/timetravel.c
+++ b/contrib/spi/timetravel.c
@@ -96,8 +96,8 @@ timetravel(PG_FUNCTION_ARGS)
 	char	   *cnulls;			/* column nulls */
 	char	   *relname;		/* triggered relation name */
 	Relation	rel;			/* triggered relation */
-	HeapTuple	trigtuple;
-	HeapTuple	newtuple = NULL;
+	TupleTableSlot *trigslot;
+	TupleTableSlot *newslot = NULL;
 	HeapTuple	rettuple;
 	TupleDesc	tupdesc;		/* tuple description */
 	int			natts;			/* # of attributes */
@@ -129,9 +129,9 @@ timetravel(PG_FUNCTION_ARGS)
 		isinsert = true;
 
 	if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-		newtuple = trigdata->tg_newtuple;
+		newslot = trigdata->tg_newslot;
 
-	trigtuple = trigdata->tg_trigtuple;
+	trigslot = trigdata->tg_trigslot;
 
 	rel = trigdata->tg_relation;
 	relname = SPI_getrelname(rel);
@@ -141,7 +141,8 @@ timetravel(PG_FUNCTION_ARGS)
 	{
 		/* OFF - nothing to do */
 		pfree(relname);
-		return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple);
+		return PointerGetDatum((newslot != NULL) ?
+				newslot->tts_tuple : trigslot->tts_tuple);
 	}
 
 	trigger = trigdata->tg_trigger;
@@ -186,7 +187,7 @@ timetravel(PG_FUNCTION_ARGS)
 		Datum		newvals[MaxAttrNum];
 		bool		newnulls[MaxAttrNum];
 
-		oldtimeon = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_on], &isnull);
+		oldtimeon = SPI_getbinval(trigslot, attnum[a_time_on], &isnull);
 		if (isnull)
 		{
 			newvals[chnattrs] = GetCurrentAbsoluteTime();
@@ -195,7 +196,7 @@ timetravel(PG_FUNCTION_ARGS)
 			chnattrs++;
 		}
 
-		oldtimeoff = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_off], &isnull);
+		oldtimeoff = SPI_getbinval(trigslot, attnum[a_time_off], &isnull);
 		if (isnull)
 		{
 			if ((chnattrs == 0 && DatumGetInt32(oldtimeon) >= NOEND_ABSTIME) ||
@@ -215,7 +216,7 @@ timetravel(PG_FUNCTION_ARGS)
 
 		pfree(relname);
 		if (chnattrs <= 0)
-			return PointerGetDatum(trigtuple);
+			return PointerGetDatum(trigslot->tts_tuple);
 
 		if (argc == MaxAttrNum)
 		{
@@ -235,7 +236,8 @@ timetravel(PG_FUNCTION_ARGS)
 			chattrs[chnattrs] = attnum[a_ins_user];
 			chnattrs++;
 		}
-		rettuple = heap_modify_tuple_by_cols(trigtuple, tupdesc,
+		rettuple = heap_modify_tuple_by_cols(trigslot->tts_tuple,
+											 tupdesc,
 											 chnattrs, chattrs,
 											 newvals, newnulls);
 		return PointerGetDatum(rettuple);
@@ -243,11 +245,11 @@ timetravel(PG_FUNCTION_ARGS)
 	}
 
 	/* UPDATE/DELETE: */
-	oldtimeon = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_on], &isnull);
+	oldtimeon = SPI_getbinval(trigslot, attnum[a_time_on], &isnull);
 	if (isnull)
 		elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_on]);
 
-	oldtimeoff = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_off], &isnull);
+	oldtimeoff = SPI_getbinval(trigslot, attnum[a_time_off], &isnull);
 	if (isnull)
 		elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_off]);
 
@@ -255,13 +257,13 @@ timetravel(PG_FUNCTION_ARGS)
 	 * If DELETE/UPDATE of tuple with stop_date neq INFINITY then say upper
 	 * Executor to skip operation for this tuple
 	 */
-	if (newtuple != NULL)
+	if (newslot != NULL)
 	{							/* UPDATE */
-		newtimeon = SPI_getbinval(newtuple, tupdesc, attnum[a_time_on], &isnull);
+		newtimeon = SPI_getbinval(newslot, attnum[a_time_on], &isnull);
 		if (isnull)
 			elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_on]);
 
-		newtimeoff = SPI_getbinval(newtuple, tupdesc, attnum[a_time_off], &isnull);
+		newtimeoff = SPI_getbinval(newslot, attnum[a_time_off], &isnull);
 		if (isnull)
 			elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_off]);
 
@@ -286,7 +288,7 @@ timetravel(PG_FUNCTION_ARGS)
 	cnulls = (char *) palloc(natts * sizeof(char));
 	for (i = 0; i < natts; i++)
 	{
-		cvals[i] = SPI_getbinval(trigtuple, tupdesc, i + 1, &isnull);
+		cvals[i] = SPI_getbinval(trigslot, i + 1, &isnull);
 		cnulls[i] = (isnull) ? 'n' : ' ';
 	}
 
@@ -294,7 +296,7 @@ timetravel(PG_FUNCTION_ARGS)
 	cvals[attnum[a_time_off] - 1] = newtimeoff; /* stop_date eq current date */
 	cnulls[attnum[a_time_off] - 1] = ' ';
 
-	if (!newtuple)
+	if (!newslot)
 	{							/* DELETE */
 		if (argc == MaxAttrNum)
 		{
@@ -362,7 +364,7 @@ timetravel(PG_FUNCTION_ARGS)
 		elog(ERROR, "timetravel (%s): SPI_execp returned %d", relname, ret);
 
 	/* Tuple to return to upper Executor ... */
-	if (newtuple)
+	if (newslot)
 	{							/* UPDATE */
 		int			chnattrs = 0;
 		int			chattrs[MaxAttrNum];
@@ -402,11 +404,11 @@ timetravel(PG_FUNCTION_ARGS)
 		 * Use SPI_modifytuple() here because we are inside SPI environment
 		 * but rettuple must be allocated in caller's context.
 		 */
-		rettuple = SPI_modifytuple(rel, newtuple, chnattrs, chattrs, newvals, newnulls);
+		rettuple = SPI_modifytuple(rel, newslot->tts_tuple, chnattrs, chattrs, newvals, newnulls);
 	}
 	else
 		/* DELETE case */
-		rettuple = trigtuple;
+		rettuple = trigslot->tts_tuple;
 
 	SPI_finish();				/* don't forget say Bye to SPI mgr */
 
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 0bc8177..cd11947 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -492,7 +492,7 @@ crosstab(PG_FUNCTION_ARGS)
 		 */
 		for (i = 0; i < num_categories; i++)
 		{
-			HeapTuple	spi_tuple;
+			TupleTableSlot*	spislot;
 			char	   *rowid;
 
 			/* see if we've gone too far already */
@@ -500,10 +500,10 @@ crosstab(PG_FUNCTION_ARGS)
 				break;
 
 			/* get the next sql result tuple */
-			spi_tuple = spi_tuptable->vals[call_cntr];
+			spislot = spi_tuptable->vals[call_cntr];
 
 			/* get the rowid from the current sql result tuple */
-			rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
+			rowid = SPI_getvalue(spislot, 1);
 
 			/*
 			 * If this is the first pass through the values for this rowid,
@@ -538,7 +538,7 @@ crosstab(PG_FUNCTION_ARGS)
 				 * Be careful to assign the value to the array index based on
 				 * which category we are presently processing.
 				 */
-				values[1 + i] = SPI_getvalue(spi_tuple, spi_tupdesc, 3);
+				values[1 + i] = SPI_getvalue(spislot, 3);
 
 				/*
 				 * increment the counter since we consume a row for each
@@ -756,13 +756,13 @@ load_categories_hash(char *cats_sql, MemoryContext per_query_ctx)
 		{
 			crosstab_cat_desc *catdesc;
 			char	   *catname;
-			HeapTuple	spi_tuple;
+			TupleTableSlot*	spislot;
 
 			/* get the next sql result tuple */
-			spi_tuple = spi_tuptable->vals[i];
+			spislot = spi_tuptable->vals[i];
 
 			/* get the category from the current sql result tuple */
-			catname = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
+			catname = SPI_getvalue(spislot, 1);
 
 			SPIcontext = MemoryContextSwitchTo(per_query_ctx);
 
@@ -875,15 +875,15 @@ get_crosstab_tuplestore(char *sql,
 
 		for (i = 0; i < proc; i++)
 		{
-			HeapTuple	spi_tuple;
+			TupleTableSlot*	spislot;
 			crosstab_cat_desc *catdesc;
 			char	   *catname;
 
 			/* get the next sql result tuple */
-			spi_tuple = spi_tuptable->vals[i];
+			spislot = spi_tuptable->vals[i];
 
 			/* get the rowid from the current sql result tuple */
-			rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
+			rowid = SPI_getvalue(spislot, 1);
 
 			/*
 			 * if we're on a new output row, grab the column values up to
@@ -908,14 +908,14 @@ get_crosstab_tuplestore(char *sql,
 
 				values[0] = rowid;
 				for (j = 1; j < ncols - 2; j++)
-					values[j] = SPI_getvalue(spi_tuple, spi_tupdesc, j + 1);
+					values[j] = SPI_getvalue(spislot, j + 1);
 
 				/* we're no longer on the first pass */
 				firstpass = false;
 			}
 
 			/* look up the category and fill in the appropriate column */
-			catname = SPI_getvalue(spi_tuple, spi_tupdesc, ncols - 1);
+			catname = SPI_getvalue(spislot, ncols - 1);
 
 			if (catname != NULL)
 			{
@@ -923,7 +923,7 @@ get_crosstab_tuplestore(char *sql,
 
 				if (catdesc)
 					values[catdesc->attidx + ncols - 2] =
-						SPI_getvalue(spi_tuple, spi_tupdesc, ncols);
+						SPI_getvalue(spislot, ncols);
 			}
 
 			xpfree(lastrowid);
@@ -1310,7 +1310,7 @@ build_tuplestore_recursively(char *key_fld,
 	/* Check for qualifying tuples */
 	if ((ret == SPI_OK_SELECT) && (proc > 0))
 	{
-		HeapTuple	spi_tuple;
+		TupleTableSlot*	spislot;
 		SPITupleTable *tuptable = SPI_tuptable;
 		TupleDesc	spi_tupdesc = tuptable->tupdesc;
 		uint64		i;
@@ -1335,13 +1335,13 @@ build_tuplestore_recursively(char *key_fld,
 			appendStringInfo(&chk_branchstr, "%s%s%s", branch_delim, branch, branch_delim);
 
 			/* get the next sql result tuple */
-			spi_tuple = tuptable->vals[i];
+			spislot = tuptable->vals[i];
 
 			/* get the current key (might be NULL) */
-			current_key = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
+			current_key = SPI_getvalue(spislot, 1);
 
 			/* get the parent key (might be NULL) */
-			current_key_parent = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
+			current_key_parent = SPI_getvalue(spislot, 2);
 
 			/* get the current level */
 			sprintf(current_level, "%d", level);
diff --git a/contrib/tcn/tcn.c b/contrib/tcn/tcn.c
index 0b9acbf..c311a3b 100644
--- a/contrib/tcn/tcn.c
+++ b/contrib/tcn/tcn.c
@@ -58,7 +58,7 @@ triggered_change_notification(PG_FUNCTION_ARGS)
 	TriggerData *trigdata = (TriggerData *) fcinfo->context;
 	Trigger    *trigger;
 	int			nargs;
-	HeapTuple	trigtuple;
+	TupleTableSlot*	trigslot;
 	Relation	rel;
 	TupleDesc	tupdesc;
 	char	   *channel;
@@ -112,7 +112,7 @@ triggered_change_notification(PG_FUNCTION_ARGS)
 		channel = trigger->tgargs[0];
 
 	/* get tuple data */
-	trigtuple = trigdata->tg_trigtuple;
+	trigslot = trigdata->tg_trigslot;
 	rel = trigdata->tg_relation;
 	tupdesc = rel->rd_att;
 
@@ -157,7 +157,7 @@ triggered_change_notification(PG_FUNCTION_ARGS)
 					appendStringInfoCharMacro(payload, ',');
 					strcpy_quoted(payload, NameStr((tupdesc->attrs[colno - 1])->attname), '"');
 					appendStringInfoCharMacro(payload, '=');
-					strcpy_quoted(payload, SPI_getvalue(trigtuple, tupdesc, colno), '\'');
+					strcpy_quoted(payload, SPI_getvalue(trigslot, colno), '\'');
 				}
 
 				Async_Notify(channel, payload->data);
diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 95e580d..b344d1f 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -533,7 +533,7 @@ xpath_table(PG_FUNCTION_ARGS)
 
 	/* SPI (input tuple) support */
 	SPITupleTable *tuptable;
-	HeapTuple	spi_tuple;
+	TupleTableSlot*	spislot;
 	TupleDesc	spi_tupdesc;
 
 	/* Output tuple (tuplestore) support */
@@ -702,9 +702,9 @@ xpath_table(PG_FUNCTION_ARGS)
 			xmlXPathCompExprPtr comppath;
 
 			/* Extract the row data as C Strings */
-			spi_tuple = tuptable->vals[i];
-			pkey = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
-			xmldoc = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
+			spislot = tuptable->vals[i];
+			pkey = SPI_getvalue(spislot, 1);
+			xmldoc = SPI_getvalue(spislot, 2);
 
 			/*
 			 * Clear the values array, so that not-well-formed documents
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index b3f0b3c..d09a0fa 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -564,7 +564,7 @@ boot_openrel(char *relname)
 	struct typmap **app;
 	Relation	rel;
 	HeapScanDesc scan;
-	HeapTuple	tup;
+	HeapTuple tup;
 
 	if (strlen(relname) >= NAMEDATALEN)
 		relname[NAMEDATALEN - 1] = '\0';
@@ -880,7 +880,7 @@ gettype(char *type)
 	int			i;
 	Relation	rel;
 	HeapScanDesc scan;
-	HeapTuple	tup;
+	HeapTuple tup;
 	struct typmap **app;
 
 	if (Typ != NULL)
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a..38a1d06 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -789,7 +789,7 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
 					ScanKeyData key[1];
 					Relation	rel;
 					HeapScanDesc scan;
-					HeapTuple	tuple;
+					HeapTuple tup;
 
 					ScanKeyInit(&key[0],
 								Anum_pg_proc_pronamespace,
@@ -799,9 +799,9 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
 					rel = heap_open(ProcedureRelationId, AccessShareLock);
 					scan = heap_beginscan_catalog(rel, 1, key);
 
-					while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+					while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
 					{
-						objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+						objects = lappend_oid(objects, HeapTupleGetOid(tup));
 					}
 
 					heap_endscan(scan);
@@ -830,7 +830,7 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
 	ScanKeyData key[2];
 	Relation	rel;
 	HeapScanDesc scan;
-	HeapTuple	tuple;
+	HeapTuple tup;
 
 	ScanKeyInit(&key[0],
 				Anum_pg_class_relnamespace,
@@ -844,9 +844,9 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
 	rel = heap_open(RelationRelationId, AccessShareLock);
 	scan = heap_beginscan_catalog(rel, 2, key);
 
-	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+	while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
 	{
-		relations = lappend_oid(relations, HeapTupleGetOid(tuple));
+		relations = lappend_oid(relations, HeapTupleGetOid(tup));
 	}
 
 	heap_endscan(scan);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 027abd5..5f807ae 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2641,11 +2641,11 @@ IndexCheckExclusion(Relation heapRelation,
 					IndexInfo *indexInfo)
 {
 	HeapScanDesc scan;
-	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	ExprState  *predicate;
 	TupleTableSlot *slot;
+	HeapTuple heapTuple;
 	EState	   *estate;
 	ExprContext *econtext;
 	Snapshot	snapshot;
@@ -2715,7 +2715,7 @@ IndexCheckExclusion(Relation heapRelation,
 		 */
 		check_exclusion_constraint(heapRelation,
 								   indexRelation, indexInfo,
-								   &(heapTuple->t_self), values, isnull,
+								   &(slot->tts_tid), values, isnull,
 								   estate, true);
 	}
 
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 5746dc3..5fbef3f 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -149,7 +149,7 @@ void
 RemoveConversionById(Oid conversionOid)
 {
 	Relation	rel;
-	HeapTuple	tuple;
+	HeapTuple tuple;
 	HeapScanDesc scan;
 	ScanKeyData scanKeyData;
 
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 323471b..52e0de7 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -172,7 +172,7 @@ DropSetting(Oid databaseid, Oid roleid)
 	Relation	relsetting;
 	HeapScanDesc scan;
 	ScanKeyData keys[2];
-	HeapTuple	tup;
+	HeapTuple tup;
 	int			numkeys = 0;
 
 	relsetting = heap_open(DbRoleSettingRelationId, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 3ef7ba8..59abef4 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -314,7 +314,7 @@ GetAllTablesPublicationRelations(void)
 	Relation	classRel;
 	ScanKeyData key[1];
 	HeapScanDesc scan;
-	HeapTuple	tuple;
+	HeapTuple tuple;
 	List	   *result = NIL;
 
 	classRel = heap_open(RelationRelationId, AccessShareLock);
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index fb53d71..a3eb79d 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -378,7 +378,7 @@ RemoveSubscriptionRel(Oid subid, Oid relid)
 	Relation	rel;
 	HeapScanDesc scan;
 	ScanKeyData skey[2];
-	HeapTuple	tup;
+	HeapTuple tup;
 	int			nkeys = 0;
 
 	rel = heap_open(SubscriptionRelRelationId, RowExclusiveLock);
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index e2544e5..02a8dc5 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -39,7 +39,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
 {
 	TriggerData *trigdata = castNode(TriggerData, fcinfo->context);
 	const char *funcname = "unique_key_recheck";
-	HeapTuple	new_row;
+	TupleTableSlot *newslot;
 	ItemPointerData tmptid;
 	Relation	indexRel;
 	IndexInfo  *indexInfo;
@@ -71,16 +71,16 @@ unique_key_recheck(PG_FUNCTION_ARGS)
 	 * Get the new data that was inserted/updated.
 	 */
 	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
-		new_row = trigdata->tg_trigtuple;
+		newslot = trigdata->tg_trigslot;
 	else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-		new_row = trigdata->tg_newtuple;
+		newslot = trigdata->tg_newslot;
 	else
 	{
 		ereport(ERROR,
 				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
 				 errmsg("function \"%s\" must be fired for INSERT or UPDATE",
 						funcname)));
-		new_row = NULL;			/* keep compiler quiet */
+		newslot = NULL;			/* keep compiler quiet */
 	}
 
 	/*
@@ -101,7 +101,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
 	 * it's possible the index entry has also been marked dead, and even
 	 * removed.
 	 */
-	tmptid = new_row->t_self;
+	tmptid = newslot->tts_tid;
 	if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
 	{
 		/*
@@ -124,7 +124,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
 	 */
 	slot = MakeSingleTupleTableSlot(RelationGetDescr(trigdata->tg_relation));
 
-	ExecStoreTuple(new_row, slot, InvalidBuffer, false);
+	ExecCopySlot(slot, newslot);
 
 	/*
 	 * Typically the index won't have expressions, but if it does we need an
@@ -164,7 +164,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
 		 * correct even if t_self is now dead, because that is the TID the
 		 * index will know about.
 		 */
-		index_insert(indexRel, values, isnull, &(new_row->t_self),
+		index_insert(indexRel, values, isnull, &tmptid,
 					 trigdata->tg_relation, UNIQUE_CHECK_EXISTING,
 					 indexInfo);
 	}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index fc5f4f6..5d3f22e 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2068,13 +2068,9 @@ CopyTo(CopyState cstate)
 
 	if (cstate->rel)
 	{
-		Datum	   *values;
-		bool	   *nulls;
 		HeapScanDesc scandesc;
-		HeapTuple	tuple;
-
-		values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
-		nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
+		HeapTuple tuple;
+		TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(cstate->rel));
 
 		scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
 
@@ -2083,18 +2079,15 @@ CopyTo(CopyState cstate)
 		{
 			CHECK_FOR_INTERRUPTS();
 
-			/* Deconstruct the tuple ... faster than repeated heap_getattr */
-			heap_deform_tuple(tuple, tupDesc, values, nulls);
+			ExecStoreTuple(tuple, slot, InvalidBuffer, false);
+			slot_getallattrs(slot);
 
 			/* Format and send the data */
-			CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls);
+			CopyOneRowTo(cstate, HeapTupleGetOid(slot->tts_tuple), slot->tts_values, slot->tts_isnull);
 			processed++;
 		}
 
 		heap_endscan(scandesc);
-
-		pfree(values);
-		pfree(nulls);
 	}
 	else
 	{
@@ -2752,6 +2745,7 @@ CopyFrom(CopyState cstate)
 					heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
 								hi_options, bistate);
 
+					ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
 					if (resultRelInfo->ri_NumIndices > 0)
 						recheckIndexes = ExecInsertIndexTuples(slot,
 															   &(tuple->t_self),
@@ -2761,7 +2755,7 @@ CopyFrom(CopyState cstate)
 															   NIL);
 
 					/* AFTER ROW INSERT Triggers */
-					ExecARInsertTriggers(estate, resultRelInfo, tuple,
+					ExecARInsertTriggers(estate, resultRelInfo, slot,
 										 recheckIndexes, cstate->transition_capture);
 
 					list_free(recheckIndexes);
@@ -2910,11 +2904,12 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
 
 			cstate->cur_lineno = firstBufferedLineNo + i;
 			ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
+			ItemPointerCopy(&(bufferedTuples[i]->t_self), &(myslot->tts_tid));
 			recheckIndexes =
 				ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
 									  estate, false, NULL, NIL);
 			ExecARInsertTriggers(estate, resultRelInfo,
-								 bufferedTuples[i],
+								 myslot,
 								 recheckIndexes, cstate->transition_capture);
 			list_free(recheckIndexes);
 		}
@@ -2931,8 +2926,10 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
 		for (i = 0; i < nBufferedTuples; i++)
 		{
 			cstate->cur_lineno = firstBufferedLineNo + i;
+			ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
+			ItemPointerCopy(&(bufferedTuples[i]->t_self), &(myslot->tts_tid));
 			ExecARInsertTriggers(estate, resultRelInfo,
-								 bufferedTuples[i],
+								 myslot,
 								 NIL, cstate->transition_capture);
 		}
 	}
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index e138539..e12c1db 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -590,6 +590,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
 		 * each one to the new database.
 		 */
 		rel = heap_open(TableSpaceRelationId, AccessShareLock);
+
 		scan = heap_beginscan_catalog(rel, 0, NULL);
 		while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
 		{
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 620704e..675b348 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1876,7 +1876,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
 	Relation	relationRelation;
 	HeapScanDesc scan;
 	ScanKeyData scan_keys[1];
-	HeapTuple	tuple;
+	HeapTuple tuple;
 	MemoryContext private_context;
 	MemoryContext old;
 	List	   *relids = NIL;
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 7d57f97..062665e 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -678,7 +678,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
 				 errmsg("new data for materialized view \"%s\" contains duplicate rows without any null columns",
 						RelationGetRelationName(matviewRel)),
 				 errdetail("Row: %s",
-						   SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1))));
+			SPI_getvalue(SPI_tuptable->vals[0], 1))));
 	}
 
 	SetUserIdAndSecContext(relowner,
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index bb00858..c47a69f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8090,7 +8090,6 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	char	   *conbin;
 	Expr	   *origexpr;
 	ExprState  *exprstate;
-	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
 	ExprContext *econtext;
@@ -8127,8 +8126,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	exprstate = ExecPrepareExpr(origexpr, estate);
 
 	econtext = GetPerTupleExprContext(estate);
-	tupdesc = RelationGetDescr(rel);
-	slot = MakeSingleTupleTableSlot(tupdesc);
+	slot = MakeSingleTupleTableSlot(RelationGetDescr(rel));
 	econtext->ecxt_scantuple = slot;
 
 	snapshot = RegisterSnapshot(GetLatestSnapshot());
@@ -8157,7 +8155,6 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	MemoryContextSwitchTo(oldcxt);
 	heap_endscan(scan);
 	UnregisterSnapshot(snapshot);
-	ExecDropSingleTupleTableSlot(slot);
 	FreeExecutorState(estate);
 }
 
@@ -8216,6 +8213,9 @@ validateForeignKeyConstraint(char *conname,
 	{
 		FunctionCallInfoData fcinfo;
 		TriggerData trigdata;
+		TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(rel));
+
+		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
 		/*
 		 * Make a call to the trigger function
@@ -8230,11 +8230,9 @@ validateForeignKeyConstraint(char *conname,
 		trigdata.type = T_TriggerData;
 		trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
 		trigdata.tg_relation = rel;
-		trigdata.tg_trigtuple = tuple;
-		trigdata.tg_newtuple = NULL;
+		trigdata.tg_trigslot = slot;
+		trigdata.tg_newslot = NULL;
 		trigdata.tg_trigger = &trig;
-		trigdata.tg_trigtuplebuf = scan->rs_cbuf;
-		trigdata.tg_newtuplebuf = InvalidBuffer;
 
 		fcinfo.context = (Node *) &trigdata;
 
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index b502941..38b7215 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -88,7 +88,7 @@ static HeapTuple GetTupleForTrigger(EState *estate,
 static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			   Trigger *trigger, TriggerEvent event,
 			   Bitmapset *modifiedCols,
-			   HeapTuple oldtup, HeapTuple newtup);
+			   TupleTableSlot *oldslot, TupleTableSlot *newslot);
 static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
 					int tgindx,
 					FmgrInfo *finfo,
@@ -96,7 +96,7 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
 					MemoryContext per_tuple_context);
 static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 					  int event, bool row_trigger,
-					  HeapTuple oldtup, HeapTuple newtup,
+					  TupleTableSlot *oldslot, TupleTableSlot *newslot,
 					  List *recheckIndexes, Bitmapset *modifiedCols,
 					  TransitionCaptureState *transition_capture);
 static void AfterTriggerEnlargeQueryState(void);
@@ -2298,12 +2298,10 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
 	LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
 		TRIGGER_EVENT_BEFORE;
 	LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
-	LocTriggerData.tg_trigtuple = NULL;
-	LocTriggerData.tg_newtuple = NULL;
+	LocTriggerData.tg_trigslot = NULL;
+	LocTriggerData.tg_newslot = NULL;
 	LocTriggerData.tg_oldtable = NULL;
 	LocTriggerData.tg_newtable = NULL;
-	LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-	LocTriggerData.tg_newtuplebuf = InvalidBuffer;
 	for (i = 0; i < trigdesc->numtriggers; i++)
 	{
 		Trigger    *trigger = &trigdesc->triggers[i];
@@ -2359,10 +2357,9 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 		TRIGGER_EVENT_ROW |
 		TRIGGER_EVENT_BEFORE;
 	LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
-	LocTriggerData.tg_newtuple = NULL;
+	LocTriggerData.tg_newslot = NULL;
 	LocTriggerData.tg_oldtable = NULL;
 	LocTriggerData.tg_newtable = NULL;
-	LocTriggerData.tg_newtuplebuf = InvalidBuffer;
 	for (i = 0; i < trigdesc->numtriggers; i++)
 	{
 		Trigger    *trigger = &trigdesc->triggers[i];
@@ -2373,21 +2370,22 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 								  TRIGGER_TYPE_INSERT))
 			continue;
 		if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-							NULL, NULL, newtuple))
+							NULL, NULL, slot))
 			continue;
 
-		LocTriggerData.tg_trigtuple = oldtuple = newtuple;
-		LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+		oldtuple = newtuple;
+		LocTriggerData.tg_trigslot = slot;
 		LocTriggerData.tg_trigger = trigger;
 		newtuple = ExecCallTriggerFunc(&LocTriggerData,
 									   i,
 									   relinfo->ri_TrigFunctions,
 									   relinfo->ri_TrigInstrument,
 									   GetPerTupleMemoryContext(estate));
-		if (oldtuple != newtuple && oldtuple != slottuple)
-			heap_freetuple(oldtuple);
 		if (newtuple == NULL)
 			return NULL;		/* "do nothing" */
+
+		if (oldtuple != newtuple) //hari
+			ExecStoreTuple(newtuple, slot, InvalidBuffer, true);
 	}
 
 	if (newtuple != slottuple)
@@ -2411,7 +2409,7 @@ 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;
@@ -2419,7 +2417,7 @@ ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 	if ((trigdesc && trigdesc->trig_insert_after_row) ||
 		(transition_capture && transition_capture->tcs_insert_new_table))
 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
-							  true, NULL, trigtuple,
+							  true, NULL, slot,
 							  recheckIndexes, NULL,
 							  transition_capture);
 }
@@ -2440,10 +2438,9 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 		TRIGGER_EVENT_ROW |
 		TRIGGER_EVENT_INSTEAD;
 	LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
-	LocTriggerData.tg_newtuple = NULL;
+	LocTriggerData.tg_newslot = NULL;
 	LocTriggerData.tg_oldtable = NULL;
 	LocTriggerData.tg_newtable = NULL;
-	LocTriggerData.tg_newtuplebuf = InvalidBuffer;
 	for (i = 0; i < trigdesc->numtriggers; i++)
 	{
 		Trigger    *trigger = &trigdesc->triggers[i];
@@ -2454,21 +2451,22 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
 								  TRIGGER_TYPE_INSERT))
 			continue;
 		if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-							NULL, NULL, newtuple))
+							NULL, NULL, slot))
 			continue;
 
-		LocTriggerData.tg_trigtuple = oldtuple = newtuple;
-		LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+		oldtuple = newtuple;
+		LocTriggerData.tg_trigslot = slot;
 		LocTriggerData.tg_trigger = trigger;
 		newtuple = ExecCallTriggerFunc(&LocTriggerData,
 									   i,
 									   relinfo->ri_TrigFunctions,
 									   relinfo->ri_TrigInstrument,
 									   GetPerTupleMemoryContext(estate));
-		if (oldtuple != newtuple && oldtuple != slottuple)
-			heap_freetuple(oldtuple);
 		if (newtuple == NULL)
 			return NULL;		/* "do nothing" */
+
+		if (oldtuple != newtuple) //hari
+			ExecStoreTuple(newtuple, slot, InvalidBuffer, true);
 	}
 
 	if (newtuple != slottuple)
@@ -2508,12 +2506,10 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
 	LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
 		TRIGGER_EVENT_BEFORE;
 	LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
-	LocTriggerData.tg_trigtuple = NULL;
-	LocTriggerData.tg_newtuple = NULL;
+	LocTriggerData.tg_trigslot = NULL;
+	LocTriggerData.tg_newslot = NULL;
 	LocTriggerData.tg_oldtable = NULL;
 	LocTriggerData.tg_newtable = NULL;
-	LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-	LocTriggerData.tg_newtuplebuf = InvalidBuffer;
 	for (i = 0; i < trigdesc->numtriggers; i++)
 	{
 		Trigger    *trigger = &trigdesc->triggers[i];
@@ -2557,7 +2553,7 @@ bool
 ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
 					 ResultRelInfo *relinfo,
 					 ItemPointer tupleid,
-					 HeapTuple fdw_trigtuple)
+					 TupleTableSlot *trigslot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
 	bool		result = true;
@@ -2565,28 +2561,31 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
 	HeapTuple	trigtuple;
 	HeapTuple	newtuple;
 	TupleTableSlot *newSlot;
+	TupleTableSlot *oldslot = trigslot;
 	int			i;
 
-	Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
-	if (fdw_trigtuple == NULL)
+	Assert(PointerIsValid(trigslot) ^ ItemPointerIsValid(tupleid));
+	if (trigslot == NULL)
 	{
 		trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
 									   LockTupleExclusive, &newSlot);
 		if (trigtuple == NULL)
 			return false;
+
+		oldslot = MakeSingleTupleTableSlot(RelationGetDescr(relinfo->ri_RelationDesc));
+		ExecStoreTuple(trigtuple, oldslot, InvalidBuffer, false);
 	}
 	else
-		trigtuple = fdw_trigtuple;
+		trigtuple = ExecMaterializeSlot(trigslot);
 
 	LocTriggerData.type = T_TriggerData;
 	LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
 		TRIGGER_EVENT_ROW |
 		TRIGGER_EVENT_BEFORE;
 	LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
-	LocTriggerData.tg_newtuple = NULL;
+	LocTriggerData.tg_newslot = NULL;
 	LocTriggerData.tg_oldtable = NULL;
 	LocTriggerData.tg_newtable = NULL;
-	LocTriggerData.tg_newtuplebuf = InvalidBuffer;
 	for (i = 0; i < trigdesc->numtriggers; i++)
 	{
 		Trigger    *trigger = &trigdesc->triggers[i];
@@ -2597,11 +2596,10 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
 								  TRIGGER_TYPE_DELETE))
 			continue;
 		if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-							NULL, trigtuple, NULL))
+							NULL, oldslot, NULL))
 			continue;
 
-		LocTriggerData.tg_trigtuple = trigtuple;
-		LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+		LocTriggerData.tg_trigslot = oldslot;
 		LocTriggerData.tg_trigger = trigger;
 		newtuple = ExecCallTriggerFunc(&LocTriggerData,
 									   i,
@@ -2616,8 +2614,9 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
 		if (newtuple != trigtuple)
 			heap_freetuple(newtuple);
 	}
-	if (trigtuple != fdw_trigtuple)
-		heap_freetuple(trigtuple);
+
+	if (trigslot != oldslot)
+		ExecDropSingleTupleTableSlot(oldslot);
 
 	return result;
 }
@@ -2625,7 +2624,7 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
 void
 ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
 					 ItemPointer tupleid,
-					 HeapTuple fdw_trigtuple,
+					 TupleTableSlot *slot,
 					 TransitionCaptureState *transition_capture)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
@@ -2634,33 +2633,38 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
 		(transition_capture && transition_capture->tcs_delete_old_table))
 	{
 		HeapTuple	trigtuple;
+		TupleTableSlot *oldslot = slot;
 
-		Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
-		if (fdw_trigtuple == NULL)
+		Assert(PointerIsValid(slot) ^ ItemPointerIsValid(tupleid));
+		if (oldslot == NULL)
+		{
 			trigtuple = GetTupleForTrigger(estate,
 										   NULL,
 										   relinfo,
 										   tupleid,
 										   LockTupleExclusive,
 										   NULL);
-		else
-			trigtuple = fdw_trigtuple;
+			oldslot = MakeSingleTupleTableSlot(RelationGetDescr(relinfo->ri_RelationDesc));
+			ExecStoreTuple(trigtuple, oldslot, InvalidBuffer, false);
+		}
 
 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_DELETE,
-							  true, trigtuple, NULL, NIL, NULL,
+							  true, oldslot, NULL, NIL, NULL,
 							  transition_capture);
-		if (trigtuple != fdw_trigtuple)
-			heap_freetuple(trigtuple);
+
+		if (oldslot != slot)
+			ExecDropSingleTupleTableSlot(oldslot);
 	}
 }
 
 bool
 ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
-					 HeapTuple trigtuple)
+					 TupleTableSlot *slot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
 	TriggerData LocTriggerData;
 	HeapTuple	rettuple;
+	HeapTuple	trigtuple = ExecMaterializeSlot(slot);
 	int			i;
 
 	LocTriggerData.type = T_TriggerData;
@@ -2668,10 +2672,9 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
 		TRIGGER_EVENT_ROW |
 		TRIGGER_EVENT_INSTEAD;
 	LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
-	LocTriggerData.tg_newtuple = NULL;
+	LocTriggerData.tg_newslot = NULL;
 	LocTriggerData.tg_oldtable = NULL;
 	LocTriggerData.tg_newtable = NULL;
-	LocTriggerData.tg_newtuplebuf = InvalidBuffer;
 	for (i = 0; i < trigdesc->numtriggers; i++)
 	{
 		Trigger    *trigger = &trigdesc->triggers[i];
@@ -2682,11 +2685,10 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
 								  TRIGGER_TYPE_DELETE))
 			continue;
 		if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-							NULL, trigtuple, NULL))
+							NULL, slot, NULL))
 			continue;
 
-		LocTriggerData.tg_trigtuple = trigtuple;
-		LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+		LocTriggerData.tg_trigslot = slot;
 		LocTriggerData.tg_trigger = trigger;
 		rettuple = ExecCallTriggerFunc(&LocTriggerData,
 									   i,
@@ -2694,10 +2696,14 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
 									   relinfo->ri_TrigInstrument,
 									   GetPerTupleMemoryContext(estate));
 		if (rettuple == NULL)
+		{
+			ExecDropSingleTupleTableSlot(slot);
 			return false;		/* Delete was suppressed */
+		}
 		if (rettuple != trigtuple)
 			heap_freetuple(rettuple);
 	}
+
 	return true;
 }
 
@@ -2722,12 +2728,10 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
 	LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
 		TRIGGER_EVENT_BEFORE;
 	LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
-	LocTriggerData.tg_trigtuple = NULL;
-	LocTriggerData.tg_newtuple = NULL;
+	LocTriggerData.tg_trigslot = NULL;
+	LocTriggerData.tg_newslot = NULL;
 	LocTriggerData.tg_oldtable = NULL;
 	LocTriggerData.tg_newtable = NULL;
-	LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-	LocTriggerData.tg_newtuplebuf = InvalidBuffer;
 	for (i = 0; i < trigdesc->numtriggers; i++)
 	{
 		Trigger    *trigger = &trigdesc->triggers[i];
@@ -2773,16 +2777,16 @@ TupleTableSlot *
 ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 					 ResultRelInfo *relinfo,
 					 ItemPointer tupleid,
-					 HeapTuple fdw_trigtuple,
+					 TupleTableSlot *trigslot,
 					 TupleTableSlot *slot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
 	HeapTuple	slottuple = ExecMaterializeSlot(slot);
 	HeapTuple	newtuple = slottuple;
 	TriggerData LocTriggerData;
-	HeapTuple	trigtuple;
 	HeapTuple	oldtuple;
 	TupleTableSlot *newSlot;
+	TupleTableSlot *oldSlot = trigslot;
 	int			i;
 	Bitmapset  *updatedCols;
 	LockTupleMode lockmode;
@@ -2790,18 +2794,22 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 	/* Determine lock mode to use */
 	lockmode = ExecUpdateLockMode(estate, relinfo);
 
-	Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
-	if (fdw_trigtuple == NULL)
+	Assert(PointerIsValid(trigslot) ^ ItemPointerIsValid(tupleid));
+	if (trigslot == NULL)
 	{
+		HeapTuple	trigtuple;
+
 		/* get a copy of the on-disk tuple we are planning to update */
 		trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
 									   lockmode, &newSlot);
 		if (trigtuple == NULL)
 			return NULL;		/* cancel the update action */
+
+		oldSlot = MakeSingleTupleTableSlot(RelationGetDescr(relinfo->ri_RelationDesc));
+		ExecStoreTuple(trigtuple, oldSlot, InvalidBuffer, true);
 	}
 	else
 	{
-		trigtuple = fdw_trigtuple;
 		newSlot = NULL;
 	}
 
@@ -2842,30 +2850,32 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 								  TRIGGER_TYPE_UPDATE))
 			continue;
 		if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-							updatedCols, trigtuple, newtuple))
+							updatedCols, oldSlot, slot))
 			continue;
 
-		LocTriggerData.tg_trigtuple = trigtuple;
-		LocTriggerData.tg_newtuple = oldtuple = newtuple;
-		LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-		LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+		oldtuple = newtuple;
+		LocTriggerData.tg_trigslot = oldSlot;
+		LocTriggerData.tg_newslot = slot;
 		LocTriggerData.tg_trigger = trigger;
 		newtuple = ExecCallTriggerFunc(&LocTriggerData,
 									   i,
 									   relinfo->ri_TrigFunctions,
 									   relinfo->ri_TrigInstrument,
 									   GetPerTupleMemoryContext(estate));
-		if (oldtuple != newtuple && oldtuple != slottuple)
-			heap_freetuple(oldtuple);
 		if (newtuple == NULL)
 		{
-			if (trigtuple != fdw_trigtuple)
-				heap_freetuple(trigtuple);
+			if (oldSlot != trigslot)
+				ExecDropSingleTupleTableSlot(oldSlot);
+
 			return NULL;		/* "do nothing" */
 		}
+
+		if (oldtuple != newtuple) //hari
+			ExecStoreTuple(newtuple, slot, InvalidBuffer, true);
 	}
-	if (trigtuple != fdw_trigtuple)
-		heap_freetuple(trigtuple);
+
+	if (oldSlot != trigslot)
+		ExecDropSingleTupleTableSlot(oldSlot);
 
 	if (newtuple != slottuple)
 	{
@@ -2889,8 +2899,8 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 void
 ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
 					 ItemPointer tupleid,
-					 HeapTuple fdw_trigtuple,
-					 HeapTuple newtuple,
+					 TupleTableSlot *oldslot,
+					 TupleTableSlot *slot,
 					 List *recheckIndexes,
 					 TransitionCaptureState *transition_capture)
 {
@@ -2902,30 +2912,35 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
 		  transition_capture->tcs_update_new_table)))
 	{
 		HeapTuple	trigtuple;
+		TupleTableSlot *trigslot = oldslot;
 
-		Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
-		if (fdw_trigtuple == NULL)
+		Assert(PointerIsValid(oldslot) ^ ItemPointerIsValid(tupleid));
+		if (oldslot == NULL)
+		{
 			trigtuple = GetTupleForTrigger(estate,
 										   NULL,
 										   relinfo,
 										   tupleid,
 										   LockTupleExclusive,
 										   NULL);
-		else
-			trigtuple = fdw_trigtuple;
+
+			trigslot = MakeSingleTupleTableSlot(RelationGetDescr(relinfo->ri_RelationDesc));
+			ExecStoreTuple(trigtuple, trigslot, InvalidBuffer, false);
+		}
 
 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
-							  true, trigtuple, newtuple, recheckIndexes,
+							  true, trigslot, slot, recheckIndexes,
 							  GetUpdatedColumns(relinfo, estate),
 							  transition_capture);
-		if (trigtuple != fdw_trigtuple)
-			heap_freetuple(trigtuple);
+
+		if (oldslot != trigslot)
+			ExecDropSingleTupleTableSlot(trigslot);
 	}
 }
 
 TupleTableSlot *
 ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
-					 HeapTuple trigtuple, TupleTableSlot *slot)
+					 TupleTableSlot *oldslot, TupleTableSlot *slot)
 {
 	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
 	HeapTuple	slottuple = ExecMaterializeSlot(slot);
@@ -2951,13 +2966,12 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
 								  TRIGGER_TYPE_UPDATE))
 			continue;
 		if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-							NULL, trigtuple, newtuple))
+							NULL, oldslot, slot))
 			continue;
 
-		LocTriggerData.tg_trigtuple = trigtuple;
-		LocTriggerData.tg_newtuple = oldtuple = newtuple;
-		LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-		LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+		oldtuple = newtuple;
+		LocTriggerData.tg_trigslot = oldslot;
+		LocTriggerData.tg_newslot = slot;
 		LocTriggerData.tg_trigger = trigger;
 		newtuple = ExecCallTriggerFunc(&LocTriggerData,
 									   i,
@@ -2968,6 +2982,8 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
 			heap_freetuple(oldtuple);
 		if (newtuple == NULL)
 			return NULL;		/* "do nothing" */
+
+		ExecStoreTuple(newtuple, slot, InvalidBuffer, false);
 	}
 
 	if (newtuple != slottuple)
@@ -3007,12 +3023,10 @@ ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
 	LocTriggerData.tg_event = TRIGGER_EVENT_TRUNCATE |
 		TRIGGER_EVENT_BEFORE;
 	LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
-	LocTriggerData.tg_trigtuple = NULL;
-	LocTriggerData.tg_newtuple = NULL;
+	LocTriggerData.tg_trigslot = NULL;
+	LocTriggerData.tg_newslot = NULL;
 	LocTriggerData.tg_oldtable = NULL;
 	LocTriggerData.tg_newtable = NULL;
-	LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-	LocTriggerData.tg_newtuplebuf = InvalidBuffer;
 	for (i = 0; i < trigdesc->numtriggers; i++)
 	{
 		Trigger    *trigger = &trigdesc->triggers[i];
@@ -3200,7 +3214,7 @@ static bool
 TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			   Trigger *trigger, TriggerEvent event,
 			   Bitmapset *modifiedCols,
-			   HeapTuple oldtup, HeapTuple newtup)
+			   TupleTableSlot *oldslot, TupleTableSlot *newslot)
 {
 	/* Check replication-role-dependent enable state */
 	if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
@@ -3245,8 +3259,8 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
 		ExprState **predicate;
 		ExprContext *econtext;
-		TupleTableSlot *oldslot = NULL;
-		TupleTableSlot *newslot = NULL;
+		TupleTableSlot *old_localslot = NULL;
+		TupleTableSlot *new_localslot = NULL;
 		MemoryContext oldContext;
 		int			i;
 
@@ -3290,7 +3304,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * These slots can be shared across the whole estate, but be careful
 		 * that they have the current resultrel's tupdesc.
 		 */
-		if (HeapTupleIsValid(oldtup))
+		if (PointerIsValid(oldslot))
 		{
 			if (estate->es_trig_oldtup_slot == NULL)
 			{
@@ -3298,12 +3312,12 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 				estate->es_trig_oldtup_slot = ExecInitExtraTupleSlot(estate);
 				MemoryContextSwitchTo(oldContext);
 			}
-			oldslot = estate->es_trig_oldtup_slot;
-			if (oldslot->tts_tupleDescriptor != tupdesc)
-				ExecSetSlotDescriptor(oldslot, tupdesc);
-			ExecStoreTuple(oldtup, oldslot, InvalidBuffer, false);
+			old_localslot = estate->es_trig_oldtup_slot;
+			if (old_localslot->tts_tupleDescriptor != tupdesc)
+				ExecSetSlotDescriptor(old_localslot, tupdesc);
+			ExecStoreTuple(oldslot->tts_tuple, old_localslot, InvalidBuffer, false);
 		}
-		if (HeapTupleIsValid(newtup))
+		if (PointerIsValid(newslot))
 		{
 			if (estate->es_trig_newtup_slot == NULL)
 			{
@@ -3311,18 +3325,18 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 				estate->es_trig_newtup_slot = ExecInitExtraTupleSlot(estate);
 				MemoryContextSwitchTo(oldContext);
 			}
-			newslot = estate->es_trig_newtup_slot;
-			if (newslot->tts_tupleDescriptor != tupdesc)
-				ExecSetSlotDescriptor(newslot, tupdesc);
-			ExecStoreTuple(newtup, newslot, InvalidBuffer, false);
+			new_localslot = estate->es_trig_newtup_slot;
+			if (new_localslot->tts_tupleDescriptor != tupdesc)
+				ExecSetSlotDescriptor(new_localslot, tupdesc);
+			ExecStoreTuple(newslot->tts_tuple, new_localslot, InvalidBuffer, false);
 		}
 
 		/*
 		 * Finally evaluate the expression, making the old and/or new tuples
 		 * available as INNER_VAR/OUTER_VAR respectively.
 		 */
-		econtext->ecxt_innertuple = oldslot;
-		econtext->ecxt_outertuple = newslot;
+		econtext->ecxt_innertuple = old_localslot;
+		econtext->ecxt_outertuple = new_localslot;
 		if (!ExecQual(*predicate, econtext))
 			return false;
 	}
@@ -3900,6 +3914,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
 	TriggerData LocTriggerData;
 	HeapTupleData tuple1;
 	HeapTupleData tuple2;
+	HeapTuple	oldtuple = NULL;
+	HeapTuple	newtuple = NULL;
 	HeapTuple	rettuple;
 	Buffer		buffer1 = InvalidBuffer;
 	Buffer		buffer2 = InvalidBuffer;
@@ -3960,15 +3976,11 @@ AfterTriggerExecute(AfterTriggerEvent event,
 			 * because we start with a minimal tuple that ExecFetchSlotTuple()
 			 * must materialize anyway.
 			 */
-			LocTriggerData.tg_trigtuple =
-				ExecMaterializeSlot(trig_tuple_slot1);
-			LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+			LocTriggerData.tg_trigslot = trig_tuple_slot1;
 
-			LocTriggerData.tg_newtuple =
+			LocTriggerData.tg_newslot =
 				((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
-				 TRIGGER_EVENT_UPDATE) ?
-				ExecMaterializeSlot(trig_tuple_slot2) : NULL;
-			LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+				 TRIGGER_EVENT_UPDATE) ? trig_tuple_slot2 : NULL;
 
 			break;
 
@@ -3978,13 +3990,12 @@ AfterTriggerExecute(AfterTriggerEvent event,
 				ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
 				if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
 					elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
-				LocTriggerData.tg_trigtuple = &tuple1;
-				LocTriggerData.tg_trigtuplebuf = buffer1;
+				LocTriggerData.tg_trigslot = MakeSingleTupleTableSlot(RelationGetDescr(rel));
+				ExecStoreTuple(&tuple1, LocTriggerData.tg_trigslot, buffer1, false);
 			}
 			else
 			{
-				LocTriggerData.tg_trigtuple = NULL;
-				LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+				LocTriggerData.tg_trigslot = NULL;
 			}
 
 			/* don't touch ctid2 if not there */
@@ -3995,13 +4006,12 @@ AfterTriggerExecute(AfterTriggerEvent event,
 				ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
 				if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
 					elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
-				LocTriggerData.tg_newtuple = &tuple2;
-				LocTriggerData.tg_newtuplebuf = buffer2;
+				LocTriggerData.tg_newslot = MakeSingleTupleTableSlot(RelationGetDescr(rel));
+				ExecStoreTuple(&tuple2, LocTriggerData.tg_newslot, buffer2, false);
 			}
 			else
 			{
-				LocTriggerData.tg_newtuple = NULL;
-				LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+				LocTriggerData.tg_newslot = NULL;
 			}
 	}
 
@@ -4039,6 +4049,12 @@ AfterTriggerExecute(AfterTriggerEvent event,
 		}
 	}
 
+	if (LocTriggerData.tg_trigslot != NULL)
+		oldtuple = ExecMaterializeSlot(LocTriggerData.tg_trigslot);
+
+	if (LocTriggerData.tg_newslot != NULL)
+		newtuple = ExecMaterializeSlot(LocTriggerData.tg_newslot);
+
 	/*
 	 * Setup the remaining trigger information
 	 */
@@ -4059,10 +4075,18 @@ AfterTriggerExecute(AfterTriggerEvent event,
 								   NULL,
 								   per_tuple_context);
 	if (rettuple != NULL &&
-		rettuple != LocTriggerData.tg_trigtuple &&
-		rettuple != LocTriggerData.tg_newtuple)
+		rettuple != oldtuple &&
+		rettuple != newtuple)
 		heap_freetuple(rettuple);
 
+	if ((LocTriggerData.tg_trigslot != NULL) &&
+			(LocTriggerData.tg_trigslot != trig_tuple_slot1))
+		ExecDropSingleTupleTableSlot(LocTriggerData.tg_trigslot);
+
+	if ((LocTriggerData.tg_newslot != NULL) &&
+			(LocTriggerData.tg_newslot != trig_tuple_slot2))
+		ExecDropSingleTupleTableSlot(LocTriggerData.tg_newslot);
+
 	/*
 	 * Release buffers
 	 */
@@ -5221,7 +5245,7 @@ AfterTriggerPendingOnRel(Oid relid)
 static void
 AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 					  int event, bool row_trigger,
-					  HeapTuple oldtup, HeapTuple newtup,
+					  TupleTableSlot *oldslot, TupleTableSlot *newslot,
 					  List *recheckIndexes, Bitmapset *modifiedCols,
 					  TransitionCaptureState *transition_capture)
 {
@@ -5265,25 +5289,27 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 		{
 			Tuplestorestate *old_tuplestore;
 
-			Assert(oldtup != NULL);
+			Assert(oldslot != NULL);
+			Assert(oldslot->tts_tuple != NULL);
 			old_tuplestore = transition_capture->tcs_old_tuplestore;
 
 			if (map != NULL)
 			{
-				HeapTuple	converted = do_convert_tuple(oldtup, map);
+				HeapTuple	converted = do_convert_tuple(oldslot->tts_tuple, map);
 
 				tuplestore_puttuple(old_tuplestore, converted);
 				pfree(converted);
 			}
 			else
-				tuplestore_puttuple(old_tuplestore, oldtup);
+				tuplestore_puttuple(old_tuplestore, oldslot->tts_tuple);
 		}
 		if ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
 			(event == TRIGGER_EVENT_UPDATE && update_new_table))
 		{
 			Tuplestorestate *new_tuplestore;
 
-			Assert(newtup != NULL);
+			Assert(newslot != NULL);
+			Assert(newslot->tts_tuple != NULL);
 			if (event == TRIGGER_EVENT_INSERT)
 				new_tuplestore = transition_capture->tcs_insert_tuplestore;
 			else
@@ -5293,13 +5319,13 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 				tuplestore_puttuple(new_tuplestore, original_insert_tuple);
 			else if (map != NULL)
 			{
-				HeapTuple	converted = do_convert_tuple(newtup, map);
+				HeapTuple	converted = do_convert_tuple(newslot->tts_tuple, map);
 
 				tuplestore_puttuple(new_tuplestore, converted);
 				pfree(converted);
 			}
 			else
-				tuplestore_puttuple(new_tuplestore, newtup);
+				tuplestore_puttuple(new_tuplestore, newslot->tts_tuple);
 		}
 
 		/* If transition tables are the only reason we're here, return. */
@@ -5323,15 +5349,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 			tgtype_event = TRIGGER_TYPE_INSERT;
 			if (row_trigger)
 			{
-				Assert(oldtup == NULL);
-				Assert(newtup != NULL);
-				ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid1));
+				Assert(oldslot == NULL);
+				Assert(newslot != NULL);
+				ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
 				ItemPointerSetInvalid(&(new_event.ate_ctid2));
 			}
 			else
 			{
-				Assert(oldtup == NULL);
-				Assert(newtup == NULL);
+				Assert(oldslot == NULL);
+				Assert(newslot == NULL);
 				ItemPointerSetInvalid(&(new_event.ate_ctid1));
 				ItemPointerSetInvalid(&(new_event.ate_ctid2));
 			}
@@ -5340,15 +5366,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 			tgtype_event = TRIGGER_TYPE_DELETE;
 			if (row_trigger)
 			{
-				Assert(oldtup != NULL);
-				Assert(newtup == NULL);
-				ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
+				Assert(oldslot != NULL);
+				Assert(newslot == NULL);
+				ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
 				ItemPointerSetInvalid(&(new_event.ate_ctid2));
 			}
 			else
 			{
-				Assert(oldtup == NULL);
-				Assert(newtup == NULL);
+				Assert(oldslot == NULL);
+				Assert(newslot == NULL);
 				ItemPointerSetInvalid(&(new_event.ate_ctid1));
 				ItemPointerSetInvalid(&(new_event.ate_ctid2));
 			}
@@ -5357,23 +5383,23 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 			tgtype_event = TRIGGER_TYPE_UPDATE;
 			if (row_trigger)
 			{
-				Assert(oldtup != NULL);
-				Assert(newtup != NULL);
-				ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
-				ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid2));
+				Assert(oldslot != NULL);
+				Assert(newslot != NULL);
+				ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
+				ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
 			}
 			else
 			{
-				Assert(oldtup == NULL);
-				Assert(newtup == NULL);
+				Assert(oldslot == NULL);
+				Assert(newslot == NULL);
 				ItemPointerSetInvalid(&(new_event.ate_ctid1));
 				ItemPointerSetInvalid(&(new_event.ate_ctid2));
 			}
 			break;
 		case TRIGGER_EVENT_TRUNCATE:
 			tgtype_event = TRIGGER_TYPE_TRUNCATE;
-			Assert(oldtup == NULL);
-			Assert(newtup == NULL);
+			Assert(oldslot == NULL);
+			Assert(newslot == NULL);
 			ItemPointerSetInvalid(&(new_event.ate_ctid1));
 			ItemPointerSetInvalid(&(new_event.ate_ctid2));
 			break;
@@ -5400,7 +5426,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 								  tgtype_event))
 			continue;
 		if (!TriggerEnabled(estate, relinfo, trigger, event,
-							modifiedCols, oldtup, newtup))
+							modifiedCols, oldslot, newslot))
 			continue;
 
 		if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
@@ -5429,7 +5455,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 				case RI_TRIGGER_PK:
 					/* Update on trigger's PK table */
 					if (!RI_FKey_pk_upd_check_required(trigger, rel,
-													   oldtup, newtup))
+													   oldslot, newslot))
 					{
 						/* skip queuing this event */
 						continue;
@@ -5439,7 +5465,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 				case RI_TRIGGER_FK:
 					/* Update on trigger's FK table */
 					if (!RI_FKey_fk_upd_check_required(trigger, rel,
-													   oldtup, newtup))
+													   oldslot, newslot))
 					{
 						/* skip queuing this event */
 						continue;
@@ -5487,10 +5513,10 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 	 */
 	if (fdw_tuplestore)
 	{
-		if (oldtup != NULL)
-			tuplestore_puttuple(fdw_tuplestore, oldtup);
-		if (newtup != NULL)
-			tuplestore_puttuple(fdw_tuplestore, newtup);
+		if (oldslot != NULL)
+			tuplestore_puttuple(fdw_tuplestore, oldslot->tts_tuple);
+		if (newslot != NULL)
+			tuplestore_puttuple(fdw_tuplestore, newslot->tts_tuple);
 	}
 }
 
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index bc53d07..ab4a6c5 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -409,14 +409,14 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
 
 		/* OK, store the tuple and create index entries for it */
 		simple_heap_insert(rel, tuple);
-
+		ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
 		if (resultRelInfo->ri_NumIndices > 0)
 			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
 												   estate, false, NULL,
 												   NIL);
 
 		/* AFTER ROW INSERT Triggers */
-		ExecARInsertTriggers(estate, resultRelInfo, tuple,
+		ExecARInsertTriggers(estate, resultRelInfo, slot,
 							 recheckIndexes, NULL);
 
 		/*
@@ -482,10 +482,11 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
 												   estate, false, NULL,
 												   NIL);
 
+		ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
 		/* AFTER ROW UPDATE Triggers */
 		ExecARUpdateTriggers(estate, resultRelInfo,
 							 &searchslot->tts_tuple->t_self,
-							 NULL, tuple, recheckIndexes, NULL);
+							 NULL, slot, recheckIndexes, NULL);
 
 		list_free(recheckIndexes);
 	}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 7ae70a8..fabb628 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -116,6 +116,7 @@ MakeTupleTableSlot(void)
 	slot->tts_shouldFree = false;
 	slot->tts_shouldFreeMin = false;
 	slot->tts_tuple = NULL;
+	ItemPointerSetInvalid(&(slot->tts_tid));
 	slot->tts_tupleDescriptor = NULL;
 	slot->tts_mcxt = CurrentMemoryContext;
 	slot->tts_buffer = InvalidBuffer;
@@ -369,6 +370,8 @@ ExecStoreTuple(HeapTuple tuple,
 			IncrBufferRefCount(buffer);
 	}
 
+	ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
+
 	return slot;
 }
 
@@ -427,6 +430,63 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
 	return slot;
 }
 
+/*
+ * heap_modify_slot_by_cols
+ *		form a new tuple from an old tuple and a set of replacement values.
+ *
+ * This is like heap_modify_tuple, except that instead of specifying which
+ * column(s) to replace by a boolean map, an array of target column numbers
+ * is used.  This is often more convenient when a fixed number of columns
+ * are to be replaced.  The replCols, replValues, and replIsnull arrays must
+ * be of length nCols.  Target column numbers are indexed from 1.
+ *
+ * The result is allocated in the current memory context.
+ */
+TupleTableSlot*
+heap_modify_slot_by_cols(TupleTableSlot *slot,
+						 int nCols,
+						 int *replCols,
+						 Datum *replValues,
+						 bool *replIsnull)
+{
+	int			numberOfAttributes = slot->tts_tupleDescriptor->natts;
+	HeapTuple	newTuple;
+	int			i;
+
+	slot_getallattrs(slot);
+
+	for (i = 0; i < nCols; i++)
+	{
+		int			attnum = replCols[i];
+
+		if (attnum <= 0 || attnum > numberOfAttributes)
+			elog(ERROR, "invalid column number %d", attnum);
+		slot->tts_values[attnum - 1] = replValues[i];
+		slot->tts_isnull[attnum - 1] = replIsnull[i];
+	}
+
+	/*
+	 * create a new tuple from the values and isnull arrays
+	 */
+	newTuple = heap_form_tuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
+
+	/*
+	 * copy the identification info of the old tuple: t_ctid, t_self, and OID
+	 * (if any)
+	 */
+	newTuple->t_data->t_ctid = slot->tts_tuple->t_data->t_ctid;
+	newTuple->t_self = slot->tts_tuple->t_self;
+	newTuple->t_tableOid = slot->tts_tuple->t_tableOid;
+	if (slot->tts_tupleDescriptor->tdhasoid)
+		HeapTupleSetOid(newTuple, HeapTupleGetOid(slot->tts_tuple));
+
+	if (slot->tts_shouldFree)
+		heap_freetuple(slot->tts_tuple);
+
+	slot->tts_tuple = newTuple;
+	return slot;
+}
+
 /* --------------------------------
  *		ExecClearTuple
  *
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 8d17425..387c059 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -557,6 +557,7 @@ ExecInsert(ModifyTableState *mtstate,
 								HEAP_INSERT_SPECULATIVE,
 								NULL);
 
+			ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
 			/* insert index entries for tuple */
 			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
 												   estate, true, &specConflict,
@@ -602,6 +603,7 @@ ExecInsert(ModifyTableState *mtstate,
 								estate->es_output_cid,
 								0, NULL);
 
+			ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
 			/* insert index entries for tuple */
 			if (resultRelInfo->ri_NumIndices > 0)
 				recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
@@ -618,7 +620,7 @@ ExecInsert(ModifyTableState *mtstate,
 	}
 
 	/* AFTER ROW INSERT Triggers */
-	ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+	ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
 						 mtstate->mt_transition_capture);
 
 	list_free(recheckIndexes);
@@ -669,7 +671,7 @@ ExecInsert(ModifyTableState *mtstate,
 static TupleTableSlot *
 ExecDelete(ModifyTableState *mtstate,
 		   ItemPointer tupleid,
-		   HeapTuple oldtuple,
+		   TupleTableSlot *oldslot,
 		   TupleTableSlot *planSlot,
 		   EPQState *epqstate,
 		   EState *estate,
@@ -694,7 +696,7 @@ ExecDelete(ModifyTableState *mtstate,
 		bool		dodelete;
 
 		dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
-										tupleid, oldtuple);
+										tupleid, oldslot);
 
 		if (!dodelete)			/* "do nothing" */
 			return NULL;
@@ -706,8 +708,8 @@ ExecDelete(ModifyTableState *mtstate,
 	{
 		bool		dodelete;
 
-		Assert(oldtuple != NULL);
-		dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
+		Assert(oldslot != NULL);
+		dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldslot);
 
 		if (!dodelete)			/* "do nothing" */
 			return NULL;
@@ -844,8 +846,11 @@ ldelete:;
 	if (canSetTag)
 		(estate->es_processed)++;
 
+	if (oldslot != NULL)
+		ItemPointerCopy(tupleid, &(oldslot->tts_tid));
+
 	/* AFTER ROW DELETE Triggers */
-	ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
+	ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldslot,
 						 mtstate->mt_transition_capture);
 
 	/* Process RETURNING if present */
@@ -868,10 +873,12 @@ ldelete:;
 		else
 		{
 			slot = estate->es_trig_tuple_slot;
-			if (oldtuple != NULL)
+			if (oldslot != NULL)
 			{
-				deltuple = *oldtuple;
-				delbuffer = InvalidBuffer;
+				ExecCopySlot(slot, oldslot);
+
+				if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
+					ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
 			}
 			else
 			{
@@ -879,11 +886,11 @@ ldelete:;
 				if (!heap_fetch(resultRelationDesc, 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);
+				if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
+					ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
+				ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+			}
 		}
 
 		rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -929,7 +936,7 @@ ldelete:;
 static TupleTableSlot *
 ExecUpdate(ModifyTableState *mtstate,
 		   ItemPointer tupleid,
-		   HeapTuple oldtuple,
+		   TupleTableSlot *oldslot,
 		   TupleTableSlot *slot,
 		   TupleTableSlot *planSlot,
 		   EPQState *epqstate,
@@ -966,7 +973,7 @@ ExecUpdate(ModifyTableState *mtstate,
 		resultRelInfo->ri_TrigDesc->trig_update_before_row)
 	{
 		slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
-									tupleid, oldtuple, slot);
+									tupleid, oldslot, slot);
 
 		if (slot == NULL)		/* "do nothing" */
 			return NULL;
@@ -980,7 +987,7 @@ ExecUpdate(ModifyTableState *mtstate,
 		resultRelInfo->ri_TrigDesc->trig_update_instead_row)
 	{
 		slot = ExecIRUpdateTriggers(estate, resultRelInfo,
-									oldtuple, slot);
+									oldslot, slot);
 
 		if (slot == NULL)		/* "do nothing" */
 			return NULL;
@@ -1138,6 +1145,7 @@ lreplace:;
 		 * All we do here is insert new index tuples.  -cim 9/27/89
 		 */
 
+		ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
 		/*
 		 * insert index entries for tuple
 		 *
@@ -1155,7 +1163,7 @@ lreplace:;
 		(estate->es_processed)++;
 
 	/* AFTER ROW UPDATE Triggers */
-	ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, tuple,
+	ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldslot, slot,
 						 recheckIndexes,
 						 mtstate->mt_transition_capture);
 
@@ -1550,6 +1558,7 @@ ExecModifyTable(ModifyTableState *node)
 	ItemPointerData tuple_ctid;
 	HeapTupleData oldtupdata;
 	HeapTuple	oldtuple;
+	TupleTableSlot *oldslot = NULL;
 
 	/*
 	 * This should NOT get called during EvalPlanQual; we should have passed a
@@ -1659,7 +1668,8 @@ ExecModifyTable(ModifyTableState *node)
 		}
 
 		EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
-		slot = planSlot;
+		slot = MakeSingleTupleTableSlot(planSlot->tts_tupleDescriptor);
+		ExecCopySlot(slot, planSlot);
 
 		oldtuple = NULL;
 		if (junkfilter != NULL)
@@ -1721,6 +1731,10 @@ ExecModifyTable(ModifyTableState *node)
 						RelationGetRelid(resultRelInfo->ri_RelationDesc);
 
 					oldtuple = &oldtupdata;
+
+					ItemPointerCopy(tupleid, &(oldslot->tts_tid));
+					oldslot = MakeSingleTupleTableSlot(RelationGetDescr(resultRelInfo->ri_RelationDesc));
+					ExecStoreTuple(oldtuple, oldslot, InvalidBuffer, false);
 				}
 				else
 					Assert(relkind == RELKIND_FOREIGN_TABLE);
@@ -1741,11 +1755,11 @@ ExecModifyTable(ModifyTableState *node)
 								  estate, node->canSetTag);
 				break;
 			case CMD_UPDATE:
-				slot = ExecUpdate(node, tupleid, oldtuple, slot, planSlot,
+				slot = ExecUpdate(node, tupleid, oldslot, slot, planSlot,
 								  &node->mt_epqstate, estate, node->canSetTag);
 				break;
 			case CMD_DELETE:
-				slot = ExecDelete(node, tupleid, oldtuple, planSlot,
+				slot = ExecDelete(node, tupleid, oldslot, planSlot,
 								  &node->mt_epqstate, estate, node->canSetTag);
 				break;
 			default:
@@ -1753,6 +1767,9 @@ ExecModifyTable(ModifyTableState *node)
 				break;
 		}
 
+		if (oldslot)
+			ExecDropSingleTupleTableSlot(oldslot);
+
 		/*
 		 * If we got a RETURNING result, return it to caller.  We'll continue
 		 * the work on next call.
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index cd00a6d..79ffa52 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -23,6 +23,7 @@
 #include "commands/trigger.h"
 #include "executor/executor.h"
 #include "executor/spi_priv.h"
+#include "executor/tuptable.h"
 #include "miscadmin.h"
 #include "tcop/pquery.h"
 #include "tcop/utility.h"
@@ -757,6 +758,72 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
 	return mtuple;
 }
 
+TupleTableSlot *
+SPI_modifyslot(TupleTableSlot *slot, int natts, int *attnum,
+				Datum *Values, const char *Nulls)
+{
+	MemoryContext oldcxt;
+	HeapTuple	mtuple;
+	int			numberOfAttributes;
+	int			i;
+
+	if (slot == NULL || natts < 0 || attnum == NULL || Values == NULL)
+	{
+		SPI_result = SPI_ERROR_ARGUMENT;
+		return NULL;
+	}
+
+	if (_SPI_current == NULL)
+	{
+		SPI_result = SPI_ERROR_UNCONNECTED;
+		return NULL;
+	}
+
+	oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
+
+	SPI_result = 0;
+
+	numberOfAttributes = slot->tts_tupleDescriptor->natts;
+
+	/* replace values and nulls */
+	for (i = 0; i < natts; i++)
+	{
+		if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
+			break;
+		slot->tts_values[attnum[i] - 1] = Values[i];
+		slot->tts_isnull[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? true : false;
+	}
+
+	if (i == natts)				/* no errors in *attnum */
+	{
+		mtuple = heap_form_tuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
+
+		/*
+		 * copy the identification info of the old tuple: t_ctid, t_self, and
+		 * OID (if any)
+		 */
+		mtuple->t_data->t_ctid = slot->tts_tuple->t_data->t_ctid;
+		mtuple->t_self = slot->tts_tuple->t_self;
+		mtuple->t_tableOid = slot->tts_tuple->t_tableOid;
+		if (slot->tts_tupleDescriptor->tdhasoid)
+			HeapTupleSetOid(mtuple, HeapTupleGetOid(slot->tts_tuple));
+	}
+	else
+	{
+		mtuple = NULL;
+		SPI_result = SPI_ERROR_NOATTRIBUTE;
+	}
+
+	MemoryContextSwitchTo(oldcxt);
+
+	if (slot->tts_shouldFree)
+		heap_freetuple(slot->tts_tuple);
+
+	slot->tts_tuple = mtuple;
+
+	return slot;
+}
+
 int
 SPI_fnumber(TupleDesc tupdesc, const char *fname)
 {
@@ -801,7 +868,7 @@ SPI_fname(TupleDesc tupdesc, int fnumber)
 }
 
 char *
-SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
+SPI_getvalue(TupleTableSlot *slot, int fnumber)
 {
 	Datum		val;
 	bool		isnull;
@@ -811,19 +878,19 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
 
 	SPI_result = 0;
 
-	if (fnumber > tupdesc->natts || fnumber == 0 ||
+	if (fnumber > slot->tts_tupleDescriptor->natts || fnumber == 0 ||
 		fnumber <= FirstLowInvalidHeapAttributeNumber)
 	{
 		SPI_result = SPI_ERROR_NOATTRIBUTE;
 		return NULL;
 	}
 
-	val = heap_getattr(tuple, fnumber, tupdesc, &isnull);
+	val = slot_getattr(slot, fnumber, &isnull);
 	if (isnull)
 		return NULL;
 
 	if (fnumber > 0)
-		typoid = tupdesc->attrs[fnumber - 1]->atttypid;
+		typoid = slot->tts_tupleDescriptor->attrs[fnumber - 1]->atttypid;
 	else
 		typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
 
@@ -833,11 +900,11 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
 }
 
 Datum
-SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
+SPI_getbinval(TupleTableSlot *slot, int fnumber, bool *isnull)
 {
 	SPI_result = 0;
 
-	if (fnumber > tupdesc->natts || fnumber == 0 ||
+	if (fnumber > slot->tts_tupleDescriptor->natts || fnumber == 0 ||
 		fnumber <= FirstLowInvalidHeapAttributeNumber)
 	{
 		SPI_result = SPI_ERROR_NOATTRIBUTE;
@@ -845,7 +912,7 @@ SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
 		return (Datum) NULL;
 	}
 
-	return heap_getattr(tuple, fnumber, tupdesc, isnull);
+	return slot_getattr(slot, fnumber, isnull);
 }
 
 char *
@@ -1681,7 +1748,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 = (TupleTableSlot **) palloc(tuptable->alloced * sizeof(TupleTableSlot *));
 	tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
 
 	MemoryContextSwitchTo(oldcxt);
@@ -1697,6 +1764,7 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
 {
 	SPITupleTable *tuptable;
 	MemoryContext oldcxt;
+	TupleTableSlot *dstslot;
 
 	if (_SPI_current == NULL)
 		elog(ERROR, "spi_printtup called while not connected to SPI");
@@ -1712,12 +1780,12 @@ 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 = (TupleTableSlot **) repalloc_huge(tuptable->vals,
+									  tuptable->alloced * sizeof(TupleTableSlot *));
 	}
 
-	tuptable->vals[tuptable->alloced - tuptable->free] =
-		ExecCopySlotTuple(slot);
+	dstslot = MakeSingleTupleTableSlot(tuptable->tupdesc);
+	tuptable->vals[tuptable->alloced - tuptable->free] = ExecCopySlot(dstslot, slot);
 	(tuptable->free)--;
 
 	MemoryContextSwitchTo(oldcxt);
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 0d48dfa..4a01fbd 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -726,6 +726,7 @@ apply_handle_update(StringInfo s)
 		/* Process and store remote tuple in the slot */
 		oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
 		ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false);
+		ItemPointerCopy(&(localslot->tts_tuple->t_self), &(remoteslot->tts_tid));
 		slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed);
 		MemoryContextSwitchTo(oldctx);
 
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6..01714f4 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -194,7 +194,7 @@ static int	ri_constraint_cache_valid_count = 0;
  * ----------
  */
 static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
-				  HeapTuple old_row,
+		 	 	  TupleTableSlot *oldslot,
 				  const RI_ConstraintInfo *riinfo);
 static Datum ri_restrict_del(TriggerData *trigdata, bool is_no_action);
 static Datum ri_restrict_upd(TriggerData *trigdata, bool is_no_action);
@@ -207,12 +207,12 @@ static void ri_GenerateQual(StringInfo buf,
 				const char *rightop, Oid rightoptype);
 static void ri_add_cast_to(StringInfo buf, Oid typid);
 static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
-static int ri_NullCheck(HeapTuple tup,
+static int ri_NullCheck(TupleTableSlot *slot,
 			 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
 static void ri_BuildQueryKey(RI_QueryKey *key,
 				 const RI_ConstraintInfo *riinfo,
 				 int32 constr_queryno);
-static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
+static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
 			 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
 static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
 				   Datum oldvalue, Datum newvalue);
@@ -234,14 +234,13 @@ static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
 static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
 				RI_QueryKey *qkey, SPIPlanPtr qplan,
 				Relation fk_rel, Relation pk_rel,
-				HeapTuple old_tuple, HeapTuple new_tuple,
+				TupleTableSlot *oldslot, TupleTableSlot *newslot,
 				bool detectNewRows, int expect_OK);
-static void ri_ExtractValues(Relation rel, HeapTuple tup,
-				 const RI_ConstraintInfo *riinfo, bool rel_is_pk,
-				 Datum *vals, char *nulls);
+static void ri_ExtractValues(TupleTableSlot *slot, const RI_ConstraintInfo *riinfo,
+				bool rel_is_pk, Datum *vals, char *nulls);
 static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
 				   Relation pk_rel, Relation fk_rel,
-				   HeapTuple violator, TupleDesc tupdesc,
+				   TupleTableSlot* violator, TupleDesc tupdesc,
 				   int queryno, bool spi_err);
 
 
@@ -257,8 +256,7 @@ RI_FKey_check(TriggerData *trigdata)
 	const RI_ConstraintInfo *riinfo;
 	Relation	fk_rel;
 	Relation	pk_rel;
-	HeapTuple	new_row;
-	Buffer		new_row_buf;
+	TupleTableSlot *newslot;
 	RI_QueryKey qkey;
 	SPIPlanPtr	qplan;
 	int			i;
@@ -270,15 +268,9 @@ RI_FKey_check(TriggerData *trigdata)
 									trigdata->tg_relation, false);
 
 	if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-	{
-		new_row = trigdata->tg_newtuple;
-		new_row_buf = trigdata->tg_newtuplebuf;
-	}
+		newslot = trigdata->tg_newslot;
 	else
-	{
-		new_row = trigdata->tg_trigtuple;
-		new_row_buf = trigdata->tg_trigtuplebuf;
-	}
+		newslot = trigdata->tg_trigslot;
 
 	/*
 	 * We should not even consider checking the row if it is no longer valid,
@@ -288,13 +280,8 @@ RI_FKey_check(TriggerData *trigdata)
 	 * and lock on the buffer to call HeapTupleSatisfiesVisibility.  Caller
 	 * should be holding pin, but not lock.
 	 */
-	LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
-	if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
-	{
-		LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
+	if (!HeapTupleSatisfiesVisibility(newslot->tts_tuple, SnapshotSelf, InvalidBuffer))
 		return PointerGetDatum(NULL);
-	}
-	LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
 
 	/*
 	 * Get the relation descriptors of the FK and PK tables.
@@ -310,7 +297,7 @@ RI_FKey_check(TriggerData *trigdata)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("MATCH PARTIAL not yet implemented")));
 
-	switch (ri_NullCheck(new_row, riinfo, false))
+	switch (ri_NullCheck(newslot, riinfo, false))
 	{
 		case RI_KEYS_ALL_NULL:
 
@@ -440,7 +427,7 @@ RI_FKey_check(TriggerData *trigdata)
 	 */
 	ri_PerformCheck(riinfo, &qkey, qplan,
 					fk_rel, pk_rel,
-					NULL, new_row,
+					NULL, newslot,
 					false,
 					SPI_OK_SELECT);
 
@@ -508,7 +495,7 @@ RI_FKey_check_upd(PG_FUNCTION_ARGS)
  */
 static bool
 ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
-				  HeapTuple old_row,
+				  TupleTableSlot *oldslot,
 				  const RI_ConstraintInfo *riinfo)
 {
 	SPIPlanPtr	qplan;
@@ -517,7 +504,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 	bool		result;
 
 	/* Only called for non-null rows */
-	Assert(ri_NullCheck(old_row, riinfo, true) == RI_KEYS_NONE_NULL);
+	Assert(ri_NullCheck(oldslot, riinfo, true) == RI_KEYS_NONE_NULL);
 
 	if (SPI_connect() != SPI_OK_CONNECT)
 		elog(ERROR, "SPI_connect failed");
@@ -575,8 +562,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 	 */
 	result = ri_PerformCheck(riinfo, &qkey, qplan,
 							 fk_rel, pk_rel,
-							 old_row, NULL,
-							 true,	/* treat like update */
+							 oldslot, NULL,
+							 true,		/* treat like update */
 							 SPI_OK_SELECT);
 
 	if (SPI_finish() != SPI_OK_FINISH)
@@ -645,7 +632,7 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
 	const RI_ConstraintInfo *riinfo;
 	Relation	fk_rel;
 	Relation	pk_rel;
-	HeapTuple	old_row;
+	TupleTableSlot *oldslot;
 	RI_QueryKey qkey;
 	SPIPlanPtr	qplan;
 	int			i;
@@ -664,7 +651,7 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
 	 */
 	fk_rel = heap_open(riinfo->fk_relid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
-	old_row = trigdata->tg_trigtuple;
+	oldslot = trigdata->tg_trigslot;
 
 	switch (riinfo->confmatchtype)
 	{
@@ -677,7 +664,7 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			switch (ri_NullCheck(old_row, riinfo, true))
+			switch (ri_NullCheck(oldslot, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -704,7 +691,7 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
 			 * allow another row to be substituted.
 			 */
 			if (is_no_action &&
-				ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
+				ri_Check_Pk_Match(pk_rel, fk_rel, oldslot, riinfo))
 			{
 				heap_close(fk_rel, RowShareLock);
 				return PointerGetDatum(NULL);
@@ -767,8 +754,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
 			 */
 			ri_PerformCheck(riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
-							old_row, NULL,
-							true,	/* must detect new rows */
+							oldslot, NULL,
+							true,		/* must detect new rows */
 							SPI_OK_SELECT);
 
 			if (SPI_finish() != SPI_OK_FINISH)
@@ -857,8 +844,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
 	const RI_ConstraintInfo *riinfo;
 	Relation	fk_rel;
 	Relation	pk_rel;
-	HeapTuple	new_row;
-	HeapTuple	old_row;
+	TupleTableSlot *newslot;
+	TupleTableSlot *oldslot;
 	RI_QueryKey qkey;
 	SPIPlanPtr	qplan;
 	int			i;
@@ -878,8 +865,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
 	 */
 	fk_rel = heap_open(riinfo->fk_relid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
-	new_row = trigdata->tg_newtuple;
-	old_row = trigdata->tg_trigtuple;
+	newslot = trigdata->tg_newslot;
+	oldslot = trigdata->tg_trigslot;
 
 	switch (riinfo->confmatchtype)
 	{
@@ -892,7 +879,7 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			switch (ri_NullCheck(old_row, riinfo, true))
+			switch (ri_NullCheck(oldslot, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -915,7 +902,7 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
 			/*
 			 * No need to check anything if old and new keys are equal
 			 */
-			if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+			if (ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
 			{
 				heap_close(fk_rel, RowShareLock);
 				return PointerGetDatum(NULL);
@@ -928,7 +915,7 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
 			 * allow another row to be substituted.
 			 */
 			if (is_no_action &&
-				ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
+				ri_Check_Pk_Match(pk_rel, fk_rel, oldslot, riinfo))
 			{
 				heap_close(fk_rel, RowShareLock);
 				return PointerGetDatum(NULL);
@@ -990,8 +977,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
 			 */
 			ri_PerformCheck(riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
-							old_row, NULL,
-							true,	/* must detect new rows */
+							oldslot, NULL,
+							true,		/* must detect new rows */
 							SPI_OK_SELECT);
 
 			if (SPI_finish() != SPI_OK_FINISH)
@@ -1034,7 +1021,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 	const RI_ConstraintInfo *riinfo;
 	Relation	fk_rel;
 	Relation	pk_rel;
-	HeapTuple	old_row;
+	TupleTableSlot *oldslot;
 	RI_QueryKey qkey;
 	SPIPlanPtr	qplan;
 	int			i;
@@ -1058,7 +1045,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 	 */
 	fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
-	old_row = trigdata->tg_trigtuple;
+	oldslot = trigdata->tg_trigslot;
 
 	switch (riinfo->confmatchtype)
 	{
@@ -1071,7 +1058,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			switch (ri_NullCheck(old_row, riinfo, true))
+			switch (ri_NullCheck(oldslot, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1146,8 +1133,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 			 */
 			ri_PerformCheck(riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
-							old_row, NULL,
-							true,	/* must detect new rows */
+							oldslot, NULL,
+							true,		/* must detect new rows */
 							SPI_OK_DELETE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
@@ -1190,8 +1177,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 	const RI_ConstraintInfo *riinfo;
 	Relation	fk_rel;
 	Relation	pk_rel;
-	HeapTuple	new_row;
-	HeapTuple	old_row;
+	TupleTableSlot *newslot;
+	TupleTableSlot *oldslot;
 	RI_QueryKey qkey;
 	SPIPlanPtr	qplan;
 	int			i;
@@ -1217,8 +1204,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 	 */
 	fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
-	new_row = trigdata->tg_newtuple;
-	old_row = trigdata->tg_trigtuple;
+	newslot = trigdata->tg_newslot;
+	oldslot = trigdata->tg_trigslot;
 
 	switch (riinfo->confmatchtype)
 	{
@@ -1231,7 +1218,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			switch (ri_NullCheck(old_row, riinfo, true))
+			switch (ri_NullCheck(oldslot, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1254,7 +1241,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 			/*
 			 * No need to do anything if old and new keys are equal
 			 */
-			if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+			if (ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
 			{
 				heap_close(fk_rel, RowExclusiveLock);
 				return PointerGetDatum(NULL);
@@ -1327,8 +1314,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 			 */
 			ri_PerformCheck(riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
-							old_row, new_row,
-							true,	/* must detect new rows */
+							oldslot, newslot,
+							true,		/* must detect new rows */
 							SPI_OK_UPDATE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
@@ -1371,7 +1358,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 	const RI_ConstraintInfo *riinfo;
 	Relation	fk_rel;
 	Relation	pk_rel;
-	HeapTuple	old_row;
+	TupleTableSlot *oldslot;
 	RI_QueryKey qkey;
 	SPIPlanPtr	qplan;
 	int			i;
@@ -1395,7 +1382,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 	 */
 	fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
-	old_row = trigdata->tg_trigtuple;
+	oldslot = trigdata->tg_trigslot;
 
 	switch (riinfo->confmatchtype)
 	{
@@ -1408,7 +1395,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			switch (ri_NullCheck(old_row, riinfo, true))
+			switch (ri_NullCheck(oldslot, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1492,8 +1479,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 			 */
 			ri_PerformCheck(riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
-							old_row, NULL,
-							true,	/* must detect new rows */
+							oldslot, NULL,
+							true,		/* must detect new rows */
 							SPI_OK_UPDATE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
@@ -1536,8 +1523,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 	const RI_ConstraintInfo *riinfo;
 	Relation	fk_rel;
 	Relation	pk_rel;
-	HeapTuple	new_row;
-	HeapTuple	old_row;
+	TupleTableSlot *newslot;
+	TupleTableSlot *oldslot;
 	RI_QueryKey qkey;
 	SPIPlanPtr	qplan;
 	int			i;
@@ -1561,8 +1548,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 	 */
 	fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
-	new_row = trigdata->tg_newtuple;
-	old_row = trigdata->tg_trigtuple;
+	newslot = trigdata->tg_newslot;
+	oldslot = trigdata->tg_trigslot;
 
 	switch (riinfo->confmatchtype)
 	{
@@ -1575,7 +1562,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			switch (ri_NullCheck(old_row, riinfo, true))
+			switch (ri_NullCheck(oldslot, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1598,7 +1585,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 			/*
 			 * No need to do anything if old and new keys are equal
 			 */
-			if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+			if (ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
 			{
 				heap_close(fk_rel, RowExclusiveLock);
 				return PointerGetDatum(NULL);
@@ -1668,8 +1655,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 			 */
 			ri_PerformCheck(riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
-							old_row, NULL,
-							true,	/* must detect new rows */
+							oldslot, NULL,
+							true,		/* must detect new rows */
 							SPI_OK_UPDATE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
@@ -1712,7 +1699,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 	const RI_ConstraintInfo *riinfo;
 	Relation	fk_rel;
 	Relation	pk_rel;
-	HeapTuple	old_row;
+	TupleTableSlot *oldslot;
 	RI_QueryKey qkey;
 	SPIPlanPtr	qplan;
 
@@ -1735,7 +1722,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 	 */
 	fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
-	old_row = trigdata->tg_trigtuple;
+	oldslot = trigdata->tg_trigslot;
 
 	switch (riinfo->confmatchtype)
 	{
@@ -1748,7 +1735,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			switch (ri_NullCheck(old_row, riinfo, true))
+			switch (ri_NullCheck(oldslot, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1834,8 +1821,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 			 */
 			ri_PerformCheck(riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
-							old_row, NULL,
-							true,	/* must detect new rows */
+							oldslot, NULL,
+							true,		/* must detect new rows */
 							SPI_OK_UPDATE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
@@ -1892,8 +1879,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 	const RI_ConstraintInfo *riinfo;
 	Relation	fk_rel;
 	Relation	pk_rel;
-	HeapTuple	new_row;
-	HeapTuple	old_row;
+	TupleTableSlot *newslot;
+	TupleTableSlot *oldslot;
 	RI_QueryKey qkey;
 	SPIPlanPtr	qplan;
 
@@ -1916,8 +1903,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 	 */
 	fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
-	new_row = trigdata->tg_newtuple;
-	old_row = trigdata->tg_trigtuple;
+	newslot = trigdata->tg_newslot;
+	oldslot = trigdata->tg_trigslot;
 
 	switch (riinfo->confmatchtype)
 	{
@@ -1930,7 +1917,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			switch (ri_NullCheck(old_row, riinfo, true))
+			switch (ri_NullCheck(oldslot, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1953,7 +1940,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 			/*
 			 * No need to do anything if old and new keys are equal
 			 */
-			if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+			if (ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
 			{
 				heap_close(fk_rel, RowExclusiveLock);
 				return PointerGetDatum(NULL);
@@ -2025,8 +2012,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 			 */
 			ri_PerformCheck(riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
-							old_row, NULL,
-							true,	/* must detect new rows */
+							oldslot, NULL,
+							true,		/* must detect new rows */
 							SPI_OK_UPDATE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
@@ -2082,7 +2069,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
  */
 bool
 RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
-							  HeapTuple old_row, HeapTuple new_row)
+							  TupleTableSlot *oldslot, TupleTableSlot *newslot)
 {
 	const RI_ConstraintInfo *riinfo;
 
@@ -2100,11 +2087,11 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
 			 * If any old key value is NULL, the row could not have been
 			 * referenced by an FK row, so no check is needed.
 			 */
-			if (ri_NullCheck(old_row, riinfo, true) != RI_KEYS_NONE_NULL)
+			if (ri_NullCheck(oldslot, riinfo, true) != RI_KEYS_NONE_NULL)
 				return false;
 
 			/* If all old and new key values are equal, no check is needed */
-			if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+			if (ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
 				return false;
 
 			/* Else we need to fire the trigger. */
@@ -2139,7 +2126,7 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
  */
 bool
 RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
-							  HeapTuple old_row, HeapTuple new_row)
+							TupleTableSlot *oldslot, TupleTableSlot *newslot)
 {
 	const RI_ConstraintInfo *riinfo;
 
@@ -2156,7 +2143,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
 			 * If any new key value is NULL, the row must satisfy the
 			 * constraint, so no check is needed.
 			 */
-			if (ri_NullCheck(new_row, riinfo, false) != RI_KEYS_NONE_NULL)
+			if (ri_NullCheck(newslot, riinfo, false) != RI_KEYS_NONE_NULL)
 				return false;
 
 			/*
@@ -2167,11 +2154,11 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
 			 * UPDATE check.  (We could skip this if we knew the INSERT
 			 * trigger already fired, but there is no easy way to know that.)
 			 */
-			if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+			if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(oldslot->tts_tuple->t_data)))
 				return true;
 
 			/* If all old and new key values are equal, no check is needed */
-			if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
+			if (ri_KeysEqual(fk_rel, oldslot, newslot, riinfo, false))
 				return false;
 
 			/* Else we need to fire the trigger. */
@@ -2187,7 +2174,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
 			 * invalidated before the constraint is to be checked, but we
 			 * should queue the event to apply the check later.
 			 */
-			switch (ri_NullCheck(new_row, riinfo, false))
+			switch (ri_NullCheck(newslot, riinfo, false))
 			{
 				case RI_KEYS_ALL_NULL:
 					return false;
@@ -2205,11 +2192,11 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
 			 * UPDATE check.  (We could skip this if we knew the INSERT
 			 * trigger already fired, but there is no easy way to know that.)
 			 */
-			if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+			if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(oldslot->tts_tuple->t_data)))
 				return true;
 
 			/* If all old and new key values are equal, no check is needed */
-			if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
+			if (ri_KeysEqual(fk_rel, oldslot, newslot, riinfo, false))
 				return false;
 
 			/* Else we need to fire the trigger. */
@@ -2458,7 +2445,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	/* Did we find a tuple violating the constraint? */
 	if (SPI_processed > 0)
 	{
-		HeapTuple	tuple = SPI_tuptable->vals[0];
+		TupleTableSlot *slot = SPI_tuptable->vals[0];
 		TupleDesc	tupdesc = SPI_tuptable->tupdesc;
 		RI_ConstraintInfo fake_riinfo;
 
@@ -2481,7 +2468,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 		 * disallows partially-null FK rows.
 		 */
 		if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
-			ri_NullCheck(tuple, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
+			ri_NullCheck(slot, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
 					 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
@@ -2498,7 +2485,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 		 */
 		ri_ReportViolation(&fake_riinfo,
 						   pk_rel, fk_rel,
-						   tuple, tupdesc,
+						   slot, tupdesc,
 						   RI_PLAN_CHECK_LOOKUPPK, false);
 	}
 
@@ -3038,11 +3025,10 @@ static bool
 ri_PerformCheck(const RI_ConstraintInfo *riinfo,
 				RI_QueryKey *qkey, SPIPlanPtr qplan,
 				Relation fk_rel, Relation pk_rel,
-				HeapTuple old_tuple, HeapTuple new_tuple,
+				TupleTableSlot *oldslot, TupleTableSlot *newslot,
 				bool detectNewRows, int expect_OK)
 {
-	Relation	query_rel,
-				source_rel;
+	Relation	query_rel;
 	bool		source_is_pk;
 	Snapshot	test_snapshot;
 	Snapshot	crosscheck_snapshot;
@@ -3070,28 +3056,22 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
 	 * need some less klugy way to determine this.
 	 */
 	if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK)
-	{
-		source_rel = fk_rel;
 		source_is_pk = false;
-	}
 	else
-	{
-		source_rel = pk_rel;
 		source_is_pk = true;
-	}
 
 	/* Extract the parameters to be passed into the query */
-	if (new_tuple)
+	if (newslot)
 	{
-		ri_ExtractValues(source_rel, new_tuple, riinfo, source_is_pk,
+		ri_ExtractValues(newslot, riinfo, source_is_pk,
 						 vals, nulls);
-		if (old_tuple)
-			ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+		if (oldslot)
+			ri_ExtractValues(oldslot, riinfo, source_is_pk,
 							 vals + riinfo->nkeys, nulls + riinfo->nkeys);
 	}
 	else
 	{
-		ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+		ri_ExtractValues(oldslot, riinfo, source_is_pk,
 						 vals, nulls);
 	}
 
@@ -3149,7 +3129,7 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
 	if (expect_OK >= 0 && spi_result != expect_OK)
 		ri_ReportViolation(riinfo,
 						   pk_rel, fk_rel,
-						   new_tuple ? new_tuple : old_tuple,
+						   newslot ? newslot : oldslot,
 						   NULL,
 						   qkey->constr_queryno, true);
 
@@ -3159,7 +3139,7 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
 		(SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
 		ri_ReportViolation(riinfo,
 						   pk_rel, fk_rel,
-						   new_tuple ? new_tuple : old_tuple,
+						   newslot ? newslot : oldslot,
 						   NULL,
 						   qkey->constr_queryno, false);
 
@@ -3170,11 +3150,9 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
  * Extract fields from a tuple into Datum/nulls arrays
  */
 static void
-ri_ExtractValues(Relation rel, HeapTuple tup,
-				 const RI_ConstraintInfo *riinfo, bool rel_is_pk,
-				 Datum *vals, char *nulls)
+ri_ExtractValues(TupleTableSlot *slot, const RI_ConstraintInfo *riinfo,
+				bool rel_is_pk, Datum *vals, char *nulls)
 {
-	TupleDesc	tupdesc = rel->rd_att;
 	const int16 *attnums;
 	int			i;
 	bool		isnull;
@@ -3186,8 +3164,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
 
 	for (i = 0; i < riinfo->nkeys; i++)
 	{
-		vals[i] = heap_getattr(tup, attnums[i], tupdesc,
-							   &isnull);
+		vals[i] = slot_getattr(slot, attnums[i], &isnull);
 		nulls[i] = isnull ? 'n' : ' ';
 	}
 }
@@ -3204,7 +3181,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
 static void
 ri_ReportViolation(const RI_ConstraintInfo *riinfo,
 				   Relation pk_rel, Relation fk_rel,
-				   HeapTuple violator, TupleDesc tupdesc,
+				   TupleTableSlot *violator, TupleDesc tupdesc,
 				   int queryno, bool spi_err)
 {
 	StringInfoData key_names;
@@ -3292,7 +3269,7 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
 					   *val;
 
 			name = SPI_fname(tupdesc, fnum);
-			val = SPI_getvalue(violator, tupdesc, fnum);
+			val = SPI_getvalue(violator, fnum);
 			if (!val)
 				val = "null";
 
@@ -3345,7 +3322,7 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
  * ----------
  */
 static int
-ri_NullCheck(HeapTuple tup,
+ri_NullCheck(TupleTableSlot *slot,
 			 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
 {
 	const int16 *attnums;
@@ -3360,7 +3337,7 @@ ri_NullCheck(HeapTuple tup,
 
 	for (i = 0; i < riinfo->nkeys; i++)
 	{
-		if (heap_attisnull(tup, attnums[i]))
+		if (slot_attisnull(slot, attnums[i]))
 			nonenull = false;
 		else
 			allnull = false;
@@ -3511,10 +3488,9 @@ ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
  * ----------
  */
 static bool
-ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
+ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
 			 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
 {
-	TupleDesc	tupdesc = RelationGetDescr(rel);
 	const int16 *attnums;
 	const Oid  *eq_oprs;
 	int			i;
@@ -3539,14 +3515,14 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
 		/*
 		 * Get one attribute's oldvalue. If it is NULL - they're not equal.
 		 */
-		oldvalue = heap_getattr(oldtup, attnums[i], tupdesc, &isnull);
+		oldvalue = slot_getattr(oldslot, attnums[i], &isnull);
 		if (isnull)
 			return false;
 
 		/*
 		 * Get one attribute's newvalue. If it is NULL - they're not equal.
 		 */
-		newvalue = heap_getattr(newtup, attnums[i], tupdesc, &isnull);
+		newvalue = slot_getattr(newslot, attnums[i], &isnull);
 		if (isnull)
 			return false;
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5cfb916..54a31ce 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -362,9 +362,8 @@ static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
 				   deparse_namespace *save_dpns);
 static void pop_ancestor_plan(deparse_namespace *dpns,
 				  deparse_namespace *save_dpns);
-static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
-			 int prettyFlags);
-static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
+static void make_ruledef(StringInfo buf, TupleTableSlot *ruleslot, int prettyFlags);
+static void make_viewdef(StringInfo buf, TupleTableSlot *ruleslot,
 			 int prettyFlags, int wrapColumn);
 static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
 			  TupleDesc resultDesc,
@@ -515,8 +514,7 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
 	Datum		args[1];
 	char		nulls[1];
 	int			spirc;
-	HeapTuple	ruletup;
-	TupleDesc	rulettc;
+	TupleTableSlot	*ruleslot;
 	StringInfoData buf;
 
 	/*
@@ -568,9 +566,8 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
 		/*
 		 * Get the rule's definition and put it into executor's memory
 		 */
-		ruletup = SPI_tuptable->vals[0];
-		rulettc = SPI_tuptable->tupdesc;
-		make_ruledef(&buf, ruletup, rulettc, prettyFlags);
+		ruleslot = SPI_tuptable->vals[0];
+		make_ruledef(&buf, ruleslot, prettyFlags);
 	}
 
 	/*
@@ -708,8 +705,7 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
 	Datum		args[2];
 	char		nulls[2];
 	int			spirc;
-	HeapTuple	ruletup;
-	TupleDesc	rulettc;
+	TupleTableSlot *ruleslot;
 	StringInfoData buf;
 
 	/*
@@ -764,9 +760,8 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
 		/*
 		 * Get the rule's definition and put it into executor's memory
 		 */
-		ruletup = SPI_tuptable->vals[0];
-		rulettc = SPI_tuptable->tupdesc;
-		make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
+		ruleslot = SPI_tuptable->vals[0];
+		make_viewdef(&buf, ruleslot, prettyFlags, wrapColumn);
 	}
 
 	/*
@@ -4580,8 +4575,7 @@ pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
  * ----------
  */
 static void
-make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
-			 int prettyFlags)
+make_ruledef(StringInfo buf, TupleTableSlot *ruleslot, int prettyFlags)
 {
 	char	   *rulename;
 	char		ev_type;
@@ -4593,36 +4587,37 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
 	int			fno;
 	Datum		dat;
 	bool		isnull;
+	TupleDesc	rulettc = ruleslot->tts_tupleDescriptor;
 
 	/*
 	 * Get the attribute values from the rules tuple
 	 */
 	fno = SPI_fnumber(rulettc, "rulename");
-	dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
+	dat = SPI_getbinval(ruleslot, fno, &isnull);
 	Assert(!isnull);
 	rulename = NameStr(*(DatumGetName(dat)));
 
 	fno = SPI_fnumber(rulettc, "ev_type");
-	dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
+	dat = SPI_getbinval(ruleslot, fno, &isnull);
 	Assert(!isnull);
 	ev_type = DatumGetChar(dat);
 
 	fno = SPI_fnumber(rulettc, "ev_class");
-	dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
+	dat = SPI_getbinval(ruleslot, fno, &isnull);
 	Assert(!isnull);
 	ev_class = DatumGetObjectId(dat);
 
 	fno = SPI_fnumber(rulettc, "is_instead");
-	dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
+	dat = SPI_getbinval(ruleslot, fno, &isnull);
 	Assert(!isnull);
 	is_instead = DatumGetBool(dat);
 
 	/* these could be nulls */
 	fno = SPI_fnumber(rulettc, "ev_qual");
-	ev_qual = SPI_getvalue(ruletup, rulettc, fno);
+	ev_qual = SPI_getvalue(ruleslot, fno);
 
 	fno = SPI_fnumber(rulettc, "ev_action");
-	ev_action = SPI_getvalue(ruletup, rulettc, fno);
+	ev_action = SPI_getvalue(ruleslot, fno);
 	if (ev_action != NULL)
 		actions = (List *) stringToNode(ev_action);
 
@@ -4762,7 +4757,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
  * ----------
  */
 static void
-make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
+make_viewdef(StringInfo buf, TupleTableSlot *ruleslot,
 			 int prettyFlags, int wrapColumn)
 {
 	Query	   *query;
@@ -4775,24 +4770,25 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
 	Relation	ev_relation;
 	int			fno;
 	bool		isnull;
+	TupleDesc rulettc = ruleslot->tts_tupleDescriptor;
 
 	/*
 	 * Get the attribute values from the rules tuple
 	 */
 	fno = SPI_fnumber(rulettc, "ev_type");
-	ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull);
+	ev_type = (char) SPI_getbinval(ruleslot, fno, &isnull);
 
 	fno = SPI_fnumber(rulettc, "ev_class");
-	ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull);
+	ev_class = (Oid) SPI_getbinval(ruleslot, fno, &isnull);
 
 	fno = SPI_fnumber(rulettc, "is_instead");
-	is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull);
+	is_instead = (bool) SPI_getbinval(ruleslot, fno, &isnull);
 
 	fno = SPI_fnumber(rulettc, "ev_qual");
-	ev_qual = SPI_getvalue(ruletup, rulettc, fno);
+	ev_qual = SPI_getvalue(ruleslot, fno);
 
 	fno = SPI_fnumber(rulettc, "ev_action");
-	ev_action = SPI_getvalue(ruletup, rulettc, fno);
+	ev_action = SPI_getvalue(ruleslot, fno);
 	if (ev_action != NULL)
 		actions = (List *) stringToNode(ev_action);
 
diff --git a/src/backend/utils/adt/trigfuncs.c b/src/backend/utils/adt/trigfuncs.c
index 50ea6d9..7709acd 100644
--- a/src/backend/utils/adt/trigfuncs.c
+++ b/src/backend/utils/adt/trigfuncs.c
@@ -60,8 +60,8 @@ suppress_redundant_updates_trigger(PG_FUNCTION_ARGS)
 				 errmsg("suppress_redundant_updates_trigger: must be called for each row")));
 
 	/* get tuple data, set default result */
-	rettuple = newtuple = trigdata->tg_newtuple;
-	oldtuple = trigdata->tg_trigtuple;
+	rettuple = newtuple = trigdata->tg_newslot->tts_tuple;
+	oldtuple = trigdata->tg_trigslot->tts_tuple;
 
 	newheader = newtuple->t_data;
 	oldheader = oldtuple->t_data;
diff --git a/src/backend/utils/adt/tsquery_rewrite.c b/src/backend/utils/adt/tsquery_rewrite.c
index 1bd3dea..e90b702 100644
--- a/src/backend/utils/adt/tsquery_rewrite.c
+++ b/src/backend/utils/adt/tsquery_rewrite.c
@@ -326,13 +326,13 @@ tsquery_rewrite_query(PG_FUNCTION_ARGS)
 
 		for (i = 0; i < SPI_processed && tree; i++)
 		{
-			Datum		qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
+			Datum		qdata = SPI_getbinval(SPI_tuptable->vals[i], 1, &isnull);
 			Datum		sdata;
 
 			if (isnull)
 				continue;
 
-			sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
+			sdata = SPI_getbinval(SPI_tuptable->vals[i], 2, &isnull);
 
 			if (!isnull)
 			{
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index 2d7407c..448a2af 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -2357,7 +2357,7 @@ ts_stat_sql(MemoryContext persistentContext, text *txt, text *ws)
 
 		for (i = 0; i < SPI_processed; i++)
 		{
-			Datum		data = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
+			Datum		data = SPI_getbinval(SPI_tuptable->vals[i], 1, &isnull);
 
 			if (!isnull)
 				stat = ts_accum(persistentContext, stat, data);
@@ -2456,7 +2456,7 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
 	TriggerData *trigdata;
 	Trigger    *trigger;
 	Relation	rel;
-	HeapTuple	rettuple = NULL;
+	TupleTableSlot *retslot = NULL;
 	int			tsvector_attr_num,
 				i;
 	ParsedText	prs;
@@ -2476,9 +2476,9 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
 		elog(ERROR, "tsvector_update_trigger: must be fired BEFORE event");
 
 	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
-		rettuple = trigdata->tg_trigtuple;
+		retslot = trigdata->tg_trigslot;
 	else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-		rettuple = trigdata->tg_newtuple;
+		retslot = trigdata->tg_newslot;
 	else
 		elog(ERROR, "tsvector_update_trigger: must be fired for INSERT or UPDATE");
 
@@ -2521,7 +2521,7 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
 					 errmsg("column \"%s\" is not of regconfig type",
 							trigger->tgargs[1])));
 
-		datum = SPI_getbinval(rettuple, rel->rd_att, config_attr_num, &isnull);
+		datum = SPI_getbinval(retslot, config_attr_num, &isnull);
 		if (isnull)
 			ereport(ERROR,
 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
@@ -2566,7 +2566,7 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
 					 errmsg("column \"%s\" is not of a character type",
 							trigger->tgargs[i])));
 
-		datum = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull);
+		datum = SPI_getbinval(retslot, numattr, &isnull);
 		if (isnull)
 			continue;
 
@@ -2583,9 +2583,11 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
 	{
 		datum = PointerGetDatum(make_tsvector(&prs));
 		isnull = false;
-		rettuple = heap_modify_tuple_by_cols(rettuple, rel->rd_att,
-											 1, &tsvector_attr_num,
-											 &datum, &isnull);
+
+		Assert(retslot->tts_tupleDescriptor != rel->rd_att);
+		retslot = heap_modify_slot_by_cols(retslot,
+									    	1, &tsvector_attr_num,
+											&datum, &isnull);
 		pfree(DatumGetPointer(datum));
 	}
 	else
@@ -2596,11 +2598,13 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
 		out->size = 0;
 		datum = PointerGetDatum(out);
 		isnull = false;
-		rettuple = heap_modify_tuple_by_cols(rettuple, rel->rd_att,
+
+		Assert(retslot->tts_tupleDescriptor != rel->rd_att);
+		retslot = heap_modify_slot_by_cols(retslot,
 											 1, &tsvector_attr_num,
 											 &datum, &isnull);
 		pfree(prs.words);
 	}
 
-	return PointerGetDatum(rettuple);
+	return PointerGetDatum(retslot->tts_tuple);
 }
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 323614c..e0deec1 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -2331,7 +2331,6 @@ query_to_oid_list(const char *query)
 		bool		isnull;
 
 		oid = SPI_getbinval(SPI_tuptable->vals[i],
-							SPI_tuptable->tupdesc,
 							1,
 							&isnull);
 		if (!isnull)
@@ -3638,7 +3637,6 @@ SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result, char *tablename,
 		colname = map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable->tupdesc, i),
 												 true, false);
 		colval = SPI_getbinval(SPI_tuptable->vals[rownum],
-							   SPI_tuptable->tupdesc,
 							   i,
 							   &isnull);
 		if (isnull)
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 61b3e68..cb3f9f0 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -68,6 +68,7 @@ typedef struct HeapTupleData
 } HeapTupleData;
 
 typedef HeapTupleData *HeapTuple;
+typedef void* StorageTuple;
 
 #define HEAPTUPLESIZE	MAXALIGN(sizeof(HeapTupleData))
 
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index 36c1134..7bf6e41 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -32,11 +32,9 @@ typedef struct TriggerData
 	NodeTag		type;
 	TriggerEvent tg_event;
 	Relation	tg_relation;
-	HeapTuple	tg_trigtuple;
-	HeapTuple	tg_newtuple;
+	TupleTableSlot*	tg_trigslot;
+	TupleTableSlot*	tg_newslot;
 	Trigger    *tg_trigger;
-	Buffer		tg_trigtuplebuf;
-	Buffer		tg_newtuplebuf;
 	Tuplestorestate *tg_oldtable;
 	Tuplestorestate *tg_newtable;
 } TriggerData;
@@ -188,7 +186,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,
@@ -203,15 +201,15 @@ extern bool ExecBRDeleteTriggers(EState *estate,
 					 EPQState *epqstate,
 					 ResultRelInfo *relinfo,
 					 ItemPointer tupleid,
-					 HeapTuple fdw_trigtuple);
+					 TupleTableSlot *trigslot);
 extern void ExecARDeleteTriggers(EState *estate,
 					 ResultRelInfo *relinfo,
 					 ItemPointer tupleid,
-					 HeapTuple fdw_trigtuple,
+					 TupleTableSlot *slot,
 					 TransitionCaptureState *transition_capture);
 extern bool ExecIRDeleteTriggers(EState *estate,
 					 ResultRelInfo *relinfo,
-					 HeapTuple trigtuple);
+					 TupleTableSlot *slot);
 extern void ExecBSUpdateTriggers(EState *estate,
 					 ResultRelInfo *relinfo);
 extern void ExecASUpdateTriggers(EState *estate,
@@ -221,18 +219,18 @@ extern TupleTableSlot *ExecBRUpdateTriggers(EState *estate,
 					 EPQState *epqstate,
 					 ResultRelInfo *relinfo,
 					 ItemPointer tupleid,
-					 HeapTuple fdw_trigtuple,
+					 TupleTableSlot *trigslot,
 					 TupleTableSlot *slot);
 extern void ExecARUpdateTriggers(EState *estate,
 					 ResultRelInfo *relinfo,
 					 ItemPointer tupleid,
-					 HeapTuple fdw_trigtuple,
-					 HeapTuple newtuple,
+					 TupleTableSlot *oldslot,
+					 TupleTableSlot *slot,
 					 List *recheckIndexes,
 					 TransitionCaptureState *transition_capture);
 extern TupleTableSlot *ExecIRUpdateTriggers(EState *estate,
 					 ResultRelInfo *relinfo,
-					 HeapTuple trigtuple,
+					 TupleTableSlot *oldslot,
 					 TupleTableSlot *slot);
 extern void ExecBSTruncateTriggers(EState *estate,
 					   ResultRelInfo *relinfo);
@@ -254,9 +252,9 @@ extern bool AfterTriggerPendingOnRel(Oid relid);
  * in utils/adt/ri_triggers.c
  */
 extern bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
-							  HeapTuple old_row, HeapTuple new_row);
+							TupleTableSlot *oldslot, TupleTableSlot *newslot);
 extern bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
-							  HeapTuple old_row, HeapTuple new_row);
+							TupleTableSlot *oldslot, TupleTableSlot *newslot);
 extern bool RI_Initial_Check(Trigger *trigger,
 				 Relation fk_rel, Relation pk_rel);
 
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index acade7e..ca93f89 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 */
+	TupleTableSlot **vals;			/* tuples */
 	slist_node	next;			/* link for internal bookkeeping */
 	SubTransactionId subid;		/* subxact in which tuptable was created */
 } SPITupleTable;
@@ -121,10 +121,12 @@ extern HeapTuple SPI_copytuple(HeapTuple tuple);
 extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
 extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
 				int *attnum, Datum *Values, const char *Nulls);
+extern TupleTableSlot* SPI_modifyslot(TupleTableSlot *slot, 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);
-extern char *SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber);
-extern Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull);
+extern char *SPI_getvalue(TupleTableSlot *slot, int fnumber);
+extern Datum SPI_getbinval(TupleTableSlot *slot, int fnumber, bool *isnull);
 extern char *SPI_gettype(TupleDesc tupdesc, int fnumber);
 extern Oid	SPI_gettypeid(TupleDesc tupdesc, int fnumber);
 extern char *SPI_getrelname(Relation rel);
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 55f4cce..13706aa 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -117,6 +117,7 @@ typedef struct TupleTableSlot
 	bool		tts_shouldFree; /* should pfree tts_tuple? */
 	bool		tts_shouldFreeMin;	/* should pfree tts_mintuple? */
 	bool		tts_slow;		/* saved state for slot_deform_tuple */
+	ItemPointerData tts_tid;
 	HeapTuple	tts_tuple;		/* physical tuple, or NULL if virtual */
 	TupleDesc	tts_tupleDescriptor;	/* slot's tuple descriptor */
 	MemoryContext tts_mcxt;		/* slot itself is in this context */
@@ -152,6 +153,11 @@ extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
 extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
 					  TupleTableSlot *slot,
 					  bool shouldFree);
+TupleTableSlot* heap_modify_slot_by_cols(TupleTableSlot *slot,
+						 int nCols,
+						 int *replCols,
+						 Datum *replValues,
+						 bool *replIsnull);
 extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
 extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
 extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 5a45e3e..a760a24 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1584,7 +1584,7 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo)
 		event = "INSERT";
 		if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
 			hv_store_string(hv, "new",
-							plperl_hash_from_tuple(tdata->tg_trigtuple,
+							plperl_hash_from_tuple(tdata->tg_trigslot,
 												   tupdesc));
 	}
 	else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
@@ -1592,7 +1592,7 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo)
 		event = "DELETE";
 		if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
 			hv_store_string(hv, "old",
-							plperl_hash_from_tuple(tdata->tg_trigtuple,
+							plperl_hash_from_tuple(tdata->tg_trigslot,
 												   tupdesc));
 	}
 	else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
@@ -1601,10 +1601,10 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo)
 		if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
 		{
 			hv_store_string(hv, "old",
-							plperl_hash_from_tuple(tdata->tg_trigtuple,
+							plperl_hash_from_tuple(tdata->tg_trigslot,
 												   tupdesc));
 			hv_store_string(hv, "new",
-							plperl_hash_from_tuple(tdata->tg_newtuple,
+							plperl_hash_from_tuple(tdata->tg_newslot,
 												   tupdesc));
 		}
 	}
@@ -2486,13 +2486,13 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
 		TriggerData *trigdata = ((TriggerData *) fcinfo->context);
 
 		if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
-			retval = (Datum) trigdata->tg_trigtuple;
+			retval = (Datum) trigdata->tg_trigslot;
 		else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-			retval = (Datum) trigdata->tg_newtuple;
+			retval = (Datum) trigdata->tg_newslot;
 		else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
-			retval = (Datum) trigdata->tg_trigtuple;
+			retval = (Datum) trigdata->tg_trigslot;
 		else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
-			retval = (Datum) trigdata->tg_trigtuple;
+			retval = (Datum) trigdata->tg_trigslot;
 		else
 			retval = (Datum) 0; /* can this happen? */
 	}
@@ -2511,10 +2511,10 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
 
 			if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
 				trv = plperl_modify_tuple(hvTD, trigdata,
-										  trigdata->tg_trigtuple);
+										  trigdata->tg_trigslot);
 			else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
 				trv = plperl_modify_tuple(hvTD, trigdata,
-										  trigdata->tg_newtuple);
+										  trigdata->tg_newslot);
 			else
 			{
 				ereport(WARNING,
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 662b3c9..019404d 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -1949,7 +1949,7 @@ plpgsql_build_record(const char *refname, int lineno, bool add2namespace)
 	rec->dtype = PLPGSQL_DTYPE_REC;
 	rec->refname = pstrdup(refname);
 	rec->lineno = lineno;
-	rec->tup = NULL;
+	rec->slot = NULL;
 	rec->tupdesc = NULL;
 	rec->freetup = false;
 	rec->freetupdesc = false;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index c98492b..6f50937 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -275,7 +275,7 @@ static void plpgsql_param_fetch(ParamListInfo params, int paramid);
 static void exec_move_row(PLpgSQL_execstate *estate,
 			  PLpgSQL_rec *rec,
 			  PLpgSQL_row *row,
-			  HeapTuple tup, TupleDesc tupdesc);
+			  TupleTableSlot *slot, TupleDesc tupdesc);
 static HeapTuple make_tuple_from_row(PLpgSQL_execstate *estate,
 					PLpgSQL_row *row,
 					TupleDesc tupdesc);
@@ -612,7 +612,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
  *				trigger execution.
  * ----------
  */
-HeapTuple
+TupleTableSlot *
 plpgsql_exec_trigger(PLpgSQL_function *func,
 					 TriggerData *trigdata)
 {
@@ -668,23 +668,23 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
 		/*
 		 * Per-statement triggers don't use OLD/NEW variables
 		 */
-		rec_new->tup = NULL;
-		rec_old->tup = NULL;
+		rec_new->slot = NULL;
+		rec_old->slot = NULL;
 	}
 	else if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
 	{
-		rec_new->tup = trigdata->tg_trigtuple;
-		rec_old->tup = NULL;
+		rec_new->slot = trigdata->tg_trigslot;
+		rec_old->slot = NULL;
 	}
 	else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
 	{
-		rec_new->tup = trigdata->tg_newtuple;
-		rec_old->tup = trigdata->tg_trigtuple;
+		rec_new->slot = trigdata->tg_newslot;
+		rec_old->slot = trigdata->tg_trigslot;
 	}
 	else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
 	{
-		rec_new->tup = NULL;
-		rec_old->tup = trigdata->tg_trigtuple;
+		rec_new->slot = NULL;
+		rec_old->slot = trigdata->tg_trigslot;
 	}
 	else
 		elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE");
@@ -1054,7 +1054,7 @@ copy_plpgsql_datum(PLpgSQL_datum *datum)
 
 				memcpy(new, datum, sizeof(PLpgSQL_rec));
 				/* should be preset to null/non-freeable */
-				Assert(new->tup == NULL);
+				Assert(new->slot == NULL);
 				Assert(new->tupdesc == NULL);
 				Assert(!new->freetup);
 				Assert(!new->freetupdesc);
@@ -1240,7 +1240,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
 
 					if (rec->freetup)
 					{
-						heap_freetuple(rec->tup);
+						ExecDropSingleTupleTableSlot(rec->slot);
 						rec->freetup = false;
 					}
 					if (rec->freetupdesc)
@@ -1248,7 +1248,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
 						FreeTupleDesc(rec->tupdesc);
 						rec->freetupdesc = false;
 					}
-					rec->tup = NULL;
+					rec->slot = NULL;
 					rec->tupdesc = NULL;
 				}
 				break;
@@ -2695,11 +2695,11 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
 					PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
 					int32		rettypmod;
 
-					if (HeapTupleIsValid(rec->tup))
+					if (HeapTupleIsValid(rec->slot))
 					{
 						if (estate->retistuple)
 						{
-							estate->retval = PointerGetDatum(rec->tup);
+							estate->retval = PointerGetDatum(rec->slot->tts_tuple);
 							estate->rettupdesc = rec->tupdesc;
 							estate->retisnull = false;
 						}
@@ -2872,7 +2872,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
 					PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
 					TupleConversionMap *tupmap;
 
-					if (!HeapTupleIsValid(rec->tup))
+					if (!HeapTupleIsValid(rec->slot))
 						ereport(ERROR,
 								(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 								 errmsg("record \"%s\" is not assigned yet",
@@ -2885,7 +2885,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
 					tupmap = convert_tuples_by_position(rec->tupdesc,
 														tupdesc,
 														gettext_noop("wrong record type supplied in RETURN NEXT"));
-					tuple = rec->tup;
+					tuple = rec->slot->tts_tuple;
 					if (tupmap)
 						tuple = do_convert_tuple(tuple, tupmap);
 					tuplestore_puttuple(estate->tuple_store, tuple);
@@ -3058,10 +3058,11 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
 
 		for (i = 0; i < SPI_processed; i++)
 		{
-			HeapTuple	tuple = SPI_tuptable->vals[i];
+			TupleTableSlot *slot = SPI_tuptable->vals[i];
+			HeapTuple	tuple;
 
 			if (tupmap)
-				tuple = do_convert_tuple(tuple, tupmap);
+				tuple = do_convert_tuple(slot->tts_tuple, tupmap);
 			tuplestore_puttuple(estate->tuple_store, tuple);
 			if (tupmap)
 				heap_freetuple(tuple);
@@ -4552,7 +4553,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
 				PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) target;
 				PLpgSQL_rec *rec;
 				int			fno;
-				HeapTuple	newtup;
 				int			colnums[1];
 				Datum		values[1];
 				bool		nulls[1];
@@ -4566,7 +4566,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
 				 * that because records don't have any predefined field
 				 * structure.
 				 */
-				if (!HeapTupleIsValid(rec->tup))
+				if (!HeapTupleIsValid(rec->slot))
 					ereport(ERROR,
 							(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 							 errmsg("record \"%s\" is not assigned yet",
@@ -4600,15 +4600,9 @@ exec_assign_value(PLpgSQL_execstate *estate,
 											atttypmod);
 				nulls[0] = isNull;
 
-				newtup = heap_modify_tuple_by_cols(rec->tup, rec->tupdesc,
+				rec->slot = heap_modify_slot_by_cols(rec->slot,
 												   1, colnums, values, nulls);
-
-				if (rec->freetup)
-					heap_freetuple(rec->tup);
-
-				rec->tup = newtup;
-				rec->freetup = true;
-
+				rec->freetup = false;
 				break;
 			}
 
@@ -4874,7 +4868,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
 			{
 				PLpgSQL_rec *rec = (PLpgSQL_rec *) datum;
 
-				if (!HeapTupleIsValid(rec->tup))
+				if (!HeapTupleIsValid(rec->slot))
 					ereport(ERROR,
 							(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 							 errmsg("record \"%s\" is not assigned yet",
@@ -4887,7 +4881,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
 				oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
 				*typeid = rec->tupdesc->tdtypeid;
 				*typetypmod = rec->tupdesc->tdtypmod;
-				*value = heap_copy_tuple_as_datum(rec->tup, rec->tupdesc);
+				*value = heap_copy_tuple_as_datum(rec->slot->tts_tuple, rec->tupdesc);
 				*isnull = false;
 				MemoryContextSwitchTo(oldcontext);
 				break;
@@ -4900,7 +4894,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
 				int			fno;
 
 				rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]);
-				if (!HeapTupleIsValid(rec->tup))
+				if (!HeapTupleIsValid(rec->slot))
 					ereport(ERROR,
 							(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 							 errmsg("record \"%s\" is not assigned yet",
@@ -4917,7 +4911,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
 					*typetypmod = rec->tupdesc->attrs[fno - 1]->atttypmod;
 				else
 					*typetypmod = -1;
-				*value = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
+				*value = SPI_getbinval(rec->slot, fno, isnull);
 				break;
 			}
 
@@ -5236,8 +5230,7 @@ exec_eval_expr(PLpgSQL_execstate *estate,
 	/*
 	 * Return the single result Datum.
 	 */
-	return SPI_getbinval(estate->eval_tuptable->vals[0],
-						 estate->eval_tuptable->tupdesc, 1, isNull);
+	return SPI_getbinval(estate->eval_tuptable->vals[0], 1, isNull);
 }
 
 
@@ -5936,7 +5929,7 @@ static void
 exec_move_row(PLpgSQL_execstate *estate,
 			  PLpgSQL_rec *rec,
 			  PLpgSQL_row *row,
-			  HeapTuple tup, TupleDesc tupdesc)
+			  TupleTableSlot *slot, TupleDesc tupdesc)
 {
 	/*
 	 * Record is simple - just copy the tuple and its descriptor into the
@@ -5944,30 +5937,13 @@ exec_move_row(PLpgSQL_execstate *estate,
 	 */
 	if (rec != NULL)
 	{
-		/*
-		 * Copy input first, just in case it is pointing at variable's value
-		 */
-		if (HeapTupleIsValid(tup))
-			tup = heap_copytuple(tup);
-		else if (tupdesc)
-		{
-			/* If we have a tupdesc but no data, form an all-nulls tuple */
-			bool	   *nulls;
-
-			nulls = (bool *)
-				eval_mcontext_alloc(estate, tupdesc->natts * sizeof(bool));
-			memset(nulls, true, tupdesc->natts * sizeof(bool));
-
-			tup = heap_form_tuple(tupdesc, NULL, nulls);
-		}
-
 		if (tupdesc)
 			tupdesc = CreateTupleDescCopy(tupdesc);
 
 		/* Free the old value ... */
 		if (rec->freetup)
 		{
-			heap_freetuple(rec->tup);
+			ExecDropSingleTupleTableSlot(rec->slot);
 			rec->freetup = false;
 		}
 		if (rec->freetupdesc)
@@ -5977,13 +5953,13 @@ exec_move_row(PLpgSQL_execstate *estate,
 		}
 
 		/* ... and install the new */
-		if (HeapTupleIsValid(tup))
+		if (HeapTupleIsValid(slot))
 		{
-			rec->tup = tup;
+			ExecCopySlot(rec->slot, slot);
 			rec->freetup = true;
 		}
 		else
-			rec->tup = NULL;
+			rec->slot = NULL;
 
 		if (tupdesc)
 		{
@@ -6019,8 +5995,8 @@ exec_move_row(PLpgSQL_execstate *estate,
 		int			fnum;
 		int			anum;
 
-		if (HeapTupleIsValid(tup))
-			t_natts = HeapTupleHeaderGetNatts(tup->t_data);
+		if (HeapTupleIsValid(slot))
+			t_natts = HeapTupleHeaderGetNatts(slot->tts_tuple->t_data);
 		else
 			t_natts = 0;
 
@@ -6044,7 +6020,7 @@ exec_move_row(PLpgSQL_execstate *estate,
 			if (anum < td_natts)
 			{
 				if (anum < t_natts)
-					value = SPI_getbinval(tup, tupdesc, anum + 1, &isnull);
+					value = SPI_getbinval(slot, anum + 1, &isnull);
 				else
 				{
 					value = (Datum) 0;
@@ -6188,6 +6164,7 @@ exec_move_row_from_datum(PLpgSQL_execstate *estate,
 	int32		tupTypmod;
 	TupleDesc	tupdesc;
 	HeapTupleData tmptup;
+	TupleTableSlot *slot;
 
 	/* Extract rowtype info and find a tupdesc */
 	tupType = HeapTupleHeaderGetTypeId(td);
@@ -6200,8 +6177,11 @@ exec_move_row_from_datum(PLpgSQL_execstate *estate,
 	tmptup.t_tableOid = InvalidOid;
 	tmptup.t_data = td;
 
+	slot = MakeSingleTupleTableSlot(tupdesc);
+	slot->tts_tuple = &tmptup;
+
 	/* Do the move */
-	exec_move_row(estate, rec, row, &tmptup, tupdesc);
+	exec_move_row(estate, rec, row, slot, tupdesc);
 
 	/* Release tupdesc usage count */
 	ReleaseTupleDesc(tupdesc);
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 2b19948..fc46206 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -304,7 +304,7 @@ typedef struct PLpgSQL_rec
 	char	   *refname;
 	int			lineno;
 
-	HeapTuple	tup;
+	TupleTableSlot *slot;
 	TupleDesc	tupdesc;
 	bool		freetup;
 	bool		freetupdesc;
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index c6938d0..63cdc31 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -790,36 +790,36 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 				pltevent = PyString_FromString("INSERT");
 
 				PyDict_SetItemString(pltdata, "old", Py_None);
-				pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
+				pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_trigslot,
 										   tdata->tg_relation->rd_att);
 				PyDict_SetItemString(pltdata, "new", pytnew);
 				Py_DECREF(pytnew);
-				*rv = tdata->tg_trigtuple;
+				*rv = tdata->tg_trigslot;
 			}
 			else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
 			{
 				pltevent = PyString_FromString("DELETE");
 
 				PyDict_SetItemString(pltdata, "new", Py_None);
-				pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
+				pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigslot,
 										   tdata->tg_relation->rd_att);
 				PyDict_SetItemString(pltdata, "old", pytold);
 				Py_DECREF(pytold);
-				*rv = tdata->tg_trigtuple;
+				*rv = tdata->tg_trigslot;
 			}
 			else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
 			{
 				pltevent = PyString_FromString("UPDATE");
 
-				pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_newtuple,
+				pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_newslot,
 										   tdata->tg_relation->rd_att);
 				PyDict_SetItemString(pltdata, "new", pytnew);
 				Py_DECREF(pytnew);
-				pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
+				pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigslot,
 										   tdata->tg_relation->rd_att);
 				PyDict_SetItemString(pltdata, "old", pytold);
 				Py_DECREF(pytold);
-				*rv = tdata->tg_newtuple;
+				*rv = tdata->tg_newslot;
 			}
 			else
 			{
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index ed494e1..c335eef 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -1134,7 +1134,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 									 Tcl_NewStringObj("ROW", -1));
 
 			/* Build the data list for the trigtuple */
-			tcl_trigtup = pltcl_build_tuple_argument(trigdata->tg_trigtuple,
+			tcl_trigtup = pltcl_build_tuple_argument(trigdata->tg_trigslot,
 													 tupdesc);
 
 			/*
@@ -1149,7 +1149,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 				Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
 				Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
 
-				rettup = trigdata->tg_trigtuple;
+				rettup = trigdata->tg_trigslot;
 			}
 			else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
 			{
@@ -1159,20 +1159,20 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 				Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
 				Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
 
-				rettup = trigdata->tg_trigtuple;
+				rettup = trigdata->tg_trigslot;
 			}
 			else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
 			{
 				Tcl_ListObjAppendElement(NULL, tcl_cmd,
 										 Tcl_NewStringObj("UPDATE", -1));
 
-				tcl_newtup = pltcl_build_tuple_argument(trigdata->tg_newtuple,
+				tcl_newtup = pltcl_build_tuple_argument(trigdata->tg_newslot,
 														tupdesc);
 
 				Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_newtup);
 				Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
 
-				rettup = trigdata->tg_newtuple;
+				rettup = trigdata->tg_newslot;
 			}
 			else
 				elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 12c8cd5..cb61dcf 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -126,7 +126,6 @@ initialize_worker_spi(worktable *table)
 		elog(FATAL, "not a singleton result");
 
 	ntup = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
-									   SPI_tuptable->tupdesc,
 									   1, &isnull));
 	if (isnull)
 		elog(FATAL, "null result");
@@ -281,7 +280,6 @@ worker_spi_main(Datum main_arg)
 			int32		val;
 
 			val = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[0],
-											  SPI_tuptable->tupdesc,
 											  1, &isnull));
 			if (!isnull)
 				elog(LOG, "%s: count in %s.%s is now %d",
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index b73bcce..5e0a941 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -347,7 +347,7 @@ funny_dup17(PG_FUNCTION_ARGS)
 	bool	   *recursion;
 	Relation	rel;
 	TupleDesc	tupdesc;
-	HeapTuple	tuple;
+	TupleTableSlot *slot;
 	char	   *query,
 			   *fieldval,
 			   *fieldtype;
@@ -359,7 +359,7 @@ funny_dup17(PG_FUNCTION_ARGS)
 	if (!CALLED_AS_TRIGGER(fcinfo))
 		elog(ERROR, "funny_dup17: not fired by trigger manager");
 
-	tuple = trigdata->tg_trigtuple;
+	slot = trigdata->tg_trigslot;
 	rel = trigdata->tg_relation;
 	tupdesc = rel->rd_att;
 	if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
@@ -387,17 +387,17 @@ funny_dup17(PG_FUNCTION_ARGS)
 	if (*level == 17)
 	{
 		*recursion = false;
-		return PointerGetDatum(tuple);
+		return PointerGetDatum(slot->tts_tuple);
 	}
 
 	if (!(*recursion))
-		return PointerGetDatum(tuple);
+		return PointerGetDatum(slot->tts_tuple);
 
 	(*level)++;
 
 	SPI_connect();
 
-	fieldval = SPI_getvalue(tuple, tupdesc, 1);
+	fieldval = SPI_getvalue(slot, 1);
 	fieldtype = SPI_gettype(tupdesc, 1);
 
 	query = (char *) palloc(100 + NAMEDATALEN * 3 +
@@ -426,11 +426,9 @@ funny_dup17(PG_FUNCTION_ARGS)
 	if (SPI_processed > 0)
 	{
 		selected = DatumGetInt32(DirectFunctionCall1(int4in,
-													 CStringGetDatum(SPI_getvalue(
-																				  SPI_tuptable->vals[0],
-																				  SPI_tuptable->tupdesc,
-																				  1
-																				  ))));
+												CStringGetDatum(SPI_getvalue(
+													   SPI_tuptable->vals[0],
+													   1))));
 	}
 
 	elog(DEBUG4, "funny_dup17 (fired %s) on level %3d: " UINT64_FORMAT "/%d tuples inserted/selected",
@@ -443,7 +441,7 @@ funny_dup17(PG_FUNCTION_ARGS)
 	if (*level == 0)
 		*xid = InvalidTransactionId;
 
-	return PointerGetDatum(tuple);
+	return PointerGetDatum(slot->tts_tuple);
 }
 
 #define TTDUMMY_INFINITY	999999
@@ -468,8 +466,8 @@ ttdummy(PG_FUNCTION_ARGS)
 	char	   *cnulls;			/* column nulls */
 	char	   *relname;		/* triggered relation name */
 	Relation	rel;			/* triggered relation */
-	HeapTuple	trigtuple;
-	HeapTuple	newtuple = NULL;
+	TupleTableSlot *trigslot;
+	TupleTableSlot *newslot = NULL;
 	HeapTuple	rettuple;
 	TupleDesc	tupdesc;		/* tuple description */
 	int			natts;			/* # of attributes */
@@ -486,9 +484,9 @@ ttdummy(PG_FUNCTION_ARGS)
 	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
 		elog(ERROR, "ttdummy: cannot process INSERT event");
 	if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-		newtuple = trigdata->tg_newtuple;
+		newslot = trigdata->tg_newslot;
 
-	trigtuple = trigdata->tg_trigtuple;
+	trigslot = trigdata->tg_trigslot;
 
 	rel = trigdata->tg_relation;
 	relname = SPI_getrelname(rel);
@@ -497,7 +495,8 @@ ttdummy(PG_FUNCTION_ARGS)
 	if (ttoff)					/* OFF - nothing to do */
 	{
 		pfree(relname);
-		return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple);
+		return PointerGetDatum((newslot != NULL) ?
+				newslot->tts_tuple : trigslot->tts_tuple);
 	}
 
 	trigger = trigdata->tg_trigger;
@@ -521,20 +520,20 @@ ttdummy(PG_FUNCTION_ARGS)
 				 relname, args[i]);
 	}
 
-	oldon = SPI_getbinval(trigtuple, tupdesc, attnum[0], &isnull);
+	oldon = SPI_getbinval(trigslot, attnum[0], &isnull);
 	if (isnull)
 		elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]);
 
-	oldoff = SPI_getbinval(trigtuple, tupdesc, attnum[1], &isnull);
+	oldoff = SPI_getbinval(trigslot, attnum[1], &isnull);
 	if (isnull)
 		elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]);
 
-	if (newtuple != NULL)		/* UPDATE */
+	if (newslot != NULL)		/* UPDATE */
 	{
-		newon = SPI_getbinval(newtuple, tupdesc, attnum[0], &isnull);
+		newon = SPI_getbinval(newslot, attnum[0], &isnull);
 		if (isnull)
 			elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]);
-		newoff = SPI_getbinval(newtuple, tupdesc, attnum[1], &isnull);
+		newoff = SPI_getbinval(newslot, attnum[1], &isnull);
 		if (isnull)
 			elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]);
 
@@ -569,13 +568,13 @@ ttdummy(PG_FUNCTION_ARGS)
 	cnulls = (char *) palloc(natts * sizeof(char));
 	for (i = 0; i < natts; i++)
 	{
-		cvals[i] = SPI_getbinval((newtuple != NULL) ? newtuple : trigtuple,
-								 tupdesc, i + 1, &isnull);
+		cvals[i] = SPI_getbinval((newslot != NULL) ? newslot : trigslot,
+								 i + 1, &isnull);
 		cnulls[i] = (isnull) ? 'n' : ' ';
 	}
 
 	/* change date column(s) */
-	if (newtuple)				/* UPDATE */
+	if (newslot)				/* UPDATE */
 	{
 		cvals[attnum[0] - 1] = newoff;	/* start_date eq current date */
 		cnulls[attnum[0] - 1] = ' ';
@@ -628,10 +627,10 @@ ttdummy(PG_FUNCTION_ARGS)
 		elog(ERROR, "ttdummy (%s): SPI_execp returned %d", relname, ret);
 
 	/* Tuple to return to upper Executor ... */
-	if (newtuple)				/* UPDATE */
-		rettuple = SPI_modifytuple(rel, trigtuple, 1, &(attnum[1]), &newoff, NULL);
-	else						/* DELETE */
-		rettuple = trigtuple;
+	if (newslot)				/* UPDATE */
+		trigslot = SPI_modifyslot(trigslot, 1, &(attnum[1]), &newoff, NULL);
+
+	rettuple = trigslot->tts_tuple;
 
 	SPI_finish();				/* don't forget say Bye to SPI mgr */
 
-- 
1.8.3.1

