LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_attrdef.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 96.2 % 104 100
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 5 5
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         2696 : 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         2696 :     Datum       valuesAtt[Natts_pg_attribute] = {0};
      49         2696 :     bool        nullsAtt[Natts_pg_attribute] = {0};
      50         2696 :     bool        replacesAtt[Natts_pg_attribute] = {0};
      51              :     char        attgenerated;
      52              :     Oid         attrdefOid;
      53              :     ObjectAddress colobject,
      54              :                 defobject;
      55              : 
      56         2696 :     adrel = table_open(AttrDefaultRelationId, RowExclusiveLock);
      57              : 
      58              :     /*
      59              :      * Flatten expression to string form for storage.
      60              :      */
      61         2696 :     adbin = nodeToString(expr);
      62              : 
      63              :     /*
      64              :      * Make the pg_attrdef entry.
      65              :      */
      66         2696 :     attrdefOid = GetNewOidWithIndex(adrel, AttrDefaultOidIndexId,
      67              :                                     Anum_pg_attrdef_oid);
      68         2696 :     values[Anum_pg_attrdef_oid - 1] = ObjectIdGetDatum(attrdefOid);
      69         2696 :     values[Anum_pg_attrdef_adrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
      70         2696 :     values[Anum_pg_attrdef_adnum - 1] = Int16GetDatum(attnum);
      71         2696 :     values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin);
      72              : 
      73         2696 :     tuple = heap_form_tuple(adrel->rd_att, values, nulls);
      74         2696 :     CatalogTupleInsert(adrel, tuple);
      75              : 
      76         2696 :     defobject.classId = AttrDefaultRelationId;
      77         2696 :     defobject.objectId = attrdefOid;
      78         2696 :     defobject.objectSubId = 0;
      79              : 
      80         2696 :     table_close(adrel, RowExclusiveLock);
      81              : 
      82              :     /* now can free some of the stuff allocated above */
      83         2696 :     pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1]));
      84         2696 :     heap_freetuple(tuple);
      85         2696 :     pfree(adbin);
      86              : 
      87              :     /*
      88              :      * Update the pg_attribute entry for the column to show that a default
      89              :      * exists.
      90              :      */
      91         2696 :     attrrel = table_open(AttributeRelationId, RowExclusiveLock);
      92         2696 :     atttup = SearchSysCacheCopy2(ATTNUM,
      93              :                                  ObjectIdGetDatum(RelationGetRelid(rel)),
      94              :                                  Int16GetDatum(attnum));
      95         2696 :     if (!HeapTupleIsValid(atttup))
      96            0 :         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
      97              :              attnum, RelationGetRelid(rel));
      98         2696 :     attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
      99         2696 :     attgenerated = attStruct->attgenerated;
     100              : 
     101         2696 :     valuesAtt[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(true);
     102         2696 :     replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
     103              : 
     104         2696 :     atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
     105              :                                valuesAtt, nullsAtt, replacesAtt);
     106              : 
     107         2696 :     CatalogTupleUpdate(attrrel, &atttup->t_self, atttup);
     108              : 
     109         2696 :     table_close(attrrel, RowExclusiveLock);
     110         2696 :     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         2696 :     colobject.classId = RelationRelationId;
     119         2696 :     colobject.objectId = RelationGetRelid(rel);
     120         2696 :     colobject.objectSubId = attnum;
     121              : 
     122         2696 :     recordDependencyOn(&defobject, &colobject,
     123              :                        attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
     124              : 
     125              :     /*
     126              :      * Record dependencies on objects used in the expression, too.
     127              :      */
     128         2696 :     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         2690 :     InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
     141              :                                   RelationGetRelid(rel), attnum, is_internal);
     142              : 
     143         2690 :     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          452 : 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          452 :     bool        found = false;
     162              : 
     163          452 :     attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
     164              : 
     165          452 :     ScanKeyInit(&scankeys[0],
     166              :                 Anum_pg_attrdef_adrelid,
     167              :                 BTEqualStrategyNumber, F_OIDEQ,
     168              :                 ObjectIdGetDatum(relid));
     169          452 :     ScanKeyInit(&scankeys[1],
     170              :                 Anum_pg_attrdef_adnum,
     171              :                 BTEqualStrategyNumber, F_INT2EQ,
     172              :                 Int16GetDatum(attnum));
     173              : 
     174          452 :     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          764 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
     179              :     {
     180              :         ObjectAddress object;
     181          312 :         Form_pg_attrdef attrtuple = (Form_pg_attrdef) GETSTRUCT(tuple);
     182              : 
     183          312 :         object.classId = AttrDefaultRelationId;
     184          312 :         object.objectId = attrtuple->oid;
     185          312 :         object.objectSubId = 0;
     186              : 
     187          312 :         performDeletion(&object, behavior,
     188              :                         internal ? PERFORM_DELETION_INTERNAL : 0);
     189              : 
     190          312 :         found = true;
     191              :     }
     192              : 
     193          452 :     systable_endscan(scan);
     194          452 :     table_close(attrdef_rel, RowExclusiveLock);
     195              : 
     196          452 :     if (complain && !found)
     197            0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
     198              :              relid, attnum);
     199          452 : }
     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         1932 : 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         1932 :     attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
     222              : 
     223              :     /* Find the pg_attrdef tuple */
     224         1932 :     ScanKeyInit(&scankeys[0],
     225              :                 Anum_pg_attrdef_oid,
     226              :                 BTEqualStrategyNumber, F_OIDEQ,
     227              :                 ObjectIdGetDatum(attrdefId));
     228              : 
     229         1932 :     scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndexId, true,
     230              :                               NULL, 1, scankeys);
     231              : 
     232         1932 :     tuple = systable_getnext(scan);
     233         1932 :     if (!HeapTupleIsValid(tuple))
     234            0 :         elog(ERROR, "could not find tuple for attrdef %u", attrdefId);
     235              : 
     236         1932 :     myrelid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid;
     237         1932 :     myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
     238              : 
     239              :     /* Get an exclusive lock on the relation owning the attribute */
     240         1932 :     myrel = relation_open(myrelid, AccessExclusiveLock);
     241              : 
     242              :     /* Now we can delete the pg_attrdef row */
     243         1932 :     CatalogTupleDelete(attrdef_rel, &tuple->t_self);
     244              : 
     245         1932 :     systable_endscan(scan);
     246         1932 :     table_close(attrdef_rel, RowExclusiveLock);
     247              : 
     248              :     /* Fix the pg_attribute row */
     249         1932 :     attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
     250              : 
     251         1932 :     tuple = SearchSysCacheCopy2(ATTNUM,
     252              :                                 ObjectIdGetDatum(myrelid),
     253              :                                 Int16GetDatum(myattnum));
     254         1932 :     if (!HeapTupleIsValid(tuple))   /* shouldn't happen */
     255            0 :         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
     256              :              myattnum, myrelid);
     257              : 
     258         1932 :     ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false;
     259              : 
     260         1932 :     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         1932 :     table_close(attr_rel, RowExclusiveLock);
     267              : 
     268              :     /* Keep lock on attribute's rel until end of xact */
     269         1932 :     relation_close(myrel, NoLock);
     270         1932 : }
     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          131 : GetAttrDefaultOid(Oid relid, AttrNumber attnum)
     281              : {
     282          131 :     Oid         result = InvalidOid;
     283              :     Relation    attrdef;
     284              :     ScanKeyData keys[2];
     285              :     SysScanDesc scan;
     286              :     HeapTuple   tup;
     287              : 
     288          131 :     attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
     289          131 :     ScanKeyInit(&keys[0],
     290              :                 Anum_pg_attrdef_adrelid,
     291              :                 BTEqualStrategyNumber,
     292              :                 F_OIDEQ,
     293              :                 ObjectIdGetDatum(relid));
     294          131 :     ScanKeyInit(&keys[1],
     295              :                 Anum_pg_attrdef_adnum,
     296              :                 BTEqualStrategyNumber,
     297              :                 F_INT2EQ,
     298              :                 Int16GetDatum(attnum));
     299          131 :     scan = systable_beginscan(attrdef, AttrDefaultIndexId, true,
     300              :                               NULL, 2, keys);
     301              : 
     302          131 :     if (HeapTupleIsValid(tup = systable_getnext(scan)))
     303              :     {
     304          131 :         Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
     305              : 
     306          131 :         result = atdform->oid;
     307              :     }
     308              : 
     309          131 :     systable_endscan(scan);
     310          131 :     table_close(attrdef, AccessShareLock);
     311              : 
     312          131 :     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         2175 : GetAttrDefaultColumnAddress(Oid attrdefoid)
     323              : {
     324         2175 :     ObjectAddress result = InvalidObjectAddress;
     325              :     Relation    attrdef;
     326              :     ScanKeyData skey[1];
     327              :     SysScanDesc scan;
     328              :     HeapTuple   tup;
     329              : 
     330         2175 :     attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
     331         2175 :     ScanKeyInit(&skey[0],
     332              :                 Anum_pg_attrdef_oid,
     333              :                 BTEqualStrategyNumber, F_OIDEQ,
     334              :                 ObjectIdGetDatum(attrdefoid));
     335         2175 :     scan = systable_beginscan(attrdef, AttrDefaultOidIndexId, true,
     336              :                               NULL, 1, skey);
     337              : 
     338         2175 :     if (HeapTupleIsValid(tup = systable_getnext(scan)))
     339              :     {
     340         2166 :         Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
     341              : 
     342         2166 :         result.classId = RelationRelationId;
     343         2166 :         result.objectId = atdform->adrelid;
     344         2166 :         result.objectSubId = atdform->adnum;
     345              :     }
     346              : 
     347         2175 :     systable_endscan(scan);
     348         2175 :     table_close(attrdef, AccessShareLock);
     349              : 
     350         2175 :     return result;
     351              : }
        

Generated by: LCOV version 2.0-1