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

Generated by: LCOV version 1.14