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

Generated by: LCOV version 1.16