From 17f3bc09284e1b529cdf524bbba709af6493f30c Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 21 Mar 2022 14:38:23 -0400 Subject: [PATCH] Move pg_attrdef manipulation code into new file catalog/pg_attrdef.c. This is a pure refactoring commit: there isn't (I hope) any functional change. StoreAttrDefault and RemoveAttrDefault[ById] are moved from heap.c, reducing the size of that overly-large file by about 300 lines. I took the opportunity to trim unused #includes from heap.c, too. Two new functions for translating between a pg_attrdef OID and the relid/attnum of the owning column are created by extracting ad-hoc code from objectaddress.c. This already removes one copy of said code, and a follow-on bug fix will create more callers. The only other function directly manipulating pg_attrdef is AttrDefaultFetch. I judged it was better to leave that in relcache.c, since it shares special concerns about recursion and error handling with the rest of that module. Discussion: https://postgr.es/m/651168.1647451676@sss.pgh.pa.us --- src/backend/catalog/Makefile | 1 + src/backend/catalog/heap.c | 330 --------------------- src/backend/catalog/objectaddress.c | 95 +----- src/backend/catalog/pg_attrdef.c | 428 ++++++++++++++++++++++++++++ src/backend/commands/tablecmds.c | 1 + src/include/catalog/heap.h | 8 +- src/include/catalog/pg_attrdef.h | 13 + 7 files changed, 450 insertions(+), 426 deletions(-) create mode 100644 src/backend/catalog/pg_attrdef.c diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index eefebb7bb8..87d7386e01 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -25,6 +25,7 @@ OBJS = \ objectaddress.o \ partition.o \ pg_aggregate.o \ + pg_attrdef.o \ pg_cast.o \ pg_class.o \ pg_collation.o \ diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 7e99de88b3..696fd5977e 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -30,19 +30,11 @@ #include "postgres.h" #include "access/genam.h" -#include "access/htup_details.h" #include "access/multixact.h" #include "access/relation.h" -#include "access/sysattr.h" #include "access/table.h" #include "access/tableam.h" -#include "access/toast_compression.h" -#include "access/transam.h" -#include "access/xact.h" -#include "access/xlog.h" -#include "catalog/binary_upgrade.h" #include "catalog/catalog.h" -#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/objectaccess.h" @@ -61,10 +53,8 @@ #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" #include "catalog/storage.h" -#include "catalog/storage_xlog.h" #include "commands/tablecmds.h" #include "commands/typecmds.h" -#include "executor/executor.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "optimizer/optimizer.h" @@ -76,16 +66,10 @@ #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "storage/predicate.h" -#include "storage/smgr.h" -#include "utils/acl.h" #include "utils/builtins.h" -#include "utils/datum.h" #include "utils/fmgroids.h" #include "utils/inval.h" #include "utils/lsyscache.h" -#include "utils/partcache.h" -#include "utils/ruleutils.h" -#include "utils/snapmgr.h" #include "utils/syscache.h" @@ -1757,131 +1741,6 @@ RemoveAttributeById(Oid relid, AttrNumber attnum) relation_close(rel, NoLock); } -/* - * RemoveAttrDefault - * - * If the specified relation/attribute has a default, remove it. - * (If no default, raise error if complain is true, else return quietly.) - */ -void -RemoveAttrDefault(Oid relid, AttrNumber attnum, - DropBehavior behavior, bool complain, bool internal) -{ - Relation attrdef_rel; - ScanKeyData scankeys[2]; - SysScanDesc scan; - HeapTuple tuple; - bool found = false; - - attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock); - - ScanKeyInit(&scankeys[0], - Anum_pg_attrdef_adrelid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relid)); - ScanKeyInit(&scankeys[1], - Anum_pg_attrdef_adnum, - BTEqualStrategyNumber, F_INT2EQ, - Int16GetDatum(attnum)); - - scan = systable_beginscan(attrdef_rel, AttrDefaultIndexId, true, - NULL, 2, scankeys); - - /* There should be at most one matching tuple, but we loop anyway */ - while (HeapTupleIsValid(tuple = systable_getnext(scan))) - { - ObjectAddress object; - Form_pg_attrdef attrtuple = (Form_pg_attrdef) GETSTRUCT(tuple); - - object.classId = AttrDefaultRelationId; - object.objectId = attrtuple->oid; - object.objectSubId = 0; - - performDeletion(&object, behavior, - internal ? PERFORM_DELETION_INTERNAL : 0); - - found = true; - } - - systable_endscan(scan); - table_close(attrdef_rel, RowExclusiveLock); - - if (complain && !found) - elog(ERROR, "could not find attrdef tuple for relation %u attnum %d", - relid, attnum); -} - -/* - * RemoveAttrDefaultById - * - * Remove a pg_attrdef entry specified by OID. This is the guts of - * attribute-default removal. Note it should be called via performDeletion, - * not directly. - */ -void -RemoveAttrDefaultById(Oid attrdefId) -{ - Relation attrdef_rel; - Relation attr_rel; - Relation myrel; - ScanKeyData scankeys[1]; - SysScanDesc scan; - HeapTuple tuple; - Oid myrelid; - AttrNumber myattnum; - - /* Grab an appropriate lock on the pg_attrdef relation */ - attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock); - - /* Find the pg_attrdef tuple */ - ScanKeyInit(&scankeys[0], - Anum_pg_attrdef_oid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(attrdefId)); - - scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndexId, true, - NULL, 1, scankeys); - - tuple = systable_getnext(scan); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "could not find tuple for attrdef %u", attrdefId); - - myrelid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid; - myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum; - - /* Get an exclusive lock on the relation owning the attribute */ - myrel = relation_open(myrelid, AccessExclusiveLock); - - /* Now we can delete the pg_attrdef row */ - CatalogTupleDelete(attrdef_rel, &tuple->t_self); - - systable_endscan(scan); - table_close(attrdef_rel, RowExclusiveLock); - - /* Fix the pg_attribute row */ - attr_rel = table_open(AttributeRelationId, RowExclusiveLock); - - tuple = SearchSysCacheCopy2(ATTNUM, - ObjectIdGetDatum(myrelid), - Int16GetDatum(myattnum)); - if (!HeapTupleIsValid(tuple)) /* shouldn't happen */ - elog(ERROR, "cache lookup failed for attribute %d of relation %u", - myattnum, myrelid); - - ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false; - - CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple); - - /* - * Our update of the pg_attribute row will force a relcache rebuild, so - * there's nothing else to do here. - */ - table_close(attr_rel, RowExclusiveLock); - - /* Keep lock on attribute's rel until end of xact */ - relation_close(myrel, NoLock); -} - /* * heap_drop_with_catalog - removes specified relation from catalogs * @@ -2193,195 +2052,6 @@ SetAttrMissing(Oid relid, char *attname, char *value) table_close(tablerel, AccessExclusiveLock); } -/* - * Store a default expression for column attnum of relation rel. - * - * Returns the OID of the new pg_attrdef tuple. - * - * add_column_mode must be true if we are storing the default for a new - * attribute, and false if it's for an already existing attribute. The reason - * for this is that the missing value must never be updated after it is set, - * which can only be when a column is added to the table. Otherwise we would - * in effect be changing existing tuples. - */ -Oid -StoreAttrDefault(Relation rel, AttrNumber attnum, - Node *expr, bool is_internal, bool add_column_mode) -{ - char *adbin; - Relation adrel; - HeapTuple tuple; - Datum values[4]; - static bool nulls[4] = {false, false, false, false}; - Relation attrrel; - HeapTuple atttup; - Form_pg_attribute attStruct; - char attgenerated; - Oid attrdefOid; - ObjectAddress colobject, - defobject; - - adrel = table_open(AttrDefaultRelationId, RowExclusiveLock); - - /* - * Flatten expression to string form for storage. - */ - adbin = nodeToString(expr); - - /* - * Make the pg_attrdef entry. - */ - attrdefOid = GetNewOidWithIndex(adrel, AttrDefaultOidIndexId, - Anum_pg_attrdef_oid); - values[Anum_pg_attrdef_oid - 1] = ObjectIdGetDatum(attrdefOid); - values[Anum_pg_attrdef_adrelid - 1] = RelationGetRelid(rel); - values[Anum_pg_attrdef_adnum - 1] = attnum; - values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin); - - tuple = heap_form_tuple(adrel->rd_att, values, nulls); - CatalogTupleInsert(adrel, tuple); - - defobject.classId = AttrDefaultRelationId; - defobject.objectId = attrdefOid; - defobject.objectSubId = 0; - - table_close(adrel, RowExclusiveLock); - - /* now can free some of the stuff allocated above */ - pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1])); - heap_freetuple(tuple); - pfree(adbin); - - /* - * Update the pg_attribute entry for the column to show that a default - * exists. - */ - attrrel = table_open(AttributeRelationId, RowExclusiveLock); - atttup = SearchSysCacheCopy2(ATTNUM, - ObjectIdGetDatum(RelationGetRelid(rel)), - Int16GetDatum(attnum)); - if (!HeapTupleIsValid(atttup)) - elog(ERROR, "cache lookup failed for attribute %d of relation %u", - attnum, RelationGetRelid(rel)); - attStruct = (Form_pg_attribute) GETSTRUCT(atttup); - attgenerated = attStruct->attgenerated; - 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)); - valuesAtt[Anum_pg_attribute_atthasdef - 1] = true; - replacesAtt[Anum_pg_attribute_atthasdef - 1] = true; - - if (rel->rd_rel->relkind == RELKIND_RELATION && add_column_mode && - !attgenerated) - { - expr2 = expression_planner(expr2); - estate = CreateExecutorState(); - exprState = ExecPrepareExpr(expr2, estate); - econtext = GetPerTupleExprContext(estate); - - missingval = ExecEvalExpr(exprState, econtext, - &missingIsNull); - - FreeExecutorState(estate); - - defAttStruct = TupleDescAttr(rel->rd_att, attnum - 1); - - if (missingIsNull) - { - /* if the default evaluates to NULL, just store a NULL array */ - missingval = (Datum) 0; - } - else - { - /* otherwise make a one-element array of the value */ - missingval = PointerGetDatum(construct_array(&missingval, - 1, - defAttStruct->atttypid, - defAttStruct->attlen, - defAttStruct->attbyval, - defAttStruct->attalign)); - } - - valuesAtt[Anum_pg_attribute_atthasmissing - 1] = !missingIsNull; - replacesAtt[Anum_pg_attribute_atthasmissing - 1] = true; - valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval; - replacesAtt[Anum_pg_attribute_attmissingval - 1] = true; - nullsAtt[Anum_pg_attribute_attmissingval - 1] = missingIsNull; - } - atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel), - valuesAtt, nullsAtt, replacesAtt); - - CatalogTupleUpdate(attrrel, &atttup->t_self, atttup); - - if (!missingIsNull) - pfree(DatumGetPointer(missingval)); - - } - table_close(attrrel, RowExclusiveLock); - heap_freetuple(atttup); - - /* - * Make a dependency so that the pg_attrdef entry goes away if the column - * (or whole table) is deleted. - */ - colobject.classId = RelationRelationId; - colobject.objectId = RelationGetRelid(rel); - colobject.objectSubId = attnum; - - recordDependencyOn(&defobject, &colobject, DEPENDENCY_AUTO); - - /* - * Record dependencies on objects used in the expression, too. - */ - if (attgenerated) - { - /* - * Generated column: Dropping anything that the generation expression - * refers to automatically drops the generated column. - */ - recordDependencyOnSingleRelExpr(&colobject, expr, RelationGetRelid(rel), - DEPENDENCY_AUTO, - DEPENDENCY_AUTO, false); - } - else - { - /* - * Normal default: Dropping anything that the default refers to - * requires CASCADE and drops the default only. - */ - recordDependencyOnSingleRelExpr(&defobject, expr, RelationGetRelid(rel), - DEPENDENCY_NORMAL, - DEPENDENCY_NORMAL, false); - } - - /* - * Post creation hook for attribute defaults. - * - * XXX. ALTER TABLE ALTER COLUMN SET/DROP DEFAULT is implemented with a - * couple of deletion/creation of the attribute's default entry, so the - * callee should check existence of an older version of this entry if it - * needs to distinguish. - */ - InvokeObjectPostCreateHookArg(AttrDefaultRelationId, - RelationGetRelid(rel), attnum, is_internal); - - return attrdefOid; -} - /* * Store a check-constraint expression for the given relation. * diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index f30c742d48..d7ce063997 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -1578,39 +1578,11 @@ get_object_address_attrdef(ObjectType objtype, List *object, tupdesc = RelationGetDescr(relation); - /* Look up attribute number and scan pg_attrdef to find its tuple */ + /* Look up attribute number and fetch the pg_attrdef OID */ attnum = get_attnum(reloid, attname); defoid = InvalidOid; if (attnum != InvalidAttrNumber && tupdesc->constr != NULL) - { - Relation attrdef; - ScanKeyData keys[2]; - SysScanDesc scan; - HeapTuple tup; - - attrdef = relation_open(AttrDefaultRelationId, AccessShareLock); - ScanKeyInit(&keys[0], - Anum_pg_attrdef_adrelid, - BTEqualStrategyNumber, - F_OIDEQ, - ObjectIdGetDatum(reloid)); - ScanKeyInit(&keys[1], - Anum_pg_attrdef_adnum, - BTEqualStrategyNumber, - F_INT2EQ, - Int16GetDatum(attnum)); - scan = systable_beginscan(attrdef, AttrDefaultIndexId, true, - NULL, 2, keys); - if (HeapTupleIsValid(tup = systable_getnext(scan))) - { - Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup); - - defoid = atdform->oid; - } - - systable_endscan(scan); - relation_close(attrdef, AccessShareLock); - } + defoid = GetAttrDefaultOid(reloid, attnum); if (!OidIsValid(defoid)) { if (!missing_ok) @@ -3161,48 +3133,21 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok) case OCLASS_DEFAULT: { - Relation attrdefDesc; - ScanKeyData skey[1]; - SysScanDesc adscan; - HeapTuple tup; - Form_pg_attrdef attrdef; ObjectAddress colobject; - attrdefDesc = table_open(AttrDefaultRelationId, AccessShareLock); + colobject = GetAttrDefaultColumnAddress(object->objectId); - ScanKeyInit(&skey[0], - Anum_pg_attrdef_oid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(object->objectId)); - - adscan = systable_beginscan(attrdefDesc, AttrDefaultOidIndexId, - true, NULL, 1, skey); - - tup = systable_getnext(adscan); - - if (!HeapTupleIsValid(tup)) + if (!OidIsValid(colobject.objectId)) { if (!missing_ok) elog(ERROR, "could not find tuple for attrdef %u", object->objectId); - - systable_endscan(adscan); - table_close(attrdefDesc, AccessShareLock); break; } - attrdef = (Form_pg_attrdef) GETSTRUCT(tup); - - colobject.classId = RelationRelationId; - colobject.objectId = attrdef->adrelid; - colobject.objectSubId = attrdef->adnum; - /* translator: %s is typically "column %s of table %s" */ appendStringInfo(&buffer, _("default value for %s"), getObjectDescription(&colobject, false)); - - systable_endscan(adscan); - table_close(attrdefDesc, AccessShareLock); break; } @@ -5006,50 +4951,22 @@ getObjectIdentityParts(const ObjectAddress *object, case OCLASS_DEFAULT: { - Relation attrdefDesc; - ScanKeyData skey[1]; - SysScanDesc adscan; - - HeapTuple tup; - Form_pg_attrdef attrdef; ObjectAddress colobject; - attrdefDesc = table_open(AttrDefaultRelationId, AccessShareLock); + colobject = GetAttrDefaultColumnAddress(object->objectId); - ScanKeyInit(&skey[0], - Anum_pg_attrdef_oid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(object->objectId)); - - adscan = systable_beginscan(attrdefDesc, AttrDefaultOidIndexId, - true, NULL, 1, skey); - - tup = systable_getnext(adscan); - - if (!HeapTupleIsValid(tup)) + if (!OidIsValid(colobject.objectId)) { if (!missing_ok) elog(ERROR, "could not find tuple for attrdef %u", object->objectId); - - systable_endscan(adscan); - table_close(attrdefDesc, AccessShareLock); break; } - attrdef = (Form_pg_attrdef) GETSTRUCT(tup); - - colobject.classId = RelationRelationId; - colobject.objectId = attrdef->adrelid; - colobject.objectSubId = attrdef->adnum; - appendStringInfo(&buffer, "for %s", getObjectIdentityParts(&colobject, objname, objargs, false)); - - systable_endscan(adscan); - table_close(attrdefDesc, AccessShareLock); break; } diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c new file mode 100644 index 0000000000..490a52a086 --- /dev/null +++ b/src/backend/catalog/pg_attrdef.c @@ -0,0 +1,428 @@ +/*------------------------------------------------------------------------- + * + * pg_attrdef.c + * routines to support manipulation of the pg_attrdef relation + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/catalog/pg_attrdef.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/relation.h" +#include "access/table.h" +#include "catalog/catalog.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_attrdef.h" +#include "executor/executor.h" +#include "optimizer/optimizer.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/rel.h" +#include "utils/syscache.h" + + +/* + * Store a default expression for column attnum of relation rel. + * + * Returns the OID of the new pg_attrdef tuple. + * + * add_column_mode must be true if we are storing the default for a new + * attribute, and false if it's for an already existing attribute. The reason + * for this is that the missing value must never be updated after it is set, + * which can only be when a column is added to the table. Otherwise we would + * in effect be changing existing tuples. + */ +Oid +StoreAttrDefault(Relation rel, AttrNumber attnum, + Node *expr, bool is_internal, bool add_column_mode) +{ + char *adbin; + Relation adrel; + HeapTuple tuple; + Datum values[4]; + static bool nulls[4] = {false, false, false, false}; + Relation attrrel; + HeapTuple atttup; + Form_pg_attribute attStruct; + char attgenerated; + Oid attrdefOid; + ObjectAddress colobject, + defobject; + + adrel = table_open(AttrDefaultRelationId, RowExclusiveLock); + + /* + * Flatten expression to string form for storage. + */ + adbin = nodeToString(expr); + + /* + * Make the pg_attrdef entry. + */ + attrdefOid = GetNewOidWithIndex(adrel, AttrDefaultOidIndexId, + Anum_pg_attrdef_oid); + values[Anum_pg_attrdef_oid - 1] = ObjectIdGetDatum(attrdefOid); + values[Anum_pg_attrdef_adrelid - 1] = RelationGetRelid(rel); + values[Anum_pg_attrdef_adnum - 1] = attnum; + values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin); + + tuple = heap_form_tuple(adrel->rd_att, values, nulls); + CatalogTupleInsert(adrel, tuple); + + defobject.classId = AttrDefaultRelationId; + defobject.objectId = attrdefOid; + defobject.objectSubId = 0; + + table_close(adrel, RowExclusiveLock); + + /* now can free some of the stuff allocated above */ + pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1])); + heap_freetuple(tuple); + pfree(adbin); + + /* + * Update the pg_attribute entry for the column to show that a default + * exists. + */ + attrrel = table_open(AttributeRelationId, RowExclusiveLock); + atttup = SearchSysCacheCopy2(ATTNUM, + ObjectIdGetDatum(RelationGetRelid(rel)), + Int16GetDatum(attnum)); + if (!HeapTupleIsValid(atttup)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attnum, RelationGetRelid(rel)); + attStruct = (Form_pg_attribute) GETSTRUCT(atttup); + attgenerated = attStruct->attgenerated; + 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)); + valuesAtt[Anum_pg_attribute_atthasdef - 1] = true; + replacesAtt[Anum_pg_attribute_atthasdef - 1] = true; + + if (rel->rd_rel->relkind == RELKIND_RELATION && add_column_mode && + !attgenerated) + { + expr2 = expression_planner(expr2); + estate = CreateExecutorState(); + exprState = ExecPrepareExpr(expr2, estate); + econtext = GetPerTupleExprContext(estate); + + missingval = ExecEvalExpr(exprState, econtext, + &missingIsNull); + + FreeExecutorState(estate); + + defAttStruct = TupleDescAttr(rel->rd_att, attnum - 1); + + if (missingIsNull) + { + /* if the default evaluates to NULL, just store a NULL array */ + missingval = (Datum) 0; + } + else + { + /* otherwise make a one-element array of the value */ + missingval = PointerGetDatum(construct_array(&missingval, + 1, + defAttStruct->atttypid, + defAttStruct->attlen, + defAttStruct->attbyval, + defAttStruct->attalign)); + } + + valuesAtt[Anum_pg_attribute_atthasmissing - 1] = !missingIsNull; + replacesAtt[Anum_pg_attribute_atthasmissing - 1] = true; + valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval; + replacesAtt[Anum_pg_attribute_attmissingval - 1] = true; + nullsAtt[Anum_pg_attribute_attmissingval - 1] = missingIsNull; + } + atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel), + valuesAtt, nullsAtt, replacesAtt); + + CatalogTupleUpdate(attrrel, &atttup->t_self, atttup); + + if (!missingIsNull) + pfree(DatumGetPointer(missingval)); + + } + table_close(attrrel, RowExclusiveLock); + heap_freetuple(atttup); + + /* + * Make a dependency so that the pg_attrdef entry goes away if the column + * (or whole table) is deleted. + */ + colobject.classId = RelationRelationId; + colobject.objectId = RelationGetRelid(rel); + colobject.objectSubId = attnum; + + recordDependencyOn(&defobject, &colobject, DEPENDENCY_AUTO); + + /* + * Record dependencies on objects used in the expression, too. + */ + if (attgenerated) + { + /* + * Generated column: Dropping anything that the generation expression + * refers to automatically drops the generated column. + */ + recordDependencyOnSingleRelExpr(&colobject, expr, RelationGetRelid(rel), + DEPENDENCY_AUTO, + DEPENDENCY_AUTO, false); + } + else + { + /* + * Normal default: Dropping anything that the default refers to + * requires CASCADE and drops the default only. + */ + recordDependencyOnSingleRelExpr(&defobject, expr, RelationGetRelid(rel), + DEPENDENCY_NORMAL, + DEPENDENCY_NORMAL, false); + } + + /* + * Post creation hook for attribute defaults. + * + * XXX. ALTER TABLE ALTER COLUMN SET/DROP DEFAULT is implemented with a + * couple of deletion/creation of the attribute's default entry, so the + * callee should check existence of an older version of this entry if it + * needs to distinguish. + */ + InvokeObjectPostCreateHookArg(AttrDefaultRelationId, + RelationGetRelid(rel), attnum, is_internal); + + return attrdefOid; +} + + +/* + * RemoveAttrDefault + * + * If the specified relation/attribute has a default, remove it. + * (If no default, raise error if complain is true, else return quietly.) + */ +void +RemoveAttrDefault(Oid relid, AttrNumber attnum, + DropBehavior behavior, bool complain, bool internal) +{ + Relation attrdef_rel; + ScanKeyData scankeys[2]; + SysScanDesc scan; + HeapTuple tuple; + bool found = false; + + attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock); + + ScanKeyInit(&scankeys[0], + Anum_pg_attrdef_adrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&scankeys[1], + Anum_pg_attrdef_adnum, + BTEqualStrategyNumber, F_INT2EQ, + Int16GetDatum(attnum)); + + scan = systable_beginscan(attrdef_rel, AttrDefaultIndexId, true, + NULL, 2, scankeys); + + /* There should be at most one matching tuple, but we loop anyway */ + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + ObjectAddress object; + Form_pg_attrdef attrtuple = (Form_pg_attrdef) GETSTRUCT(tuple); + + object.classId = AttrDefaultRelationId; + object.objectId = attrtuple->oid; + object.objectSubId = 0; + + performDeletion(&object, behavior, + internal ? PERFORM_DELETION_INTERNAL : 0); + + found = true; + } + + systable_endscan(scan); + table_close(attrdef_rel, RowExclusiveLock); + + if (complain && !found) + elog(ERROR, "could not find attrdef tuple for relation %u attnum %d", + relid, attnum); +} + +/* + * RemoveAttrDefaultById + * + * Remove a pg_attrdef entry specified by OID. This is the guts of + * attribute-default removal. Note it should be called via performDeletion, + * not directly. + */ +void +RemoveAttrDefaultById(Oid attrdefId) +{ + Relation attrdef_rel; + Relation attr_rel; + Relation myrel; + ScanKeyData scankeys[1]; + SysScanDesc scan; + HeapTuple tuple; + Oid myrelid; + AttrNumber myattnum; + + /* Grab an appropriate lock on the pg_attrdef relation */ + attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock); + + /* Find the pg_attrdef tuple */ + ScanKeyInit(&scankeys[0], + Anum_pg_attrdef_oid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(attrdefId)); + + scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndexId, true, + NULL, 1, scankeys); + + tuple = systable_getnext(scan); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "could not find tuple for attrdef %u", attrdefId); + + myrelid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid; + myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum; + + /* Get an exclusive lock on the relation owning the attribute */ + myrel = relation_open(myrelid, AccessExclusiveLock); + + /* Now we can delete the pg_attrdef row */ + CatalogTupleDelete(attrdef_rel, &tuple->t_self); + + systable_endscan(scan); + table_close(attrdef_rel, RowExclusiveLock); + + /* Fix the pg_attribute row */ + attr_rel = table_open(AttributeRelationId, RowExclusiveLock); + + tuple = SearchSysCacheCopy2(ATTNUM, + ObjectIdGetDatum(myrelid), + Int16GetDatum(myattnum)); + if (!HeapTupleIsValid(tuple)) /* shouldn't happen */ + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + myattnum, myrelid); + + ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false; + + CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple); + + /* + * Our update of the pg_attribute row will force a relcache rebuild, so + * there's nothing else to do here. + */ + table_close(attr_rel, RowExclusiveLock); + + /* Keep lock on attribute's rel until end of xact */ + relation_close(myrel, NoLock); +} + + +/* + * Get the pg_attrdef OID of the default expression for a column + * identified by relation OID and and column number. + * + * Returns InvalidOid if there is no such pg_attrdef entry. + */ +Oid +GetAttrDefaultOid(Oid relid, AttrNumber attnum) +{ + Oid result = InvalidOid; + Relation attrdef; + ScanKeyData keys[2]; + SysScanDesc scan; + HeapTuple tup; + + attrdef = table_open(AttrDefaultRelationId, AccessShareLock); + ScanKeyInit(&keys[0], + Anum_pg_attrdef_adrelid, + BTEqualStrategyNumber, + F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&keys[1], + Anum_pg_attrdef_adnum, + BTEqualStrategyNumber, + F_INT2EQ, + Int16GetDatum(attnum)); + scan = systable_beginscan(attrdef, AttrDefaultIndexId, true, + NULL, 2, keys); + + if (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup); + + result = atdform->oid; + } + + systable_endscan(scan); + table_close(attrdef, AccessShareLock); + + return result; +} + +/* + * Given a pg_attrdef OID, return the relation OID and column number of + * the owning column (represented as an ObjectAddress for convenience). + * + * Returns InvalidObjectAddress if there is no such pg_attrdef entry. + */ +ObjectAddress +GetAttrDefaultColumnAddress(Oid attrdefoid) +{ + ObjectAddress result = InvalidObjectAddress; + Relation attrdef; + ScanKeyData skey[1]; + SysScanDesc scan; + HeapTuple tup; + + attrdef = table_open(AttrDefaultRelationId, AccessShareLock); + ScanKeyInit(&skey[0], + Anum_pg_attrdef_oid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(attrdefoid)); + scan = systable_beginscan(attrdef, AttrDefaultOidIndexId, true, + NULL, 1, skey); + + if (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup); + + result.classId = RelationRelationId; + result.objectId = atdform->adrelid; + result.objectSubId = atdform->adnum; + } + + systable_endscan(scan); + table_close(attrdef, AccessShareLock); + + return result; +} diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index ab9a53b27c..fc3fc9b384 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -34,6 +34,7 @@ #include "catalog/objectaccess.h" #include "catalog/partition.h" #include "catalog/pg_am.h" +#include "catalog/pg_attrdef.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_depend.h" diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index c4757bda2d..07c5b88f0e 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -117,10 +117,6 @@ extern List *AddRelationNewConstraints(Relation rel, extern void RelationClearMissing(Relation rel); extern void SetAttrMissing(Oid relid, char *attname, char *value); -extern Oid StoreAttrDefault(Relation rel, AttrNumber attnum, - Node *expr, bool is_internal, - bool add_column_mode); - extern Node *cookDefault(ParseState *pstate, Node *raw_default, Oid atttypid, @@ -132,9 +128,7 @@ extern void DeleteRelationTuple(Oid relid); extern void DeleteAttributeTuples(Oid relid); extern void DeleteSystemAttributeTuples(Oid relid); extern void RemoveAttributeById(Oid relid, AttrNumber attnum); -extern void RemoveAttrDefault(Oid relid, AttrNumber attnum, - DropBehavior behavior, bool complain, bool internal); -extern void RemoveAttrDefaultById(Oid attrdefId); + extern void CopyStatistics(Oid fromrelid, Oid torelid); extern void RemoveStatistics(Oid relid, AttrNumber attnum); diff --git a/src/include/catalog/pg_attrdef.h b/src/include/catalog/pg_attrdef.h index 2916feb5c9..a21dd3812b 100644 --- a/src/include/catalog/pg_attrdef.h +++ b/src/include/catalog/pg_attrdef.h @@ -19,6 +19,7 @@ #define PG_ATTRDEF_H #include "catalog/genbki.h" +#include "catalog/objectaddress.h" #include "catalog/pg_attrdef_d.h" /* ---------------- @@ -54,4 +55,16 @@ DECLARE_UNIQUE_INDEX_PKEY(pg_attrdef_oid_index, 2657, AttrDefaultOidIndexId, on DECLARE_FOREIGN_KEY((adrelid, adnum), pg_attribute, (attrelid, attnum)); + +extern Oid StoreAttrDefault(Relation rel, AttrNumber attnum, + Node *expr, bool is_internal, + bool add_column_mode); +extern void RemoveAttrDefault(Oid relid, AttrNumber attnum, + DropBehavior behavior, + bool complain, bool internal); +extern void RemoveAttrDefaultById(Oid attrdefId); + +extern Oid GetAttrDefaultOid(Oid relid, AttrNumber attnum); +extern ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid); + #endif /* PG_ATTRDEF_H */