#include "pgstat.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/inval.h"
    table_close(attr_rel, RowExclusiveLock);
 }
 
+/*
+ * StoreAttrMissingVal
+ *
+ * Set the missing value of a single attribute.
+ */
+void
+StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
+{
+   Datum       valuesAtt[Natts_pg_attribute] = {0};
+   bool        nullsAtt[Natts_pg_attribute] = {0};
+   bool        replacesAtt[Natts_pg_attribute] = {0};
+   Relation    attrrel;
+   Form_pg_attribute attStruct;
+   HeapTuple   atttup,
+               newtup;
+
+   /* This is only supported for plain tables */
+   Assert(rel->rd_rel->relkind == RELKIND_RELATION);
+
+   /* Fetch the pg_attribute row */
+   attrrel = table_open(AttributeRelationId, RowExclusiveLock);
+
+   atttup = SearchSysCache2(ATTNUM,
+                            ObjectIdGetDatum(RelationGetRelid(rel)),
+                            Int16GetDatum(attnum));
+   if (!HeapTupleIsValid(atttup))  /* shouldn't happen */
+       elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+            attnum, RelationGetRelid(rel));
+   attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
+
+   /* Make a one-element array containing the value */
+   missingval = PointerGetDatum(construct_array(&missingval,
+                                                1,
+                                                attStruct->atttypid,
+                                                attStruct->attlen,
+                                                attStruct->attbyval,
+                                                attStruct->attalign));
+
+   /* Update the pg_attribute row */
+   valuesAtt[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(true);
+   replacesAtt[Anum_pg_attribute_atthasmissing - 1] = true;
+
+   valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
+   replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
+
+   newtup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
+                              valuesAtt, nullsAtt, replacesAtt);
+   CatalogTupleUpdate(attrrel, &newtup->t_self, newtup);
+
+   /* clean up */
+   ReleaseSysCache(atttup);
+   table_close(attrrel, RowExclusiveLock);
+}
+
 /*
  * SetAttrMissing
  *
             castNode(Const, expr)->constisnull))
            continue;
 
-       /* If the DEFAULT is volatile we cannot use a missing value */
-       if (colDef->missingMode &&
-           contain_volatile_functions_after_planning((Expr *) expr))
-           colDef->missingMode = false;
-
        defOid = StoreAttrDefault(rel, colDef->attnum, expr, is_internal,
-                                 colDef->missingMode);
+                                 false);
 
        cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
        cooked->contype = CONSTR_DEFAULT;
 
        valuesAtt[Anum_pg_attribute_atthasdef - 1] = true;
        replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
 
+       /*
+        * Note: this code is dead so far as core Postgres is concerned,
+        * because no caller passes add_column_mode = true anymore.  We keep
+        * it in back branches on the slight chance that some extension is
+        * depending on it.
+        */
        if (rel->rd_rel->relkind == RELKIND_RELATION && add_column_mode &&
            !attgenerated)
        {
 
        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 = (!colDef->generated);
-
+       rawEnt->missingMode = false;    /* XXX vestigial */
        rawEnt->generated = colDef->generated;
 
        /*
 
        /* Make the additional catalog changes visible */
        CommandCounterIncrement();
-
-       /*
-        * Did the request for a missing value work? If not we'll have to do a
-        * rewrite
-        */
-       if (!rawEnt->missingMode)
-           tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    }
 
    /*
     * rejects nulls.  If there are any domain constraints then we construct
     * an explicit NULL default value that will be passed through
     * CoerceToDomain processing.  (This is a tad inefficient, since it causes
-    * rewriting the table which we really don't have to do, but the present
-    * design of domain processing doesn't offer any simple way of checking
-    * the constraints more directly.)
+    * rewriting the table which we really wouldn't have to do; but we do it
+    * to preserve the historical behavior that such a failure will be raised
+    * only if the table currently contains some rows.)
     *
     * Note: we use build_column_default, and not just the cooked default
     * returned by AddRelationNewConstraints, so that the right thing happens
     */
    if (RELKIND_HAS_STORAGE(relkind) && attribute.attnum > 0)
    {
+       bool        has_domain_constraints;
+       bool        has_missing = false;
+
        /*
         * For an identity column, we can't use build_column_default(),
         * because the sequence ownership isn't set yet.  So do it manually.
            nve->typeId = typeOid;
 
            defval = (Expr *) nve;
-
-           /* must do a rewrite for identity columns */
-           tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
        }
        else
            defval = (Expr *) build_column_default(rel, attribute.attnum);
 
-       if (!defval && DomainHasConstraints(typeOid))
+       /* Build CoerceToDomain(NULL) expression if needed */
+       has_domain_constraints = DomainHasConstraints(typeOid);
+       if (!defval && has_domain_constraints)
        {
            Oid         baseTypeId;
            int32       baseTypeMod;
        {
            NewColumnValue *newval;
 
+           /* Prepare defval for execution, either here or in Phase 3 */
+           defval = expression_planner(defval);
+
+           /* Add the new default to the newvals list */
            newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
            newval->attnum = attribute.attnum;
-           newval->expr = expression_planner(defval);
+           newval->expr = defval;
            newval->is_generated = (colDef->generated != '\0');
 
            tab->newvals = lappend(tab->newvals, newval);
-       }
 
-       if (DomainHasConstraints(typeOid))
-           tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
+           /*
+            * Attempt to skip a complete table rewrite by storing the
+            * specified DEFAULT value outside of the heap.  This is only
+            * allowed for plain relations and non-generated columns, and the
+            * default expression can't be volatile (stable is OK).  Note that
+            * contain_volatile_functions deems CoerceToDomain immutable, but
+            * here we consider that coercion to a domain with constraints is
+            * volatile; else it might fail even when the table is empty.
+            */
+           if (rel->rd_rel->relkind == RELKIND_RELATION &&
+               !colDef->generated &&
+               !has_domain_constraints &&
+               !contain_volatile_functions((Node *) defval))
+           {
+               EState     *estate;
+               ExprState  *exprState;
+               Datum       missingval;
+               bool        missingIsNull;
+
+               /* Evaluate the default expression */
+               estate = CreateExecutorState();
+               exprState = ExecPrepareExpr(defval, estate);
+               missingval = ExecEvalExpr(exprState,
+                                         GetPerTupleExprContext(estate),
+                                         &missingIsNull);
+               /* If it turns out NULL, nothing to do; else store it */
+               if (!missingIsNull)
+               {
+                   StoreAttrMissingVal(rel, attribute.attnum, missingval);
+                   has_missing = true;
+               }
+               FreeExecutorState(estate);
+           }
+           else
+           {
+               /*
+                * Failed to use missing mode.  We have to do a table rewrite
+                * to install the value.
+                */
+               tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
+           }
+       }
 
-       if (!TupleDescAttr(rel->rd_att, attribute.attnum - 1)->atthasmissing)
+       if (!has_missing)
        {
            /*
             * If the new column is NOT NULL, and there is no missing value,
 
 {
    AttrNumber  attnum;         /* attribute to attach default to */
    Node       *raw_default;    /* default value (untransformed parse tree) */
-   bool        missingMode;    /* true if part of add column processing */
+   bool        missingMode;    /* obsolete, no longer used */
    char        generated;      /* attgenerated setting */
 } RawColumnDefault;
 
                                       const char *queryString);
 
 extern void RelationClearMissing(Relation rel);
+
+extern void StoreAttrMissingVal(Relation rel, AttrNumber attnum,
+                               Datum missingval);
 extern void SetAttrMissing(Oid relid, char *attname, char *value);
 
 extern Node *cookDefault(ParseState *pstate,
 
 (1 row)
 
 DROP TABLE T;
+-- Test domains with default value for table rewrite.
+CREATE DOMAIN domain1 AS int DEFAULT 11;  -- constant
+CREATE DOMAIN domain2 AS int DEFAULT 10 + (random() * 10)::int;  -- volatile
+CREATE DOMAIN domain3 AS text DEFAULT foo(4);  -- stable
+CREATE DOMAIN domain4 AS text[]
+  DEFAULT ('{"This", "is", "' || foo(4) || '","the", "real", "world"}')::TEXT[];
+CREATE TABLE t2 (a domain1);
+INSERT INTO t2 VALUES (1),(2);
+-- no table rewrite
+ALTER TABLE t2 ADD COLUMN b domain1 default 3;
+SELECT attnum, attname, atthasmissing, atthasdef, attmissingval
+FROM pg_attribute
+WHERE attnum > 0 AND attrelid = 't2'::regclass
+ORDER BY attnum;
+ attnum | attname | atthasmissing | atthasdef | attmissingval 
+--------+---------+---------------+-----------+---------------
+      1 | a       | f             | f         | 
+      2 | b       | t             | t         | {3}
+(2 rows)
+
+-- table rewrite should happen
+ALTER TABLE t2 ADD COLUMN c domain3 default left(random()::text,3);
+NOTICE:  rewriting table t2 for reason 2
+-- no table rewrite
+ALTER TABLE t2 ADD COLUMN d domain4;
+SELECT attnum, attname, atthasmissing, atthasdef, attmissingval
+FROM pg_attribute
+WHERE attnum > 0 AND attrelid = 't2'::regclass
+ORDER BY attnum;
+ attnum | attname | atthasmissing | atthasdef |           attmissingval           
+--------+---------+---------------+-----------+-----------------------------------
+      1 | a       | f             | f         | 
+      2 | b       | f             | t         | 
+      3 | c       | f             | t         | 
+      4 | d       | t             | f         | {"{This,is,abcd,the,real,world}"}
+(4 rows)
+
+-- table rewrite should happen
+ALTER TABLE t2 ADD COLUMN e domain2;
+NOTICE:  rewriting table t2 for reason 2
+SELECT attnum, attname, atthasmissing, atthasdef, attmissingval
+FROM pg_attribute
+WHERE attnum > 0 AND attrelid = 't2'::regclass
+ORDER BY attnum;
+ attnum | attname | atthasmissing | atthasdef | attmissingval 
+--------+---------+---------------+-----------+---------------
+      1 | a       | f             | f         | 
+      2 | b       | f             | t         | 
+      3 | c       | f             | t         | 
+      4 | d       | f             | f         | 
+      5 | e       | f             | f         | 
+(5 rows)
+
+SELECT a, b, length(c) = 3 as c_ok, d, e >= 10 as e_ok FROM t2;
+ a | b | c_ok |               d               | e_ok 
+---+---+------+-------------------------------+------
+ 1 | 3 | t    | {This,is,abcd,the,real,world} | t
+ 2 | 3 | t    | {This,is,abcd,the,real,world} | t
+(2 rows)
+
+DROP TABLE t2;
+DROP DOMAIN domain1;
+DROP DOMAIN domain2;
+DROP DOMAIN domain3;
+DROP DOMAIN domain4;
 DROP FUNCTION foo(INT);
 -- Fall back to full rewrite for volatile expressions
 CREATE TABLE T(pk INT NOT NULL PRIMARY KEY);
 
 
 DROP TABLE T;
 
+-- Test domains with default value for table rewrite.
+CREATE DOMAIN domain1 AS int DEFAULT 11;  -- constant
+CREATE DOMAIN domain2 AS int DEFAULT 10 + (random() * 10)::int;  -- volatile
+CREATE DOMAIN domain3 AS text DEFAULT foo(4);  -- stable
+CREATE DOMAIN domain4 AS text[]
+  DEFAULT ('{"This", "is", "' || foo(4) || '","the", "real", "world"}')::TEXT[];
+
+CREATE TABLE t2 (a domain1);
+INSERT INTO t2 VALUES (1),(2);
+
+-- no table rewrite
+ALTER TABLE t2 ADD COLUMN b domain1 default 3;
+
+SELECT attnum, attname, atthasmissing, atthasdef, attmissingval
+FROM pg_attribute
+WHERE attnum > 0 AND attrelid = 't2'::regclass
+ORDER BY attnum;
+
+-- table rewrite should happen
+ALTER TABLE t2 ADD COLUMN c domain3 default left(random()::text,3);
+
+-- no table rewrite
+ALTER TABLE t2 ADD COLUMN d domain4;
+
+SELECT attnum, attname, atthasmissing, atthasdef, attmissingval
+FROM pg_attribute
+WHERE attnum > 0 AND attrelid = 't2'::regclass
+ORDER BY attnum;
+
+-- table rewrite should happen
+ALTER TABLE t2 ADD COLUMN e domain2;
+
+SELECT attnum, attname, atthasmissing, atthasdef, attmissingval
+FROM pg_attribute
+WHERE attnum > 0 AND attrelid = 't2'::regclass
+ORDER BY attnum;
+
+SELECT a, b, length(c) = 3 as c_ok, d, e >= 10 as e_ok FROM t2;
+
+DROP TABLE t2;
+DROP DOMAIN domain1;
+DROP DOMAIN domain2;
+DROP DOMAIN domain3;
+DROP DOMAIN domain4;
 DROP FUNCTION foo(INT);
 
 -- Fall back to full rewrite for volatile expressions