commit 863379cc8478422aaeb55a73536ab286d9bbfa29
Author: Serge Rielau <srielau@salesforce.com>
Date:   Wed Oct 19 17:06:12 2016 -0700

    fast_defaults

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 6d0f3f3..8880ed3 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -76,6 +76,95 @@
  * ----------------------------------------------------------------
  */

+/*
+ * Pad an attribute beyond the size of the tuple
+ * with NULL or the exist default
+ */
+static Datum getmissingattr(TupleDesc tupleDesc,
+                            int attnum, bool *isnull)
+{
+	int         defnum;
+	Form_pg_attribute att;
+	AttrDefault *attrdef;
+
+	Assert(attnum <= tupleDesc->natts);
+	Assert(attnum > 0);
+
+	att = tupleDesc->attrs[attnum - 1];
+
+	if(att->atthasexistdef)
+	{
+		Assert(tupleDesc->constr);
+		Assert(tupleDesc->constr->num_defval > 0);
+		Assert(tupleDesc->constr->has_existdefval);
+
+		attrdef = tupleDesc->constr->defval;
+		for (defnum = tupleDesc->constr->num_defval - 1;
+			 defnum >= 0; defnum--)
+		{
+			if (attrdef[defnum].adnum == attnum)
+			{
+				if (attrdef[defnum].adexistNull)
+				{
+					*isnull = true;
+					return (Datum) 0;
+				}
+				else
+				{
+					*isnull = false;
+					return attrdef[defnum].adexist;
+				}
+			}
+		}
+		Assert(defnum >= 0);
+	}
+
+	*isnull = true;
+	return PointerGetDatum(NULL);
+}
+
+static void slot_getdefaultattrs(TupleTableSlot *slot, int startAttNum)
+{
+	int         tdesc_natts = slot->tts_tupleDescriptor->natts;
+	int         defattnum;
+	int         defnum;
+	AttrDefault *attrdef;
+
+	if (slot->tts_tupleDescriptor->constr
+		&& slot->tts_tupleDescriptor->constr->has_existdefval)
+	{
+		defnum = slot->tts_tupleDescriptor->constr->num_defval - 1;
+		attrdef = slot->tts_tupleDescriptor->constr->defval;
+	}
+	else
+	{
+		defnum = -1;
+		attrdef = NULL;
+	}
+
+	for (defattnum = tdesc_natts - 1; defattnum >= startAttNum; defattnum--)
+	{
+		if (defnum >= 0 && attrdef[defnum].adnum - 1 == defattnum)
+		{
+			if (attrdef[defnum].adexistNull)
+			{
+				slot->tts_values[defattnum] = PointerGetDatum(NULL);
+				slot->tts_isnull[defattnum] = true;
+			}
+			else
+			{
+				slot->tts_values[defattnum] = attrdef[defnum].adexist;
+				slot->tts_isnull[defattnum] = false;
+			}
+			defnum--;
+		}
+		else
+		{
+			slot->tts_values[defattnum] = PointerGetDatum(NULL);
+			slot->tts_isnull[defattnum] = true;
+		}
+	}
+}

 /*
  * heap_compute_data_size
@@ -133,6 +222,127 @@ heap_compute_data_size(TupleDesc tupleDesc,
 	return data_length;
 }

+static inline void
+fill_val(Form_pg_attribute  att,
+		 bits8            **bit,
+		 int               *bitmask,
+		 char             **dataP,
+		 uint16            *infomask,
+		 Datum              datum,
+		 bool               isnull)
+{
+	Size data_length;
+	char *data = *dataP;
+	/*
+	 * If we're building a null bitmap, set the appropriate bit for the
+	 * current column value here.
+	 */
+	if (bit != NULL)
+	{
+		if (*bitmask != HIGHBIT)
+			*bitmask <<= 1;
+		else
+		{
+			*bit += 1;
+			**bit = 0x0;
+			*bitmask = 1;
+		}
+
+		if (isnull)
+		{
+			*infomask |= HEAP_HASNULL;
+			return;
+		}
+
+		**bit |= *bitmask;
+	}
+
+	Assert(att);
+
+	/*
+	 * XXX we use the att_align macros on the pointer value itself, not on
+	 * an offset.  This is a bit of a hack.
+	 */
+	if (att->attbyval)
+	{
+		/* pass-by-value */
+		data = (char *) att_align_nominal(data, att->attalign);
+		store_att_byval(data, datum, att->attlen);
+		data_length = att->attlen;
+	}
+	else if (att->attlen == -1)
+	{
+		/* varlena */
+		Pointer		val = DatumGetPointer(datum);
+
+		*infomask |= HEAP_HASVARWIDTH;
+		if (VARATT_IS_EXTERNAL(val))
+		{
+			if (VARATT_IS_EXTERNAL_EXPANDED(val))
+			{
+				/*
+				 * we want to flatten the expanded value so that the
+				 * constructed tuple doesn't depend on it
+				 */
+				ExpandedObjectHeader *eoh = DatumGetEOHP(datum);
+
+				data = (char *) att_align_nominal(data,
+												  att->attalign);
+				data_length = EOH_get_flat_size(eoh);
+				EOH_flatten_into(eoh, data, data_length);
+			}
+			else
+			{
+				*infomask |= HEAP_HASEXTERNAL;
+				/* no alignment, since it's short by definition */
+				data_length = VARSIZE_EXTERNAL(val);
+				memcpy(data, val, data_length);
+			}
+		}
+		else if (VARATT_IS_SHORT(val))
+		{
+			/* no alignment for short varlenas */
+			data_length = VARSIZE_SHORT(val);
+			memcpy(data, val, data_length);
+		}
+		else if (VARLENA_ATT_IS_PACKABLE(att) &&
+				 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 + 1, VARDATA(val), data_length - 1);
+		}
+		else
+		{
+			/* full 4-byte header varlena */
+			data = (char *) att_align_nominal(data,
+											  att->attalign);
+			data_length = VARSIZE(val);
+			memcpy(data, val, data_length);
+		}
+	}
+	else if (att->attlen == -2)
+	{
+		/* cstring ... never needs alignment */
+		*infomask |= HEAP_HASVARWIDTH;
+		Assert(att->attalign == 'c');
+		data_length = strlen(DatumGetCString(datum)) + 1;
+		memcpy(data, DatumGetPointer(datum), data_length);
+	}
+	else
+	{
+		/* fixed-length pass-by-reference */
+		data = (char *) att_align_nominal(data, att->attalign);
+		Assert(att->attlen > 0);
+		data_length = att->attlen;
+		memcpy(data, DatumGetPointer(datum), data_length);
+	}
+
+	data += data_length;
+	*dataP = data;
+}
+
 /*
  * heap_fill_tuple
  *		Load data portion of a tuple from values/isnull arrays
@@ -174,110 +384,13 @@ heap_fill_tuple(TupleDesc tupleDesc,

 	for (i = 0; i < numberOfAttributes; i++)
 	{
-		Size		data_length;
-
-		if (bit != NULL)
-		{
-			if (bitmask != HIGHBIT)
-				bitmask <<= 1;
-			else
-			{
-				bitP += 1;
-				*bitP = 0x0;
-				bitmask = 1;
-			}
-
-			if (isnull[i])
-			{
-				*infomask |= HEAP_HASNULL;
-				continue;
-			}
-
-			*bitP |= bitmask;
-		}
-
-		/*
-		 * XXX we use the att_align macros on the pointer value itself, not on
-		 * an offset.  This is a bit of a hack.
-		 */
-
-		if (att[i]->attbyval)
-		{
-			/* pass-by-value */
-			data = (char *) att_align_nominal(data, att[i]->attalign);
-			store_att_byval(data, values[i], att[i]->attlen);
-			data_length = att[i]->attlen;
-		}
-		else if (att[i]->attlen == -1)
-		{
-			/* varlena */
-			Pointer		val = DatumGetPointer(values[i]);
-
-			*infomask |= HEAP_HASVARWIDTH;
-			if (VARATT_IS_EXTERNAL(val))
-			{
-				if (VARATT_IS_EXTERNAL_EXPANDED(val))
-				{
-					/*
-					 * we want to flatten the expanded value so that the
-					 * constructed tuple doesn't depend on it
-					 */
-					ExpandedObjectHeader *eoh = DatumGetEOHP(values[i]);
-
-					data = (char *) att_align_nominal(data,
-													  att[i]->attalign);
-					data_length = EOH_get_flat_size(eoh);
-					EOH_flatten_into(eoh, data, data_length);
-				}
-				else
-				{
-					*infomask |= HEAP_HASEXTERNAL;
-					/* no alignment, since it's short by definition */
-					data_length = VARSIZE_EXTERNAL(val);
-					memcpy(data, val, data_length);
-				}
-			}
-			else if (VARATT_IS_SHORT(val))
-			{
-				/* no alignment for short varlenas */
-				data_length = VARSIZE_SHORT(val);
-				memcpy(data, val, data_length);
-			}
-			else if (VARLENA_ATT_IS_PACKABLE(att[i]) &&
-					 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 + 1, VARDATA(val), data_length - 1);
-			}
-			else
-			{
-				/* full 4-byte header varlena */
-				data = (char *) att_align_nominal(data,
-												  att[i]->attalign);
-				data_length = VARSIZE(val);
-				memcpy(data, val, data_length);
-			}
-		}
-		else if (att[i]->attlen == -2)
-		{
-			/* cstring ... never needs alignment */
-			*infomask |= HEAP_HASVARWIDTH;
-			Assert(att[i]->attalign == 'c');
-			data_length = strlen(DatumGetCString(values[i])) + 1;
-			memcpy(data, DatumGetPointer(values[i]), data_length);
-		}
-		else
-		{
-			/* fixed-length pass-by-reference */
-			data = (char *) att_align_nominal(data, att[i]->attalign);
-			Assert(att[i]->attlen > 0);
-			data_length = att[i]->attlen;
-			memcpy(data, DatumGetPointer(values[i]), data_length);
-		}
-
-		data += data_length;
+		fill_val(att[i],
+				 bitP ? &bitP : NULL,
+				 &bitmask,
+				 &data,
+				 infomask,
+				 values ? values[i] : PointerGetDatum(NULL),
+				 isnull ? isnull[i] : true);
 	}

 	Assert((data - start) == data_size);
@@ -294,10 +407,24 @@ heap_fill_tuple(TupleDesc tupleDesc,
  * ----------------
  */
 bool
-heap_attisnull(HeapTuple tup, int attnum)
+heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
 {
+	/* We allow a NULL tupledesc for catalog relations */
+	Assert(tupleDesc || attnum <= tupleDesc->natts);
+
 	if (attnum > (int) HeapTupleHeaderGetNatts(tup->t_data))
-		return true;
+	{
+		if (tupleDesc &&
+			attnum <= tupleDesc->natts &&
+			tupleDesc->attrs[attnum - 1]->atthasexistdef)
+		{
+			return false;
+		}
+		else
+		{
+			return true;
+		}
+    }

 	if (attnum > 0)
 	{
@@ -646,6 +773,253 @@ heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest)
 	memcpy((char *) dest->t_data, (char *) src->t_data, src->t_len);
 }

+static void
+expand_tuple(HeapTuple    *targetHeapTuple,
+				  MinimalTuple *targetMinimalTuple,
+				  HeapTuple     sourceTuple,
+				  TupleDesc     tupleDesc)
+{
+	Form_pg_attribute *att     = NULL;
+	AttrDefault       *attrdef = NULL;
+	int                attnum;
+	int                defnum;
+	int                firstdefnum = 0;
+	int                numdef = 0;
+	bool               hasNulls    = HeapTupleHasNulls(sourceTuple);
+	bool               hasVarWidth = HeapTupleHasVarWidth(sourceTuple);
+	HeapTupleHeader    targetTHeader;
+	HeapTupleHeader    sourceTHeader = sourceTuple->t_data;
+	int                sourceNatts = HeapTupleHeaderGetNatts(sourceTHeader);
+	int                natts       = tupleDesc->natts;
+	int                sourceIndicatorLen;
+	int                targetIndicatorLen;
+	Size               sourceDataLen = sourceTuple->t_len - sourceTHeader->t_hoff;
+	Size               targetDataLen;
+	Size               len;
+	int                hoff;
+	bits8             *nullBits = NULL;
+	int                bitMask;
+	char              *targetData;
+	uint16            *infoMask;
+
+	Assert(targetHeapTuple && !targetMinimalTuple
+		   || !targetHeapTuple && targetMinimalTuple);
+
+	Assert(sourceNatts < natts);
+
+	sourceIndicatorLen = (hasNulls ? BITMAPLEN(sourceNatts) : 0);
+
+	targetDataLen = sourceDataLen;
+
+	if (tupleDesc->constr &&
+		tupleDesc->constr->has_existdefval)
+	{
+		/* If so we want to ingest the exist defaults into the tuple
+		 * Compute the extra length for the values array and the variable length data
+		 */
+		numdef  = tupleDesc->constr->num_defval;
+		att     = tupleDesc->attrs;
+		attrdef = tupleDesc->constr->defval;
+
+		/*
+		 * Unfortunately the data length computation is
+		 * directional we have to walk the attributes incrementally
+		 */
+
+		for (firstdefnum = 0;
+			 firstdefnum < numdef
+				 && attrdef[firstdefnum].adnum <= sourceNatts;
+			 firstdefnum++)
+		{ /* empty */ }
+
+		for (defnum = firstdefnum, attnum = sourceNatts + 1; attnum <= natts; attnum++)
+		{
+			if (defnum >= numdef)
+			{
+				hasNulls = true;
+				break;
+			}
+
+			if (attrdef[defnum].adnum == attnum)
+			{
+				if (attrdef[defnum].adexistNull)
+					hasNulls = true;
+				else
+				{
+					targetDataLen = att_align_datum(targetDataLen,
+													att[attnum - 1]->attalign,
+													att[attnum - 1]->attlen,
+													attrdef[defnum].adexist);
+
+					targetDataLen = att_addlength_pointer(targetDataLen,
+														  att[attnum - 1]->attlen,
+														  attrdef[defnum].adexist);
+					if (att[attnum - 1]->attlen == -1 ||
+						att[attnum - 1]->attlen == -2)
+						hasVarWidth = true;
+				}
+				defnum++;
+			}
+			else
+				hasNulls = true;
+		}
+	} /* end if Have exist defaults */
+	else
+	{
+		hasNulls = true;
+	}
+
+	len = 0;
+
+	if (hasNulls)
+	{
+		targetIndicatorLen = BITMAPLEN(natts);
+		len += targetIndicatorLen;
+	}
+	else
+		targetIndicatorLen = 0;
+
+	if (tupleDesc->tdhasoid)
+		len += sizeof(Oid);
+
+	/*
+	 * Allocate and zero the space needed.  Note that the tuple body and
+	 * HeapTupleData management structure are allocated in one chunk.
+	 */
+	if (targetHeapTuple)
+	{
+		len += offsetof(HeapTupleHeaderData, t_bits);
+		hoff = len = MAXALIGN(len); /* align user data safely */
+		len += targetDataLen;
+
+		*targetHeapTuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
+		(*targetHeapTuple)->t_data
+			= targetTHeader
+			= (HeapTupleHeader) ((char *) *targetHeapTuple + HEAPTUPLESIZE);
+		(*targetHeapTuple)->t_len = len;
+		(*targetHeapTuple)->t_tableOid = sourceTuple->t_tableOid;
+		ItemPointerSetInvalid(&((*targetHeapTuple)->t_self));
+
+		targetTHeader->t_infomask = sourceTHeader->t_infomask;
+		targetTHeader->t_hoff = hoff;
+		HeapTupleHeaderSetNatts(targetTHeader, natts);
+		HeapTupleHeaderSetDatumLength(targetTHeader, len);
+		HeapTupleHeaderSetTypeId(targetTHeader, tupleDesc->tdtypeid);
+		HeapTupleHeaderSetTypMod(targetTHeader, tupleDesc->tdtypmod);
+		/* We also make sure that t_ctid is invalid unless explicitly set */
+		ItemPointerSetInvalid(&(targetTHeader->t_ctid));
+		if (targetIndicatorLen > 0)
+			nullBits = (bits8 *) ((char *) (*targetHeapTuple)->t_data
+								  + offsetof(HeapTupleHeaderData, t_bits));
+		targetData = (char *) (*targetHeapTuple)->t_data + hoff;
+		infoMask = &(targetTHeader->t_infomask);
+	}
+	else
+	{
+		len += SizeofMinimalTupleHeader;
+		hoff = len = MAXALIGN(len); /* align user data safely */
+		len += targetDataLen;
+
+		*targetMinimalTuple = (MinimalTuple) palloc0(len);
+		(*targetMinimalTuple)->t_len = len;
+		(*targetMinimalTuple)->t_hoff = hoff + MINIMAL_TUPLE_OFFSET;
+		(*targetMinimalTuple)->t_infomask = sourceTHeader->t_infomask;
+		/* Same macro works for MinimalTuples */
+		HeapTupleHeaderSetNatts(*targetMinimalTuple, natts);
+		if (targetIndicatorLen > 0)
+			nullBits = (bits8 *) ((char *) *targetMinimalTuple
+								  + offsetof(MinimalTupleData, t_bits));
+		targetData = (char *) *targetMinimalTuple + hoff;
+		infoMask = &((*targetMinimalTuple)->t_infomask);
+	}
+
+	if (targetIndicatorLen > 0)
+	{
+		if (sourceIndicatorLen > 0)
+		{
+			/* if bitmap pre-existed copy in - all is set */
+			memcpy(nullBits,
+				   ((char *) sourceTHeader)
+				   +  offsetof(HeapTupleHeaderData, t_bits),
+				   sourceIndicatorLen);
+			nullBits += sourceIndicatorLen - 1;
+		}
+		else
+		{
+			sourceIndicatorLen = BITMAPLEN(sourceNatts);
+			/* Set NOT NULL for all existing attributes */
+			memset(nullBits, 0xff, sourceIndicatorLen);
+
+			nullBits += sourceIndicatorLen - 1;
+
+			if (sourceNatts & 0x07)
+			{
+				/* build the mask (inverted!) */
+				bitMask = 0xff << (sourceNatts & 0x07);
+				/* Voila */
+				*nullBits = ~bitMask;
+			}
+		}
+
+		bitMask = (1 << ((sourceNatts - 1) & 0x07));
+	} /* End if have null bitmap */
+
+	memcpy(targetData,
+		   ((char *) sourceTuple->t_data) + sourceTHeader->t_hoff,
+		   sourceDataLen);
+
+	/* if there are no exist defaults there is nothing more to do */
+	if (firstdefnum < numdef)
+	{
+		targetData += sourceDataLen;
+		defnum = firstdefnum;
+
+		/* Now fill in the defaults */
+		for (attnum = sourceNatts + 1;
+			 attnum <= natts && defnum < tupleDesc->constr->num_defval;
+			 attnum++)
+		{
+			if (attrdef[defnum].adnum == attnum)
+			{
+				fill_val(att[attnum -1],
+						 nullBits ? &nullBits : NULL,
+						 &bitMask,
+						 &targetData,
+						 infoMask,
+						 attrdef[defnum].adexist,
+						 attrdef[defnum].adexistNull);
+				defnum++;
+			}
+			else
+			{
+				fill_val(att[attnum -1],
+						 &nullBits,
+						 &bitMask,
+						 &targetData,
+						 infoMask,
+						 (Datum) 0,
+						 true);
+			}
+		} /* end loop over defaulted attributes */
+	} /* end if there is no exist default */
+}
+
+MinimalTuple
+minimal_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc)
+{
+	MinimalTuple minimalTuple;
+	expand_tuple(NULL, &minimalTuple, sourceTuple, tupleDesc);
+	return minimalTuple;
+}
+
+HeapTuple
+heap_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc)
+{
+	HeapTuple heapTuple;
+	expand_tuple(&heapTuple, NULL, sourceTuple, tupleDesc);
+	return heapTuple;
+}
+
 /* ----------------
  *		heap_copy_tuple_as_datum
  *
@@ -1125,8 +1499,7 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
 	tup = tuple->t_data;
 	if (attnum > HeapTupleHeaderGetNatts(tup))
 	{
-		*isnull = true;
-		return (Datum) 0;
+		return getmissingattr(slot->tts_tupleDescriptor, attnum, isnull);
 	}

 	/*
@@ -1196,13 +1569,13 @@ slot_getallattrs(TupleTableSlot *slot)

 	/*
 	 * If tuple doesn't have all the atts indicated by tupleDesc, read the
-	 * rest as null
+	 * rest as defaults
 	 */
-	for (; attnum < tdesc_natts; attnum++)
+	if (attnum < tdesc_natts)
 	{
-		slot->tts_values[attnum] = (Datum) 0;
-		slot->tts_isnull[attnum] = true;
+		slot_getdefaultattrs(slot, attnum);
 	}
+
 	slot->tts_nvalid = tdesc_natts;
 }

@@ -1243,12 +1616,11 @@ slot_getsomeattrs(TupleTableSlot *slot, int attnum)

 	/*
 	 * If tuple doesn't have all the atts indicated by tupleDesc, read the
-	 * rest as null
+	 * rest as defaults
 	 */
-	for (; attno < attnum; attno++)
+	if (attno < attnum)
 	{
-		slot->tts_values[attno] = (Datum) 0;
-		slot->tts_isnull[attno] = true;
+		slot_getdefaultattrs(slot, attno);
 	}
 	slot->tts_nvalid = attnum;
 }
@@ -1273,7 +1645,7 @@ slot_attisnull(TupleTableSlot *slot, int attnum)
 			elog(ERROR, "cannot extract system attribute from virtual tuple");
 		if (tuple == &(slot->tts_minhdr))		/* internal error */
 			elog(ERROR, "cannot extract system attribute from minimal tuple");
-		return heap_attisnull(tuple, attnum);
+		return heap_attisnull(tuple, attnum, tupleDesc);
 	}

 	/*
@@ -1296,7 +1668,7 @@ slot_attisnull(TupleTableSlot *slot, int attnum)
 		elog(ERROR, "cannot extract attribute from empty tuple slot");

 	/* and let the tuple tell it */
-	return heap_attisnull(tuple, attnum);
+	return heap_attisnull(tuple, attnum, tupleDesc);
 }

 /*
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index b56d0e3..52b5f79 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -25,6 +25,7 @@
 #include "parser/parse_type.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/datum.h"
 #include "utils/resowner_private.h"
 #include "utils/syscache.h"

@@ -190,6 +191,13 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
 			{
 				if (constr->defval[i].adbin)
 					cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin);
+				if (constr->defval[i].adexist)
+				{
+					int attnum = constr->defval[i].adnum - 1;
+					cpy->defval[i].adexist = datumCopy(constr->defval[i].adexist,
+													   tupdesc->attrs[attnum]->attbyval,
+													   tupdesc->attrs[attnum]->attlen);
+				}
 			}
 		}

@@ -282,6 +290,9 @@ FreeTupleDesc(TupleDesc tupdesc)
 			{
 				if (attrdef[i].adbin)
 					pfree(attrdef[i].adbin);
+				if (attrdef[i].adexist
+					&& !tupdesc->attrs[attrdef[i].adnum -1]->attbyval)
+					pfree(DatumGetPointer(attrdef[i].adexist));
 			}
 			pfree(attrdef);
 		}
@@ -350,9 +361,10 @@ DecrTupleDescRefCount(TupleDesc tupdesc)
 bool
 equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
 {
-	int			i,
-				j,
-				n;
+	int	i,
+		j,
+		n1,
+		n2;

 	if (tupdesc1->natts != tupdesc2->natts)
 		return false;
@@ -420,10 +432,9 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
 			return false;
 		if (constr1->has_not_null != constr2->has_not_null)
 			return false;
-		n = constr1->num_defval;
-		if (n != (int) constr2->num_defval)
-			return false;
-		for (i = 0; i < n; i++)
+		n1 = constr1->num_defval;
+		n2 = constr2->num_defval;
+		for (i = 0; i < n1; i++)
 		{
 			AttrDefault *defval1 = constr1->defval + i;
 			AttrDefault *defval2 = constr2->defval;
@@ -433,20 +444,24 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
 			 * catalogs in the same order; so use the adnum field to identify
 			 * the matching item to compare.
 			 */
-			for (j = 0; j < n; defval2++, j++)
+			for (j = 0; j < n2; defval2++, j++)
 			{
 				if (defval1->adnum == defval2->adnum)
 					break;
 			}
-			if (j >= n)
+			if (j >= n2)
+				return false;
+			if ((!defval1->adbin && defval2->adbin) ||
+                (defval1->adbin && !defval2->adbin))
 				return false;
-			if (strcmp(defval1->adbin, defval2->adbin) != 0)
+			if (defval1->adbin && defval2->adbin &&
+				strcmp(defval1->adbin, defval2->adbin) != 0)
 				return false;
 		}
-		n = constr1->num_check;
-		if (n != (int) constr2->num_check)
+		n1 = constr1->num_check;
+		if (n1 != (int) constr2->num_check)
 			return false;
-		for (i = 0; i < n; i++)
+		for (i = 0; i < n1; i++)
 		{
 			ConstrCheck *check1 = constr1->check + i;
 			ConstrCheck *check2 = constr2->check;
@@ -456,7 +471,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
 			 * same order; match them up by name and contents. (The name
 			 * *should* be unique, but...)
 			 */
-			for (j = 0; j < n; check2++, j++)
+			for (j = 0; j < n2; check2++, j++)
 			{
 				if (strcmp(check1->ccname, check2->ccname) == 0 &&
 					strcmp(check1->ccbin, check2->ccbin) == 0 &&
@@ -464,7 +479,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
 					check1->ccnoinherit == check2->ccnoinherit)
 					break;
 			}
-			if (j >= n)
+			if (j >= n1)
 				return false;
 		}
 	}
@@ -533,6 +548,7 @@ TupleDescInitEntry(TupleDesc desc,

 	att->attnotnull = false;
 	att->atthasdef = false;
+	att->atthasexistdef = false;
 	att->attisdropped = false;
 	att->attislocal = true;
 	att->attinhcount = 0;
@@ -657,6 +673,7 @@ BuildDescForRelation(List *schema)
 		constr->has_not_null = true;
 		constr->defval = NULL;
 		constr->num_defval = 0;
+		constr->has_existdefval = false;
 		constr->check = NULL;
 		constr->num_check = 0;
 		desc->constr = constr;
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index c0df671..037a0ce 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -4377,7 +4377,7 @@ pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
 		 * grants no privileges, so that we can fall out quickly in the very
 		 * common case where attacl is null.
 		 */
-		if (heap_attisnull(attTuple, Anum_pg_attribute_attacl))
+		if (heap_attisnull(attTuple, Anum_pg_attribute_attacl, NULL))
 			attmask = 0;
 		else
 			attmask = pg_attribute_aclmask(table_oid, curr_att, roleid,
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 26d1652..e76c10e 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -409,12 +409,14 @@ sub emit_pgattr_row
 		attcacheoff   => '-1',
 		atttypmod     => '-1',
 		atthasdef     => 'f',
+		atthasexistdef=> 'f',
 		attisdropped  => 'f',
 		attislocal    => 't',
 		attinhcount   => '0',
 		attacl        => '_null_',
 		attoptions    => '_null_',
-		attfdwoptions => '_null_');
+		attfdwoptions => '_null_',
+		attexistdefault => '_null_');
 	return { %PGATTR_DEFAULTS, %row };
 }

@@ -445,6 +447,7 @@ sub emit_schemapg_row
 	delete $row->{attacl};
 	delete $row->{attoptions};
 	delete $row->{attfdwoptions};
+	delete $row->{attexistdefault};

 	# Expand booleans from 'f'/'t' to 'false'/'true'.
 	# Some values might be other macros (eg FLOAT4PASSBYVAL), don't change.
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 0cf7b9e..233c20f 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -56,9 +56,12 @@
 #include "catalog/storage_xlog.h"
 #include "commands/tablecmds.h"
 #include "commands/typecmds.h"
+#include "executor/executor.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
+#include "optimizer/clauses.h"
 #include "optimizer/var.h"
+#include "optimizer/planner.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_collate.h"
 #include "parser/parse_expr.h"
@@ -67,6 +70,7 @@
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/datum.h"
 #include "utils/fmgroids.h"
 #include "utils/inval.h"
 #include "utils/lsyscache.h"
@@ -139,37 +143,37 @@ static List *insert_ordered_unique_oid(List *list, Oid datum);
 static FormData_pg_attribute a1 = {
 	0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData),
 	SelfItemPointerAttributeNumber, 0, -1, -1,
-	false, 'p', 's', true, false, false, true, 0
+	false, 'p', 's', true, false, false, false, true, 0
 };

 static FormData_pg_attribute a2 = {
 	0, {"oid"}, OIDOID, 0, sizeof(Oid),
 	ObjectIdAttributeNumber, 0, -1, -1,
-	true, 'p', 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, false, true, 0
 };

 static FormData_pg_attribute a3 = {
 	0, {"xmin"}, XIDOID, 0, sizeof(TransactionId),
 	MinTransactionIdAttributeNumber, 0, -1, -1,
-	true, 'p', 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, false, true, 0
 };

 static FormData_pg_attribute a4 = {
 	0, {"cmin"}, CIDOID, 0, sizeof(CommandId),
 	MinCommandIdAttributeNumber, 0, -1, -1,
-	true, 'p', 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, false, true, 0
 };

 static FormData_pg_attribute a5 = {
 	0, {"xmax"}, XIDOID, 0, sizeof(TransactionId),
 	MaxTransactionIdAttributeNumber, 0, -1, -1,
-	true, 'p', 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, false, true, 0
 };

 static FormData_pg_attribute a6 = {
 	0, {"cmax"}, CIDOID, 0, sizeof(CommandId),
 	MaxCommandIdAttributeNumber, 0, -1, -1,
-	true, 'p', 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, false, true, 0
 };

 /*
@@ -181,7 +185,7 @@ static FormData_pg_attribute a6 = {
 static FormData_pg_attribute a7 = {
 	0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
 	TableOidAttributeNumber, 0, -1, -1,
-	true, 'p', 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, false, true, 0
 };

 static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
@@ -625,6 +629,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
 	values[Anum_pg_attribute_attalign - 1] = CharGetDatum(new_attribute->attalign);
 	values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(new_attribute->attnotnull);
 	values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(new_attribute->atthasdef);
+	values[Anum_pg_attribute_atthasexistdef - 1] = BoolGetDatum(new_attribute->atthasexistdef);
 	values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped);
 	values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
 	values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
@@ -634,6 +639,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
 	nulls[Anum_pg_attribute_attacl - 1] = true;
 	nulls[Anum_pg_attribute_attoptions - 1] = true;
 	nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
+	nulls[Anum_pg_attribute_attexistdefault - 1] = true;

 	tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);

@@ -1860,7 +1866,7 @@ heap_drop_with_catalog(Oid relid)
  */
 Oid
 StoreAttrDefault(Relation rel, AttrNumber attnum,
-				 Node *expr, bool is_internal)
+				 Node *expr, bool is_internal, bool add_column_mode)
 {
 	char	   *adbin;
 	char	   *adsrc;
@@ -1871,9 +1877,20 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	Relation	attrrel;
 	HeapTuple	atttup;
 	Form_pg_attribute attStruct;
+	Form_pg_attribute defAttStruct;
 	Oid			attrdefOid;
 	ObjectAddress colobject,
 				defobject;
+	ExprState  *exprState;
+	Expr       *expr2 = (Expr *) expr;
+	EState     *estate = NULL;
+	ExprContext *econtext;
+	char        *existBuf = NULL;
+	Datum       valuesAtt[Natts_pg_attribute];
+	bool        nullsAtt[Natts_pg_attribute];
+	bool        replacesAtt[Natts_pg_attribute];
+	Datum       existDefault = (Datum) 0;
+	bool        existDefIsNull = true;

 	/*
 	 * Flatten expression to string form for storage.
@@ -1887,6 +1904,44 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 							deparse_context_for(RelationGetRelationName(rel),
 												RelationGetRelid(rel)),
 							   false, false);
+    /*
+	 * Compute the exist default
+	 */
+	expr2 = expression_planner(expr2);
+
+	exprState = ExecInitExpr(expr2, NULL);
+	estate   = CreateExecutorState();
+	econtext = GetPerTupleExprContext(estate);
+
+	if (add_column_mode)
+	{
+		existDefault = ExecEvalExpr(exprState, econtext,
+									&existDefIsNull,
+									NULL);
+
+		defAttStruct = rel->rd_att->attrs[attnum - 1];
+
+		if (existDefIsNull)
+		{
+			existDefault = PointerGetDatum(NULL);
+		}
+		else if (defAttStruct->attbyval)
+		{
+			existBuf = palloc(VARHDRSZ + sizeof(Datum));
+			memcpy(VARDATA(existBuf), &existDefault, sizeof(Datum));
+			SET_VARSIZE(existBuf, VARHDRSZ + sizeof(Datum));
+			existDefault = PointerGetDatum(existBuf);
+		}
+		else if (defAttStruct->attlen >= 0)
+		{
+			existBuf = palloc(VARHDRSZ + defAttStruct->attlen);
+			memcpy(VARDATA(existBuf), DatumGetPointer(existDefault),
+				   defAttStruct->attlen);
+			SET_VARSIZE(existBuf,
+						VARHDRSZ + defAttStruct->attlen);
+			existDefault = PointerGetDatum(existBuf);
+		}
+	}

 	/*
 	 * Make the pg_attrdef entry.
@@ -1930,7 +1985,23 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
 	if (!attStruct->atthasdef)
 	{
-		attStruct->atthasdef = true;
+		MemSet(valuesAtt, 0, sizeof(valuesAtt));
+        MemSet(nullsAtt, false, sizeof(nullsAtt));
+        MemSet(replacesAtt, false, sizeof(replacesAtt));
+        valuesAtt[Anum_pg_attribute_atthasdef - 1] = true;
+        replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
+        if (add_column_mode)
+        {
+            valuesAtt[Anum_pg_attribute_atthasexistdef - 1] = true;
+            replacesAtt[Anum_pg_attribute_atthasexistdef - 1] = true;
+            valuesAtt[Anum_pg_attribute_atthasexistdef - 1] = true;
+            replacesAtt[Anum_pg_attribute_atthasexistdef - 1] = true;
+            valuesAtt[Anum_pg_attribute_attexistdefault -1] = existDefault;
+            replacesAtt[Anum_pg_attribute_attexistdefault -1] = true;
+            nullsAtt[Anum_pg_attribute_attexistdefault -1] = existDefIsNull;
+        }
+        atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
+                                   valuesAtt, nullsAtt, replacesAtt);
 		simple_heap_update(attrrel, &atttup->t_self, atttup);
 		/* keep catalog indexes current */
 		CatalogUpdateIndexes(attrrel, atttup);
@@ -1964,6 +2035,17 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
 								  RelationGetRelid(rel), attnum, is_internal);

+	if (estate)
+	{
+		FreeExecutorState(estate);
+	}
+
+	if (existBuf)
+	{
+		pfree(existBuf);
+		existBuf = NULL;
+	}
+
 	return attrdefOid;
 }

@@ -2105,7 +2187,7 @@ StoreConstraints(Relation rel, List *cooked_constraints, bool is_internal)
 		{
 			case CONSTR_DEFAULT:
 				con->conoid = StoreAttrDefault(rel, con->attnum, con->expr,
-											   is_internal);
+											   is_internal, false);
 				break;
 			case CONSTR_CHECK:
 				con->conoid =
@@ -2221,7 +2303,14 @@ AddRelationNewConstraints(Relation rel,
 			(IsA(expr, Const) &&((Const *) expr)->constisnull))
 			continue;

-		defOid = StoreAttrDefault(rel, colDef->attnum, expr, is_internal);
+		/* If the default is volatile we cannot use an exist default */
+        if (contain_volatile_functions((Node *) expr))
+		{
+			colDef->existDefMode = false;
+		}
+
+		defOid = StoreAttrDefault(rel, colDef->attnum, expr, is_internal,
+								  colDef->existDefMode);

 		cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
 		cooked->contype = CONSTR_DEFAULT;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 08b646d..8188e2f 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -353,6 +353,7 @@ ConstructTupleDescriptor(Relation heapRelation,
 			to->attcacheoff = -1;
 			to->attnotnull = false;
 			to->atthasdef = false;
+			to->atthasexistdef = false;
 			to->attislocal = true;
 			to->attinhcount = 0;
 			to->attcollation = collationObjectId[i];
@@ -1575,7 +1576,8 @@ index_drop(Oid indexId, bool concurrent)
 	if (!HeapTupleIsValid(tuple))
 		elog(ERROR, "cache lookup failed for index %u", indexId);

-	hasexprs = !heap_attisnull(tuple, Anum_pg_index_indexprs);
+	hasexprs = !heap_attisnull(tuple, Anum_pg_index_indexprs,
+							   RelationGetDescr(indexRelation));

 	simple_heap_delete(indexRelation, &tuple->t_self);

diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index dc1f79f..6956e99 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -445,7 +445,8 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMOD
 	 * seqscan pass over the table to copy the missing rows, but that seems
 	 * expensive and tedious.
 	 */
-	if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred))
+	if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred,
+						RelationGetDescr(OldIndex)))
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot cluster on partial index \"%s\"",
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 85817c6..1cf750a 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -206,8 +206,8 @@ CheckIndexCompatible(Oid oldId,
 	 * We don't assess expressions or predicates; assume incompatibility.
 	 * Also, if the index is invalid for any reason, treat it as incompatible.
 	 */
-	if (!(heap_attisnull(tuple, Anum_pg_index_indpred) &&
-		  heap_attisnull(tuple, Anum_pg_index_indexprs) &&
+	if (!(heap_attisnull(tuple, Anum_pg_index_indpred, NULL) &&
+		  heap_attisnull(tuple, Anum_pg_index_indexprs, NULL) &&
 		  IndexIsValid(indexForm)))
 	{
 		ReleaseSysCache(tuple);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f822ed9..9461368 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -630,6 +630,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 			rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
 			rawEnt->attnum = attnum;
 			rawEnt->raw_default = colDef->raw_default;
+			rawEnt->existDefMode = false;
 			rawDefaults = lappend(rawDefaults, rawEnt);
 			descriptor->attrs[attnum - 1]->atthasdef = true;
 		}
@@ -4180,7 +4181,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 			{
 				int			attn = lfirst_int(l);

-				if (heap_attisnull(tuple, attn + 1))
+				if (heap_attisnull(tuple, attn + 1, newTupDesc))
 					ereport(ERROR,
 							(errcode(ERRCODE_NOT_NULL_VIOLATION),
 							 errmsg("column \"%s\" contains null values",
@@ -4267,7 +4268,7 @@ ATGetQueueEntry(List **wqueue, Relation rel)
 	tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
 	tab->relid = relid;
 	tab->relkind = rel->rd_rel->relkind;
-	tab->oldDesc = CreateTupleDescCopy(RelationGetDescr(rel));
+	tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
 	tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
 	tab->chgPersistence = false;

@@ -4850,6 +4851,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	attribute.attalign = tform->typalign;
 	attribute.attnotnull = colDef->is_not_null;
 	attribute.atthasdef = false;
+	attribute.atthasexistdef = false;
 	attribute.attisdropped = false;
 	attribute.attislocal = colDef->is_local;
 	attribute.attinhcount = colDef->inhcount;
@@ -4895,6 +4897,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
 		rawEnt->attnum = attribute.attnum;
 		rawEnt->raw_default = copyObject(colDef->raw_default);
+		rawEnt->existDefMode = true;

 		/*
 		 * This function is intended for CREATE TABLE, so it processes a
@@ -4905,6 +4908,15 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,

 		/* Make the additional catalog changes visible */
 		CommandCounterIncrement();
+
+		/*
+		 * Did the request for an exist default work?
+		 * If not we'll have to do a rewrite
+		 */
+		if (!rawEnt->existDefMode)
+		{
+			tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
+		}
 	}

 	/*
@@ -4971,16 +4983,26 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 			newval->expr = expression_planner(defval);

 			tab->newvals = lappend(tab->newvals, newval);
-			tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
 		}

+		if (DomainHasConstraints(typeOid))
+			tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
+
 		/*
-		 * If the new column is NOT NULL, tell Phase 3 it needs to test that.
-		 * (Note we don't do this for an OID column.  OID will be marked not
-		 * null, but since it's filled specially, there's no need to test
-		 * anything.)
+		 * If we add a column that is not null and there is no exist default
+		 * I.e. the exist default is NULL then this ADD COLUMN is doomed.
+		 * Unless the table is empty...
 		 */
-		tab->new_notnull |= colDef->is_not_null;
+		if (!rel->rd_att->attrs[attribute.attnum - 1]->atthasexistdef)
+		{
+			/*
+			 * If the new column is NOT NULL, tell Phase 3 it needs to test that.
+			 * (Note we don't do this for an OID column.  OID will be marked not
+			 * null, but since it's filled specially, there's no need to test
+			 * anything.)
+			 */
+			tab->new_notnull |= colDef->is_not_null;
+		}
 	}

 	/*
@@ -5390,6 +5412,7 @@ ATExecColumnDefault(Relation rel, const char *colName,
 		rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
 		rawEnt->attnum = attnum;
 		rawEnt->raw_default = newDefault;
+		rawEnt->existDefMode = false;

 		/*
 		 * This function is intended for CREATE TABLE, so it processes a
@@ -7165,8 +7188,8 @@ transformFkeyCheckAttrs(Relation pkrel,
 		if (indexStruct->indnatts == numattrs &&
 			indexStruct->indisunique &&
 			IndexIsValid(indexStruct) &&
-			heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
-			heap_attisnull(indexTuple, Anum_pg_index_indexprs))
+			heap_attisnull(indexTuple, Anum_pg_index_indpred, RelationGetDescr(pkrel)) &&
+			heap_attisnull(indexTuple, Anum_pg_index_indexprs, RelationGetDescr(pkrel)))
 		{
 			Datum		indclassDatum;
 			bool		isnull;
@@ -8438,7 +8461,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 		RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
 						  true);

-		StoreAttrDefault(rel, attnum, defaultexpr, true);
+		StoreAttrDefault(rel, attnum, defaultexpr, true, false);
 	}

 	ObjectAddressSubSet(address, RelationRelationId,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 056933a..559a741 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2324,7 +2324,7 @@ AlterDomainNotNull(List *names, bool notNull)
 				{
 					int			attnum = rtc->atts[i];

-					if (heap_attisnull(tuple, attnum))
+					if (heap_attisnull(tuple, attnum, tupdesc))
 					{
 						/*
 						 * In principle the auxiliary information for this
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..58156b7 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2633,8 +2633,16 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
 								false, NULL))
 					elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");

-				/* successful, copy tuple */
-				copyTuple = heap_copytuple(&tuple);
+				if (HeapTupleHeaderGetNatts(tuple.t_data) < RelationGetDescr(erm->relation)->natts)
+				{
+					copyTuple = heap_expand_tuple(&tuple,
+												  RelationGetDescr(erm->relation));
+				}
+				else
+				{
+					/* successful, copy tuple */
+					copyTuple = heap_copytuple(&tuple);
+				}
 				ReleaseBuffer(buffer);
 			}

diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 743e7d6..ed3396b 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -1064,7 +1064,7 @@ ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,

 		if (!vattr->attisdropped)
 			continue;			/* already checked non-dropped cols */
-		if (heap_attisnull(tuple, i + 1))
+		if (heap_attisnull(tuple, i + 1, tupleDesc))
 			continue;			/* null is always okay */
 		if (vattr->attlen != sattr->attlen ||
 			vattr->attalign != sattr->attalign)
@@ -3948,7 +3948,7 @@ ExecEvalNullTest(NullTestState *nstate,
 			/* ignore dropped columns */
 			if (tupDesc->attrs[att - 1]->attisdropped)
 				continue;
-			if (heap_attisnull(&tmptup, att))
+			if (heap_attisnull(&tmptup, att, tupDesc))
 			{
 				/* null field disproves IS NOT NULL */
 				if (ntest->nulltesttype == IS_NOT_NULL)
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 533050d..6116056 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -592,8 +592,15 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
 	if (slot->tts_mintuple)
 		return heap_copy_minimal_tuple(slot->tts_mintuple);
 	if (slot->tts_tuple)
-		return minimal_tuple_from_heap_tuple(slot->tts_tuple);
-
+	{
+		if (TTS_HAS_PHYSICAL_TUPLE(slot) &&
+			HeapTupleHeaderGetNatts(slot->tts_tuple->t_data)
+			< slot->tts_tupleDescriptor->natts)
+			return minimal_expand_tuple(slot->tts_tuple,
+										slot->tts_tupleDescriptor);
+		else
+			return minimal_tuple_from_heap_tuple(slot->tts_tuple);
+	}
 	/*
 	 * Otherwise we need to build a tuple from the Datum array.
 	 */
@@ -630,8 +637,21 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
 	 * If we have a regular physical tuple then just return it.
 	 */
 	if (TTS_HAS_PHYSICAL_TUPLE(slot))
-		return slot->tts_tuple;
-
+	{
+		if (HeapTupleHeaderGetNatts(slot->tts_tuple->t_data) < slot->tts_tupleDescriptor->natts)
+		{
+			MemoryContext oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+			slot->tts_tuple = heap_expand_tuple(slot->tts_tuple,
+												slot->tts_tupleDescriptor);
+			slot->tts_shouldFree = true;
+			MemoryContextSwitchTo(oldContext);
+			return slot->tts_tuple;
+		}
+		else
+		{
+			return slot->tts_tuple;
+		}
+	}
 	/*
 	 * Otherwise materialize the slot...
 	 */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 663ffe0..7f02a3f 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -4356,7 +4356,7 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
 		funcform->prosecdef ||
 		funcform->proretset ||
 		funcform->prorettype == RECORDOID ||
-		!heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
+		!heap_attisnull(func_tuple, Anum_pg_proc_proconfig, NULL) ||
 		funcform->pronargs != list_length(args))
 		return NULL;

@@ -4879,7 +4879,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 		funcform->provolatile == PROVOLATILE_VOLATILE ||
 		funcform->prosecdef ||
 		!funcform->proretset ||
-		!heap_attisnull(func_tuple, Anum_pg_proc_proconfig))
+		!heap_attisnull(func_tuple, Anum_pg_proc_proconfig, NULL))
 	{
 		ReleaseSysCache(func_tuple);
 		return NULL;
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index b828e3c..e352adb 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1034,7 +1034,7 @@ build_column_default(Relation rel, int attrno)
 	/*
 	 * Scan to see if relation has a default for this column.
 	 */
-	if (rd_att->constr && rd_att->constr->num_defval > 0)
+	if (att_tup->atthasdef && rd_att->constr && rd_att->constr->num_defval > 0)
 	{
 		AttrDefault *defval = rd_att->constr->defval;
 		int			ndef = rd_att->constr->num_defval;
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index b476500..614aadb 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -209,7 +209,7 @@ 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(TupleDesc tupdesc, HeapTuple tup,
 			 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
 static void ri_BuildQueryKey(RI_QueryKey *key,
 				 const RI_ConstraintInfo *riinfo,
@@ -315,7 +315,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(RelationGetDescr(pk_rel), new_row, riinfo, false))
 	{
 		case RI_KEYS_ALL_NULL:

@@ -522,7 +522,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(RelationGetDescr(fk_rel), old_row, riinfo, true) == RI_KEYS_NONE_NULL);

 	if (SPI_connect() != SPI_OK_CONNECT)
 		elog(ERROR, "SPI_connect failed");
@@ -682,7 +682,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(RelationGetDescr(pk_rel), old_row, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -897,7 +897,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(RelationGetDescr(pk_rel), old_row, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1076,7 +1076,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(RelationGetDescr(pk_rel), old_row, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1236,7 +1236,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(RelationGetDescr(pk_rel), old_row, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1413,7 +1413,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(RelationGetDescr(pk_rel), old_row, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1580,7 +1580,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(RelationGetDescr(pk_rel), old_row, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1753,7 +1753,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(RelationGetDescr(pk_rel), old_row, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1935,7 +1935,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(RelationGetDescr(pk_rel), old_row, riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -2105,7 +2105,7 @@ 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(RelationGetDescr(pk_rel), old_row, riinfo, true) != RI_KEYS_NONE_NULL)
 				return false;

 			/* If all old and new key values are equal, no check is needed */
@@ -2161,7 +2161,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(RelationGetDescr(fk_rel), new_row, riinfo, false) != RI_KEYS_NONE_NULL)
 				return false;

 			/*
@@ -2192,7 +2192,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(RelationGetDescr(fk_rel), new_row, riinfo, false))
 			{
 				case RI_KEYS_ALL_NULL:
 					return false;
@@ -2486,7 +2486,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(tupdesc, tuple, &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\"",
@@ -3350,7 +3350,8 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
  * ----------
  */
 static int
-ri_NullCheck(HeapTuple tup,
+ri_NullCheck(TupleDesc tupDesc,
+	         HeapTuple tup,
 			 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
 {
 	const int16 *attnums;
@@ -3365,7 +3366,7 @@ ri_NullCheck(HeapTuple tup,

 	for (i = 0; i < riinfo->nkeys; i++)
 	{
-		if (heap_attisnull(tup, attnums[i]))
+		if (heap_attisnull(tup, attnums[i], tupDesc))
 			nonenull = false;
 		else
 			allnull = false;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 8a81d7a..a2ebedd 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -1190,7 +1190,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	 * versions of the expressions and predicate, because we want to display
 	 * non-const-folded expressions.)
 	 */
-	if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs))
+	if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs, NULL))
 	{
 		Datum		exprsDatum;
 		bool		isnull;
@@ -1356,7 +1356,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 		/*
 		 * If it's a partial index, decompile and append the predicate
 		 */
-		if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
+		if (!heap_attisnull(ht_idx, Anum_pg_index_indpred, NULL))
 		{
 			Node	   *node;
 			Datum		predDatum;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 79e0b1f..8f321a4 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -70,6 +70,7 @@
 #include "storage/smgr.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
+#include "utils/datum.h"
 #include "utils/fmgroids.h"
 #include "utils/inval.h"
 #include "utils/lsyscache.h"
@@ -482,6 +483,8 @@ RelationBuildTupleDesc(Relation relation)
 	TupleConstr *constr;
 	AttrDefault *attrdef = NULL;
 	int			ndef = 0;
+	int         attnum = 0;
+	bool        has_existdefval = false;

 	/* copy some fields from pg_class row to rd_att */
 	relation->rd_att->tdtypeid = relation->rd_rel->reltype;
@@ -534,7 +537,18 @@ RelationBuildTupleDesc(Relation relation)
 			elog(ERROR, "invalid attribute number %d for %s",
 				 attp->attnum, RelationGetRelationName(relation));

-		memcpy(relation->rd_att->attrs[attp->attnum - 1],
+		/*
+		 * We have a dependency on the attrdef array being filled in
+		 * in ascending attnum order.
+		 * This should be guaranteed by the index driving the scan.
+		 * But we want to be double sure
+		 */
+		if (!(attp->attnum > attnum))
+			elog(ERROR, "attribute numbers not ascending");
+
+		attnum = attp->attnum;
+
+		memcpy(relation->rd_att->attrs[attnum - 1],
 			   attp,
 			   ATTRIBUTE_FIXED_PART_SIZE);

@@ -542,15 +556,67 @@ RelationBuildTupleDesc(Relation relation)
 		if (attp->attnotnull)
 			constr->has_not_null = true;

-		if (attp->atthasdef)
+		/*
+		 * If the column has a current or exist default
+		 * Fill it into the attrdef array
+		 */
+		if (attp->atthasdef || attp->atthasexistdef)
 		{
+			Datum existDefault;
+			bool  existDefNull;
+
 			if (attrdef == NULL)
 				attrdef = (AttrDefault *)
 					MemoryContextAllocZero(CacheMemoryContext,
 										   relation->rd_rel->relnatts *
 										   sizeof(AttrDefault));
-			attrdef[ndef].adnum = attp->attnum;
+			attrdef[ndef].adnum = attnum;
 			attrdef[ndef].adbin = NULL;
+
+			/* Do we have an exist default? */
+			existDefault = SysCacheGetAttr(ATTNUM, pg_attribute_tuple,
+										   Anum_pg_attribute_attexistdefault,
+										   &existDefNull);
+			if (existDefNull)
+			{
+				/* No, then the store a NULL */
+				attrdef[ndef].adexist = PointerGetDatum(NULL);
+				attrdef[ndef].adexistNull = true;
+			}
+			else if (attp->attbyval)
+			{
+				/* Yes, and its of the by-value kind
+				 * Copy in the Datum */
+				memcpy(&attrdef[ndef].adexist,
+					   VARDATA_ANY(existDefault), sizeof(Datum));
+				attrdef[ndef].adexistNull = false;
+				has_existdefval = true;
+			}
+			else if (attp->attlen > 0)
+			{
+				/* Yes, and its fixed length
+				 * Copy it out and have teh Datum point to it. */
+				MemoryContext oldcxt;
+				oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+				attrdef[ndef].adexist = PointerGetDatum(palloc(attp->attlen));
+				memcpy(DatumGetPointer(attrdef[ndef].adexist),
+					   VARDATA_ANY(existDefault), attp->attlen);
+				MemoryContextSwitchTo(oldcxt);
+				attrdef[ndef].adexistNull = false;
+				has_existdefval = true;
+			}
+			else
+			{
+				/* Yes, variable length */
+				MemoryContext oldcxt;
+				oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+				attrdef[ndef].adexist = datumCopy(existDefault,
+												  attp->attbyval,
+												  attp->attlen);
+				attrdef[ndef].adexistNull = false;
+				MemoryContextSwitchTo(oldcxt);
+				has_existdefval = true;
+			}
 			ndef++;
 		}
 		need--;
@@ -605,10 +671,14 @@ RelationBuildTupleDesc(Relation relation)
 			else
 				constr->defval = attrdef;
 			constr->num_defval = ndef;
+			constr->has_existdefval = has_existdefval;
 			AttrDefaultFetch(relation);
 		}
 		else
+		{
 			constr->num_defval = 0;
+			constr->has_existdefval = false;
+		}

 		if (relation->rd_rel->relchecks > 0)	/* CHECKs */
 		{
@@ -3711,10 +3781,6 @@ AttrDefaultFetch(Relation relation)

 	systable_endscan(adscan);
 	heap_close(adrel, AccessShareLock);
-
-	if (found != ndef)
-		elog(WARNING, "%d attrdef record(s) missing for rel %s",
-			 ndef - found, RelationGetRelationName(relation));
 }

 /*
@@ -4052,7 +4118,8 @@ RelationGetIndexList(Relation relation)
 		 */
 		if (!IndexIsValid(index) || !index->indisunique ||
 			!index->indimmediate ||
-			!heap_attisnull(htup, Anum_pg_index_indpred))
+			!heap_attisnull(htup, Anum_pg_index_indpred,
+							GetPgIndexDescriptor()))
 			continue;

 		/* Check to see if is a usable btree index on OID */
@@ -4242,7 +4309,8 @@ RelationGetIndexExpressions(Relation relation)

 	/* Quick exit if there is nothing to do. */
 	if (relation->rd_indextuple == NULL ||
-		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs))
+		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs,
+					   GetPgIndexDescriptor()))
 		return NIL;

 	/*
@@ -4303,7 +4371,8 @@ RelationGetIndexPredicate(Relation relation)

 	/* Quick exit if there is nothing to do. */
 	if (relation->rd_indextuple == NULL ||
-		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred))
+		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred,
+					   GetPgIndexDescriptor()))
 		return NIL;

 	/*
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 46a55ba..ed02db4 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -237,7 +237,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
 	 */
 	if (!ignore_security &&
 		(procedureStruct->prosecdef ||
-		 !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig) ||
+		 !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig, NULL) ||
 		 FmgrHookIsNeeded(functionId)))
 	{
 		finfo->fn_addr = fmgr_security_definer;
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 5d49fe5..00cc1f7 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1035,8 +1035,8 @@ get_func_result_name(Oid functionId)
 		elog(ERROR, "cache lookup failed for function %u", functionId);

 	/* If there are no named OUT parameters, return NULL */
-	if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes) ||
-		heap_attisnull(procTuple, Anum_pg_proc_proargnames))
+	if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes, NULL) ||
+		heap_attisnull(procTuple, Anum_pg_proc_proargnames, NULL))
 		result = NULL;
 	else
 	{
@@ -1130,8 +1130,8 @@ build_function_result_tupdesc_t(HeapTuple procTuple)
 		return NULL;

 	/* If there are no OUT parameters, return NULL */
-	if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes) ||
-		heap_attisnull(procTuple, Anum_pg_proc_proargmodes))
+	if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes, NULL) ||
+		heap_attisnull(procTuple, Anum_pg_proc_proargmodes, NULL))
 		return NULL;

 	/* Get the data out of the tuple */
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index d7e5fad..c1f00ef 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -790,7 +790,7 @@ extern void heap_fill_tuple(TupleDesc tupleDesc,
 				Datum *values, bool *isnull,
 				char *data, Size data_size,
 				uint16 *infomask, bits8 *bit);
-extern bool heap_attisnull(HeapTuple tup, int attnum);
+extern bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc);
 extern Datum nocachegetattr(HeapTuple tup, int attnum,
 			   TupleDesc att);
 extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
@@ -814,5 +814,8 @@ extern void heap_free_minimal_tuple(MinimalTuple mtup);
 extern MinimalTuple heap_copy_minimal_tuple(MinimalTuple mtup);
 extern HeapTuple heap_tuple_from_minimal_tuple(MinimalTuple mtup);
 extern MinimalTuple minimal_tuple_from_heap_tuple(HeapTuple htup);
+extern HeapTuple heap_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc);
+extern MinimalTuple minimal_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc);
+

 #endif   /* HTUP_DETAILS_H */
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index de18f74..f55eccf 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -14,6 +14,7 @@
 #ifndef TUPDESC_H
 #define TUPDESC_H

+#include "postgres.h"
 #include "access/attnum.h"
 #include "catalog/pg_attribute.h"
 #include "nodes/pg_list.h"
@@ -22,7 +23,9 @@
 typedef struct attrDefault
 {
 	AttrNumber	adnum;
+	bool        adexistNull;    /* true if exist default is NULL */
 	char	   *adbin;			/* nodeToString representation of expr */
+	Datum       adexist;        /* exist default */
 } AttrDefault;

 typedef struct constrCheck
@@ -40,6 +43,7 @@ typedef struct tupleConstr
 	ConstrCheck *check;			/* array */
 	uint16		num_defval;
 	uint16		num_check;
+	bool        has_existdefval;
 	bool		has_not_null;
 } TupleConstr;

diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index cd3048d..7868b43 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */

 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201610201
+#define CATALOG_VERSION_NO	201610211

 #endif
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index b80d8d8..6f4fd37 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -23,6 +23,7 @@ typedef struct RawColumnDefault
 {
 	AttrNumber	attnum;			/* attribute to attach default to */
 	Node	   *raw_default;	/* default value (untransformed parse tree) */
+	bool        existDefMode;   /* true if part of add column processing */
 } RawColumnDefault;

 typedef struct CookedConstraint
@@ -103,7 +104,8 @@ extern List *AddRelationNewConstraints(Relation rel,
 						  bool is_internal);

 extern Oid StoreAttrDefault(Relation rel, AttrNumber attnum,
-				 Node *expr, bool is_internal);
+				Node *expr, bool is_internal,
+				bool add_column_mode);

 extern Node *cookDefault(ParseState *pstate,
 			Node *raw_default,
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 39d8eed..f84debf 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -133,6 +133,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
 	/* Has DEFAULT value or not */
 	bool		atthasdef;

+	/* Has exist default or not */
+	bool        atthasexistdef;
+
 	/* Is dropped (ie, logically invisible) or not */
 	bool		attisdropped;

@@ -164,6 +167,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK

 	/* Column-level FDW options */
 	text		attfdwoptions[1];
+
+	/* Exist default for added columns */
+    bytea       attexistdefault;
 #endif
 } FormData_pg_attribute;

@@ -188,28 +194,30 @@ typedef FormData_pg_attribute *Form_pg_attribute;
  * ----------------
  */

-#define Natts_pg_attribute				21
-#define Anum_pg_attribute_attrelid		1
-#define Anum_pg_attribute_attname		2
-#define Anum_pg_attribute_atttypid		3
-#define Anum_pg_attribute_attstattarget 4
-#define Anum_pg_attribute_attlen		5
-#define Anum_pg_attribute_attnum		6
-#define Anum_pg_attribute_attndims		7
-#define Anum_pg_attribute_attcacheoff	8
-#define Anum_pg_attribute_atttypmod		9
-#define Anum_pg_attribute_attbyval		10
-#define Anum_pg_attribute_attstorage	11
-#define Anum_pg_attribute_attalign		12
-#define Anum_pg_attribute_attnotnull	13
-#define Anum_pg_attribute_atthasdef		14
-#define Anum_pg_attribute_attisdropped	15
-#define Anum_pg_attribute_attislocal	16
-#define Anum_pg_attribute_attinhcount	17
-#define Anum_pg_attribute_attcollation	18
-#define Anum_pg_attribute_attacl		19
-#define Anum_pg_attribute_attoptions	20
-#define Anum_pg_attribute_attfdwoptions 21
+#define Natts_pg_attribute				  23
+#define Anum_pg_attribute_attrelid	      1
+#define Anum_pg_attribute_attname		  2
+#define Anum_pg_attribute_atttypid		  3
+#define Anum_pg_attribute_attstattarget   4
+#define Anum_pg_attribute_attlen		  5
+#define Anum_pg_attribute_attnum		  6
+#define Anum_pg_attribute_attndims		  7
+#define Anum_pg_attribute_attcacheoff	  8
+#define Anum_pg_attribute_atttypmod		  9
+#define Anum_pg_attribute_attbyval		  10
+#define Anum_pg_attribute_attstorage	  11
+#define Anum_pg_attribute_attalign		  12
+#define Anum_pg_attribute_attnotnull	  13
+#define Anum_pg_attribute_atthasdef		  14
+#define Anum_pg_attribute_atthasexistdef  15
+#define Anum_pg_attribute_attisdropped	  16
+#define Anum_pg_attribute_attislocal	  17
+#define Anum_pg_attribute_attinhcount	  18
+#define Anum_pg_attribute_attcollation	  19
+#define Anum_pg_attribute_attacl		  20
+#define Anum_pg_attribute_attoptions	  21
+#define Anum_pg_attribute_attfdwoptions   22
+#define Anum_pg_attribute_attexistdefault 23


 /* ----------------
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index e57b81c..d00f5fb 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -145,7 +145,7 @@ typedef FormData_pg_class *Form_pg_class;
  */
 DATA(insert OID = 1247 (  pg_type		PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n 3 1 _null_ _null_ ));
 DESCR("");
-DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f f f t n 3 1 _null_ _null_ ));
+DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 23 0 f f f f f f f t n 3 1 _null_ _null_ ));
 DESCR("");
 DATA(insert OID = 1255 (  pg_proc		PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f f f t n 3 1 _null_ _null_ ));
 DESCR("");
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index e124552..60eb03c 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -374,8 +374,6 @@ alter table rewriteme alter column foo type numeric;
 ERROR:  rewrites not allowed
 CONTEXT:  PL/pgSQL function test_evtrig_no_rewrite() line 3 at RAISE
 alter table rewriteme add column baz int default 0;
-ERROR:  rewrites not allowed
-CONTEXT:  PL/pgSQL function test_evtrig_no_rewrite() line 3 at RAISE
 -- test with more than one reason to rewrite a single table
 CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite() RETURNS event_trigger
 LANGUAGE plpgsql AS $$
@@ -389,7 +387,7 @@ alter table rewriteme
  add column onemore int default 0,
  add column another int default -1,
  alter column foo type numeric(10,4);
-NOTICE:  Table 'rewriteme' is being rewritten (reason = 6)
+NOTICE:  Table 'rewriteme' is being rewritten (reason = 4)
 -- shouldn't trigger a table_rewrite event
 alter table rewriteme alter column foo type numeric(12,4);
 -- typed tables are rewritten when their type changes.  Don't emit table
diff --git a/src/test/regress/expected/fast_default.out b/src/test/regress/expected/fast_default.out
new file mode 100644
index 0000000..44806f0
--- /dev/null
+++ b/src/test/regress/expected/fast_default.out
@@ -0,0 +1,446 @@
+--
+-- ALTER TABLE ADD COLUMN DEFAULT test
+--
+SET search_path = fast_default;
+CREATE SCHEMA fast_default;
+CREATE TABLE m(id OID);
+INSERT INTO m VALUES (NULL::OID);
+CREATE FUNCTION set(tabname name) RETURNS VOID
+AS $$
+BEGIN
+  UPDATE m SET id = (SELECT c.relfilenode FROM pg_class AS c, pg_namespace AS s
+           WHERE c.relname = tabname AND c.relnamespace = s.oid AND s.nspname = 'fast_default');
+END;
+$$ LANGUAGE 'plpgsql';
+CREATE FUNCTION comp() RETURNS TEXT
+AS $$
+BEGIN
+  RETURN (SELECT CASE WHEN m.id = c.relfilenode THEN 'Unchanged' ELSE 'Rewritten' END
+            FROM m, pg_class AS c, pg_namespace AS s
+           WHERE c.relname = 't' AND c.relnamespace = s.oid AND s.nspname = 'fast_default');
+END;
+$$ LANGUAGE 'plpgsql';
+-- 1. Test a large sample of dfferent datatypes
+CREATE TABLE T(pk INT NOT NULL PRIMARY KEY, c_int INT DEFAULT 1);
+SELECT set('t');
+ set
+-----
+
+(1 row)
+
+INSERT INTO T VALUES (1), (2);
+ALTER TABLE T ADD COLUMN c_bpchar BPCHAR(5) DEFAULT 'hello',
+              ALTER COLUMN c_int SET DEFAULT 2;
+INSERT INTO T VALUES (3), (4);
+ALTER TABLE T ADD COLUMN c_text TEXT  DEFAULT 'world',
+              ALTER COLUMN c_bpchar SET DEFAULT 'dog';
+INSERT INTO T VALUES (5), (6);
+ALTER TABLE T ADD COLUMN c_date DATE DEFAULT '2016-06-02',
+              ALTER COLUMN c_text SET DEFAULT 'cat';
+INSERT INTO T VALUES (7), (8);
+ALTER TABLE T ADD COLUMN c_timestamp TIMESTAMP DEFAULT '2016-09-01 12:00:00',
+              ADD COLUMN c_timestamp_null TIMESTAMP,
+              ALTER COLUMN c_date SET DEFAULT '2010-01-01';
+INSERT INTO T VALUES (9), (10);
+ALTER TABLE T ADD COLUMN c_array TEXT[] DEFAULT '{"This", "is", "the", "real", "world"}',
+              ALTER COLUMN c_timestamp SET DEFAULT '1970-12-31 11:12:13',
+              ALTER COLUMN c_timestamp_null SET DEFAULT '2016-09-29 12:00:00';
+INSERT INTO T VALUES (11), (12);
+ALTER TABLE T ADD COLUMN c_small SMALLINT DEFAULT -5,
+              ADD COLUMN c_small_null SMALLINT,
+              ALTER COLUMN c_array SET DEFAULT '{"This", "is", "no", "fantasy"}';
+INSERT INTO T VALUES (13), (14);
+ALTER TABLE T ADD COLUMN c_big BIGINT DEFAULT 180000000000018,
+              ALTER COLUMN c_small SET DEFAULT 9,
+              ALTER COLUMN c_small_null SET DEFAULT 13;
+INSERT INTO T VALUES (15), (16);
+ALTER TABLE T ADD COLUMN c_num NUMERIC DEFAULT 1.00000000001,
+              ALTER COLUMN c_big SET DEFAULT -9999999999999999;
+INSERT INTO T VALUES (17), (18);
+ALTER TABLE T ADD COLUMN c_time TIME DEFAULT '12:00:00',
+              ALTER COLUMN c_num SET DEFAULT 2.000000000000002;
+INSERT INTO T VALUES (19), (20);
+ALTER TABLE T ADD COLUMN c_interval INTERVAL DEFAULT '1 day',
+              ALTER COLUMN c_time SET DEFAULT '23:59:59';
+INSERT INTO T VALUES (21), (22);
+ALTER TABLE T ALTER COLUMN c_time DROP DEFAULT,
+              ALTER COLUMN c_interval SET DEFAULT '3 hours';
+INSERT INTO T VALUES (23), (24);
+ALTER TABLE T ALTER COLUMN c_bpchar    DROP DEFAULT,
+              ALTER COLUMN c_date      DROP DEFAULT,
+              ALTER COLUMN c_text      DROP DEFAULT,
+              ALTER COLUMN c_timestamp DROP DEFAULT,
+              ALTER COLUMN c_array     DROP DEFAULT,
+              ALTER COLUMN c_small     DROP DEFAULT,
+              ALTER COLUMN c_big       DROP DEFAULT,
+              ALTER COLUMN c_num       DROP DEFAULT,
+              ALTER COLUMN c_interval  DROP DEFAULT;
+INSERT INTO T VALUES (25), (26);
+SELECT * FROM T ORDER BY pk;
+ pk | c_int | c_bpchar | c_text |   c_date   |       c_timestamp        |     c_timestamp_null     |         c_array          | c_small | c_small_null |       c_big       |       c_num       |  c_time  | c_interval
+----+-------+----------+--------+------------+--------------------------+--------------------------+--------------------------+---------+--------------+-------------------+-------------------+----------+------------
+  1 |     1 | hello    | world  | 06-02-2016 | Thu Sep 01 12:00:00 2016 |                          | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+  2 |     1 | hello    | world  | 06-02-2016 | Thu Sep 01 12:00:00 2016 |                          | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+  3 |     2 | hello    | world  | 06-02-2016 | Thu Sep 01 12:00:00 2016 |                          | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+  4 |     2 | hello    | world  | 06-02-2016 | Thu Sep 01 12:00:00 2016 |                          | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+  5 |     2 | dog      | world  | 06-02-2016 | Thu Sep 01 12:00:00 2016 |                          | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+  6 |     2 | dog      | world  | 06-02-2016 | Thu Sep 01 12:00:00 2016 |                          | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+  7 |     2 | dog      | cat    | 06-02-2016 | Thu Sep 01 12:00:00 2016 |                          | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+  8 |     2 | dog      | cat    | 06-02-2016 | Thu Sep 01 12:00:00 2016 |                          | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+  9 |     2 | dog      | cat    | 01-01-2010 | Thu Sep 01 12:00:00 2016 |                          | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+ 10 |     2 | dog      | cat    | 01-01-2010 | Thu Sep 01 12:00:00 2016 |                          | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+ 11 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+ 12 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,the,real,world} |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+ 13 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+ 14 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |      -5 |              |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+ 15 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |       9 |           13 |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+ 16 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |       9 |           13 |   180000000000018 |     1.00000000001 | 12:00:00 | @ 1 day
+ 17 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |       9 |           13 | -9999999999999999 |     1.00000000001 | 12:00:00 | @ 1 day
+ 18 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |       9 |           13 | -9999999999999999 |     1.00000000001 | 12:00:00 | @ 1 day
+ 19 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |       9 |           13 | -9999999999999999 | 2.000000000000002 | 12:00:00 | @ 1 day
+ 20 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |       9 |           13 | -9999999999999999 | 2.000000000000002 | 12:00:00 | @ 1 day
+ 21 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |       9 |           13 | -9999999999999999 | 2.000000000000002 | 23:59:59 | @ 1 day
+ 22 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |       9 |           13 | -9999999999999999 | 2.000000000000002 | 23:59:59 | @ 1 day
+ 23 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |       9 |           13 | -9999999999999999 | 2.000000000000002 |          | @ 3 hours
+ 24 |     2 | dog      | cat    | 01-01-2010 | Thu Dec 31 11:12:13 1970 | Thu Sep 29 12:00:00 2016 | {This,is,no,fantasy}     |       9 |           13 | -9999999999999999 | 2.000000000000002 |          | @ 3 hours
+ 25 |     2 |          |        |            |                          | Thu Sep 29 12:00:00 2016 |                          |         |           13 |                   |                   |          |
+ 26 |     2 |          |        |            |                          | Thu Sep 29 12:00:00 2016 |                          |         |           13 |                   |                   |          |
+(26 rows)
+
+SELECT comp();
+   comp
+-----------
+ Unchanged
+(1 row)
+
+DROP TABLE T;
+-- 2.1 Test expressions in the defaults
+CREATE OR REPLACE FUNCTION foo(a INT) RETURNS TEXT AS $$
+DECLARE res TEXT := '';
+        i INT;
+BEGIN
+  i := 0;
+  WHILE (i < a) LOOP
+    res := res || chr(ascii('a') + i);
+    i := i + 1;
+  END LOOP;
+  RETURN res;
+END; $$ LANGUAGE PLPGSQL STABLE;
+CREATE TABLE T(pk INT NOT NULL PRIMARY KEY, c_int INT DEFAULT LENGTH(foo(6)));
+SELECT set('t');
+ set
+-----
+
+(1 row)
+
+INSERT INTO T VALUES (1), (2);
+ALTER TABLE T ADD COLUMN c_bpchar BPCHAR(5) DEFAULT foo(4),
+              ALTER COLUMN c_int SET DEFAULT LENGTH(foo(8));
+INSERT INTO T VALUES (3), (4);
+ALTER TABLE T ADD COLUMN c_text TEXT  DEFAULT foo(6),
+              ALTER COLUMN c_bpchar SET DEFAULT foo(3);
+INSERT INTO T VALUES (5), (6);
+ALTER TABLE T ADD COLUMN c_date DATE DEFAULT '2016-06-02'::DATE  + LENGTH(foo(10)),
+              ALTER COLUMN c_text SET DEFAULT foo(12);
+INSERT INTO T VALUES (7), (8);
+ALTER TABLE T ADD COLUMN c_timestamp TIMESTAMP DEFAULT '2016-09-01'::DATE + LENGTH(foo(10)),
+              ALTER COLUMN c_date SET DEFAULT '2010-01-01'::DATE - LENGTH(foo(4));
+INSERT INTO T VALUES (9), (10);
+ALTER TABLE T ADD COLUMN c_array TEXT[] DEFAULT ('{"This", "is", "' || foo(4) || '", "the", "real", "world"}')::TEXT[],
+              ALTER COLUMN c_timestamp SET DEFAULT '1970-12-31'::DATE + LENGTH(foo(30));
+INSERT INTO T VALUES (11), (12);
+ALTER TABLE T ALTER COLUMN c_int DROP DEFAULT,
+              ALTER COLUMN c_array SET DEFAULT ('{"This", "is", "' || foo(1) || '", "fantasy"}')::text[];
+INSERT INTO T VALUES (13), (14);
+ALTER TABLE T ALTER COLUMN c_bpchar    DROP DEFAULT,
+              ALTER COLUMN c_date      DROP DEFAULT,
+              ALTER COLUMN c_text      DROP DEFAULT,
+              ALTER COLUMN c_timestamp DROP DEFAULT,
+              ALTER COLUMN c_array     DROP DEFAULT;
+INSERT INTO T VALUES (15), (16);
+SELECT * FROM T;
+ pk | c_int | c_bpchar |    c_text    |   c_date   |       c_timestamp        |            c_array
+----+-------+----------+--------------+------------+--------------------------+-------------------------------
+  1 |     6 | abcd     | abcdef       | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world}
+  2 |     6 | abcd     | abcdef       | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world}
+  3 |     8 | abcd     | abcdef       | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world}
+  4 |     8 | abcd     | abcdef       | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world}
+  5 |     8 | abc      | abcdef       | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world}
+  6 |     8 | abc      | abcdef       | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world}
+  7 |     8 | abc      | abcdefghijkl | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world}
+  8 |     8 | abc      | abcdefghijkl | 06-12-2016 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world}
+  9 |     8 | abc      | abcdefghijkl | 12-28-2009 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world}
+ 10 |     8 | abc      | abcdefghijkl | 12-28-2009 | Sun Sep 11 00:00:00 2016 | {This,is,abcd,the,real,world}
+ 11 |     8 | abc      | abcdefghijkl | 12-28-2009 | Sat Jan 30 00:00:00 1971 | {This,is,abcd,the,real,world}
+ 12 |     8 | abc      | abcdefghijkl | 12-28-2009 | Sat Jan 30 00:00:00 1971 | {This,is,abcd,the,real,world}
+ 13 |       | abc      | abcdefghijkl | 12-28-2009 | Sat Jan 30 00:00:00 1971 | {This,is,a,fantasy}
+ 14 |       | abc      | abcdefghijkl | 12-28-2009 | Sat Jan 30 00:00:00 1971 | {This,is,a,fantasy}
+ 15 |       |          |              |            |                          |
+ 16 |       |          |              |            |                          |
+(16 rows)
+
+SELECT comp();
+   comp
+-----------
+ Unchanged
+(1 row)
+
+DROP TABLE T;
+DROP FUNCTION foo(INT);
+-- 2.2 Limits
+-- 2.2.1 Fall back to full rewrite for volatile expressions
+CREATE TABLE T(pk INT NOT NULL PRIMARY KEY);
+INSERT INTO T VALUES (1);
+SELECT set('t');
+ set
+-----
+
+(1 row)
+
+-- now() is stable, because it returns the transaction timestamp
+ALTER TABLE T ADD COLUMN c1 TIMESTAMP DEFAULT now();
+SELECT comp();
+   comp
+-----------
+ Unchanged
+(1 row)
+
+-- clock_timestamp() is volatile
+ALTER TABLE T ADD COLUMN c2 TIMESTAMP DEFAULT clock_timestamp();
+SELECT comp();
+   comp
+-----------
+ Rewritten
+(1 row)
+
+DROP TABLE T;
+-- 3. Drive simple queries DML
+CREATE TABLE T (pk INT NOT NULL PRIMARY KEY);
+SELECT set('t');
+ set
+-----
+
+(1 row)
+
+INSERT INTO T SELECT * FROM generate_series(1, 10) a;
+ALTER TABLE T ADD COLUMN c_bigint BIGINT NOT NULL DEFAULT -1;
+INSERT INTO T SELECT b, b - 10 FROM generate_series(11, 20) a(b);
+ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT 'hello';
+INSERT INTO T SELECT b, b - 10, (b + 10)::text FROM generate_series(21, 30) a(b);
+-- 3.a A WHERE clause
+SELECT c_bigint, c_text FROM T WHERE c_bigint = -1 LIMIT 1;
+ c_bigint | c_text
+----------+--------
+       -1 | hello
+(1 row)
+
+EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT c_bigint, c_text FROM T WHERE c_bigint = -1 LIMIT 1;
+                  QUERY PLAN
+----------------------------------------------
+ Limit
+   Output: c_bigint, c_text
+   ->  Seq Scan on fast_default.t
+         Output: c_bigint, c_text
+         Filter: (t.c_bigint = '-1'::integer)
+(5 rows)
+
+SELECT c_bigint, c_text FROM T WHERE c_text = 'hello' LIMIT 1;
+ c_bigint | c_text
+----------+--------
+       -1 | hello
+(1 row)
+
+EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT c_bigint, c_text FROM T WHERE c_text = 'hello' LIMIT 1;
+                 QUERY PLAN
+--------------------------------------------
+ Limit
+   Output: c_bigint, c_text
+   ->  Seq Scan on fast_default.t
+         Output: c_bigint, c_text
+         Filter: (t.c_text = 'hello'::text)
+(5 rows)
+
+-- 3.b COALESCE
+SELECT COALESCE(c_bigint, pk), COALESCE(c_text, pk::text) FROM T ORDER BY pk LIMIT 10;
+ coalesce | coalesce
+----------+----------
+       -1 | hello
+       -1 | hello
+       -1 | hello
+       -1 | hello
+       -1 | hello
+       -1 | hello
+       -1 | hello
+       -1 | hello
+       -1 | hello
+       -1 | hello
+(10 rows)
+
+-- 3.c Aggregate function
+SELECT SUM(c_bigint), MAX(c_text), MIN(c_text) FROM T;
+ sum |  max  | min
+-----+-------+-----
+ 201 | hello | 31
+(1 row)
+
+-- 3.d ORDER BY
+SELECT * FROM T ORDER BY c_bigint, c_text, pk LIMIT 10;
+ pk | c_bigint | c_text
+----+----------+--------
+  1 |       -1 | hello
+  2 |       -1 | hello
+  3 |       -1 | hello
+  4 |       -1 | hello
+  5 |       -1 | hello
+  6 |       -1 | hello
+  7 |       -1 | hello
+  8 |       -1 | hello
+  9 |       -1 | hello
+ 10 |       -1 | hello
+(10 rows)
+
+EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT * FROM T ORDER BY c_bigint, c_text, pk LIMIT 10;
+                  QUERY PLAN
+----------------------------------------------
+ Limit
+   Output: pk, c_bigint, c_text
+   ->  Sort
+         Output: pk, c_bigint, c_text
+         Sort Key: t.c_bigint, t.c_text, t.pk
+         ->  Seq Scan on fast_default.t
+               Output: pk, c_bigint, c_text
+(7 rows)
+
+SELECT * FROM T WHERE c_bigint > -1 ORDER BY c_bigint, c_text, pk LIMIT 10;
+ pk | c_bigint | c_text
+----+----------+--------
+ 11 |        1 | hello
+ 12 |        2 | hello
+ 13 |        3 | hello
+ 14 |        4 | hello
+ 15 |        5 | hello
+ 16 |        6 | hello
+ 17 |        7 | hello
+ 18 |        8 | hello
+ 19 |        9 | hello
+ 20 |       10 | hello
+(10 rows)
+
+EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT * FROM T WHERE c_bigint > -1 ORDER BY c_bigint, c_text, pk LIMIT 10;
+                     QUERY PLAN
+----------------------------------------------------
+ Limit
+   Output: pk, c_bigint, c_text
+   ->  Sort
+         Output: pk, c_bigint, c_text
+         Sort Key: t.c_bigint, t.c_text, t.pk
+         ->  Seq Scan on fast_default.t
+               Output: pk, c_bigint, c_text
+               Filter: (t.c_bigint > '-1'::integer)
+(8 rows)
+
+-- 3.y DELETE with RETURNING
+DELETE FROM T WHERE pk BETWEEN 10 AND 20 RETURNING *;
+ pk | c_bigint | c_text
+----+----------+--------
+ 10 |       -1 | hello
+ 11 |        1 | hello
+ 12 |        2 | hello
+ 13 |        3 | hello
+ 14 |        4 | hello
+ 15 |        5 | hello
+ 16 |        6 | hello
+ 17 |        7 | hello
+ 18 |        8 | hello
+ 19 |        9 | hello
+ 20 |       10 | hello
+(11 rows)
+
+EXPLAIN (VERBOSE TRUE, COSTS FALSE) DELETE FROM T WHERE pk BETWEEN 10 AND 20 RETURNING *;
+                        QUERY PLAN
+-----------------------------------------------------------
+ Delete on fast_default.t
+   Output: pk, c_bigint, c_text
+   ->  Bitmap Heap Scan on fast_default.t
+         Output: ctid
+         Recheck Cond: ((t.pk >= 10) AND (t.pk <= 20))
+         ->  Bitmap Index Scan on t_pkey
+               Index Cond: ((t.pk >= 10) AND (t.pk <= 20))
+(7 rows)
+
+-- 3.z UPDATE
+UPDATE T SET c_text = '"' || c_text || '"'  WHERE pk < 10;
+SELECT * FROM T WHERE c_text LIKE '"%"' ORDER BY PK;
+ pk | c_bigint | c_text
+----+----------+---------
+  1 |       -1 | "hello"
+  2 |       -1 | "hello"
+  3 |       -1 | "hello"
+  4 |       -1 | "hello"
+  5 |       -1 | "hello"
+  6 |       -1 | "hello"
+  7 |       -1 | "hello"
+  8 |       -1 | "hello"
+  9 |       -1 | "hello"
+(9 rows)
+
+SELECT comp();
+   comp
+-----------
+ Unchanged
+(1 row)
+
+DROP TABLE T;
+-- 4. Combine with other DDL
+CREATE TABLE T(pk INT NOT NULL PRIMARY KEY);
+SELECT set('t');
+ set
+-----
+
+(1 row)
+
+INSERT INTO T VALUES (1), (2);
+ALTER TABLE T ADD COLUMN c_int INT NOT NULL DEFAULT -1;
+INSERT INTO T VALUES (3), (4);
+ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT 'Hello';
+INSERT INTO T VALUES (5), (6);
+ALTER TABLE T ALTER COLUMN c_text SET DEFAULT 'world',
+              ALTER COLUMN c_int  SET DEFAULT 1;
+INSERT INTO T VALUES (7), (8);
+SELECT * FROM T ORDER BY pk;
+ pk | c_int | c_text
+----+-------+--------
+  1 |    -1 | Hello
+  2 |    -1 | Hello
+  3 |    -1 | Hello
+  4 |    -1 | Hello
+  5 |    -1 | Hello
+  6 |    -1 | Hello
+  7 |     1 | world
+  8 |     1 | world
+(8 rows)
+
+-- Add an index
+CREATE INDEX i ON T(c_int, c_text);
+SELECT c_text FROM T WHERE c_int = -1;
+ c_text
+--------
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+(6 rows)
+
+SELECT comp();
+   comp
+-----------
+ Unchanged
+(1 row)
+
+DROP TABLE T;
+DROP FUNCTION set(name);
+DROP FUNCTION comp();
+DROP TABLE m;
+DROP SCHEMA fast_default;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 8641769..1bcce97 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -106,7 +106,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
 # so keep this parallel group to at most 19 tests
 # ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml fast_default

 # event triggers cannot run concurrently with any test that runs DDL
 test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 835cf35..a080890 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -169,3 +169,4 @@ test: with
 test: xml
 test: event_trigger
 test: stats
+test: fast_default
diff --git a/src/test/regress/sql/fast_default.sql b/src/test/regress/sql/fast_default.sql
new file mode 100644
index 0000000..cc73055
--- /dev/null
+++ b/src/test/regress/sql/fast_default.sql
@@ -0,0 +1,283 @@
+--
+-- ALTER TABLE ADD COLUMN DEFAULT test
+--
+
+SET search_path = fast_default;
+CREATE SCHEMA fast_default;
+CREATE TABLE m(id OID);
+INSERT INTO m VALUES (NULL::OID);
+
+CREATE FUNCTION set(tabname name) RETURNS VOID
+AS $$
+BEGIN
+  UPDATE m SET id = (SELECT c.relfilenode FROM pg_class AS c, pg_namespace AS s
+           WHERE c.relname = tabname AND c.relnamespace = s.oid AND s.nspname = 'fast_default');
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE FUNCTION comp() RETURNS TEXT
+AS $$
+BEGIN
+  RETURN (SELECT CASE WHEN m.id = c.relfilenode THEN 'Unchanged' ELSE 'Rewritten' END
+            FROM m, pg_class AS c, pg_namespace AS s
+           WHERE c.relname = 't' AND c.relnamespace = s.oid AND s.nspname = 'fast_default');
+END;
+$$ LANGUAGE 'plpgsql';
+
+-- 1. Test a large sample of dfferent datatypes
+CREATE TABLE T(pk INT NOT NULL PRIMARY KEY, c_int INT DEFAULT 1);
+
+SELECT set('t');
+
+INSERT INTO T VALUES (1), (2);
+
+ALTER TABLE T ADD COLUMN c_bpchar BPCHAR(5) DEFAULT 'hello',
+              ALTER COLUMN c_int SET DEFAULT 2;
+
+INSERT INTO T VALUES (3), (4);
+
+
+ALTER TABLE T ADD COLUMN c_text TEXT  DEFAULT 'world',
+              ALTER COLUMN c_bpchar SET DEFAULT 'dog';
+
+INSERT INTO T VALUES (5), (6);
+
+ALTER TABLE T ADD COLUMN c_date DATE DEFAULT '2016-06-02',
+              ALTER COLUMN c_text SET DEFAULT 'cat';
+
+INSERT INTO T VALUES (7), (8);
+
+ALTER TABLE T ADD COLUMN c_timestamp TIMESTAMP DEFAULT '2016-09-01 12:00:00',
+              ADD COLUMN c_timestamp_null TIMESTAMP,
+              ALTER COLUMN c_date SET DEFAULT '2010-01-01';
+
+INSERT INTO T VALUES (9), (10);
+
+ALTER TABLE T ADD COLUMN c_array TEXT[] DEFAULT '{"This", "is", "the", "real", "world"}',
+              ALTER COLUMN c_timestamp SET DEFAULT '1970-12-31 11:12:13',
+              ALTER COLUMN c_timestamp_null SET DEFAULT '2016-09-29 12:00:00';
+
+INSERT INTO T VALUES (11), (12);
+
+ALTER TABLE T ADD COLUMN c_small SMALLINT DEFAULT -5,
+              ADD COLUMN c_small_null SMALLINT,
+              ALTER COLUMN c_array SET DEFAULT '{"This", "is", "no", "fantasy"}';
+
+INSERT INTO T VALUES (13), (14);
+
+ALTER TABLE T ADD COLUMN c_big BIGINT DEFAULT 180000000000018,
+              ALTER COLUMN c_small SET DEFAULT 9,
+              ALTER COLUMN c_small_null SET DEFAULT 13;
+
+INSERT INTO T VALUES (15), (16);
+
+ALTER TABLE T ADD COLUMN c_num NUMERIC DEFAULT 1.00000000001,
+              ALTER COLUMN c_big SET DEFAULT -9999999999999999;
+
+INSERT INTO T VALUES (17), (18);
+
+ALTER TABLE T ADD COLUMN c_time TIME DEFAULT '12:00:00',
+              ALTER COLUMN c_num SET DEFAULT 2.000000000000002;
+
+INSERT INTO T VALUES (19), (20);
+
+ALTER TABLE T ADD COLUMN c_interval INTERVAL DEFAULT '1 day',
+              ALTER COLUMN c_time SET DEFAULT '23:59:59';
+
+INSERT INTO T VALUES (21), (22);
+
+ALTER TABLE T ALTER COLUMN c_time DROP DEFAULT,
+              ALTER COLUMN c_interval SET DEFAULT '3 hours';
+
+INSERT INTO T VALUES (23), (24);
+
+ALTER TABLE T ALTER COLUMN c_bpchar    DROP DEFAULT,
+              ALTER COLUMN c_date      DROP DEFAULT,
+              ALTER COLUMN c_text      DROP DEFAULT,
+              ALTER COLUMN c_timestamp DROP DEFAULT,
+              ALTER COLUMN c_array     DROP DEFAULT,
+              ALTER COLUMN c_small     DROP DEFAULT,
+              ALTER COLUMN c_big       DROP DEFAULT,
+              ALTER COLUMN c_num       DROP DEFAULT,
+              ALTER COLUMN c_interval  DROP DEFAULT;
+
+INSERT INTO T VALUES (25), (26);
+
+SELECT * FROM T ORDER BY pk;
+
+SELECT comp();
+
+DROP TABLE T;
+
+-- 2.1 Test expressions in the defaults
+CREATE OR REPLACE FUNCTION foo(a INT) RETURNS TEXT AS $$
+DECLARE res TEXT := '';
+        i INT;
+BEGIN
+  i := 0;
+  WHILE (i < a) LOOP
+    res := res || chr(ascii('a') + i);
+    i := i + 1;
+  END LOOP;
+  RETURN res;
+END; $$ LANGUAGE PLPGSQL STABLE;
+
+CREATE TABLE T(pk INT NOT NULL PRIMARY KEY, c_int INT DEFAULT LENGTH(foo(6)));
+
+SELECT set('t');
+
+INSERT INTO T VALUES (1), (2);
+
+ALTER TABLE T ADD COLUMN c_bpchar BPCHAR(5) DEFAULT foo(4),
+              ALTER COLUMN c_int SET DEFAULT LENGTH(foo(8));
+
+INSERT INTO T VALUES (3), (4);
+
+ALTER TABLE T ADD COLUMN c_text TEXT  DEFAULT foo(6),
+              ALTER COLUMN c_bpchar SET DEFAULT foo(3);
+
+INSERT INTO T VALUES (5), (6);
+
+ALTER TABLE T ADD COLUMN c_date DATE DEFAULT '2016-06-02'::DATE  + LENGTH(foo(10)),
+              ALTER COLUMN c_text SET DEFAULT foo(12);
+
+INSERT INTO T VALUES (7), (8);
+
+ALTER TABLE T ADD COLUMN c_timestamp TIMESTAMP DEFAULT '2016-09-01'::DATE + LENGTH(foo(10)),
+              ALTER COLUMN c_date SET DEFAULT '2010-01-01'::DATE - LENGTH(foo(4));
+
+INSERT INTO T VALUES (9), (10);
+
+ALTER TABLE T ADD COLUMN c_array TEXT[] DEFAULT ('{"This", "is", "' || foo(4) || '", "the", "real", "world"}')::TEXT[],
+              ALTER COLUMN c_timestamp SET DEFAULT '1970-12-31'::DATE + LENGTH(foo(30));
+
+INSERT INTO T VALUES (11), (12);
+
+ALTER TABLE T ALTER COLUMN c_int DROP DEFAULT,
+              ALTER COLUMN c_array SET DEFAULT ('{"This", "is", "' || foo(1) || '", "fantasy"}')::text[];
+
+INSERT INTO T VALUES (13), (14);
+
+ALTER TABLE T ALTER COLUMN c_bpchar    DROP DEFAULT,
+              ALTER COLUMN c_date      DROP DEFAULT,
+              ALTER COLUMN c_text      DROP DEFAULT,
+              ALTER COLUMN c_timestamp DROP DEFAULT,
+              ALTER COLUMN c_array     DROP DEFAULT;
+
+INSERT INTO T VALUES (15), (16);
+
+SELECT * FROM T;
+
+SELECT comp();
+
+DROP TABLE T;
+
+DROP FUNCTION foo(INT);
+
+-- 2.2 Limits
+-- 2.2.1 Fall back to full rewrite for volatile expressions
+CREATE TABLE T(pk INT NOT NULL PRIMARY KEY);
+
+INSERT INTO T VALUES (1);
+
+SELECT set('t');
+
+-- now() is stable, because it returns the transaction timestamp
+ALTER TABLE T ADD COLUMN c1 TIMESTAMP DEFAULT now();
+
+SELECT comp();
+
+-- clock_timestamp() is volatile
+ALTER TABLE T ADD COLUMN c2 TIMESTAMP DEFAULT clock_timestamp();
+
+SELECT comp();
+
+DROP TABLE T;
+
+-- 3. Drive simple queries DML
+CREATE TABLE T (pk INT NOT NULL PRIMARY KEY);
+
+SELECT set('t');
+
+INSERT INTO T SELECT * FROM generate_series(1, 10) a;
+
+ALTER TABLE T ADD COLUMN c_bigint BIGINT NOT NULL DEFAULT -1;
+
+INSERT INTO T SELECT b, b - 10 FROM generate_series(11, 20) a(b);
+
+ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT 'hello';
+
+INSERT INTO T SELECT b, b - 10, (b + 10)::text FROM generate_series(21, 30) a(b);
+
+-- 3.a A WHERE clause
+SELECT c_bigint, c_text FROM T WHERE c_bigint = -1 LIMIT 1;
+
+EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT c_bigint, c_text FROM T WHERE c_bigint = -1 LIMIT 1;
+
+SELECT c_bigint, c_text FROM T WHERE c_text = 'hello' LIMIT 1;
+
+EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT c_bigint, c_text FROM T WHERE c_text = 'hello' LIMIT 1;
+
+
+-- 3.b COALESCE
+SELECT COALESCE(c_bigint, pk), COALESCE(c_text, pk::text) FROM T ORDER BY pk LIMIT 10;
+
+-- 3.c Aggregate function
+SELECT SUM(c_bigint), MAX(c_text), MIN(c_text) FROM T;
+
+-- 3.d ORDER BY
+SELECT * FROM T ORDER BY c_bigint, c_text, pk LIMIT 10;
+
+EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT * FROM T ORDER BY c_bigint, c_text, pk LIMIT 10;
+
+SELECT * FROM T WHERE c_bigint > -1 ORDER BY c_bigint, c_text, pk LIMIT 10;
+
+EXPLAIN (VERBOSE TRUE, COSTS FALSE) SELECT * FROM T WHERE c_bigint > -1 ORDER BY c_bigint, c_text, pk LIMIT 10;
+
+-- 3.y DELETE with RETURNING
+DELETE FROM T WHERE pk BETWEEN 10 AND 20 RETURNING *;
+EXPLAIN (VERBOSE TRUE, COSTS FALSE) DELETE FROM T WHERE pk BETWEEN 10 AND 20 RETURNING *;
+
+-- 3.z UPDATE
+UPDATE T SET c_text = '"' || c_text || '"'  WHERE pk < 10;
+SELECT * FROM T WHERE c_text LIKE '"%"' ORDER BY PK;
+
+SELECT comp();
+
+DROP TABLE T;
+
+
+-- 4. Combine with other DDL
+CREATE TABLE T(pk INT NOT NULL PRIMARY KEY);
+
+SELECT set('t');
+
+INSERT INTO T VALUES (1), (2);
+
+ALTER TABLE T ADD COLUMN c_int INT NOT NULL DEFAULT -1;
+
+INSERT INTO T VALUES (3), (4);
+
+ALTER TABLE T ADD COLUMN c_text TEXT DEFAULT 'Hello';
+
+INSERT INTO T VALUES (5), (6);
+
+ALTER TABLE T ALTER COLUMN c_text SET DEFAULT 'world',
+              ALTER COLUMN c_int  SET DEFAULT 1;
+
+INSERT INTO T VALUES (7), (8);
+
+SELECT * FROM T ORDER BY pk;
+
+-- Add an index
+CREATE INDEX i ON T(c_int, c_text);
+
+SELECT c_text FROM T WHERE c_int = -1;
+
+SELECT comp();
+
+DROP TABLE T;
+DROP FUNCTION set(name);
+DROP FUNCTION comp();
+DROP TABLE m;
+DROP SCHEMA fast_default;
