LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_depend.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 301 308 97.7 %
Date: 2025-01-18 03:14:54 Functions: 19 19 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pg_depend.c
       4             :  *    routines to support manipulation of the pg_depend relation
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/catalog/pg_depend.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/genam.h"
      18             : #include "access/htup_details.h"
      19             : #include "access/table.h"
      20             : #include "catalog/catalog.h"
      21             : #include "catalog/dependency.h"
      22             : #include "catalog/indexing.h"
      23             : #include "catalog/pg_constraint.h"
      24             : #include "catalog/pg_depend.h"
      25             : #include "catalog/pg_extension.h"
      26             : #include "catalog/partition.h"
      27             : #include "commands/extension.h"
      28             : #include "miscadmin.h"
      29             : #include "utils/fmgroids.h"
      30             : #include "utils/lsyscache.h"
      31             : #include "utils/rel.h"
      32             : 
      33             : 
      34             : static bool isObjectPinned(const ObjectAddress *object);
      35             : 
      36             : 
      37             : /*
      38             :  * Record a dependency between 2 objects via their respective ObjectAddress.
      39             :  * The first argument is the dependent object, the second the one it
      40             :  * references.
      41             :  *
      42             :  * This simply creates an entry in pg_depend, without any other processing.
      43             :  */
      44             : void
      45      731910 : recordDependencyOn(const ObjectAddress *depender,
      46             :                    const ObjectAddress *referenced,
      47             :                    DependencyType behavior)
      48             : {
      49      731910 :     recordMultipleDependencies(depender, referenced, 1, behavior);
      50      731910 : }
      51             : 
      52             : /*
      53             :  * Record multiple dependencies (of the same kind) for a single dependent
      54             :  * object.  This has a little less overhead than recording each separately.
      55             :  */
      56             : void
      57     1138646 : recordMultipleDependencies(const ObjectAddress *depender,
      58             :                            const ObjectAddress *referenced,
      59             :                            int nreferenced,
      60             :                            DependencyType behavior)
      61             : {
      62             :     Relation    dependDesc;
      63             :     CatalogIndexState indstate;
      64             :     TupleTableSlot **slot;
      65             :     int         i,
      66             :                 max_slots,
      67             :                 slot_init_count,
      68             :                 slot_stored_count;
      69             : 
      70     1138646 :     if (nreferenced <= 0)
      71       46296 :         return;                 /* nothing to do */
      72             : 
      73             :     /*
      74             :      * During bootstrap, do nothing since pg_depend may not exist yet.
      75             :      *
      76             :      * Objects created during bootstrap are most likely pinned, and the few
      77             :      * that are not do not have dependencies on each other, so that there
      78             :      * would be no need to make a pg_depend entry anyway.
      79             :      */
      80     1092350 :     if (IsBootstrapProcessingMode())
      81       60390 :         return;
      82             : 
      83     1031960 :     dependDesc = table_open(DependRelationId, RowExclusiveLock);
      84             : 
      85             :     /*
      86             :      * Allocate the slots to use, but delay costly initialization until we
      87             :      * know that they will be used.
      88             :      */
      89     1031960 :     max_slots = Min(nreferenced,
      90             :                     MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
      91     1031960 :     slot = palloc(sizeof(TupleTableSlot *) * max_slots);
      92             : 
      93             :     /* Don't open indexes unless we need to make an update */
      94     1031960 :     indstate = NULL;
      95             : 
      96             :     /* number of slots currently storing tuples */
      97     1031960 :     slot_stored_count = 0;
      98             :     /* number of slots currently initialized */
      99     1031960 :     slot_init_count = 0;
     100     3007022 :     for (i = 0; i < nreferenced; i++, referenced++)
     101             :     {
     102             :         /*
     103             :          * If the referenced object is pinned by the system, there's no real
     104             :          * need to record dependencies on it.  This saves lots of space in
     105             :          * pg_depend, so it's worth the time taken to check.
     106             :          */
     107     1975062 :         if (isObjectPinned(referenced))
     108     1441466 :             continue;
     109             : 
     110      533596 :         if (slot_init_count < max_slots)
     111             :         {
     112      533596 :             slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
     113             :                                                                &TTSOpsHeapTuple);
     114      533596 :             slot_init_count++;
     115             :         }
     116             : 
     117      533596 :         ExecClearTuple(slot[slot_stored_count]);
     118             : 
     119             :         /*
     120             :          * Record the dependency.  Note we don't bother to check for duplicate
     121             :          * dependencies; there's no harm in them.
     122             :          */
     123      533596 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
     124      533596 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
     125      533596 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
     126      533596 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
     127      533596 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
     128      533596 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
     129      533596 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
     130             : 
     131      533596 :         memset(slot[slot_stored_count]->tts_isnull, false,
     132      533596 :                slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
     133             : 
     134      533596 :         ExecStoreVirtualTuple(slot[slot_stored_count]);
     135      533596 :         slot_stored_count++;
     136             : 
     137             :         /* If slots are full, insert a batch of tuples */
     138      533596 :         if (slot_stored_count == max_slots)
     139             :         {
     140             :             /* fetch index info only when we know we need it */
     141      370206 :             if (indstate == NULL)
     142      370206 :                 indstate = CatalogOpenIndexes(dependDesc);
     143             : 
     144      370206 :             CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
     145             :                                              indstate);
     146      370206 :             slot_stored_count = 0;
     147             :         }
     148             :     }
     149             : 
     150             :     /* Insert any tuples left in the buffer */
     151     1031960 :     if (slot_stored_count > 0)
     152             :     {
     153             :         /* fetch index info only when we know we need it */
     154       83398 :         if (indstate == NULL)
     155       83398 :             indstate = CatalogOpenIndexes(dependDesc);
     156             : 
     157       83398 :         CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
     158             :                                          indstate);
     159             :     }
     160             : 
     161     1031960 :     if (indstate != NULL)
     162      453604 :         CatalogCloseIndexes(indstate);
     163             : 
     164     1031960 :     table_close(dependDesc, RowExclusiveLock);
     165             : 
     166             :     /* Drop only the number of slots used */
     167     1565556 :     for (i = 0; i < slot_init_count; i++)
     168      533596 :         ExecDropSingleTupleTableSlot(slot[i]);
     169     1031960 :     pfree(slot);
     170             : }
     171             : 
     172             : /*
     173             :  * If we are executing a CREATE EXTENSION operation, mark the given object
     174             :  * as being a member of the extension, or check that it already is one.
     175             :  * Otherwise, do nothing.
     176             :  *
     177             :  * This must be called during creation of any user-definable object type
     178             :  * that could be a member of an extension.
     179             :  *
     180             :  * isReplace must be true if the object already existed, and false if it is
     181             :  * newly created.  In the former case we insist that it already be a member
     182             :  * of the current extension.  In the latter case we can skip checking whether
     183             :  * it is already a member of any extension.
     184             :  *
     185             :  * Note: isReplace = true is typically used when updating an object in
     186             :  * CREATE OR REPLACE and similar commands.  We used to allow the target
     187             :  * object to not already be an extension member, instead silently absorbing
     188             :  * it into the current extension.  However, this was both error-prone
     189             :  * (extensions might accidentally overwrite free-standing objects) and
     190             :  * a security hazard (since the object would retain its previous ownership).
     191             :  */
     192             : void
     193      283398 : recordDependencyOnCurrentExtension(const ObjectAddress *object,
     194             :                                    bool isReplace)
     195             : {
     196             :     /* Only whole objects can be extension members */
     197             :     Assert(object->objectSubId == 0);
     198             : 
     199      283398 :     if (creating_extension)
     200             :     {
     201             :         ObjectAddress extension;
     202             : 
     203             :         /* Only need to check for existing membership if isReplace */
     204        8844 :         if (isReplace)
     205             :         {
     206             :             Oid         oldext;
     207             : 
     208             :             /*
     209             :              * Side note: these catalog lookups are safe only because the
     210             :              * object is a pre-existing one.  In the not-isReplace case, the
     211             :              * caller has most likely not yet done a CommandCounterIncrement
     212             :              * that would make the new object visible.
     213             :              */
     214         766 :             oldext = getExtensionOfObject(object->classId, object->objectId);
     215         766 :             if (OidIsValid(oldext))
     216             :             {
     217             :                 /* If already a member of this extension, nothing to do */
     218         758 :                 if (oldext == CurrentExtensionObject)
     219         758 :                     return;
     220             :                 /* Already a member of some other extension, so reject */
     221           0 :                 ereport(ERROR,
     222             :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     223             :                          errmsg("%s is already a member of extension \"%s\"",
     224             :                                 getObjectDescription(object, false),
     225             :                                 get_extension_name(oldext))));
     226             :             }
     227             :             /* It's a free-standing object, so reject */
     228           8 :             ereport(ERROR,
     229             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     230             :                      errmsg("%s is not a member of extension \"%s\"",
     231             :                             getObjectDescription(object, false),
     232             :                             get_extension_name(CurrentExtensionObject)),
     233             :                      errdetail("An extension is not allowed to replace an object that it does not own.")));
     234             :         }
     235             : 
     236             :         /* OK, record it as a member of CurrentExtensionObject */
     237        8078 :         extension.classId = ExtensionRelationId;
     238        8078 :         extension.objectId = CurrentExtensionObject;
     239        8078 :         extension.objectSubId = 0;
     240             : 
     241        8078 :         recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
     242             :     }
     243             : }
     244             : 
     245             : /*
     246             :  * If we are executing a CREATE EXTENSION operation, check that the given
     247             :  * object is a member of the extension, and throw an error if it isn't.
     248             :  * Otherwise, do nothing.
     249             :  *
     250             :  * This must be called whenever a CREATE IF NOT EXISTS operation (for an
     251             :  * object type that can be an extension member) has found that an object of
     252             :  * the desired name already exists.  It is insecure for an extension to use
     253             :  * IF NOT EXISTS except when the conflicting object is already an extension
     254             :  * member; otherwise a hostile user could substitute an object with arbitrary
     255             :  * properties.
     256             :  */
     257             : void
     258         138 : checkMembershipInCurrentExtension(const ObjectAddress *object)
     259             : {
     260             :     /*
     261             :      * This is actually the same condition tested in
     262             :      * recordDependencyOnCurrentExtension; but we want to issue a
     263             :      * differently-worded error, and anyway it would be pretty confusing to
     264             :      * call recordDependencyOnCurrentExtension in these circumstances.
     265             :      */
     266             : 
     267             :     /* Only whole objects can be extension members */
     268             :     Assert(object->objectSubId == 0);
     269             : 
     270         138 :     if (creating_extension)
     271             :     {
     272             :         Oid         oldext;
     273             : 
     274          28 :         oldext = getExtensionOfObject(object->classId, object->objectId);
     275             :         /* If already a member of this extension, OK */
     276          28 :         if (oldext == CurrentExtensionObject)
     277          14 :             return;
     278             :         /* Else complain */
     279          14 :         ereport(ERROR,
     280             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     281             :                  errmsg("%s is not a member of extension \"%s\"",
     282             :                         getObjectDescription(object, false),
     283             :                         get_extension_name(CurrentExtensionObject)),
     284             :                  errdetail("An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.")));
     285             :     }
     286             : }
     287             : 
     288             : /*
     289             :  * deleteDependencyRecordsFor -- delete all records with given depender
     290             :  * classId/objectId.  Returns the number of records deleted.
     291             :  *
     292             :  * This is used when redefining an existing object.  Links leading to the
     293             :  * object do not change, and links leading from it will be recreated
     294             :  * (possibly with some differences from before).
     295             :  *
     296             :  * If skipExtensionDeps is true, we do not delete any dependencies that
     297             :  * show that the given object is a member of an extension.  This avoids
     298             :  * needing a lot of extra logic to fetch and recreate that dependency.
     299             :  */
     300             : long
     301       18668 : deleteDependencyRecordsFor(Oid classId, Oid objectId,
     302             :                            bool skipExtensionDeps)
     303             : {
     304       18668 :     long        count = 0;
     305             :     Relation    depRel;
     306             :     ScanKeyData key[2];
     307             :     SysScanDesc scan;
     308             :     HeapTuple   tup;
     309             : 
     310       18668 :     depRel = table_open(DependRelationId, RowExclusiveLock);
     311             : 
     312       18668 :     ScanKeyInit(&key[0],
     313             :                 Anum_pg_depend_classid,
     314             :                 BTEqualStrategyNumber, F_OIDEQ,
     315             :                 ObjectIdGetDatum(classId));
     316       18668 :     ScanKeyInit(&key[1],
     317             :                 Anum_pg_depend_objid,
     318             :                 BTEqualStrategyNumber, F_OIDEQ,
     319             :                 ObjectIdGetDatum(objectId));
     320             : 
     321       18668 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     322             :                               NULL, 2, key);
     323             : 
     324       31010 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     325             :     {
     326       12342 :         if (skipExtensionDeps &&
     327        9924 :             ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
     328        1230 :             continue;
     329             : 
     330       11112 :         CatalogTupleDelete(depRel, &tup->t_self);
     331       11112 :         count++;
     332             :     }
     333             : 
     334       18668 :     systable_endscan(scan);
     335             : 
     336       18668 :     table_close(depRel, RowExclusiveLock);
     337             : 
     338       18668 :     return count;
     339             : }
     340             : 
     341             : /*
     342             :  * deleteDependencyRecordsForClass -- delete all records with given depender
     343             :  * classId/objectId, dependee classId, and deptype.
     344             :  * Returns the number of records deleted.
     345             :  *
     346             :  * This is a variant of deleteDependencyRecordsFor, useful when revoking
     347             :  * an object property that is expressed by a dependency record (such as
     348             :  * extension membership).
     349             :  */
     350             : long
     351       12558 : deleteDependencyRecordsForClass(Oid classId, Oid objectId,
     352             :                                 Oid refclassId, char deptype)
     353             : {
     354       12558 :     long        count = 0;
     355             :     Relation    depRel;
     356             :     ScanKeyData key[2];
     357             :     SysScanDesc scan;
     358             :     HeapTuple   tup;
     359             : 
     360       12558 :     depRel = table_open(DependRelationId, RowExclusiveLock);
     361             : 
     362       12558 :     ScanKeyInit(&key[0],
     363             :                 Anum_pg_depend_classid,
     364             :                 BTEqualStrategyNumber, F_OIDEQ,
     365             :                 ObjectIdGetDatum(classId));
     366       12558 :     ScanKeyInit(&key[1],
     367             :                 Anum_pg_depend_objid,
     368             :                 BTEqualStrategyNumber, F_OIDEQ,
     369             :                 ObjectIdGetDatum(objectId));
     370             : 
     371       12558 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     372             :                               NULL, 2, key);
     373             : 
     374       18334 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     375             :     {
     376        5776 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     377             : 
     378        5776 :         if (depform->refclassid == refclassId && depform->deptype == deptype)
     379             :         {
     380        1198 :             CatalogTupleDelete(depRel, &tup->t_self);
     381        1198 :             count++;
     382             :         }
     383             :     }
     384             : 
     385       12558 :     systable_endscan(scan);
     386             : 
     387       12558 :     table_close(depRel, RowExclusiveLock);
     388             : 
     389       12558 :     return count;
     390             : }
     391             : 
     392             : /*
     393             :  * deleteDependencyRecordsForSpecific -- delete all records with given depender
     394             :  * classId/objectId, dependee classId/objectId, of the given deptype.
     395             :  * Returns the number of records deleted.
     396             :  */
     397             : long
     398          56 : deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype,
     399             :                                    Oid refclassId, Oid refobjectId)
     400             : {
     401          56 :     long        count = 0;
     402             :     Relation    depRel;
     403             :     ScanKeyData key[2];
     404             :     SysScanDesc scan;
     405             :     HeapTuple   tup;
     406             : 
     407          56 :     depRel = table_open(DependRelationId, RowExclusiveLock);
     408             : 
     409          56 :     ScanKeyInit(&key[0],
     410             :                 Anum_pg_depend_classid,
     411             :                 BTEqualStrategyNumber, F_OIDEQ,
     412             :                 ObjectIdGetDatum(classId));
     413          56 :     ScanKeyInit(&key[1],
     414             :                 Anum_pg_depend_objid,
     415             :                 BTEqualStrategyNumber, F_OIDEQ,
     416             :                 ObjectIdGetDatum(objectId));
     417             : 
     418          56 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     419             :                               NULL, 2, key);
     420             : 
     421         316 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     422             :     {
     423         260 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     424             : 
     425         260 :         if (depform->refclassid == refclassId &&
     426          56 :             depform->refobjid == refobjectId &&
     427          56 :             depform->deptype == deptype)
     428             :         {
     429          56 :             CatalogTupleDelete(depRel, &tup->t_self);
     430          56 :             count++;
     431             :         }
     432             :     }
     433             : 
     434          56 :     systable_endscan(scan);
     435             : 
     436          56 :     table_close(depRel, RowExclusiveLock);
     437             : 
     438          56 :     return count;
     439             : }
     440             : 
     441             : /*
     442             :  * Adjust dependency record(s) to point to a different object of the same type
     443             :  *
     444             :  * classId/objectId specify the referencing object.
     445             :  * refClassId/oldRefObjectId specify the old referenced object.
     446             :  * newRefObjectId is the new referenced object (must be of class refClassId).
     447             :  *
     448             :  * Note the lack of objsubid parameters.  If there are subobject references
     449             :  * they will all be readjusted.  Also, there is an expectation that we are
     450             :  * dealing with NORMAL dependencies: if we have to replace an (implicit)
     451             :  * dependency on a pinned object with an explicit dependency on an unpinned
     452             :  * one, the new one will be NORMAL.
     453             :  *
     454             :  * Returns the number of records updated -- zero indicates a problem.
     455             :  */
     456             : long
     457         374 : changeDependencyFor(Oid classId, Oid objectId,
     458             :                     Oid refClassId, Oid oldRefObjectId,
     459             :                     Oid newRefObjectId)
     460             : {
     461         374 :     long        count = 0;
     462             :     Relation    depRel;
     463             :     ScanKeyData key[2];
     464             :     SysScanDesc scan;
     465             :     HeapTuple   tup;
     466             :     ObjectAddress objAddr;
     467             :     ObjectAddress depAddr;
     468             :     bool        oldIsPinned;
     469             :     bool        newIsPinned;
     470             : 
     471             :     /*
     472             :      * Check to see if either oldRefObjectId or newRefObjectId is pinned.
     473             :      * Pinned objects should not have any dependency entries pointing to them,
     474             :      * so in these cases we should add or remove a pg_depend entry, or do
     475             :      * nothing at all, rather than update an entry as in the normal case.
     476             :      */
     477         374 :     objAddr.classId = refClassId;
     478         374 :     objAddr.objectId = oldRefObjectId;
     479         374 :     objAddr.objectSubId = 0;
     480             : 
     481         374 :     oldIsPinned = isObjectPinned(&objAddr);
     482             : 
     483         374 :     objAddr.objectId = newRefObjectId;
     484             : 
     485         374 :     newIsPinned = isObjectPinned(&objAddr);
     486             : 
     487         374 :     if (oldIsPinned)
     488             :     {
     489             :         /*
     490             :          * If both are pinned, we need do nothing.  However, return 1 not 0,
     491             :          * else callers will think this is an error case.
     492             :          */
     493          56 :         if (newIsPinned)
     494           0 :             return 1;
     495             : 
     496             :         /*
     497             :          * There is no old dependency record, but we should insert a new one.
     498             :          * Assume a normal dependency is wanted.
     499             :          */
     500          56 :         depAddr.classId = classId;
     501          56 :         depAddr.objectId = objectId;
     502          56 :         depAddr.objectSubId = 0;
     503          56 :         recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
     504             : 
     505          56 :         return 1;
     506             :     }
     507             : 
     508         318 :     depRel = table_open(DependRelationId, RowExclusiveLock);
     509             : 
     510             :     /* There should be existing dependency record(s), so search. */
     511         318 :     ScanKeyInit(&key[0],
     512             :                 Anum_pg_depend_classid,
     513             :                 BTEqualStrategyNumber, F_OIDEQ,
     514             :                 ObjectIdGetDatum(classId));
     515         318 :     ScanKeyInit(&key[1],
     516             :                 Anum_pg_depend_objid,
     517             :                 BTEqualStrategyNumber, F_OIDEQ,
     518             :                 ObjectIdGetDatum(objectId));
     519             : 
     520         318 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     521             :                               NULL, 2, key);
     522             : 
     523         862 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     524             :     {
     525         544 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     526             : 
     527         544 :         if (depform->refclassid == refClassId &&
     528         318 :             depform->refobjid == oldRefObjectId)
     529             :         {
     530         318 :             if (newIsPinned)
     531          66 :                 CatalogTupleDelete(depRel, &tup->t_self);
     532             :             else
     533             :             {
     534             :                 /* make a modifiable copy */
     535         252 :                 tup = heap_copytuple(tup);
     536         252 :                 depform = (Form_pg_depend) GETSTRUCT(tup);
     537             : 
     538         252 :                 depform->refobjid = newRefObjectId;
     539             : 
     540         252 :                 CatalogTupleUpdate(depRel, &tup->t_self, tup);
     541             : 
     542         252 :                 heap_freetuple(tup);
     543             :             }
     544             : 
     545         318 :             count++;
     546             :         }
     547             :     }
     548             : 
     549         318 :     systable_endscan(scan);
     550             : 
     551         318 :     table_close(depRel, RowExclusiveLock);
     552             : 
     553         318 :     return count;
     554             : }
     555             : 
     556             : /*
     557             :  * Adjust all dependency records to come from a different object of the same type
     558             :  *
     559             :  * classId/oldObjectId specify the old referencing object.
     560             :  * newObjectId is the new referencing object (must be of class classId).
     561             :  *
     562             :  * Returns the number of records updated.
     563             :  */
     564             : long
     565        1004 : changeDependenciesOf(Oid classId, Oid oldObjectId,
     566             :                      Oid newObjectId)
     567             : {
     568        1004 :     long        count = 0;
     569             :     Relation    depRel;
     570             :     ScanKeyData key[2];
     571             :     SysScanDesc scan;
     572             :     HeapTuple   tup;
     573             : 
     574        1004 :     depRel = table_open(DependRelationId, RowExclusiveLock);
     575             : 
     576        1004 :     ScanKeyInit(&key[0],
     577             :                 Anum_pg_depend_classid,
     578             :                 BTEqualStrategyNumber, F_OIDEQ,
     579             :                 ObjectIdGetDatum(classId));
     580        1004 :     ScanKeyInit(&key[1],
     581             :                 Anum_pg_depend_objid,
     582             :                 BTEqualStrategyNumber, F_OIDEQ,
     583             :                 ObjectIdGetDatum(oldObjectId));
     584             : 
     585        1004 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     586             :                               NULL, 2, key);
     587             : 
     588        2788 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     589             :     {
     590             :         Form_pg_depend depform;
     591             : 
     592             :         /* make a modifiable copy */
     593        1784 :         tup = heap_copytuple(tup);
     594        1784 :         depform = (Form_pg_depend) GETSTRUCT(tup);
     595             : 
     596        1784 :         depform->objid = newObjectId;
     597             : 
     598        1784 :         CatalogTupleUpdate(depRel, &tup->t_self, tup);
     599             : 
     600        1784 :         heap_freetuple(tup);
     601             : 
     602        1784 :         count++;
     603             :     }
     604             : 
     605        1004 :     systable_endscan(scan);
     606             : 
     607        1004 :     table_close(depRel, RowExclusiveLock);
     608             : 
     609        1004 :     return count;
     610             : }
     611             : 
     612             : /*
     613             :  * Adjust all dependency records to point to a different object of the same type
     614             :  *
     615             :  * refClassId/oldRefObjectId specify the old referenced object.
     616             :  * newRefObjectId is the new referenced object (must be of class refClassId).
     617             :  *
     618             :  * Returns the number of records updated.
     619             :  */
     620             : long
     621        1004 : changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
     622             :                      Oid newRefObjectId)
     623             : {
     624        1004 :     long        count = 0;
     625             :     Relation    depRel;
     626             :     ScanKeyData key[2];
     627             :     SysScanDesc scan;
     628             :     HeapTuple   tup;
     629             :     ObjectAddress objAddr;
     630             :     bool        newIsPinned;
     631             : 
     632        1004 :     depRel = table_open(DependRelationId, RowExclusiveLock);
     633             : 
     634             :     /*
     635             :      * If oldRefObjectId is pinned, there won't be any dependency entries on
     636             :      * it --- we can't cope in that case.  (This isn't really worth expending
     637             :      * code to fix, in current usage; it just means you can't rename stuff out
     638             :      * of pg_catalog, which would likely be a bad move anyway.)
     639             :      */
     640        1004 :     objAddr.classId = refClassId;
     641        1004 :     objAddr.objectId = oldRefObjectId;
     642        1004 :     objAddr.objectSubId = 0;
     643             : 
     644        1004 :     if (isObjectPinned(&objAddr))
     645           0 :         ereport(ERROR,
     646             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     647             :                  errmsg("cannot remove dependency on %s because it is a system object",
     648             :                         getObjectDescription(&objAddr, false))));
     649             : 
     650             :     /*
     651             :      * We can handle adding a dependency on something pinned, though, since
     652             :      * that just means deleting the dependency entry.
     653             :      */
     654        1004 :     objAddr.objectId = newRefObjectId;
     655             : 
     656        1004 :     newIsPinned = isObjectPinned(&objAddr);
     657             : 
     658             :     /* Now search for dependency records */
     659        1004 :     ScanKeyInit(&key[0],
     660             :                 Anum_pg_depend_refclassid,
     661             :                 BTEqualStrategyNumber, F_OIDEQ,
     662             :                 ObjectIdGetDatum(refClassId));
     663        1004 :     ScanKeyInit(&key[1],
     664             :                 Anum_pg_depend_refobjid,
     665             :                 BTEqualStrategyNumber, F_OIDEQ,
     666             :                 ObjectIdGetDatum(oldRefObjectId));
     667             : 
     668        1004 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
     669             :                               NULL, 2, key);
     670             : 
     671        1022 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     672             :     {
     673          18 :         if (newIsPinned)
     674           0 :             CatalogTupleDelete(depRel, &tup->t_self);
     675             :         else
     676             :         {
     677             :             Form_pg_depend depform;
     678             : 
     679             :             /* make a modifiable copy */
     680          18 :             tup = heap_copytuple(tup);
     681          18 :             depform = (Form_pg_depend) GETSTRUCT(tup);
     682             : 
     683          18 :             depform->refobjid = newRefObjectId;
     684             : 
     685          18 :             CatalogTupleUpdate(depRel, &tup->t_self, tup);
     686             : 
     687          18 :             heap_freetuple(tup);
     688             :         }
     689             : 
     690          18 :         count++;
     691             :     }
     692             : 
     693        1004 :     systable_endscan(scan);
     694             : 
     695        1004 :     table_close(depRel, RowExclusiveLock);
     696             : 
     697        1004 :     return count;
     698             : }
     699             : 
     700             : /*
     701             :  * isObjectPinned()
     702             :  *
     703             :  * Test if an object is required for basic database functionality.
     704             :  *
     705             :  * The passed subId, if any, is ignored; we assume that only whole objects
     706             :  * are pinned (and that this implies pinning their components).
     707             :  */
     708             : static bool
     709     1977818 : isObjectPinned(const ObjectAddress *object)
     710             : {
     711     1977818 :     return IsPinnedObject(object->classId, object->objectId);
     712             : }
     713             : 
     714             : 
     715             : /*
     716             :  * Various special-purpose lookups and manipulations of pg_depend.
     717             :  */
     718             : 
     719             : 
     720             : /*
     721             :  * Find the extension containing the specified object, if any
     722             :  *
     723             :  * Returns the OID of the extension, or InvalidOid if the object does not
     724             :  * belong to any extension.
     725             :  *
     726             :  * Extension membership is marked by an EXTENSION dependency from the object
     727             :  * to the extension.  Note that the result will be indeterminate if pg_depend
     728             :  * contains links from this object to more than one extension ... but that
     729             :  * should never happen.
     730             :  */
     731             : Oid
     732        1220 : getExtensionOfObject(Oid classId, Oid objectId)
     733             : {
     734        1220 :     Oid         result = InvalidOid;
     735             :     Relation    depRel;
     736             :     ScanKeyData key[2];
     737             :     SysScanDesc scan;
     738             :     HeapTuple   tup;
     739             : 
     740        1220 :     depRel = table_open(DependRelationId, AccessShareLock);
     741             : 
     742        1220 :     ScanKeyInit(&key[0],
     743             :                 Anum_pg_depend_classid,
     744             :                 BTEqualStrategyNumber, F_OIDEQ,
     745             :                 ObjectIdGetDatum(classId));
     746        1220 :     ScanKeyInit(&key[1],
     747             :                 Anum_pg_depend_objid,
     748             :                 BTEqualStrategyNumber, F_OIDEQ,
     749             :                 ObjectIdGetDatum(objectId));
     750             : 
     751        1220 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     752             :                               NULL, 2, key);
     753             : 
     754        2988 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     755             :     {
     756        2840 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     757             : 
     758        2840 :         if (depform->refclassid == ExtensionRelationId &&
     759        1072 :             depform->deptype == DEPENDENCY_EXTENSION)
     760             :         {
     761        1072 :             result = depform->refobjid;
     762        1072 :             break;              /* no need to keep scanning */
     763             :         }
     764             :     }
     765             : 
     766        1220 :     systable_endscan(scan);
     767             : 
     768        1220 :     table_close(depRel, AccessShareLock);
     769             : 
     770        1220 :     return result;
     771             : }
     772             : 
     773             : /*
     774             :  * Return (possibly NIL) list of extensions that the given object depends on
     775             :  * in DEPENDENCY_AUTO_EXTENSION mode.
     776             :  */
     777             : List *
     778          38 : getAutoExtensionsOfObject(Oid classId, Oid objectId)
     779             : {
     780          38 :     List       *result = NIL;
     781             :     Relation    depRel;
     782             :     ScanKeyData key[2];
     783             :     SysScanDesc scan;
     784             :     HeapTuple   tup;
     785             : 
     786          38 :     depRel = table_open(DependRelationId, AccessShareLock);
     787             : 
     788          38 :     ScanKeyInit(&key[0],
     789             :                 Anum_pg_depend_classid,
     790             :                 BTEqualStrategyNumber, F_OIDEQ,
     791             :                 ObjectIdGetDatum(classId));
     792          38 :     ScanKeyInit(&key[1],
     793             :                 Anum_pg_depend_objid,
     794             :                 BTEqualStrategyNumber, F_OIDEQ,
     795             :                 ObjectIdGetDatum(objectId));
     796             : 
     797          38 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     798             :                               NULL, 2, key);
     799             : 
     800          96 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     801             :     {
     802          58 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     803             : 
     804          58 :         if (depform->refclassid == ExtensionRelationId &&
     805           2 :             depform->deptype == DEPENDENCY_AUTO_EXTENSION)
     806           2 :             result = lappend_oid(result, depform->refobjid);
     807             :     }
     808             : 
     809          38 :     systable_endscan(scan);
     810             : 
     811          38 :     table_close(depRel, AccessShareLock);
     812             : 
     813          38 :     return result;
     814             : }
     815             : 
     816             : /*
     817             :  * Detect whether a sequence is marked as "owned" by a column
     818             :  *
     819             :  * An ownership marker is an AUTO or INTERNAL dependency from the sequence to the
     820             :  * column.  If we find one, store the identity of the owning column
     821             :  * into *tableId and *colId and return true; else return false.
     822             :  *
     823             :  * Note: if there's more than one such pg_depend entry then you get
     824             :  * a random one of them returned into the out parameters.  This should
     825             :  * not happen, though.
     826             :  */
     827             : bool
     828         808 : sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
     829             : {
     830         808 :     bool        ret = false;
     831             :     Relation    depRel;
     832             :     ScanKeyData key[2];
     833             :     SysScanDesc scan;
     834             :     HeapTuple   tup;
     835             : 
     836         808 :     depRel = table_open(DependRelationId, AccessShareLock);
     837             : 
     838         808 :     ScanKeyInit(&key[0],
     839             :                 Anum_pg_depend_classid,
     840             :                 BTEqualStrategyNumber, F_OIDEQ,
     841             :                 ObjectIdGetDatum(RelationRelationId));
     842         808 :     ScanKeyInit(&key[1],
     843             :                 Anum_pg_depend_objid,
     844             :                 BTEqualStrategyNumber, F_OIDEQ,
     845             :                 ObjectIdGetDatum(seqId));
     846             : 
     847         808 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     848             :                               NULL, 2, key);
     849             : 
     850        1620 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     851             :     {
     852         824 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     853             : 
     854         824 :         if (depform->refclassid == RelationRelationId &&
     855          12 :             depform->deptype == deptype)
     856             :         {
     857          12 :             *tableId = depform->refobjid;
     858          12 :             *colId = depform->refobjsubid;
     859          12 :             ret = true;
     860          12 :             break;              /* no need to keep scanning */
     861             :         }
     862             :     }
     863             : 
     864         808 :     systable_endscan(scan);
     865             : 
     866         808 :     table_close(depRel, AccessShareLock);
     867             : 
     868         808 :     return ret;
     869             : }
     870             : 
     871             : /*
     872             :  * Collect a list of OIDs of all sequences owned by the specified relation,
     873             :  * and column if specified.  If deptype is not zero, then only find sequences
     874             :  * with the specified dependency type.
     875             :  */
     876             : static List *
     877         706 : getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)
     878             : {
     879         706 :     List       *result = NIL;
     880             :     Relation    depRel;
     881             :     ScanKeyData key[3];
     882             :     SysScanDesc scan;
     883             :     HeapTuple   tup;
     884             : 
     885         706 :     depRel = table_open(DependRelationId, AccessShareLock);
     886             : 
     887         706 :     ScanKeyInit(&key[0],
     888             :                 Anum_pg_depend_refclassid,
     889             :                 BTEqualStrategyNumber, F_OIDEQ,
     890             :                 ObjectIdGetDatum(RelationRelationId));
     891         706 :     ScanKeyInit(&key[1],
     892             :                 Anum_pg_depend_refobjid,
     893             :                 BTEqualStrategyNumber, F_OIDEQ,
     894             :                 ObjectIdGetDatum(relid));
     895         706 :     if (attnum)
     896         604 :         ScanKeyInit(&key[2],
     897             :                     Anum_pg_depend_refobjsubid,
     898             :                     BTEqualStrategyNumber, F_INT4EQ,
     899             :                     Int32GetDatum(attnum));
     900             : 
     901         706 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
     902             :                               NULL, attnum ? 3 : 2, key);
     903             : 
     904        2354 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     905             :     {
     906        1648 :         Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
     907             : 
     908             :         /*
     909             :          * We assume any auto or internal dependency of a sequence on a column
     910             :          * must be what we are looking for.  (We need the relkind test because
     911             :          * indexes can also have auto dependencies on columns.)
     912             :          */
     913        1648 :         if (deprec->classid == RelationRelationId &&
     914         716 :             deprec->objsubid == 0 &&
     915         716 :             deprec->refobjsubid != 0 &&
     916        1372 :             (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
     917         686 :             get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
     918             :         {
     919         682 :             if (!deptype || deprec->deptype == deptype)
     920         676 :                 result = lappend_oid(result, deprec->objid);
     921             :         }
     922             :     }
     923             : 
     924         706 :     systable_endscan(scan);
     925             : 
     926         706 :     table_close(depRel, AccessShareLock);
     927             : 
     928         706 :     return result;
     929             : }
     930             : 
     931             : /*
     932             :  * Collect a list of OIDs of all sequences owned (identity or serial) by the
     933             :  * specified relation.
     934             :  */
     935             : List *
     936         102 : getOwnedSequences(Oid relid)
     937             : {
     938         102 :     return getOwnedSequences_internal(relid, 0, 0);
     939             : }
     940             : 
     941             : /*
     942             :  * Get owned identity sequence, error if not exactly one.
     943             :  */
     944             : Oid
     945         604 : getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
     946             : {
     947         604 :     Oid         relid = RelationGetRelid(rel);
     948             :     List       *seqlist;
     949             : 
     950             :     /*
     951             :      * The identity sequence is associated with the topmost partitioned table,
     952             :      * which might have column order different than the given partition.
     953             :      */
     954         604 :     if (RelationGetForm(rel)->relispartition)
     955             :     {
     956          54 :         List       *ancestors = get_partition_ancestors(relid);
     957          54 :         const char *attname = get_attname(relid, attnum, false);
     958             : 
     959          54 :         relid = llast_oid(ancestors);
     960          54 :         attnum = get_attnum(relid, attname);
     961          54 :         if (attnum == InvalidAttrNumber)
     962           0 :             elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
     963             :                  attname, relid);
     964          54 :         list_free(ancestors);
     965             :     }
     966             : 
     967         604 :     seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL);
     968         604 :     if (list_length(seqlist) > 1)
     969           0 :         elog(ERROR, "more than one owned sequence found");
     970         604 :     else if (seqlist == NIL)
     971             :     {
     972          12 :         if (missing_ok)
     973          12 :             return InvalidOid;
     974             :         else
     975           0 :             elog(ERROR, "no owned sequence found");
     976             :     }
     977             : 
     978         592 :     return linitial_oid(seqlist);
     979             : }
     980             : 
     981             : /*
     982             :  * get_index_constraint
     983             :  *      Given the OID of an index, return the OID of the owning unique,
     984             :  *      primary-key, or exclusion constraint, or InvalidOid if there
     985             :  *      is no owning constraint.
     986             :  */
     987             : Oid
     988       12530 : get_index_constraint(Oid indexId)
     989             : {
     990       12530 :     Oid         constraintId = InvalidOid;
     991             :     Relation    depRel;
     992             :     ScanKeyData key[3];
     993             :     SysScanDesc scan;
     994             :     HeapTuple   tup;
     995             : 
     996             :     /* Search the dependency table for the index */
     997       12530 :     depRel = table_open(DependRelationId, AccessShareLock);
     998             : 
     999       12530 :     ScanKeyInit(&key[0],
    1000             :                 Anum_pg_depend_classid,
    1001             :                 BTEqualStrategyNumber, F_OIDEQ,
    1002             :                 ObjectIdGetDatum(RelationRelationId));
    1003       12530 :     ScanKeyInit(&key[1],
    1004             :                 Anum_pg_depend_objid,
    1005             :                 BTEqualStrategyNumber, F_OIDEQ,
    1006             :                 ObjectIdGetDatum(indexId));
    1007       12530 :     ScanKeyInit(&key[2],
    1008             :                 Anum_pg_depend_objsubid,
    1009             :                 BTEqualStrategyNumber, F_INT4EQ,
    1010             :                 Int32GetDatum(0));
    1011             : 
    1012       12530 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
    1013             :                               NULL, 3, key);
    1014             : 
    1015       15474 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
    1016             :     {
    1017        4432 :         Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
    1018             : 
    1019             :         /*
    1020             :          * We assume any internal dependency on a constraint must be what we
    1021             :          * are looking for.
    1022             :          */
    1023        4432 :         if (deprec->refclassid == ConstraintRelationId &&
    1024        1488 :             deprec->refobjsubid == 0 &&
    1025        1488 :             deprec->deptype == DEPENDENCY_INTERNAL)
    1026             :         {
    1027        1488 :             constraintId = deprec->refobjid;
    1028        1488 :             break;
    1029             :         }
    1030             :     }
    1031             : 
    1032       12530 :     systable_endscan(scan);
    1033       12530 :     table_close(depRel, AccessShareLock);
    1034             : 
    1035       12530 :     return constraintId;
    1036             : }
    1037             : 
    1038             : /*
    1039             :  * get_index_ref_constraints
    1040             :  *      Given the OID of an index, return the OID of all foreign key
    1041             :  *      constraints which reference the index.
    1042             :  */
    1043             : List *
    1044         502 : get_index_ref_constraints(Oid indexId)
    1045             : {
    1046         502 :     List       *result = NIL;
    1047             :     Relation    depRel;
    1048             :     ScanKeyData key[3];
    1049             :     SysScanDesc scan;
    1050             :     HeapTuple   tup;
    1051             : 
    1052             :     /* Search the dependency table for the index */
    1053         502 :     depRel = table_open(DependRelationId, AccessShareLock);
    1054             : 
    1055         502 :     ScanKeyInit(&key[0],
    1056             :                 Anum_pg_depend_refclassid,
    1057             :                 BTEqualStrategyNumber, F_OIDEQ,
    1058             :                 ObjectIdGetDatum(RelationRelationId));
    1059         502 :     ScanKeyInit(&key[1],
    1060             :                 Anum_pg_depend_refobjid,
    1061             :                 BTEqualStrategyNumber, F_OIDEQ,
    1062             :                 ObjectIdGetDatum(indexId));
    1063         502 :     ScanKeyInit(&key[2],
    1064             :                 Anum_pg_depend_refobjsubid,
    1065             :                 BTEqualStrategyNumber, F_INT4EQ,
    1066             :                 Int32GetDatum(0));
    1067             : 
    1068         502 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
    1069             :                               NULL, 3, key);
    1070             : 
    1071         520 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
    1072             :     {
    1073          18 :         Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
    1074             : 
    1075             :         /*
    1076             :          * We assume any normal dependency from a constraint must be what we
    1077             :          * are looking for.
    1078             :          */
    1079          18 :         if (deprec->classid == ConstraintRelationId &&
    1080          18 :             deprec->objsubid == 0 &&
    1081          18 :             deprec->deptype == DEPENDENCY_NORMAL)
    1082             :         {
    1083          18 :             result = lappend_oid(result, deprec->objid);
    1084             :         }
    1085             :     }
    1086             : 
    1087         502 :     systable_endscan(scan);
    1088         502 :     table_close(depRel, AccessShareLock);
    1089             : 
    1090         502 :     return result;
    1091             : }

Generated by: LCOV version 1.14