aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/tablecmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r--src/backend/commands/tablecmds.c87
1 files changed, 60 insertions, 27 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ce7d115667e..fd31216b27b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7321,14 +7321,7 @@ 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 = (colDef->generated != ATTRIBUTE_GENERATED_STORED);
-
+ rawEnt->missingMode = false; /* XXX vestigial */
rawEnt->generated = colDef->generated;
/*
@@ -7340,13 +7333,6 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* 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;
}
/*
@@ -7363,9 +7349,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
* 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
@@ -7384,6 +7370,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
*/
if (RELKIND_HAS_STORAGE(relkind))
{
+ 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.
@@ -7396,14 +7385,13 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
nve->typeId = attribute->atttypid;
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(attribute->atttypid))
+ /* Build CoerceToDomain(NULL) expression if needed */
+ has_domain_constraints = DomainHasConstraints(attribute->atttypid);
+ if (!defval && has_domain_constraints)
{
Oid baseTypeId;
int32 baseTypeMod;
@@ -7429,18 +7417,63 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
{
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(attribute->atttypid))
- 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 --- unless it's a virtual generated
+ * column.
+ */
+ if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
+ 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,