From 0304a86ce5a7e4113cb2203b4b19f755c2107628 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@pgaddict.com>
Date: Wed, 15 Jul 2015 21:43:54 +0200
Subject: [PATCH 21/24] initial implementation of nodeModifyTable

- defines execColumnStore mimicking execIndexing, with several methods
  a) ExecOpenColumnStores (~ ExecOpenIndices)
  b) ExecCloseColumnStores (~ ExecCloseIndices)
  c) ExecInsertColStoreTuples (~ ExecInsertIndexTuples)

Notes:

1) This disables HOT when there are column stores on the relation.
We may eventually fix this and relax the check so that we only disable
HOT when we update columns referenced in column stores (so we can allow
HOT on the heap part).

2) The inserts seem to be working just fine, but for updates this
complaints about missing ctid junk attribute.

3) Some of the signatures were simplified to use HeapTuple instead of
TupleTableSlot, but this seems a bad idea because everything else works
with TupleTableSlot (which also combine TupleDesc + HeapTuple).
---
 src/backend/access/heap/heapam.c       |   9 +
 src/backend/catalog/colstore.c         |   1 +
 src/backend/commands/copy.c            |   4 +
 src/backend/commands/trigger.c         |   1 +
 src/backend/executor/Makefile          |   4 +-
 src/backend/executor/execColumnStore.c | 323 +++++++++++++++++++++++++++++++++
 src/backend/executor/execMain.c        |   3 +
 src/backend/executor/nodeModifyTable.c | 137 ++++++++++++--
 src/include/catalog/colstore.h         |   1 +
 src/include/executor/executor.h        |   8 +
 10 files changed, 474 insertions(+), 17 deletions(-)
 create mode 100644 src/backend/executor/execColumnStore.c

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 3701d8e..fdf3ffd 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -3328,6 +3328,15 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 	}
 
 	/*
+	 * FIXME If there are any column stores on the table, we'll just disable
+	 *       HOT for now. This is effectively wrong and rather awful - we can
+	 *       do the HOT if no columns from column stores are updated, so we
+	 *       should fix this eventually.
+	 */
+	if (relation->rd_rel->relhascstore > 0)
+		satisfies_hot = false;
+
+	/*
 	 * Note: beyond this point, use oldtup not otid to refer to old tuple.
 	 * otid may very well point at newtup->t_self, which we will overwrite
 	 * with the new tuple's location, so there's great risk of confusion if we
diff --git a/src/backend/catalog/colstore.c b/src/backend/catalog/colstore.c
index 15cb8d4..25d490d 100644
--- a/src/backend/catalog/colstore.c
+++ b/src/backend/catalog/colstore.c
@@ -29,6 +29,7 @@
 #include "miscadmin.h"
 #include "nodes/bitmapset.h"
 #include "nodes/parsenodes.h"
+#include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 8db1b35..2d397d1 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2302,6 +2302,8 @@ CopyFrom(CopyState cstate)
 
 	ExecOpenIndices(resultRelInfo, false);
 
+	ExecOpenColumnStores(resultRelInfo);
+
 	estate->es_result_relations = resultRelInfo;
 	estate->es_num_result_relations = 1;
 	estate->es_result_relation_info = resultRelInfo;
@@ -2508,6 +2510,8 @@ CopyFrom(CopyState cstate)
 
 	ExecCloseIndices(resultRelInfo);
 
+	ExecCloseColumnStores(resultRelInfo);
+
 	FreeExecutorState(estate);
 
 	/*
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 43421d6..d9cb227 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -3884,6 +3884,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
 
 			/* Close indices and then the relation itself */
 			ExecCloseIndices(resultRelInfo);
+			ExecCloseColumnStores(resultRelInfo);
 			heap_close(resultRelInfo->ri_RelationDesc, NoLock);
 		}
 		FreeExecutorState(estate);
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 703a9c8..348d21b 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -14,8 +14,8 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
        execMain.o execProcnode.o execQual.o execScan.o execTuples.o \
-       execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
-       nodeBitmapAnd.o nodeBitmapOr.o \
+       execUtils.o execColumnStore.o functions.o instrument.o \
+       nodeAppend.o nodeAgg.o nodeBitmapAnd.o nodeBitmapOr.o \
        nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeColumnStoreMaterial.o \
        nodeCustom.o nodeHash.o nodeHashjoin.o \
        nodeIndexscan.o nodeIndexonlyscan.o nodeLimit.o nodeLockRows.o \
diff --git a/src/backend/executor/execColumnStore.c b/src/backend/executor/execColumnStore.c
new file mode 100644
index 0000000..49c123c
--- /dev/null
+++ b/src/backend/executor/execColumnStore.c
@@ -0,0 +1,323 @@
+/*-------------------------------------------------------------------------
+ *
+ * execColumnStore.c
+ *	  routines for inserting tuples into column stores.
+ *
+ * ExecInsertColStoreTuples() is the main entry point.  It's called after
+ * inserting a tuple to the heap, and it inserts corresponding values
+ * into all column stores.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/executor/execColumnStore.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/relscan.h"
+#include "catalog/colstore.h"
+#include "colstore/colstoreapi.h"
+#include "executor/executor.h"
+#include "nodes/nodeFuncs.h"
+#include "storage/lmgr.h"
+#include "utils/tqual.h"
+
+/* ----------------------------------------------------------------
+ *		ExecOpenColumnStores
+ *
+ *		Find the column stores associated with a result relation, open them,
+ *		and save information about them in the result ResultRelInfo.
+ *
+ *		At entry, caller has already opened and locked
+ *		resultRelInfo->ri_RelationDesc.
+ * ----------------------------------------------------------------
+ */
+void
+ExecOpenColumnStores(ResultRelInfo *resultRelInfo)
+{
+	Relation	resultRelation = resultRelInfo->ri_RelationDesc;
+	List	   *colstoreoidlist;
+	ListCell   *l;
+	int			len,
+				i;
+	RelationPtr relationDescs;
+	ColumnStoreInfo **columnStoreInfoArray;
+
+	resultRelInfo->ri_NumColumnStores = 0;
+
+	/* fast path if no column stores */
+	if (!RelationGetForm(resultRelation)->relhascstore)
+		return;
+
+	/*
+	 * Get cached list of colstore OIDs
+	 */
+	colstoreoidlist = RelationGetColStoreList(resultRelation);
+	len = list_length(colstoreoidlist);
+	if (len == 0)
+		return;
+
+	/*
+	 * allocate space for result arrays
+	 */
+	relationDescs = (RelationPtr) palloc(len * sizeof(Relation));
+	columnStoreInfoArray
+		= (ColumnStoreInfo **) palloc(len * sizeof(ColumnStoreInfo *));
+
+	resultRelInfo->ri_NumColumnStores = len;
+	resultRelInfo->ri_ColumnStoreRelationDescs = relationDescs;
+	resultRelInfo->ri_ColumnStoreRelationInfo = columnStoreInfoArray;
+
+	/*
+	 * For each column store, open the column store relation and save pg_cstore
+	 * info. We acquire RowExclusiveLock, signifying we will update the column
+	 * store.
+	 */
+	i = 0;
+	foreach(l, colstoreoidlist)
+	{
+		Oid			cstoreOid = lfirst_oid(l);
+		Relation	cstoreDesc;
+		ColumnStoreInfo  *csi;
+
+		cstoreDesc = relation_open(cstoreOid, RowExclusiveLock);
+
+		/* extract column store information from the pg_cstore info */
+		csi = BuildColumnStoreInfo(cstoreDesc);
+
+		relationDescs[i] = cstoreDesc;
+		columnStoreInfoArray[i] = csi;
+		i++;
+	}
+
+	list_free(colstoreoidlist);
+}
+
+/* ----------------------------------------------------------------
+ *		ExecCloseColumnStores
+ *
+ *		Close the column store relations stored in resultRelInfo
+ * ----------------------------------------------------------------
+ */
+void
+ExecCloseColumnStores(ResultRelInfo *resultRelInfo)
+{
+	int			i;
+	int			numColumnStores;
+	RelationPtr cstoreDescs;
+
+	numColumnStores = resultRelInfo->ri_NumColumnStores;
+	cstoreDescs = resultRelInfo->ri_ColumnStoreRelationDescs;
+
+	for (i = 0; i < numColumnStores; i++)
+	{
+		if (cstoreDescs[i] == NULL)
+			continue;			/* shouldn't happen? */
+
+		/* Drop lock acquired by ExecOpenColumnStores */
+		relation_close(cstoreDescs[i], RowExclusiveLock);
+	}
+
+	/*
+	 * XXX should free ColumnStoreInfo array here too? Currently we assume that
+	 * such stuff will be cleaned up automatically in FreeExecutorState.
+	 */
+}
+
+/* ----------------------------------------------------------------
+ *		ExecInsertColStoreTuples
+ *
+ *		This routine takes care of inserting column store tuples
+ *		into all the relations vertically partitioning the result relation
+ *		when a heap tuple is inserted into the result relation.
+ *
+ *		CAUTION: this must not be called for a HOT update.
+ *		We can't defend against that here for lack of info.
+ *		Should we change the API to make it safer?
+ * ----------------------------------------------------------------
+ */
+void
+ExecInsertColStoreTuples(HeapTuple tuple, EState *estate)
+{
+	ResultRelInfo *resultRelInfo;
+	int			i;
+	int			numColumnStores;
+	RelationPtr relationDescs;
+	Relation	heapRelation;
+	ColumnStoreInfo **columnStoreInfoArray;
+	Datum		values[INDEX_MAX_KEYS];	/* FIXME INDEX_MAX_KEYS=32 seems a bit low */
+	bool		isnull[INDEX_MAX_KEYS];
+	ItemPointer	tupleid = &(tuple->t_self);
+	TupleDesc	tupdesc;
+
+	/*
+	 * Get information from the result relation info structure.
+	 */
+	resultRelInfo = estate->es_result_relation_info;
+	numColumnStores = resultRelInfo->ri_NumColumnStores;
+	relationDescs = resultRelInfo->ri_ColumnStoreRelationDescs;
+	columnStoreInfoArray = resultRelInfo->ri_ColumnStoreRelationInfo;
+	heapRelation = resultRelInfo->ri_RelationDesc;
+	tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
+
+	/*
+	 * for each column store, form and insert the tuple
+	 */
+	for (i = 0; i < numColumnStores; i++)
+	{
+		Relation	cstoreRelation = relationDescs[i];
+		ColumnStoreInfo  *cstoreInfo;
+
+		if (cstoreRelation == NULL)	/* XXX seems a bit strange ... */
+			continue;
+
+		cstoreInfo = columnStoreInfoArray[i];
+
+		if (cstoreInfo->csi_ColumnStoreRoutine == NULL)
+			elog(ERROR, "column store routine not available");
+
+		if (cstoreInfo->csi_ColumnStoreRoutine->ExecColumnStoreInsert == NULL)
+			elog(ERROR, "ExecColumnStoreInsert routine not available");
+
+		/*
+		 * FormColumnStoreDatum fills in its values and isnull parameters with
+		 * the appropriate values for the column(s) of the column store.
+		 */
+		FormColumnStoreDatum(cstoreInfo, tuple, tupdesc, values, isnull);
+
+		cstoreInfo->csi_ColumnStoreRoutine->ExecColumnStoreInsert(
+			heapRelation,	/* heap relation */
+			cstoreRelation, /* column store relation */
+			cstoreInfo,		/* column store info */
+			cstoreInfo->csi_NumColumnStoreAttrs,
+			values,			/* array of column store Datums */
+			isnull,			/* null flags */
+			tupleid);		/* tid of heap tuple */
+
+	}
+}
+
+void
+ExecBatchInsertColStoreTuples(int ntuples, HeapTuple *tuples, EState *estate)
+{
+	ResultRelInfo *resultRelInfo;
+	int			i, j, natts;
+	int			numColumnStores;
+	RelationPtr relationDescs;
+	Relation	heapRelation;
+	ColumnStoreInfo **columnStoreInfoArray;
+	Datum		**values;
+	bool		**isnull;
+	ItemPointer	*tupleids;
+	TupleDesc	tupdesc;
+
+	/*
+	 * Get information from the result relation info structure.
+	 */
+	resultRelInfo = estate->es_result_relation_info;
+	numColumnStores = resultRelInfo->ri_NumColumnStores;
+	relationDescs = resultRelInfo->ri_ColumnStoreRelationDescs;
+	columnStoreInfoArray = resultRelInfo->ri_ColumnStoreRelationInfo;
+	heapRelation = resultRelInfo->ri_RelationDesc;
+
+	tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
+	natts = tupdesc->natts;
+
+	/* we'll build the Datum / isnull arrays once for all the column stores */
+	values = (Datum**)palloc0(sizeof(Datum*) * natts);
+	isnull = ( bool**)palloc0(sizeof(bool*) * natts);
+
+	/* allocate arrays only for attributes that are referenced in column stores */
+	for (i = 0; i < numColumnStores; i++)
+	{
+		ColumnStoreInfo *cstinfo = resultRelInfo->ri_ColumnStoreRelationInfo[i];
+
+		for (j = 0; j < cstinfo->csi_NumColumnStoreAttrs; j++)
+		{
+			AttrNumber attnum = cstinfo->csi_KeyAttrNumbers[j];
+			if (values[attnum-1] == NULL)
+			{
+				values[attnum-1] = (Datum*)palloc0(sizeof(Datum) * ntuples);
+				isnull[attnum-1] = ( bool*)palloc0(sizeof(bool)  * ntuples);
+			}
+		}
+	}
+
+	/* we also need an array for tuple IDs */
+	tupleids = (ItemPointer*)palloc0(sizeof(ItemPointer) * ntuples);
+
+	/* populate the arrays */
+	for (i = 0; i < ntuples; i++)
+	{
+		tupleids[i] = &(tuples[i]->t_self);
+
+		for (j = 0; j < natts; j++)
+		{
+			if (values[j] != NULL)
+				values[j][i] = heap_getattr(tuples[i], j, tupdesc, &isnull[j][i]);
+		}
+	}
+
+	/*
+	 * for each column store, form and insert the tuple
+	 */
+	for (i = 0; i < numColumnStores; i++)
+	{
+		Relation	cstoreRelation = relationDescs[i];
+		ColumnStoreInfo  *cstoreInfo;
+
+		/* local subset of values */
+		Datum	  **lvalues;
+		bool	  **lisnull;
+
+		if (cstoreRelation == NULL)	/* XXX seems a bit strange ... */
+			continue;
+
+		cstoreInfo = columnStoreInfoArray[i];
+
+		if (cstoreInfo->csi_ColumnStoreRoutine == NULL)
+			elog(ERROR, "column store routine not available");
+
+		if (cstoreInfo->csi_ColumnStoreRoutine->ExecColumnStoreBatchInsert == NULL)
+			elog(ERROR, "ExecColumnStoreBatchInsert routine not available");
+
+		lvalues = (Datum**)palloc0(sizeof(Datum*) * cstoreInfo->csi_NumColumnStoreAttrs);
+		lisnull = ( bool**)palloc0(sizeof(bool*)  * cstoreInfo->csi_NumColumnStoreAttrs);
+
+		for (j = 0; j < cstoreInfo->csi_NumColumnStoreAttrs; j++)
+		{
+			AttrNumber attnum = cstoreInfo->csi_KeyAttrNumbers[j];
+			lvalues[j] = values[attnum-1];
+			lisnull[j] = isnull[attnum-1];
+		}
+
+		cstoreInfo->csi_ColumnStoreRoutine->ExecColumnStoreBatchInsert(
+			heapRelation,	/* heap relation */
+			cstoreRelation, /* column store relation */
+			cstoreInfo,		/* column store info */
+			ntuples,		/* number of rows in the batch */
+			cstoreInfo->csi_NumColumnStoreAttrs,
+			lvalues,		/* arrays of column store Datums */
+			lisnull,		/* arrays of null flags */
+			tupleids);		/* array of tid of heap tuples */
+
+		pfree(lvalues);
+		pfree(lisnull);
+
+	}
+
+	for (j = 0; j < natts; j++)
+	{
+		if (values[j] != NULL)
+		{
+			pfree(values[j]);
+			pfree(isnull[j]);
+		}
+	}
+
+}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 2c65a90..f379ff9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1473,6 +1473,7 @@ ExecEndPlan(PlanState *planstate, EState *estate)
 	{
 		/* Close indices and then the relation itself */
 		ExecCloseIndices(resultRelInfo);
+		ExecCloseColumnStores(resultRelInfo);
 		heap_close(resultRelInfo->ri_RelationDesc, NoLock);
 		resultRelInfo++;
 	}
@@ -1485,6 +1486,7 @@ ExecEndPlan(PlanState *planstate, EState *estate)
 		resultRelInfo = (ResultRelInfo *) lfirst(l);
 		/* Close indices and then the relation itself */
 		ExecCloseIndices(resultRelInfo);
+		ExecCloseColumnStores(resultRelInfo);
 		heap_close(resultRelInfo->ri_RelationDesc, NoLock);
 	}
 
@@ -2868,6 +2870,7 @@ EvalPlanQualEnd(EPQState *epqstate)
 
 		/* Close indices and then the relation itself */
 		ExecCloseIndices(resultRelInfo);
+		ExecCloseColumnStores(resultRelInfo);
 		heap_close(resultRelInfo->ri_RelationDesc, NoLock);
 	}
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 1ef76d0..c43ee9f 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -39,6 +39,7 @@
 
 #include "access/htup_details.h"
 #include "access/xact.h"
+#include "catalog/colstore.h"
 #include "commands/trigger.h"
 #include "executor/executor.h"
 #include "executor/nodeModifyTable.h"
@@ -402,11 +403,48 @@ ExecInsert(ModifyTableState *mtstate,
 			specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
 			HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
 
-			/* insert the tuple, with the speculative token */
-			newId = heap_insert(resultRelationDesc, tuple,
-								estate->es_output_cid,
-								HEAP_INSERT_SPECULATIVE,
-								NULL);
+			/*
+			 * insert the tuple, with the speculative token
+			 *
+			 * Note: heap_insert returns the tid (location) of the new tuple in
+			 * the t_self field.
+			 *
+			 * We need to remove the columns that are stored in the column store
+			 * from the descriptor and heap tuple, so that we only store the heap
+			 * part using heap_insert. We'll create a new tuple descriptor with
+			 * only the heap attributes, and create a small 'heap tuple' matching
+			 * the descriptor.
+			 *
+			 * FIXME This is just temporary solution, a bit dirty. Needs to be
+			 *       done properly (moved to methods, possibly applied to other
+			 *       places, etc.).
+			 */
+			if (resultRelInfo->ri_NumColumnStores > 0)
+			{
+				HeapTuple heaptuple;
+				TupleDesc heapdesc;
+				TupleDesc fulldesc;
+
+				heaptuple = FilterHeapTuple(resultRelInfo, tuple, &heapdesc);
+
+				fulldesc = resultRelationDesc->rd_att;
+				resultRelationDesc->rd_att = heapdesc;
+
+				newId = heap_insert(resultRelationDesc, heaptuple,
+									estate->es_output_cid,
+									HEAP_INSERT_SPECULATIVE,
+									NULL);
+
+				resultRelationDesc->rd_att = fulldesc;
+
+				heap_freetuple(heaptuple);
+				FreeTupleDesc(heapdesc);
+			}
+			else
+				newId = heap_insert(resultRelationDesc, tuple,
+									estate->es_output_cid,
+									HEAP_INSERT_SPECULATIVE,
+									NULL);
 
 			/* insert index entries for tuple */
 			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
@@ -449,9 +487,30 @@ ExecInsert(ModifyTableState *mtstate,
 			 * Note: heap_insert returns the tid (location) of the new tuple
 			 * in the t_self field.
 			 */
-			newId = heap_insert(resultRelationDesc, tuple,
-								estate->es_output_cid,
-								0, NULL);
+			if (resultRelInfo->ri_NumColumnStores > 0)
+			{
+				HeapTuple heaptuple;
+				TupleDesc heapdesc;
+				TupleDesc fulldesc;
+
+				heaptuple = FilterHeapTuple(resultRelInfo, tuple, &heapdesc);
+
+				fulldesc = resultRelationDesc->rd_att;
+				resultRelationDesc->rd_att = heapdesc;
+
+				newId = heap_insert(resultRelationDesc, heaptuple,
+									estate->es_output_cid,
+									0, NULL);
+
+				resultRelationDesc->rd_att = fulldesc;
+
+				heap_freetuple(heaptuple);
+				FreeTupleDesc(heapdesc);
+			}
+			else
+				newId = heap_insert(resultRelationDesc, tuple,
+									estate->es_output_cid,
+									0, NULL);
 
 			/* insert index entries for tuple */
 			if (resultRelInfo->ri_NumIndices > 0)
@@ -459,6 +518,12 @@ ExecInsert(ModifyTableState *mtstate,
 													   estate, false, NULL,
 													   arbiterIndexes);
 		}
+
+		/*
+		 * insert column store entries for tuple
+		 */
+		if (resultRelInfo->ri_NumColumnStores > 0)
+			ExecInsertColStoreTuples(tuple, estate);
 	}
 
 	if (canSetTag)
@@ -880,12 +945,47 @@ lreplace:;
 		 * can't-serialize error if not. This is a special-case behavior
 		 * needed for referential integrity updates in transaction-snapshot
 		 * mode transactions.
+		 *
+		 * We need to remove the columns that are stored in the column store
+		 * from the descriptor and heap tuple, so that we only store the heap
+		 * part using heap_insert. We'll create a new tuple descriptor with
+		 * only the heap attributes, and create a small 'heap tuple' matching
+		 * the descriptor.
+		 *
+		 * FIXME This is just temporary solution, a bit dirty. Needs to be
+		 *       done properly (moved to methods, possibly applied to other
+		 *       places, etc.).
 		 */
-		result = heap_update(resultRelationDesc, tupleid, tuple,
-							 estate->es_output_cid,
-							 estate->es_crosscheck_snapshot,
-							 true /* wait for commit */ ,
-							 &hufd, &lockmode);
+		if (resultRelInfo->ri_NumColumnStores > 0)
+		{
+			HeapTuple heaptuple;
+			TupleDesc heapdesc;
+			TupleDesc fulldesc;
+
+			heaptuple = FilterHeapTuple(resultRelInfo, tuple, &heapdesc);
+
+			fulldesc = resultRelationDesc->rd_att;
+			resultRelationDesc->rd_att = heapdesc;
+
+			result = heap_update(resultRelationDesc, tupleid, heaptuple,
+								 estate->es_output_cid,
+								 estate->es_crosscheck_snapshot,
+								 true /* wait for commit */ ,
+								 &hufd, &lockmode);
+
+			resultRelationDesc->rd_att = fulldesc;
+
+			heap_freetuple(heaptuple);
+			FreeTupleDesc(heapdesc);
+		}
+		else
+			result = heap_update(resultRelationDesc, tupleid, tuple,
+								 estate->es_output_cid,
+								 estate->es_crosscheck_snapshot,
+								 true /* wait for commit */ ,
+								 &hufd, &lockmode);
+
+
 		switch (result)
 		{
 			case HeapTupleSelfUpdated:
@@ -966,16 +1066,20 @@ lreplace:;
 		 */
 
 		/*
-		 * insert index entries for tuple
+		 * insert index and column store entries for tuple
 		 *
 		 * Note: heap_update returns the tid (location) of the new tuple in
 		 * the t_self field.
 		 *
-		 * If it's a HOT update, we mustn't insert new index entries.
+		 * If it's a HOT update, we mustn't insert new index and column store
+		 * entries.
 		 */
 		if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
 			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
 												   estate, false, NULL, NIL);
+
+		if (resultRelInfo->ri_NumColumnStores > 0 && !HeapTupleIsHeapOnly(tuple))
+			ExecInsertColStoreTuples(tuple, estate);
 	}
 
 	if (canSetTag)
@@ -1546,6 +1650,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 			resultRelInfo->ri_IndexRelationDescs == NULL)
 			ExecOpenIndices(resultRelInfo, mtstate->mt_onconflict != ONCONFLICT_NONE);
 
+		/* TODO should use relhascolstore just like indexes*/
+		ExecOpenColumnStores(resultRelInfo);
+
 		/* Now init the plan for this result rel */
 		estate->es_result_relation_info = resultRelInfo;
 		mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
diff --git a/src/include/catalog/colstore.h b/src/include/catalog/colstore.h
index 20be191..93806d2 100644
--- a/src/include/catalog/colstore.h
+++ b/src/include/catalog/colstore.h
@@ -4,6 +4,7 @@
 #include "nodes/execnodes.h"
 #include "nodes/nodes.h"
 #include "nodes/parsenodes.h"
+#include "nodes/execnodes.h"
 #include "nodes/pg_list.h"
 #include "utils/relcache.h"
 
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 226f905..16dd529 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -375,5 +375,13 @@ extern void check_exclusion_constraint(Relation heap, Relation index,
 						   Datum *values, bool *isnull,
 						   EState *estate, bool newIndex);
 
+/*
+ * prototypes from functions in execColumnStore.c
+ */
+extern void ExecOpenColumnStores(ResultRelInfo *resultRelInfo);
+extern void ExecCloseColumnStores(ResultRelInfo *resultRelInfo);
+extern void ExecInsertColStoreTuples(HeapTuple tuple, EState *estate);
+extern void ExecBatchInsertColStoreTuples(int ntuples, HeapTuple *tuples,
+										  EState *estate);
 
 #endif   /* EXECUTOR_H  */
-- 
2.1.4

