wal_update_changes.patch

text/plain

Filename: wal_update_changes.patch
Type: text/plain
Part: 0
Message: [WIP] Performance Improvement by reducing WAL for Update Operation

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
src/backend/access/heap/heapam.c 392 44
src/backend/executor/nodeModifyTable.c 12 1
src/include/access/heapam.h 2 1
src/include/access/htup.h 9 3
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index f28026b..27efa6b 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -53,6 +53,7 @@
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "storage/bufmgr.h"
@@ -84,7 +85,8 @@ static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
 					TransactionId xid, CommandId cid, int options);
 static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
 				ItemPointerData from, Buffer newbuf, HeapTuple newtup,
-				bool all_visible_cleared, bool new_all_visible_cleared);
+				bool all_visible_cleared, bool new_all_visible_cleared,
+				bool diff_update);
 static bool HeapSatisfiesHOTUpdate(Relation relation, Bitmapset *hot_attrs,
 					   HeapTuple oldtup, HeapTuple newtup);
 
@@ -2673,6 +2675,77 @@ simple_heap_delete(Relation relation, ItemPointer tid)
 }
 
 /*
+ * get_tuple_info - Gets the tuple offset and value.
+ *
+ * calculates the attribute value and offset, where the attribute ends in the
+ * tuple based on the attribute number and previous fetched attribute info.
+ *
+ * offset (I/P and O/P variable) - Input as end of previous attribute offset
+ *		and incase if it is a first attribute then it's value is zero.
+ *		Output as end of the current attribute in the tuple.
+ * usecacheoff (I/P and O/P variable) - Attribute cacheoff can be used or not.
+ */
+static void
+get_tuple_info(Form_pg_attribute *att, HeapTuple tuple, bits8 *bp,
+			   bool hasnulls, int attnum, Datum *value, uint16 *offset,
+			   bool *usecacheoff)
+{
+	Form_pg_attribute thisatt = att[attnum];
+	uint16		off = *offset;
+	bool		slow = *usecacheoff;
+	char	   *tp;
+	HeapTupleHeader tup = tuple->t_data;
+
+	tp = (char *) tup + tup->t_hoff;
+
+	if (hasnulls && att_isnull(attnum, bp))
+	{
+		slow = true;			/* can't use attcacheoff anymore */
+		return;
+	}
+
+	if (!slow && thisatt->attcacheoff >= 0)
+		off = thisatt->attcacheoff;
+	else if (thisatt->attlen == -1)
+	{
+		/*
+		 * We can only cache the offset for a varlena attribute if the offset
+		 * is already suitably aligned, so that there would be no pad bytes in
+		 * any case: then the offset will be valid for either an aligned or
+		 * unaligned value.
+		 */
+		if (!slow &&
+			off == att_align_nominal(off, thisatt->attalign))
+			thisatt->attcacheoff = off;
+		else
+		{
+			off = att_align_pointer(off, thisatt->attalign, -1,
+									tp + off);
+			slow = true;
+		}
+	}
+	else
+	{
+		/* not varlena, so safe to use att_align_nominal */
+		off = att_align_nominal(off, thisatt->attalign);
+
+		if (!slow)
+			thisatt->attcacheoff = off;
+	}
+
+	*value = fetchatt(thisatt, tp + off);
+
+	off = att_addlength_pointer(off, thisatt->attlen, tp + off);
+
+	if (thisatt->attlen <= 0)
+		slow = true;			/* can't use attcacheoff anymore */
+
+	*offset = off;
+	*usecacheoff = slow;
+}
+
+
+/*
  *	heap_update - replace a tuple
  *
  * NB: do not call this directly unless you are prepared to deal with
@@ -2707,7 +2780,8 @@ simple_heap_delete(Relation relation, ItemPointer tid)
 HTSU_Result
 heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 			ItemPointer ctid, TransactionId *update_xmax,
-			CommandId cid, Snapshot crosscheck, bool wait)
+			CommandId cid, Snapshot crosscheck, Bitmapset *modifiedCols,
+			bool wait)
 {
 	HTSU_Result result;
 	TransactionId xid = GetCurrentTransactionId();
@@ -2715,6 +2789,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 	ItemId		lp;
 	HeapTupleData oldtup;
 	HeapTuple	heaptup;
+	HeapTupleData redotup;
 	Page		page;
 	BlockNumber block;
 	Buffer		buffer,
@@ -2730,6 +2805,14 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 	bool		use_hot_update = false;
 	bool		all_visible_cleared = false;
 	bool		all_visible_cleared_new = false;
+	struct
+	{
+		HeapTupleHeaderData hdr;
+		char		data[MaxHeapTupleSize];
+	}			tbuf;
+	char	   *data;
+	bool		diff_update = false;
+	uint16		offset = 0;
 
 	Assert(ItemPointerIsValid(otid));
 
@@ -3098,6 +3181,132 @@ l2:
 		PageSetFull(page);
 	}
 
+	if (modifiedCols)
+	{
+		Form_pg_attribute *att = relation->rd_att->attrs;
+		int			numberOfAttributes;
+		uint16		newOffset = 0;
+		int			attnum;
+		HeapTupleHeader newtuphdr = heaptup->t_data;
+		bits8	   *new_bp = newtuphdr->t_bits;
+		bool		old_hasnulls = HeapTupleHasNulls(&oldtup);
+		bool		new_hasnulls = HeapTupleHasNulls(heaptup);
+		bool		new_usecacheoff = false;
+		Datum		new_value;
+		uint16		data_length;
+
+		/* For NULL value tuples, don't use the optimized path */
+		if (old_hasnulls || new_hasnulls)
+		{
+			goto record_insert;
+		}
+
+		numberOfAttributes = HeapTupleHeaderGetNatts(newtuphdr);
+
+		/*
+		 * Skip the WAL record header for now and frame data of optimized WAL
+		 * update record.
+		 */
+		data = (char *) &tbuf.hdr;
+		offset = newtuphdr->t_hoff;
+
+		for (attnum = 0; attnum < numberOfAttributes; attnum++)
+		{
+			/*
+			 * If the attribute is modified by the update operation, store the
+			 * appropiate offsets in the WAL record, otherwise skip to the
+			 * next attribute.
+			 */
+			if (bms_is_member((attnum + 1) - FirstLowInvalidHeapAttributeNumber,
+							  modifiedCols))
+			{
+				/*
+				 * calculate the offset where the modified attribute starts in
+				 * the new tuple used to store in the WAL record, this will be
+				 * used to traverse the old tuple during recovery.
+				 */
+				newOffset = att_align_nominal(newOffset, att[attnum]->attalign);
+				offset = SHORTALIGN(offset);
+
+				memcpy((data + offset), &newOffset, sizeof(uint16));
+				offset += sizeof(uint16);
+
+				/* get the attribute value and end offset for same */
+				get_tuple_info(att, heaptup, new_bp, new_hasnulls, attnum,
+							   &new_value, &newOffset, &new_usecacheoff);
+
+				/* Increment the offset to store the data of modified column */
+				offset += sizeof(uint16);
+
+				if (att[attnum]->attbyval)
+				{
+					/* pass-by-value */
+					data_length = att[attnum]->attlen;
+					store_att_byval((data + offset), new_value, data_length);
+				}
+				else
+				{
+					if (BPCHAROID == att[attnum]->atttypid)
+					{
+						/* varlena */
+						Pointer		val = DatumGetPointer(new_value);
+
+						if (VARATT_IS_SHORT(val))
+						{
+							/* no alignment for short varlenas */
+							data_length = VARSIZE_SHORT(val);
+							memcpy((data + offset), val, data_length);
+						}
+						else if ((att[attnum]->attstorage != 'p')
+								 && VARATT_CAN_MAKE_SHORT(val))
+						{
+							/* convert to short varlena -- no alignment */
+							data_length = VARATT_CONVERTED_SHORT_SIZE(val);
+							SET_VARSIZE_SHORT(data, data_length);
+							memcpy((data + offset + 1),
+								   VARDATA(val),
+								   (data_length - 1));
+						}
+						else
+						{
+							/* full 4-byte header varlena */
+							data_length = VARSIZE(val);
+							memcpy((data + offset), val, data_length);
+						}
+					}
+					else
+					{
+						/* Not a BPCHAR, proceed without optimization */
+						goto record_insert;
+					}
+				}
+
+				/* Store the length of the modified attribute */
+				memcpy((data + offset - sizeof(uint16)),
+					   &data_length,
+					   sizeof(uint16));
+				offset += data_length;
+			}
+			else
+			{
+				get_tuple_info(att, heaptup, new_bp, new_hasnulls, attnum,
+							   &new_value, &newOffset, &new_usecacheoff);
+			}
+		}
+
+		/*
+		 * FIXME: At the end of calculating the optimization tuple, if the
+		 * optimized tuple length is more than 3/4 of the original tuple then
+		 * ignore the optimization.
+		 */
+		if (offset < ((heaptup->t_len >> 1) + (heaptup->t_len >> 2)))
+		{
+			diff_update = true;
+		}
+	}
+
+record_insert:;
+
 	/* NO EREPORT(ERROR) from here till changes are logged */
 	START_CRIT_SECTION();
 
@@ -3173,10 +3382,27 @@ l2:
 	/* XLOG stuff */
 	if (RelationNeedsWAL(relation))
 	{
-		XLogRecPtr	recptr = log_heap_update(relation, buffer, oldtup.t_self,
-											 newbuf, heaptup,
-											 all_visible_cleared,
-											 all_visible_cleared_new);
+		XLogRecPtr	recptr;
+
+		if (diff_update)
+		{
+			/* Copy the tuple header to the WAL tuple */
+			memcpy(&tbuf.hdr, heaptup->t_data, heaptup->t_data->t_hoff);
+			redotup.t_len = offset;
+			redotup.t_data = (HeapTupleHeader) &tbuf;
+			redotup.t_self = heaptup->t_self;
+			redotup.t_tableOid = heaptup->t_tableOid;
+		}
+		else
+		{
+			memcpy(&redotup, heaptup, sizeof(HeapTupleData));
+		}
+
+		recptr = log_heap_update(relation, buffer, oldtup.t_self,
+								 newbuf, &redotup,
+								 all_visible_cleared,
+								 all_visible_cleared_new,
+								 diff_update);
 
 		if (newbuf != buffer)
 		{
@@ -3363,6 +3589,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
 	result = heap_update(relation, otid, tup,
 						 &update_ctid, &update_xmax,
 						 GetCurrentCommandId(true), InvalidSnapshot,
+						 NULL,
 						 true /* wait for commit */ );
 	switch (result)
 	{
@@ -4407,7 +4634,8 @@ log_heap_visible(RelFileNode rnode, BlockNumber block, Buffer vm_buffer,
 static XLogRecPtr
 log_heap_update(Relation reln, Buffer oldbuf, ItemPointerData from,
 				Buffer newbuf, HeapTuple newtup,
-				bool all_visible_cleared, bool new_all_visible_cleared)
+				bool all_visible_cleared, bool new_all_visible_cleared,
+				bool diff_update)
 {
 	xl_heap_update xlrec;
 	xl_heap_header xlhdr;
@@ -4426,9 +4654,15 @@ log_heap_update(Relation reln, Buffer oldbuf, ItemPointerData from,
 
 	xlrec.target.node = reln->rd_node;
 	xlrec.target.tid = from;
-	xlrec.all_visible_cleared = all_visible_cleared;
+	xlrec.diff_update = diff_update;
 	xlrec.newtid = newtup->t_self;
-	xlrec.new_all_visible_cleared = new_all_visible_cleared;
+
+	/*
+	 * MSB 4 bits tells PD_ALL_VISIBLE was cleared of new page and rest 4 bits
+	 * for the old page
+	 */
+	xlrec.new_all_visible_cleared |= all_visible_cleared;
+	xlrec.new_all_visible_cleared |= new_all_visible_cleared << 4;
 
 	rdata[0].data = (char *) &xlrec;
 	rdata[0].len = SizeOfHeapUpdate;
@@ -5217,14 +5451,20 @@ heap_xlog_update(XLogRecPtr lsn, XLogRecord *record, bool hot_update)
 	}			tbuf;
 	xl_heap_header xlhdr;
 	int			hsize;
-	uint32		newlen;
+	uint32		newlen = 0;
 	Size		freespace;
+	bool		old_tup_modify = true;	/* flag used to indicate, whether old
+										 * tuple needs the modification or not */
+
+	/* Initialize the buffer, used to frame the new tuple */
+	MemSet((char *) &tbuf.hdr, 0, sizeof(HeapTupleHeaderData));
+	hsize = SizeOfHeapUpdate + SizeOfHeapHeader;
 
 	/*
 	 * The visibility map may need to be fixed even if the heap page is
 	 * already up-to-date.
 	 */
-	if (xlrec->all_visible_cleared)
+	if (xlrec->new_all_visible_cleared & 0x0F)
 	{
 		Relation	reln = CreateFakeRelcacheEntry(xlrec->target.node);
 		BlockNumber block = ItemPointerGetBlockNumber(&xlrec->target.tid);
@@ -5240,16 +5480,32 @@ heap_xlog_update(XLogRecPtr lsn, XLogRecord *record, bool hot_update)
 	{
 		if (samepage)
 			return;				/* backup block covered both changes */
-		goto newt;
+
+		/* Need the old page to read the old tuple data, no update required */
+		if (!xlrec->diff_update)
+			goto newt;
+
+		old_tup_modify = false;
 	}
 
 	/* Deal with old tuple version */
-
 	buffer = XLogReadBuffer(xlrec->target.node,
 							ItemPointerGetBlockNumber(&(xlrec->target.tid)),
 							false);
 	if (!BufferIsValid(buffer))
+	{
+		/*
+		 * Incase of diff update, if the old buffer is not available raise a
+		 * panic as diff update needs the old buffer to frame the new tuple.
+		 */
+		if (xlrec->diff_update)
+		{
+			elog(PANIC, "heap_update_redo: invalid buffer");
+		}
+
 		goto newt;
+	}
+
 	page = (Page) BufferGetPage(buffer);
 
 	if (XLByteLE(lsn, PageGetLSN(page)))		/* changes are applied */
@@ -5257,7 +5513,12 @@ heap_xlog_update(XLogRecPtr lsn, XLogRecord *record, bool hot_update)
 		UnlockReleaseBuffer(buffer);
 		if (samepage)
 			return;
-		goto newt;
+
+		/* Need the old page to read the old tuple data, no update required */
+		if (!xlrec->diff_update)
+			goto newt;
+
+		old_tup_modify = false;
 	}
 
 	offnum = ItemPointerGetOffsetNumber(&(xlrec->target.tid));
@@ -5269,25 +5530,103 @@ heap_xlog_update(XLogRecPtr lsn, XLogRecord *record, bool hot_update)
 
 	htup = (HeapTupleHeader) PageGetItem(page, lp);
 
-	htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
-						  HEAP_XMAX_INVALID |
-						  HEAP_XMAX_IS_MULTI |
-						  HEAP_IS_LOCKED |
-						  HEAP_MOVED);
-	if (hot_update)
-		HeapTupleHeaderSetHotUpdated(htup);
-	else
-		HeapTupleHeaderClearHotUpdated(htup);
-	HeapTupleHeaderSetXmax(htup, record->xl_xid);
-	HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
-	/* Set forward chain link in t_ctid */
-	htup->t_ctid = xlrec->newtid;
+	if (xlrec->diff_update)
+	{
+		uint16		len = 0,
+					data_length,
+					oldoffset = 0;
+		uint32		t_length;
+		char	   *olddata = (char *) htup + htup->t_hoff;
+		char	   *data = (char *) &tbuf.hdr + htup->t_hoff;
+		char	   *redodata = (char *) xlrec + hsize + htup->t_hoff
+		- offsetof(HeapTupleHeaderData, t_bits);
 
-	/* Mark the page as a candidate for pruning */
-	PageSetPrunable(page, record->xl_xid);
+		/*
+		 * Frame the new tuple from old and wal tuple
+		 *
+		 * Get the data start pointer from old and redo data.
+		 *
+		 * calculate the tuple length from old tuple and redo record length.
+		 *
+		 * The redo data is in the format as "offset + length + new data"
+		 *
+		 * The first offset in the redo record gives the offset where the
+		 * modification starts, the same will give you the length of the data
+		 * needs to be copied from old tuple.
+		 *
+		 * Once the old tuple data copied, then increase the offset by the
+		 * copied length.
+		 *
+		 * Get the length and value of modified column from wal tuple and
+		 * increase the old tuple offset also with the modified column length.
+		 *
+		 * Repeat this procedure until the wal tuple reaches the end.
+		 */
 
-	if (xlrec->all_visible_cleared)
-		PageClearAllVisible(page);
+		newlen = record->xl_len - hsize;
+		Assert(newlen <= MaxHeapTupleSize);
+
+		t_length = ItemIdGetLength(lp) - htup->t_hoff;
+		newlen -= (htup->t_hoff - offsetof(HeapTupleHeaderData, t_bits));
+		len = 0;
+
+		/* Frame the new tuple from the old and WAL tuples */
+		while (len < newlen)
+		{
+			data_length = *(uint16 *) (redodata + len) - oldoffset;
+
+			/* Copy the old tuple data */
+			memcpy(data, (olddata + oldoffset), data_length);
+			data += data_length;
+			oldoffset += data_length;
+
+			len += sizeof(uint16);
+			data_length = *(uint16 *) (redodata + len);
+			oldoffset += data_length;
+
+			len += sizeof(uint16);
+
+			/* Copy the modified attribute data from WAL tuple */
+			memcpy(data, (redodata + len), data_length);
+
+			data += data_length;
+			len += data_length;
+
+			len = SHORTALIGN(len);
+		}
+
+		/* Copy the remaining old tuple data to the new tuple */
+		if (oldoffset < t_length)
+		{
+			memcpy(data, (olddata + oldoffset), (t_length - oldoffset));
+		}
+
+		newlen = t_length
+			+ (htup->t_hoff - offsetof(HeapTupleHeaderData, t_bits));
+	}
+
+	if (old_tup_modify)
+	{
+		htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
+							  HEAP_XMAX_INVALID |
+							  HEAP_XMAX_IS_MULTI |
+							  HEAP_IS_LOCKED |
+							  HEAP_MOVED);
+		if (hot_update)
+			HeapTupleHeaderSetHotUpdated(htup);
+		else
+			HeapTupleHeaderClearHotUpdated(htup);
+		HeapTupleHeaderSetXmax(htup, record->xl_xid);
+		HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
+		/* Set forward chain link in t_ctid */
+		htup->t_ctid = xlrec->newtid;
+
+		/* Mark the page as a candidate for pruning */
+		PageSetPrunable(page, record->xl_xid);
+
+		if (xlrec->new_all_visible_cleared & 0x0F)
+			PageClearAllVisible(page);
+	}
 
 	/*
 	 * this test is ugly, but necessary to avoid thinking that insert change
@@ -5295,9 +5634,14 @@ heap_xlog_update(XLogRecPtr lsn, XLogRecord *record, bool hot_update)
 	 */
 	if (samepage)
 		goto newsame;
-	PageSetLSN(page, lsn);
-	PageSetTLI(page, ThisTimeLineID);
-	MarkBufferDirty(buffer);
+
+	if (old_tup_modify)
+	{
+		PageSetLSN(page, lsn);
+		PageSetTLI(page, ThisTimeLineID);
+		MarkBufferDirty(buffer);
+	}
+
 	UnlockReleaseBuffer(buffer);
 
 	/* Deal with new tuple */
@@ -5308,7 +5652,7 @@ newt:;
 	 * The visibility map may need to be fixed even if the heap page is
 	 * already up-to-date.
 	 */
-	if (xlrec->new_all_visible_cleared)
+	if ((xlrec->new_all_visible_cleared >> 4) & 0x0F)
 	{
 		Relation	reln = CreateFakeRelcacheEntry(xlrec->target.node);
 		BlockNumber block = ItemPointerGetBlockNumber(&xlrec->newtid);
@@ -5355,19 +5699,23 @@ newsame:;
 	if (PageGetMaxOffsetNumber(page) + 1 < offnum)
 		elog(PANIC, "heap_update_redo: invalid max offset number");
 
-	hsize = SizeOfHeapUpdate + SizeOfHeapHeader;
-
-	newlen = record->xl_len - hsize;
-	Assert(newlen <= MaxHeapTupleSize);
 	memcpy((char *) &xlhdr,
 		   (char *) xlrec + SizeOfHeapUpdate,
 		   SizeOfHeapHeader);
+
 	htup = &tbuf.hdr;
-	MemSet((char *) htup, 0, sizeof(HeapTupleHeaderData));
-	/* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
-	memcpy((char *) htup + offsetof(HeapTupleHeaderData, t_bits),
-		   (char *) xlrec + hsize,
-		   newlen);
+
+	if (!xlrec->diff_update)
+	{
+		newlen = record->xl_len - hsize;
+		Assert(newlen <= MaxHeapTupleSize);
+
+		/* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
+		memcpy((char *) htup + offsetof(HeapTupleHeaderData, t_bits),
+			   (char *) xlrec + hsize,
+			   newlen);
+	}
+
 	newlen += offsetof(HeapTupleHeaderData, t_bits);
 	htup->t_infomask2 = xlhdr.t_infomask2;
 	htup->t_infomask = xlhdr.t_infomask;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index a7bce75..4c22aea 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -48,6 +48,7 @@
 #include "utils/memutils.h"
 #include "utils/rel.h"
 #include "utils/tqual.h"
+#include "parser/parsetree.h"
 
 
 /*
@@ -478,12 +479,14 @@ ExecUpdate(ItemPointer tupleid,
 		   bool canSetTag)
 {
 	HeapTuple	tuple;
+	HeapTuple	old_tuple;
 	ResultRelInfo *resultRelInfo;
 	Relation	resultRelationDesc;
 	HTSU_Result result;
 	ItemPointerData update_ctid;
 	TransactionId update_xmax;
 	List	   *recheckIndexes = NIL;
+	Bitmapset  *modifiedCols = NULL;
 
 	/*
 	 * abort the operation if not running transactions
@@ -495,7 +498,7 @@ ExecUpdate(ItemPointer tupleid,
 	 * get the heap tuple out of the tuple table slot, making sure we have a
 	 * writable copy
 	 */
-	tuple = ExecMaterializeSlot(slot);
+	tuple = old_tuple = ExecMaterializeSlot(slot);
 
 	/*
 	 * get information on the (current) result relation
@@ -553,6 +556,13 @@ lreplace:;
 		if (resultRelationDesc->rd_att->constr)
 			ExecConstraints(resultRelInfo, slot, estate);
 
+		if ((resultRelationDesc->rd_toastoid == InvalidOid)
+			&& (old_tuple == tuple))
+		{
+			modifiedCols = (rt_fetch(resultRelInfo->ri_RangeTableIndex,
+									 estate->es_range_table)->modifiedCols);
+		}
+
 		/*
 		 * replace the heap tuple
 		 *
@@ -566,6 +576,7 @@ lreplace:;
 							 &update_ctid, &update_xmax,
 							 estate->es_output_cid,
 							 estate->es_crosscheck_snapshot,
+							 modifiedCols,
 							 true /* wait for commit */ );
 		switch (result)
 		{
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 660a854..5e91ba8 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -105,7 +105,8 @@ extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
 extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
 			HeapTuple newtup,
 			ItemPointer ctid, TransactionId *update_xmax,
-			CommandId cid, Snapshot crosscheck, bool wait);
+			CommandId cid, Snapshot crosscheck, Bitmapset  *modifiedCols,
+			bool wait);
 extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
 				Buffer *buffer, ItemPointer ctid,
 				TransactionId *update_xmax, CommandId cid,
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index b289e14..f5c08ed 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -692,13 +692,19 @@ typedef struct xl_multi_insert_tuple
 
 #define SizeOfMultiInsertTuple	(offsetof(xl_multi_insert_tuple, t_hoff) + sizeof(uint8))
 
-/* This is what we need to know about update|hot_update */
+/* This is what we need to know about update|hot_update|optimized_update */
 typedef struct xl_heap_update
 {
 	xl_heaptid	target;			/* deleted tuple id */
 	ItemPointerData newtid;		/* new inserted tuple id */
-	bool		all_visible_cleared;	/* PD_ALL_VISIBLE was cleared */
-	bool		new_all_visible_cleared;		/* same for the page of newtid */
+	bool	diff_update;		/* optimized update or not */
+	/*
+	 * To keep the structure size same all_visible_cleared is merged with
+	 * new_all_visible_cleared.
+	 */
+	bool	new_all_visible_cleared; /* MSB 4 bits tells PD_ALL_VISIBLE	was
+										cleared of new page and rest 4 bits
+										for the old page */
 	/* NEW TUPLE xl_heap_header AND TUPLE DATA FOLLOWS AT END OF STRUCT */
 } xl_heap_update;