LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_attrdef.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 100 104 96.2 %
Date: 2026-01-20 23:17:14 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pg_attrdef.c
       4             :  *    routines to support manipulation of the pg_attrdef relation
       5             :  *
       6             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/catalog/pg_attrdef.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/genam.h"
      18             : #include "access/htup_details.h"
      19             : #include "access/relation.h"
      20             : #include "access/table.h"
      21             : #include "catalog/dependency.h"
      22             : #include "catalog/indexing.h"
      23             : #include "catalog/objectaccess.h"
      24             : #include "catalog/pg_attrdef.h"
      25             : #include "utils/builtins.h"
      26             : #include "utils/fmgroids.h"
      27             : #include "utils/rel.h"
      28             : #include "utils/syscache.h"
      29             : 
      30             : 
      31             : /*
      32             :  * Store a default expression for column attnum of relation rel.
      33             :  *
      34             :  * Returns the OID of the new pg_attrdef tuple.
      35             :  */
      36             : Oid
      37        5392 : StoreAttrDefault(Relation rel, AttrNumber attnum,
      38             :                  Node *expr, bool is_internal)
      39             : {
      40             :     char       *adbin;
      41             :     Relation    adrel;
      42             :     HeapTuple   tuple;
      43             :     Datum       values[Natts_pg_attrdef];
      44             :     static bool nulls[Natts_pg_attrdef] = {false, false, false, false};
      45             :     Relation    attrrel;
      46             :     HeapTuple   atttup;
      47             :     Form_pg_attribute attStruct;
      48        5392 :     Datum       valuesAtt[Natts_pg_attribute] = {0};
      49        5392 :     bool        nullsAtt[Natts_pg_attribute] = {0};
      50        5392 :     bool        replacesAtt[Natts_pg_attribute] = {0};
      51             :     char        attgenerated;
      52             :     Oid         attrdefOid;
      53             :     ObjectAddress colobject,
      54             :                 defobject;
      55             : 
      56        5392 :     adrel = table_open(AttrDefaultRelationId, RowExclusiveLock);
      57             : 
      58             :     /*
      59             :      * Flatten expression to string form for storage.
      60             :      */
      61        5392 :     adbin = nodeToString(expr);
      62             : 
      63             :     /*
      64             :      * Make the pg_attrdef entry.
      65             :      */
      66        5392 :     attrdefOid = GetNewOidWithIndex(adrel, AttrDefaultOidIndexId,
      67             :                                     Anum_pg_attrdef_oid);
      68        5392 :     values[Anum_pg_attrdef_oid - 1] = ObjectIdGetDatum(attrdefOid);
      69        5392 :     values[Anum_pg_attrdef_adrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
      70        5392 :     values[Anum_pg_attrdef_adnum - 1] = Int16GetDatum(attnum);
      71        5392 :     values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin);
      72             : 
      73        5392 :     tuple = heap_form_tuple(adrel->rd_att, values, nulls);
      74        5392 :     CatalogTupleInsert(adrel, tuple);
      75             : 
      76        5392 :     defobject.classId = AttrDefaultRelationId;
      77        5392 :     defobject.objectId = attrdefOid;
      78        5392 :     defobject.objectSubId = 0;
      79             : 
      80        5392 :     table_close(adrel, RowExclusiveLock);
      81             : 
      82             :     /* now can free some of the stuff allocated above */
      83        5392 :     pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1]));
      84        5392 :     heap_freetuple(tuple);
      85        5392 :     pfree(adbin);
      86             : 
      87             :     /*
      88             :      * Update the pg_attribute entry for the column to show that a default
      89             :      * exists.
      90             :      */
      91        5392 :     attrrel = table_open(AttributeRelationId, RowExclusiveLock);
      92        5392 :     atttup = SearchSysCacheCopy2(ATTNUM,
      93             :                                  ObjectIdGetDatum(RelationGetRelid(rel)),
      94             :                                  Int16GetDatum(attnum));
      95        5392 :     if (!HeapTupleIsValid(atttup))
      96           0 :         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
      97             :              attnum, RelationGetRelid(rel));
      98        5392 :     attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
      99        5392 :     attgenerated = attStruct->attgenerated;
     100             : 
     101        5392 :     valuesAtt[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(true);
     102        5392 :     replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
     103             : 
     104        5392 :     atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
     105             :                                valuesAtt, nullsAtt, replacesAtt);
     106             : 
     107        5392 :     CatalogTupleUpdate(attrrel, &atttup->t_self, atttup);
     108             : 
     109        5392 :     table_close(attrrel, RowExclusiveLock);
     110        5392 :     heap_freetuple(atttup);
     111             : 
     112             :     /*
     113             :      * Make a dependency so that the pg_attrdef entry goes away if the column
     114             :      * (or whole table) is deleted.  In the case of a generated column, make
     115             :      * it an internal dependency to prevent the default expression from being
     116             :      * deleted separately.
     117             :      */
     118        5392 :     colobject.classId = RelationRelationId;
     119        5392 :     colobject.objectId = RelationGetRelid(rel);
     120        5392 :     colobject.objectSubId = attnum;
     121             : 
     122        5392 :     recordDependencyOn(&defobject, &colobject,
     123             :                        attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
     124             : 
     125             :     /*
     126             :      * Record dependencies on objects used in the expression, too.
     127             :      */
     128        5392 :     recordDependencyOnSingleRelExpr(&defobject, expr, RelationGetRelid(rel),
     129             :                                     DEPENDENCY_NORMAL,
     130             :                                     DEPENDENCY_NORMAL, false);
     131             : 
     132             :     /*
     133             :      * Post creation hook for attribute defaults.
     134             :      *
     135             :      * XXX. ALTER TABLE ALTER COLUMN SET/DROP DEFAULT is implemented with a
     136             :      * couple of deletion/creation of the attribute's default entry, so the
     137             :      * callee should check existence of an older version of this entry if it
     138             :      * needs to distinguish.
     139             :      */
     140        5380 :     InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
     141             :                                   RelationGetRelid(rel), attnum, is_internal);
     142             : 
     143        5380 :     return attrdefOid;
     144             : }
     145             : 
     146             : 
     147             : /*
     148             :  *      RemoveAttrDefault
     149             :  *
     150             :  * If the specified relation/attribute has a default, remove it.
     151             :  * (If no default, raise error if complain is true, else return quietly.)
     152             :  */
     153             : void
     154         904 : RemoveAttrDefault(Oid relid, AttrNumber attnum,
     155             :                   DropBehavior behavior, bool complain, bool internal)
     156             : {
     157             :     Relation    attrdef_rel;
     158             :     ScanKeyData scankeys[2];
     159             :     SysScanDesc scan;
     160             :     HeapTuple   tuple;
     161         904 :     bool        found = false;
     162             : 
     163         904 :     attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
     164             : 
     165         904 :     ScanKeyInit(&scankeys[0],
     166             :                 Anum_pg_attrdef_adrelid,
     167             :                 BTEqualStrategyNumber, F_OIDEQ,
     168             :                 ObjectIdGetDatum(relid));
     169         904 :     ScanKeyInit(&scankeys[1],
     170             :                 Anum_pg_attrdef_adnum,
     171             :                 BTEqualStrategyNumber, F_INT2EQ,
     172             :                 Int16GetDatum(attnum));
     173             : 
     174         904 :     scan = systable_beginscan(attrdef_rel, AttrDefaultIndexId, true,
     175             :                               NULL, 2, scankeys);
     176             : 
     177             :     /* There should be at most one matching tuple, but we loop anyway */
     178        1528 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
     179             :     {
     180             :         ObjectAddress object;
     181         624 :         Form_pg_attrdef attrtuple = (Form_pg_attrdef) GETSTRUCT(tuple);
     182             : 
     183         624 :         object.classId = AttrDefaultRelationId;
     184         624 :         object.objectId = attrtuple->oid;
     185         624 :         object.objectSubId = 0;
     186             : 
     187         624 :         performDeletion(&object, behavior,
     188             :                         internal ? PERFORM_DELETION_INTERNAL : 0);
     189             : 
     190         624 :         found = true;
     191             :     }
     192             : 
     193         904 :     systable_endscan(scan);
     194         904 :     table_close(attrdef_rel, RowExclusiveLock);
     195             : 
     196         904 :     if (complain && !found)
     197           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
     198             :              relid, attnum);
     199         904 : }
     200             : 
     201             : /*
     202             :  *      RemoveAttrDefaultById
     203             :  *
     204             :  * Remove a pg_attrdef entry specified by OID.  This is the guts of
     205             :  * attribute-default removal.  Note it should be called via performDeletion,
     206             :  * not directly.
     207             :  */
     208             : void
     209        3864 : RemoveAttrDefaultById(Oid attrdefId)
     210             : {
     211             :     Relation    attrdef_rel;
     212             :     Relation    attr_rel;
     213             :     Relation    myrel;
     214             :     ScanKeyData scankeys[1];
     215             :     SysScanDesc scan;
     216             :     HeapTuple   tuple;
     217             :     Oid         myrelid;
     218             :     AttrNumber  myattnum;
     219             : 
     220             :     /* Grab an appropriate lock on the pg_attrdef relation */
     221        3864 :     attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
     222             : 
     223             :     /* Find the pg_attrdef tuple */
     224        3864 :     ScanKeyInit(&scankeys[0],
     225             :                 Anum_pg_attrdef_oid,
     226             :                 BTEqualStrategyNumber, F_OIDEQ,
     227             :                 ObjectIdGetDatum(attrdefId));
     228             : 
     229        3864 :     scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndexId, true,
     230             :                               NULL, 1, scankeys);
     231             : 
     232        3864 :     tuple = systable_getnext(scan);
     233        3864 :     if (!HeapTupleIsValid(tuple))
     234           0 :         elog(ERROR, "could not find tuple for attrdef %u", attrdefId);
     235             : 
     236        3864 :     myrelid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid;
     237        3864 :     myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
     238             : 
     239             :     /* Get an exclusive lock on the relation owning the attribute */
     240        3864 :     myrel = relation_open(myrelid, AccessExclusiveLock);
     241             : 
     242             :     /* Now we can delete the pg_attrdef row */
     243        3864 :     CatalogTupleDelete(attrdef_rel, &tuple->t_self);
     244             : 
     245        3864 :     systable_endscan(scan);
     246        3864 :     table_close(attrdef_rel, RowExclusiveLock);
     247             : 
     248             :     /* Fix the pg_attribute row */
     249        3864 :     attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
     250             : 
     251        3864 :     tuple = SearchSysCacheCopy2(ATTNUM,
     252             :                                 ObjectIdGetDatum(myrelid),
     253             :                                 Int16GetDatum(myattnum));
     254        3864 :     if (!HeapTupleIsValid(tuple))   /* shouldn't happen */
     255           0 :         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
     256             :              myattnum, myrelid);
     257             : 
     258        3864 :     ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false;
     259             : 
     260        3864 :     CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
     261             : 
     262             :     /*
     263             :      * Our update of the pg_attribute row will force a relcache rebuild, so
     264             :      * there's nothing else to do here.
     265             :      */
     266        3864 :     table_close(attr_rel, RowExclusiveLock);
     267             : 
     268             :     /* Keep lock on attribute's rel until end of xact */
     269        3864 :     relation_close(myrel, NoLock);
     270        3864 : }
     271             : 
     272             : 
     273             : /*
     274             :  * Get the pg_attrdef OID of the default expression for a column
     275             :  * identified by relation OID and column number.
     276             :  *
     277             :  * Returns InvalidOid if there is no such pg_attrdef entry.
     278             :  */
     279             : Oid
     280         262 : GetAttrDefaultOid(Oid relid, AttrNumber attnum)
     281             : {
     282         262 :     Oid         result = InvalidOid;
     283             :     Relation    attrdef;
     284             :     ScanKeyData keys[2];
     285             :     SysScanDesc scan;
     286             :     HeapTuple   tup;
     287             : 
     288         262 :     attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
     289         262 :     ScanKeyInit(&keys[0],
     290             :                 Anum_pg_attrdef_adrelid,
     291             :                 BTEqualStrategyNumber,
     292             :                 F_OIDEQ,
     293             :                 ObjectIdGetDatum(relid));
     294         262 :     ScanKeyInit(&keys[1],
     295             :                 Anum_pg_attrdef_adnum,
     296             :                 BTEqualStrategyNumber,
     297             :                 F_INT2EQ,
     298             :                 Int16GetDatum(attnum));
     299         262 :     scan = systable_beginscan(attrdef, AttrDefaultIndexId, true,
     300             :                               NULL, 2, keys);
     301             : 
     302         262 :     if (HeapTupleIsValid(tup = systable_getnext(scan)))
     303             :     {
     304         262 :         Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
     305             : 
     306         262 :         result = atdform->oid;
     307             :     }
     308             : 
     309         262 :     systable_endscan(scan);
     310         262 :     table_close(attrdef, AccessShareLock);
     311             : 
     312         262 :     return result;
     313             : }
     314             : 
     315             : /*
     316             :  * Given a pg_attrdef OID, return the relation OID and column number of
     317             :  * the owning column (represented as an ObjectAddress for convenience).
     318             :  *
     319             :  * Returns InvalidObjectAddress if there is no such pg_attrdef entry.
     320             :  */
     321             : ObjectAddress
     322        4350 : GetAttrDefaultColumnAddress(Oid attrdefoid)
     323             : {
     324        4350 :     ObjectAddress result = InvalidObjectAddress;
     325             :     Relation    attrdef;
     326             :     ScanKeyData skey[1];
     327             :     SysScanDesc scan;
     328             :     HeapTuple   tup;
     329             : 
     330        4350 :     attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
     331        4350 :     ScanKeyInit(&skey[0],
     332             :                 Anum_pg_attrdef_oid,
     333             :                 BTEqualStrategyNumber, F_OIDEQ,
     334             :                 ObjectIdGetDatum(attrdefoid));
     335        4350 :     scan = systable_beginscan(attrdef, AttrDefaultOidIndexId, true,
     336             :                               NULL, 1, skey);
     337             : 
     338        4350 :     if (HeapTupleIsValid(tup = systable_getnext(scan)))
     339             :     {
     340        4332 :         Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
     341             : 
     342        4332 :         result.classId = RelationRelationId;
     343        4332 :         result.objectId = atdform->adrelid;
     344        4332 :         result.objectSubId = atdform->adnum;
     345             :     }
     346             : 
     347        4350 :     systable_endscan(scan);
     348        4350 :     table_close(attrdef, AccessShareLock);
     349             : 
     350        4350 :     return result;
     351             : }

Generated by: LCOV version 1.16