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