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 @@
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 DEFAULT value after the row is created.
+ The actual value used is stored in the
attmissingval column.
@@ -1248,8 +1249,9 @@
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
- atthasmissing is true. If there is no value
+ added with a non-volatile DEFAULT value after the
+ row is created. The value is only used when
+ atthasmissing is true. If there is no value
the column is null.
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 numeric_literal, REM
When a column is added with ADD COLUMN and a
non-volatile DEFAULT 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 DEFAULT is specified, NULL is used. In
+ table's metadata. That value will be used for the column for all existing
+ rows. If no DEFAULT is specified, NULL is used. In
neither case is a rewrite of the table required.
@@ -1201,10 +1201,9 @@ WITH ( MODULUS numeric_literal, 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 oid 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.
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