Move pg_attrdef manipulation code into new file catalog/pg_attrdef.c.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 21 Mar 2022 18:38:23 +0000 (14:38 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 21 Mar 2022 18:38:23 +0000 (14:38 -0400)
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
src/backend/catalog/heap.c
src/backend/catalog/objectaddress.c
src/backend/catalog/pg_attrdef.c [new file with mode: 0644]
src/backend/commands/tablecmds.c
src/include/catalog/heap.h
src/include/catalog/pg_attrdef.h

index eefebb7bb83509a78561945596f03e426bc66603..87d7386e013a700a98efb440b2ad0d4552f4d97c 100644 (file)
@@ -25,6 +25,7 @@ OBJS = \
        objectaddress.o \
        partition.o \
        pg_aggregate.o \
+       pg_attrdef.o \
        pg_cast.o \
        pg_class.o \
        pg_collation.o \
index 7e99de88b343651d2757cacfe08943f0f383dcb3..696fd5977e032a270d2028bf04365cd370ecdd88 100644 (file)
 #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"
 #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"
 #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.
  *
index f30c742d48f00b8a25db4a87bce188fd2056e6ab..d7ce063997a098be50dabb7378ef6d10d7f6009f 100644 (file)
@@ -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);
-
-                               ScanKeyInit(&skey[0],
-                                                       Anum_pg_attrdef_oid,
-                                                       BTEqualStrategyNumber, F_OIDEQ,
-                                                       ObjectIdGetDatum(object->objectId));
-
-                               adscan = systable_beginscan(attrdefDesc, AttrDefaultOidIndexId,
-                                                                                       true, NULL, 1, skey);
+                               colobject = GetAttrDefaultColumnAddress(object->objectId);
 
-                               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 (file)
index 0000000..490a52a
--- /dev/null
@@ -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;
+}
index ab9a53b27c65ad76c0e6969ae79cabcc9c1d9645..fc3fc9b3846d2f4a069c1eb0ad35ea8bc6fba138 100644 (file)
@@ -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"
index c4757bda2d5f08c7750ac15ef0ffa7dbb9d3e072..07c5b88f0e32a134a0adc8409388f25a8c536653 100644 (file)
@@ -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);
 
index 2916feb5c939ac95e3a5cd335f9642b64db48b22..a21dd3812bc8158d9c2bbf254bfb183d867980a7 100644 (file)
@@ -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 */