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 */