diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ebdb120a1a8..d6a9d8c5808 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1155,8 +1155,9 @@
       <entry></entry>
       <entry>
        This column has a value which is used where the column is entirely
-       missing from the row, as happens when a column is added after the
-       row is created. The actual value used is stored in the
+       missing from the row, as happens when a column is added with a
+       non-volatile <literal>DEFAULT</literal> value after the row is created.
+       The actual value used is stored in the
        <structfield>attmissingval</structfield> column.
       </entry>
      </row>
@@ -1248,8 +1249,9 @@
       <entry>
        This column has a one element array containing the value used when the
        column is entirely missing from the row, as happens when the column is
-       added after the row is created. The value is only used when
-       <structfield>atthasmissing</structfield> is true. If there is no value
+       added with a non-volatile <literal>DEFAULT</literal> value after the
+       row is created.  The value is only used when
+       <structfield>atthasmissing</structfield> is true.  If there is no value
        the column is null.
       </entry>
      </row>
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index edf459ae6b1..69f3355eded 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -1187,8 +1187,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
     When a column is added with <literal>ADD COLUMN</literal> and a
     non-volatile <literal>DEFAULT</literal> is specified, the default is
     evaluated at the time of the statement and the result stored in the
-    table's metadata. That value will be used for the column for all existing
-    rows. If no <literal>DEFAULT</literal> is specified, NULL is used. In
+    table's metadata.  That value will be used for the column for all existing
+    rows.  If no <literal>DEFAULT</literal> is specified, NULL is used.  In
     neither case is a rewrite of the table required.
    </para>
 
@@ -1201,10 +1201,9 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
     type or an unconstrained domain over the new type, a table rewrite is not
     needed; but any indexes on the affected columns must still be rebuilt.
     Adding or removing a system <literal>oid</literal> column also requires
-    rewriting the entire table.
-    Table and/or index rebuilds may take a significant amount of time
-    for a large table; and will temporarily require as much as double the disk
-    space.
+    rewriting the entire table.  Table and/or index rebuilds may take a
+    significant amount of time for a large table; and will temporarily require
+    as much as double the disk space.
    </para>
 
    <para>
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 6d19537489c..aae7e2d9049 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -85,7 +85,6 @@ getmissingattr(TupleDesc tupleDesc,
 			   int attnum, bool *isnull)
 {
 	Form_pg_attribute att;
-	AttrMissing *attrmiss;
 
 	Assert(attnum <= tupleDesc->natts);
 	Assert(attnum > 0);
@@ -94,6 +93,8 @@ getmissingattr(TupleDesc tupleDesc,
 
 	if (att->atthasmissing)
 	{
+		AttrMissing *attrmiss;
+
 		Assert(tupleDesc->constr);
 		Assert(tupleDesc->constr->missing);
 
@@ -323,9 +324,6 @@ fill_val(Form_pg_attribute att,
 
 	data += data_length;
 	*dataP = data;
-
-
-
 }
 
 /*
@@ -1558,7 +1556,8 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
 		elog(ERROR, "cannot extract attribute from empty tuple slot");
 
 	/*
-	 * return NULL if attnum is out of range according to the tuple
+	 * return NULL or missing value if attnum is out of range according to the
+	 * tuple
 	 *
 	 * (We have to check this separately because of various inheritance and
 	 * table-alteration scenarios: the tuple could be either longer or shorter
@@ -1566,9 +1565,7 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
 	 */
 	tup = tuple->t_data;
 	if (attnum > HeapTupleHeaderGetNatts(tup))
-	{
 		return getmissingattr(slot->tts_tupleDescriptor, attnum, isnull);
-	}
 
 	/*
 	 * check if target attribute is null: no point in groveling through tuple
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 54393607222..2658399484b 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -131,6 +131,7 @@ CreateTupleDescCopy(TupleDesc tupdesc)
 
 		att->attnotnull = false;
 		att->atthasdef = false;
+		att->atthasmissing = false;
 		att->attidentity = '\0';
 	}
 
@@ -246,6 +247,7 @@ TupleDescCopy(TupleDesc dst, TupleDesc src)
 
 		att->attnotnull = false;
 		att->atthasdef = false;
+		att->atthasmissing = false;
 		att->attidentity = '\0';
 	}
 	dst->constr = NULL;
@@ -298,6 +300,7 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
 	/* since we're not copying constraints or defaults, clear these */
 	dstAtt->attnotnull = false;
 	dstAtt->atthasdef = false;
+	dstAtt->atthasmissing = false;
 	dstAtt->attidentity = '\0';
 }
 
@@ -697,6 +700,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
 
 	att->attnotnull = false;
 	att->atthasdef = false;
+	att->atthasmissing = false;
 	att->attidentity = '\0';
 	att->attisdropped = false;
 	att->attislocal = true;
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 2b34bb8313d..a1def77944c 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1934,14 +1934,13 @@ heap_drop_with_catalog(Oid relid)
 /*
  * RelationClearMissing
  *
- * set atthasmissing and attmissingval to false/null for all attributes
+ * Set atthasmissing and attmissingval to false/null for all attributes
  * where they are currently set. This can be safely and usefully done if
  * the table is rewritten (e.g. by VACUUM FULL or CLUSTER) where we know there
  * are no rows left with less than a full complement of attributes.
  *
  * The caller must have an AccessExclusive lock on the relation.
  */
-
 void
 RelationClearMissing(Relation rel)
 {
@@ -1970,7 +1969,7 @@ RelationClearMissing(Relation rel)
 	/* Get a lock on pg_attribute */
 	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
 
-	/* process each non-system attribute */
+	/* process each non-system attribute, including any dropped columns */
 	for (attnum = 1; attnum <= natts; attnum++)
 	{
 		tuple = SearchSysCache2(ATTNUM,
@@ -2027,19 +2026,9 @@ 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;
-	Datum		valuesAtt[Natts_pg_attribute];
-	bool		nullsAtt[Natts_pg_attribute];
-	bool		replacesAtt[Natts_pg_attribute];
-	Datum		missingval = (Datum) 0;
-	bool		missingIsNull = true;
 
 	/*
 	 * Flatten expression to string form for storage.
@@ -2094,6 +2083,18 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
 	if (!attStruct->atthasdef)
 	{
+		Form_pg_attribute defAttStruct;
+
+		ExprState  *exprState;
+		Expr	   *expr2 = (Expr *) expr;
+		EState	   *estate = NULL;
+		ExprContext *econtext;
+		Datum		valuesAtt[Natts_pg_attribute];
+		bool		nullsAtt[Natts_pg_attribute];
+		bool		replacesAtt[Natts_pg_attribute];
+		Datum		missingval = (Datum) 0;
+		bool		missingIsNull = true;
+
 		MemSet(valuesAtt, 0, sizeof(valuesAtt));
 		MemSet(nullsAtt, false, sizeof(nullsAtt));
 		MemSet(replacesAtt, false, sizeof(replacesAtt));
@@ -2110,6 +2111,8 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 			missingval = ExecEvalExpr(exprState, econtext,
 									  &missingIsNull);
 
+			FreeExecutorState(estate);
+
 			defAttStruct = TupleDescAttr(rel->rd_att, attnum - 1);
 
 			if (missingIsNull)
@@ -2139,6 +2142,10 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 								   valuesAtt, nullsAtt, replacesAtt);
 
 		CatalogTupleUpdate(attrrel, &atttup->t_self, atttup);
+
+		if (!missingIsNull)
+			pfree(DatumGetPointer(missingval));
+
 	}
 	heap_close(attrrel, RowExclusiveLock);
 	heap_freetuple(atttup);
@@ -2169,14 +2176,6 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
 								  RelationGetRelid(rel), attnum, is_internal);
 
-	if (estate)
-	{
-		FreeExecutorState(estate);
-	}
-
-	if (!missingIsNull)
-		pfree(DatumGetPointer(missingval));
-
 	return attrdefOid;
 }
 
@@ -2446,11 +2445,9 @@ AddRelationNewConstraints(Relation rel,
 			(IsA(expr, Const) &&((Const *) expr)->constisnull))
 			continue;
 
-		/* If the default is volatile we cannot use a missing value */
+		/* If the DEFAULT is volatile we cannot use a missing value */
 		if (colDef->missingMode && contain_volatile_functions((Node *) expr))
-		{
 			colDef->missingMode = false;
-		}
 
 		defOid = StoreAttrDefault(rel, colDef->attnum, expr, is_internal,
 								  colDef->missingMode);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c0790a724f8..83a881eff38 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5449,6 +5449,12 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
 		rawEnt->attnum = attribute.attnum;
 		rawEnt->raw_default = copyObject(colDef->raw_default);
+
+		/*
+		 * Attempt to skip a complete table rewrite by storing the specified
+		 * DEFAULT value outside of the heap.  This may be disabled inside
+		 * AddRelationNewConstraints if the optimization cannot be applied.
+		 */
 		rawEnt->missingMode = true;
 
 		/*
@@ -5466,9 +5472,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		 * a rewrite
 		 */
 		if (!rawEnt->missingMode)
-		{
 			tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
-		}
 	}
 
 	/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 5348c77b23d..0231f8bf7c6 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1494,11 +1494,11 @@ relation_excluded_by_constraints(PlannerInfo *root,
  * step at runtime, so we use such tlists preferentially for scan nodes.
  *
  * Exception: if there are any dropped or missing columns, we punt and return
- * NIL.  Ideally we would like to handle these cases too.  However
- * this creates problems for ExecTypeFromTL, which may be asked to build a
- * tupdesc for a tlist that includes vars of no-longer-existent types.  In
- * theory we could dig out the required info from the pg_attribute entries of
- * the relation, but that data is not readily available to ExecTypeFromTL.
+ * NIL.  Ideally we would like to handle these cases too.  However this
+ * creates problems for ExecTypeFromTL, which may be asked to build a tupdesc
+ * for a tlist that includes vars of no-longer-existent types.  In theory we
+ * could dig out the required info from the pg_attribute entries of the
+ * relation, but that data is not readily available to ExecTypeFromTL.
  * For now, we don't apply the physical-tlist optimization when there are
  * dropped cols.
  *
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index db6d8b0ceb3..555ffed43ff 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -680,9 +680,8 @@ RelationBuildTupleDesc(Relation relation)
 			AttrDefaultFetch(relation);
 		}
 		else
-		{
 			constr->num_defval = 0;
-		}
+
 		constr->missing = attrmiss;
 
 		if (relation->rd_rel->relchecks > 0)	/* CHECKs */
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index e2f16e39a67..002c104ce3c 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -832,5 +832,4 @@ 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/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 280e36c5d23..5bb64f7c31d 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -204,7 +204,7 @@ typedef FormData_pg_attribute *Form_pg_attribute;
 #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_attstattarget 4
 #define Anum_pg_attribute_attlen		5
 #define Anum_pg_attribute_attnum		6
 #define Anum_pg_attribute_attndims		7
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index b54e30256ed..fccddc6ecd4 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -186,6 +186,6 @@ test: reloptions
 test: hash_part
 test: indexing
 test: partition_aggregate
+test: fast_default
 test: event_trigger
 test: stats
-test: fast_default
