LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_attrdef.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 125 130 96.2 %
Date: 2024-03-29 14:11:41 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-2024, 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/relation.h"
      19             : #include "access/table.h"
      20             : #include "catalog/catalog.h"
      21             : #include "catalog/dependency.h"
      22             : #include "catalog/indexing.h"
      23             : #include "catalog/objectaccess.h"
      24             : #include "catalog/pg_attrdef.h"
      25             : #include "executor/executor.h"
      26             : #include "optimizer/optimizer.h"
      27             : #include "utils/array.h"
      28             : #include "utils/builtins.h"
      29             : #include "utils/fmgroids.h"
      30             : #include "utils/rel.h"
      31             : #include "utils/syscache.h"
      32             : 
      33             : 
      34             : /*
      35             :  * Store a default expression for column attnum of relation rel.
      36             :  *
      37             :  * Returns the OID of the new pg_attrdef tuple.
      38             :  *
      39             :  * add_column_mode must be true if we are storing the default for a new
      40             :  * attribute, and false if it's for an already existing attribute. The reason
      41             :  * for this is that the missing value must never be updated after it is set,
      42             :  * which can only be when a column is added to the table. Otherwise we would
      43             :  * in effect be changing existing tuples.
      44             :  */
      45             : Oid
      46        3694 : StoreAttrDefault(Relation rel, AttrNumber attnum,
      47             :                  Node *expr, bool is_internal, bool add_column_mode)
      48             : {
      49             :     char       *adbin;
      50             :     Relation    adrel;
      51             :     HeapTuple   tuple;
      52             :     Datum       values[4];
      53             :     static bool nulls[4] = {false, false, false, false};
      54             :     Relation    attrrel;
      55             :     HeapTuple   atttup;
      56             :     Form_pg_attribute attStruct;
      57             :     char        attgenerated;
      58             :     Oid         attrdefOid;
      59             :     ObjectAddress colobject,
      60             :                 defobject;
      61             : 
      62        3694 :     adrel = table_open(AttrDefaultRelationId, RowExclusiveLock);
      63             : 
      64             :     /*
      65             :      * Flatten expression to string form for storage.
      66             :      */
      67        3694 :     adbin = nodeToString(expr);
      68             : 
      69             :     /*
      70             :      * Make the pg_attrdef entry.
      71             :      */
      72        3694 :     attrdefOid = GetNewOidWithIndex(adrel, AttrDefaultOidIndexId,
      73             :                                     Anum_pg_attrdef_oid);
      74        3694 :     values[Anum_pg_attrdef_oid - 1] = ObjectIdGetDatum(attrdefOid);
      75        3694 :     values[Anum_pg_attrdef_adrelid - 1] = RelationGetRelid(rel);
      76        3694 :     values[Anum_pg_attrdef_adnum - 1] = attnum;
      77        3694 :     values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin);
      78             : 
      79        3694 :     tuple = heap_form_tuple(adrel->rd_att, values, nulls);
      80        3694 :     CatalogTupleInsert(adrel, tuple);
      81             : 
      82        3694 :     defobject.classId = AttrDefaultRelationId;
      83        3694 :     defobject.objectId = attrdefOid;
      84        3694 :     defobject.objectSubId = 0;
      85             : 
      86        3694 :     table_close(adrel, RowExclusiveLock);
      87             : 
      88             :     /* now can free some of the stuff allocated above */
      89        3694 :     pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1]));
      90        3694 :     heap_freetuple(tuple);
      91        3694 :     pfree(adbin);
      92             : 
      93             :     /*
      94             :      * Update the pg_attribute entry for the column to show that a default
      95             :      * exists.
      96             :      */
      97        3694 :     attrrel = table_open(AttributeRelationId, RowExclusiveLock);
      98        3694 :     atttup = SearchSysCacheCopy2(ATTNUM,
      99             :                                  ObjectIdGetDatum(RelationGetRelid(rel)),
     100             :                                  Int16GetDatum(attnum));
     101        3694 :     if (!HeapTupleIsValid(atttup))
     102           0 :         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
     103             :              attnum, RelationGetRelid(rel));
     104        3694 :     attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
     105        3694 :     attgenerated = attStruct->attgenerated;
     106        3694 :     if (!attStruct->atthasdef)
     107             :     {
     108             :         Form_pg_attribute defAttStruct;
     109             : 
     110             :         ExprState  *exprState;
     111        3694 :         Expr       *expr2 = (Expr *) expr;
     112        3694 :         EState     *estate = NULL;
     113             :         ExprContext *econtext;
     114        3694 :         Datum       valuesAtt[Natts_pg_attribute] = {0};
     115        3694 :         bool        nullsAtt[Natts_pg_attribute] = {0};
     116        3694 :         bool        replacesAtt[Natts_pg_attribute] = {0};
     117        3694 :         Datum       missingval = (Datum) 0;
     118        3694 :         bool        missingIsNull = true;
     119             : 
     120        3694 :         valuesAtt[Anum_pg_attribute_atthasdef - 1] = true;
     121        3694 :         replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
     122             : 
     123        3694 :         if (rel->rd_rel->relkind == RELKIND_RELATION && add_column_mode &&
     124             :             !attgenerated)
     125             :         {
     126         450 :             expr2 = expression_planner(expr2);
     127         450 :             estate = CreateExecutorState();
     128         450 :             exprState = ExecPrepareExpr(expr2, estate);
     129         450 :             econtext = GetPerTupleExprContext(estate);
     130             : 
     131         450 :             missingval = ExecEvalExpr(exprState, econtext,
     132             :                                       &missingIsNull);
     133             : 
     134         450 :             FreeExecutorState(estate);
     135             : 
     136         450 :             defAttStruct = TupleDescAttr(rel->rd_att, attnum - 1);
     137             : 
     138         450 :             if (missingIsNull)
     139             :             {
     140             :                 /* if the default evaluates to NULL, just store a NULL array */
     141           0 :                 missingval = (Datum) 0;
     142             :             }
     143             :             else
     144             :             {
     145             :                 /* otherwise make a one-element array of the value */
     146         450 :                 missingval = PointerGetDatum(construct_array(&missingval,
     147             :                                                              1,
     148             :                                                              defAttStruct->atttypid,
     149         450 :                                                              defAttStruct->attlen,
     150         450 :                                                              defAttStruct->attbyval,
     151         450 :                                                              defAttStruct->attalign));
     152             :             }
     153             : 
     154         450 :             valuesAtt[Anum_pg_attribute_atthasmissing - 1] = !missingIsNull;
     155         450 :             replacesAtt[Anum_pg_attribute_atthasmissing - 1] = true;
     156         450 :             valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
     157         450 :             replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
     158         450 :             nullsAtt[Anum_pg_attribute_attmissingval - 1] = missingIsNull;
     159             :         }
     160        3694 :         atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
     161             :                                    valuesAtt, nullsAtt, replacesAtt);
     162             : 
     163        3694 :         CatalogTupleUpdate(attrrel, &atttup->t_self, atttup);
     164             : 
     165        3694 :         if (!missingIsNull)
     166         450 :             pfree(DatumGetPointer(missingval));
     167             :     }
     168        3694 :     table_close(attrrel, RowExclusiveLock);
     169        3694 :     heap_freetuple(atttup);
     170             : 
     171             :     /*
     172             :      * Make a dependency so that the pg_attrdef entry goes away if the column
     173             :      * (or whole table) is deleted.  In the case of a generated column, make
     174             :      * it an internal dependency to prevent the default expression from being
     175             :      * deleted separately.
     176             :      */
     177        3694 :     colobject.classId = RelationRelationId;
     178        3694 :     colobject.objectId = RelationGetRelid(rel);
     179        3694 :     colobject.objectSubId = attnum;
     180             : 
     181        3694 :     recordDependencyOn(&defobject, &colobject,
     182             :                        attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
     183             : 
     184             :     /*
     185             :      * Record dependencies on objects used in the expression, too.
     186             :      */
     187        3694 :     recordDependencyOnSingleRelExpr(&defobject, expr, RelationGetRelid(rel),
     188             :                                     DEPENDENCY_NORMAL,
     189             :                                     DEPENDENCY_NORMAL, false);
     190             : 
     191             :     /*
     192             :      * Post creation hook for attribute defaults.
     193             :      *
     194             :      * XXX. ALTER TABLE ALTER COLUMN SET/DROP DEFAULT is implemented with a
     195             :      * couple of deletion/creation of the attribute's default entry, so the
     196             :      * callee should check existence of an older version of this entry if it
     197             :      * needs to distinguish.
     198             :      */
     199        3694 :     InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
     200             :                                   RelationGetRelid(rel), attnum, is_internal);
     201             : 
     202        3694 :     return attrdefOid;
     203             : }
     204             : 
     205             : 
     206             : /*
     207             :  *      RemoveAttrDefault
     208             :  *
     209             :  * If the specified relation/attribute has a default, remove it.
     210             :  * (If no default, raise error if complain is true, else return quietly.)
     211             :  */
     212             : void
     213         716 : RemoveAttrDefault(Oid relid, AttrNumber attnum,
     214             :                   DropBehavior behavior, bool complain, bool internal)
     215             : {
     216             :     Relation    attrdef_rel;
     217             :     ScanKeyData scankeys[2];
     218             :     SysScanDesc scan;
     219             :     HeapTuple   tuple;
     220         716 :     bool        found = false;
     221             : 
     222         716 :     attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
     223             : 
     224         716 :     ScanKeyInit(&scankeys[0],
     225             :                 Anum_pg_attrdef_adrelid,
     226             :                 BTEqualStrategyNumber, F_OIDEQ,
     227             :                 ObjectIdGetDatum(relid));
     228         716 :     ScanKeyInit(&scankeys[1],
     229             :                 Anum_pg_attrdef_adnum,
     230             :                 BTEqualStrategyNumber, F_INT2EQ,
     231             :                 Int16GetDatum(attnum));
     232             : 
     233         716 :     scan = systable_beginscan(attrdef_rel, AttrDefaultIndexId, true,
     234             :                               NULL, 2, scankeys);
     235             : 
     236             :     /* There should be at most one matching tuple, but we loop anyway */
     237        1188 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
     238             :     {
     239             :         ObjectAddress object;
     240         472 :         Form_pg_attrdef attrtuple = (Form_pg_attrdef) GETSTRUCT(tuple);
     241             : 
     242         472 :         object.classId = AttrDefaultRelationId;
     243         472 :         object.objectId = attrtuple->oid;
     244         472 :         object.objectSubId = 0;
     245             : 
     246         472 :         performDeletion(&object, behavior,
     247             :                         internal ? PERFORM_DELETION_INTERNAL : 0);
     248             : 
     249         472 :         found = true;
     250             :     }
     251             : 
     252         716 :     systable_endscan(scan);
     253         716 :     table_close(attrdef_rel, RowExclusiveLock);
     254             : 
     255         716 :     if (complain && !found)
     256           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
     257             :              relid, attnum);
     258         716 : }
     259             : 
     260             : /*
     261             :  *      RemoveAttrDefaultById
     262             :  *
     263             :  * Remove a pg_attrdef entry specified by OID.  This is the guts of
     264             :  * attribute-default removal.  Note it should be called via performDeletion,
     265             :  * not directly.
     266             :  */
     267             : void
     268        2788 : RemoveAttrDefaultById(Oid attrdefId)
     269             : {
     270             :     Relation    attrdef_rel;
     271             :     Relation    attr_rel;
     272             :     Relation    myrel;
     273             :     ScanKeyData scankeys[1];
     274             :     SysScanDesc scan;
     275             :     HeapTuple   tuple;
     276             :     Oid         myrelid;
     277             :     AttrNumber  myattnum;
     278             : 
     279             :     /* Grab an appropriate lock on the pg_attrdef relation */
     280        2788 :     attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
     281             : 
     282             :     /* Find the pg_attrdef tuple */
     283        2788 :     ScanKeyInit(&scankeys[0],
     284             :                 Anum_pg_attrdef_oid,
     285             :                 BTEqualStrategyNumber, F_OIDEQ,
     286             :                 ObjectIdGetDatum(attrdefId));
     287             : 
     288        2788 :     scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndexId, true,
     289             :                               NULL, 1, scankeys);
     290             : 
     291        2788 :     tuple = systable_getnext(scan);
     292        2788 :     if (!HeapTupleIsValid(tuple))
     293           0 :         elog(ERROR, "could not find tuple for attrdef %u", attrdefId);
     294             : 
     295        2788 :     myrelid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid;
     296        2788 :     myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
     297             : 
     298             :     /* Get an exclusive lock on the relation owning the attribute */
     299        2788 :     myrel = relation_open(myrelid, AccessExclusiveLock);
     300             : 
     301             :     /* Now we can delete the pg_attrdef row */
     302        2788 :     CatalogTupleDelete(attrdef_rel, &tuple->t_self);
     303             : 
     304        2788 :     systable_endscan(scan);
     305        2788 :     table_close(attrdef_rel, RowExclusiveLock);
     306             : 
     307             :     /* Fix the pg_attribute row */
     308        2788 :     attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
     309             : 
     310        2788 :     tuple = SearchSysCacheCopy2(ATTNUM,
     311             :                                 ObjectIdGetDatum(myrelid),
     312             :                                 Int16GetDatum(myattnum));
     313        2788 :     if (!HeapTupleIsValid(tuple))   /* shouldn't happen */
     314           0 :         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
     315             :              myattnum, myrelid);
     316             : 
     317        2788 :     ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false;
     318             : 
     319        2788 :     CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
     320             : 
     321             :     /*
     322             :      * Our update of the pg_attribute row will force a relcache rebuild, so
     323             :      * there's nothing else to do here.
     324             :      */
     325        2788 :     table_close(attr_rel, RowExclusiveLock);
     326             : 
     327             :     /* Keep lock on attribute's rel until end of xact */
     328        2788 :     relation_close(myrel, NoLock);
     329        2788 : }
     330             : 
     331             : 
     332             : /*
     333             :  * Get the pg_attrdef OID of the default expression for a column
     334             :  * identified by relation OID and column number.
     335             :  *
     336             :  * Returns InvalidOid if there is no such pg_attrdef entry.
     337             :  */
     338             : Oid
     339         116 : GetAttrDefaultOid(Oid relid, AttrNumber attnum)
     340             : {
     341         116 :     Oid         result = InvalidOid;
     342             :     Relation    attrdef;
     343             :     ScanKeyData keys[2];
     344             :     SysScanDesc scan;
     345             :     HeapTuple   tup;
     346             : 
     347         116 :     attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
     348         116 :     ScanKeyInit(&keys[0],
     349             :                 Anum_pg_attrdef_adrelid,
     350             :                 BTEqualStrategyNumber,
     351             :                 F_OIDEQ,
     352             :                 ObjectIdGetDatum(relid));
     353         116 :     ScanKeyInit(&keys[1],
     354             :                 Anum_pg_attrdef_adnum,
     355             :                 BTEqualStrategyNumber,
     356             :                 F_INT2EQ,
     357             :                 Int16GetDatum(attnum));
     358         116 :     scan = systable_beginscan(attrdef, AttrDefaultIndexId, true,
     359             :                               NULL, 2, keys);
     360             : 
     361         116 :     if (HeapTupleIsValid(tup = systable_getnext(scan)))
     362             :     {
     363         116 :         Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
     364             : 
     365         116 :         result = atdform->oid;
     366             :     }
     367             : 
     368         116 :     systable_endscan(scan);
     369         116 :     table_close(attrdef, AccessShareLock);
     370             : 
     371         116 :     return result;
     372             : }
     373             : 
     374             : /*
     375             :  * Given a pg_attrdef OID, return the relation OID and column number of
     376             :  * the owning column (represented as an ObjectAddress for convenience).
     377             :  *
     378             :  * Returns InvalidObjectAddress if there is no such pg_attrdef entry.
     379             :  */
     380             : ObjectAddress
     381        2776 : GetAttrDefaultColumnAddress(Oid attrdefoid)
     382             : {
     383        2776 :     ObjectAddress result = InvalidObjectAddress;
     384             :     Relation    attrdef;
     385             :     ScanKeyData skey[1];
     386             :     SysScanDesc scan;
     387             :     HeapTuple   tup;
     388             : 
     389        2776 :     attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
     390        2776 :     ScanKeyInit(&skey[0],
     391             :                 Anum_pg_attrdef_oid,
     392             :                 BTEqualStrategyNumber, F_OIDEQ,
     393             :                 ObjectIdGetDatum(attrdefoid));
     394        2776 :     scan = systable_beginscan(attrdef, AttrDefaultOidIndexId, true,
     395             :                               NULL, 1, skey);
     396             : 
     397        2776 :     if (HeapTupleIsValid(tup = systable_getnext(scan)))
     398             :     {
     399        2758 :         Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
     400             : 
     401        2758 :         result.classId = RelationRelationId;
     402        2758 :         result.objectId = atdform->adrelid;
     403        2758 :         result.objectSubId = atdform->adnum;
     404             :     }
     405             : 
     406        2776 :     systable_endscan(scan);
     407        2776 :     table_close(attrdef, AccessShareLock);
     408             : 
     409        2776 :     return result;
     410             : }

Generated by: LCOV version 1.14