LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_depend.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 97.3 % 331 322
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 20 20
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       428037 : recordDependencyOn(const ObjectAddress *depender,
      48              :                    const ObjectAddress *referenced,
      49              :                    DependencyType behavior)
      50              : {
      51       428037 :     recordMultipleDependencies(depender, referenced, 1, behavior);
      52       428037 : }
      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       655720 : 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       655720 :     if (nreferenced <= 0)
      73        26221 :         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       629499 :     if (IsBootstrapProcessingMode())
      83        34527 :         return;
      84              : 
      85       594972 :     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       594972 :     max_slots = Min(nreferenced,
      92              :                     MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
      93       594972 :     slot = palloc_array(TupleTableSlot *, max_slots);
      94              : 
      95              :     /* Don't open indexes unless we need to make an update */
      96       594972 :     indstate = NULL;
      97              : 
      98              :     /* number of slots currently storing tuples */
      99       594972 :     slot_stored_count = 0;
     100              :     /* number of slots currently initialized */
     101       594972 :     slot_init_count = 0;
     102      1729566 :     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      1134594 :         if (isObjectPinned(referenced))
     110       827414 :             continue;
     111              : 
     112       307180 :         if (slot_init_count < max_slots)
     113              :         {
     114       307180 :             slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
     115              :                                                                &TTSOpsHeapTuple);
     116       307180 :             slot_init_count++;
     117              :         }
     118              : 
     119       307180 :         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       307180 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
     126       307180 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
     127       307180 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
     128       307180 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
     129       307180 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
     130       307180 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
     131       307180 :         slot[slot_stored_count]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
     132              : 
     133       307180 :         memset(slot[slot_stored_count]->tts_isnull, false,
     134       307180 :                slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
     135              : 
     136       307180 :         ExecStoreVirtualTuple(slot[slot_stored_count]);
     137       307180 :         slot_stored_count++;
     138              : 
     139              :         /* If slots are full, insert a batch of tuples */
     140       307180 :         if (slot_stored_count == max_slots)
     141              :         {
     142              :             /* fetch index info only when we know we need it */
     143       214355 :             if (indstate == NULL)
     144       214355 :                 indstate = CatalogOpenIndexes(dependDesc);
     145              : 
     146       214355 :             CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
     147              :                                              indstate);
     148       214355 :             slot_stored_count = 0;
     149              :         }
     150              :     }
     151              : 
     152              :     /* Insert any tuples left in the buffer */
     153       594972 :     if (slot_stored_count > 0)
     154              :     {
     155              :         /* fetch index info only when we know we need it */
     156        48450 :         if (indstate == NULL)
     157        48450 :             indstate = CatalogOpenIndexes(dependDesc);
     158              : 
     159        48450 :         CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
     160              :                                          indstate);
     161              :     }
     162              : 
     163       594972 :     if (indstate != NULL)
     164       262805 :         CatalogCloseIndexes(indstate);
     165              : 
     166       594972 :     table_close(dependDesc, RowExclusiveLock);
     167              : 
     168              :     /* Drop only the number of slots used */
     169       902152 :     for (i = 0; i < slot_init_count; i++)
     170       307180 :         ExecDropSingleTupleTableSlot(slot[i]);
     171       594972 :     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       167517 : recordDependencyOnCurrentExtension(const ObjectAddress *object,
     196              :                                    bool isReplace)
     197              : {
     198              :     /* Only whole objects can be extension members */
     199              :     Assert(object->objectSubId == 0);
     200              : 
     201       167517 :     if (creating_extension)
     202              :     {
     203              :         ObjectAddress extension;
     204              : 
     205              :         /* Only need to check for existing membership if isReplace */
     206         5699 :         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          397 :             oldext = getExtensionOfObject(object->classId, object->objectId);
     217          397 :             if (OidIsValid(oldext))
     218              :             {
     219              :                 /* If already a member of this extension, nothing to do */
     220          393 :                 if (oldext == CurrentExtensionObject)
     221          393 :                     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            4 :             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         5302 :         extension.classId = ExtensionRelationId;
     240         5302 :         extension.objectId = CurrentExtensionObject;
     241         5302 :         extension.objectSubId = 0;
     242              : 
     243         5302 :         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           69 : 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           69 :     if (creating_extension)
     273              :     {
     274              :         Oid         oldext;
     275              : 
     276           14 :         oldext = getExtensionOfObject(object->classId, object->objectId);
     277              :         /* If already a member of this extension, OK */
     278           14 :         if (oldext == CurrentExtensionObject)
     279            7 :             return;
     280              :         /* Else complain */
     281            7 :         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        11079 : deleteDependencyRecordsFor(Oid classId, Oid objectId,
     304              :                            bool skipExtensionDeps)
     305              : {
     306        11079 :     long        count = 0;
     307              :     Relation    depRel;
     308              :     ScanKeyData key[2];
     309              :     SysScanDesc scan;
     310              :     HeapTuple   tup;
     311              : 
     312        11079 :     depRel = table_open(DependRelationId, RowExclusiveLock);
     313              : 
     314        11079 :     ScanKeyInit(&key[0],
     315              :                 Anum_pg_depend_classid,
     316              :                 BTEqualStrategyNumber, F_OIDEQ,
     317              :                 ObjectIdGetDatum(classId));
     318        11079 :     ScanKeyInit(&key[1],
     319              :                 Anum_pg_depend_objid,
     320              :                 BTEqualStrategyNumber, F_OIDEQ,
     321              :                 ObjectIdGetDatum(objectId));
     322              : 
     323        11079 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     324              :                               NULL, 2, key);
     325              : 
     326        17898 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     327              :     {
     328         6819 :         if (skipExtensionDeps &&
     329         5294 :             ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
     330          629 :             continue;
     331              : 
     332         6190 :         CatalogTupleDelete(depRel, &tup->t_self);
     333         6190 :         count++;
     334              :     }
     335              : 
     336        11079 :     systable_endscan(scan);
     337              : 
     338        11079 :     table_close(depRel, RowExclusiveLock);
     339              : 
     340        11079 :     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         7678 : deleteDependencyRecordsForClass(Oid classId, Oid objectId,
     354              :                                 Oid refclassId, char deptype)
     355              : {
     356         7678 :     long        count = 0;
     357              :     Relation    depRel;
     358              :     ScanKeyData key[2];
     359              :     SysScanDesc scan;
     360              :     HeapTuple   tup;
     361              : 
     362         7678 :     depRel = table_open(DependRelationId, RowExclusiveLock);
     363              : 
     364         7678 :     ScanKeyInit(&key[0],
     365              :                 Anum_pg_depend_classid,
     366              :                 BTEqualStrategyNumber, F_OIDEQ,
     367              :                 ObjectIdGetDatum(classId));
     368         7678 :     ScanKeyInit(&key[1],
     369              :                 Anum_pg_depend_objid,
     370              :                 BTEqualStrategyNumber, F_OIDEQ,
     371              :                 ObjectIdGetDatum(objectId));
     372              : 
     373         7678 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     374              :                               NULL, 2, key);
     375              : 
     376        12619 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     377              :     {
     378         4941 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     379              : 
     380         4941 :         if (depform->refclassid == refclassId && depform->deptype == deptype)
     381              :         {
     382         1109 :             CatalogTupleDelete(depRel, &tup->t_self);
     383         1109 :             count++;
     384              :         }
     385              :     }
     386              : 
     387         7678 :     systable_endscan(scan);
     388              : 
     389         7678 :     table_close(depRel, RowExclusiveLock);
     390              : 
     391         7678 :     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           43 : deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype,
     401              :                                    Oid refclassId, Oid refobjectId)
     402              : {
     403           43 :     long        count = 0;
     404              :     Relation    depRel;
     405              :     ScanKeyData key[2];
     406              :     SysScanDesc scan;
     407              :     HeapTuple   tup;
     408              : 
     409           43 :     depRel = table_open(DependRelationId, RowExclusiveLock);
     410              : 
     411           43 :     ScanKeyInit(&key[0],
     412              :                 Anum_pg_depend_classid,
     413              :                 BTEqualStrategyNumber, F_OIDEQ,
     414              :                 ObjectIdGetDatum(classId));
     415           43 :     ScanKeyInit(&key[1],
     416              :                 Anum_pg_depend_objid,
     417              :                 BTEqualStrategyNumber, F_OIDEQ,
     418              :                 ObjectIdGetDatum(objectId));
     419              : 
     420           43 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     421              :                               NULL, 2, key);
     422              : 
     423          233 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     424              :     {
     425          190 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     426              : 
     427          190 :         if (depform->refclassid == refclassId &&
     428           43 :             depform->refobjid == refobjectId &&
     429           43 :             depform->deptype == deptype)
     430              :         {
     431           43 :             CatalogTupleDelete(depRel, &tup->t_self);
     432           43 :             count++;
     433              :         }
     434              :     }
     435              : 
     436           43 :     systable_endscan(scan);
     437              : 
     438           43 :     table_close(depRel, RowExclusiveLock);
     439              : 
     440           43 :     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          187 : changeDependencyFor(Oid classId, Oid objectId,
     460              :                     Oid refClassId, Oid oldRefObjectId,
     461              :                     Oid newRefObjectId)
     462              : {
     463          187 :     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          187 :     objAddr.classId = refClassId;
     480          187 :     objAddr.objectId = oldRefObjectId;
     481          187 :     objAddr.objectSubId = 0;
     482              : 
     483          187 :     oldIsPinned = isObjectPinned(&objAddr);
     484              : 
     485          187 :     objAddr.objectId = newRefObjectId;
     486              : 
     487          187 :     newIsPinned = isObjectPinned(&objAddr);
     488              : 
     489          187 :     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           28 :         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           28 :         depAddr.classId = classId;
     503           28 :         depAddr.objectId = objectId;
     504           28 :         depAddr.objectSubId = 0;
     505           28 :         recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
     506              : 
     507           28 :         return 1;
     508              :     }
     509              : 
     510          159 :     depRel = table_open(DependRelationId, RowExclusiveLock);
     511              : 
     512              :     /* There should be existing dependency record(s), so search. */
     513          159 :     ScanKeyInit(&key[0],
     514              :                 Anum_pg_depend_classid,
     515              :                 BTEqualStrategyNumber, F_OIDEQ,
     516              :                 ObjectIdGetDatum(classId));
     517          159 :     ScanKeyInit(&key[1],
     518              :                 Anum_pg_depend_objid,
     519              :                 BTEqualStrategyNumber, F_OIDEQ,
     520              :                 ObjectIdGetDatum(objectId));
     521              : 
     522          159 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     523              :                               NULL, 2, key);
     524              : 
     525          425 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     526              :     {
     527          266 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     528              : 
     529          266 :         if (depform->refclassid == refClassId &&
     530          159 :             depform->refobjid == oldRefObjectId)
     531              :         {
     532          159 :             if (newIsPinned)
     533           33 :                 CatalogTupleDelete(depRel, &tup->t_self);
     534              :             else
     535              :             {
     536              :                 /* make a modifiable copy */
     537          126 :                 tup = heap_copytuple(tup);
     538          126 :                 depform = (Form_pg_depend) GETSTRUCT(tup);
     539              : 
     540          126 :                 depform->refobjid = newRefObjectId;
     541              : 
     542          126 :                 CatalogTupleUpdate(depRel, &tup->t_self, tup);
     543              : 
     544          126 :                 heap_freetuple(tup);
     545              :             }
     546              : 
     547          159 :             count++;
     548              :         }
     549              :     }
     550              : 
     551          159 :     systable_endscan(scan);
     552              : 
     553          159 :     table_close(depRel, RowExclusiveLock);
     554              : 
     555          159 :     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          534 : changeDependenciesOf(Oid classId, Oid oldObjectId,
     568              :                      Oid newObjectId)
     569              : {
     570          534 :     long        count = 0;
     571              :     Relation    depRel;
     572              :     ScanKeyData key[2];
     573              :     SysScanDesc scan;
     574              :     HeapTuple   tup;
     575              : 
     576          534 :     depRel = table_open(DependRelationId, RowExclusiveLock);
     577              : 
     578          534 :     ScanKeyInit(&key[0],
     579              :                 Anum_pg_depend_classid,
     580              :                 BTEqualStrategyNumber, F_OIDEQ,
     581              :                 ObjectIdGetDatum(classId));
     582          534 :     ScanKeyInit(&key[1],
     583              :                 Anum_pg_depend_objid,
     584              :                 BTEqualStrategyNumber, F_OIDEQ,
     585              :                 ObjectIdGetDatum(oldObjectId));
     586              : 
     587          534 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     588              :                               NULL, 2, key);
     589              : 
     590         1472 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     591              :     {
     592              :         Form_pg_depend depform;
     593              : 
     594              :         /* make a modifiable copy */
     595          938 :         tup = heap_copytuple(tup);
     596          938 :         depform = (Form_pg_depend) GETSTRUCT(tup);
     597              : 
     598          938 :         depform->objid = newObjectId;
     599              : 
     600          938 :         CatalogTupleUpdate(depRel, &tup->t_self, tup);
     601              : 
     602          938 :         heap_freetuple(tup);
     603              : 
     604          938 :         count++;
     605              :     }
     606              : 
     607          534 :     systable_endscan(scan);
     608              : 
     609          534 :     table_close(depRel, RowExclusiveLock);
     610              : 
     611          534 :     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          534 : changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
     624              :                      Oid newRefObjectId)
     625              : {
     626          534 :     long        count = 0;
     627              :     Relation    depRel;
     628              :     ScanKeyData key[2];
     629              :     SysScanDesc scan;
     630              :     HeapTuple   tup;
     631              :     ObjectAddress objAddr;
     632              :     bool        newIsPinned;
     633              : 
     634          534 :     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          534 :     objAddr.classId = refClassId;
     643          534 :     objAddr.objectId = oldRefObjectId;
     644          534 :     objAddr.objectSubId = 0;
     645              : 
     646          534 :     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          534 :     objAddr.objectId = newRefObjectId;
     657              : 
     658          534 :     newIsPinned = isObjectPinned(&objAddr);
     659              : 
     660              :     /* Now search for dependency records */
     661          534 :     ScanKeyInit(&key[0],
     662              :                 Anum_pg_depend_refclassid,
     663              :                 BTEqualStrategyNumber, F_OIDEQ,
     664              :                 ObjectIdGetDatum(refClassId));
     665          534 :     ScanKeyInit(&key[1],
     666              :                 Anum_pg_depend_refobjid,
     667              :                 BTEqualStrategyNumber, F_OIDEQ,
     668              :                 ObjectIdGetDatum(oldRefObjectId));
     669              : 
     670          534 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
     671              :                               NULL, 2, key);
     672              : 
     673          543 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     674              :     {
     675            9 :         if (newIsPinned)
     676            0 :             CatalogTupleDelete(depRel, &tup->t_self);
     677              :         else
     678              :         {
     679              :             Form_pg_depend depform;
     680              : 
     681              :             /* make a modifiable copy */
     682            9 :             tup = heap_copytuple(tup);
     683            9 :             depform = (Form_pg_depend) GETSTRUCT(tup);
     684              : 
     685            9 :             depform->refobjid = newRefObjectId;
     686              : 
     687            9 :             CatalogTupleUpdate(depRel, &tup->t_self, tup);
     688              : 
     689            9 :             heap_freetuple(tup);
     690              :         }
     691              : 
     692            9 :         count++;
     693              :     }
     694              : 
     695          534 :     systable_endscan(scan);
     696              : 
     697          534 :     table_close(depRel, RowExclusiveLock);
     698              : 
     699          534 :     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      1136036 : isObjectPinned(const ObjectAddress *object)
     712              : {
     713      1136036 :     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          650 : getExtensionOfObject(Oid classId, Oid objectId)
     735              : {
     736          650 :     Oid         result = InvalidOid;
     737              :     Relation    depRel;
     738              :     ScanKeyData key[2];
     739              :     SysScanDesc scan;
     740              :     HeapTuple   tup;
     741              : 
     742          650 :     depRel = table_open(DependRelationId, AccessShareLock);
     743              : 
     744          650 :     ScanKeyInit(&key[0],
     745              :                 Anum_pg_depend_classid,
     746              :                 BTEqualStrategyNumber, F_OIDEQ,
     747              :                 ObjectIdGetDatum(classId));
     748          650 :     ScanKeyInit(&key[1],
     749              :                 Anum_pg_depend_objid,
     750              :                 BTEqualStrategyNumber, F_OIDEQ,
     751              :                 ObjectIdGetDatum(objectId));
     752              : 
     753          650 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     754              :                               NULL, 2, key);
     755              : 
     756         1548 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     757              :     {
     758         1470 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     759              : 
     760         1470 :         if (depform->refclassid == ExtensionRelationId &&
     761          572 :             depform->deptype == DEPENDENCY_EXTENSION)
     762              :         {
     763          572 :             result = depform->refobjid;
     764          572 :             break;              /* no need to keep scanning */
     765              :         }
     766              :     }
     767              : 
     768          650 :     systable_endscan(scan);
     769              : 
     770          650 :     table_close(depRel, AccessShareLock);
     771              : 
     772          650 :     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           19 : getAutoExtensionsOfObject(Oid classId, Oid objectId)
     781              : {
     782           19 :     List       *result = NIL;
     783              :     Relation    depRel;
     784              :     ScanKeyData key[2];
     785              :     SysScanDesc scan;
     786              :     HeapTuple   tup;
     787              : 
     788           19 :     depRel = table_open(DependRelationId, AccessShareLock);
     789              : 
     790           19 :     ScanKeyInit(&key[0],
     791              :                 Anum_pg_depend_classid,
     792              :                 BTEqualStrategyNumber, F_OIDEQ,
     793              :                 ObjectIdGetDatum(classId));
     794           19 :     ScanKeyInit(&key[1],
     795              :                 Anum_pg_depend_objid,
     796              :                 BTEqualStrategyNumber, F_OIDEQ,
     797              :                 ObjectIdGetDatum(objectId));
     798              : 
     799           19 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     800              :                               NULL, 2, key);
     801              : 
     802           48 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     803              :     {
     804           29 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     805              : 
     806           29 :         if (depform->refclassid == ExtensionRelationId &&
     807            1 :             depform->deptype == DEPENDENCY_AUTO_EXTENSION)
     808            1 :             result = lappend_oid(result, depform->refobjid);
     809              :     }
     810              : 
     811           19 :     systable_endscan(scan);
     812              : 
     813           19 :     table_close(depRel, AccessShareLock);
     814              : 
     815           19 :     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            1 : getExtensionType(Oid extensionOid, const char *typname)
     833              : {
     834            1 :     Oid         result = InvalidOid;
     835              :     Relation    depRel;
     836              :     ScanKeyData key[3];
     837              :     SysScanDesc scan;
     838              :     HeapTuple   tup;
     839              : 
     840            1 :     depRel = table_open(DependRelationId, AccessShareLock);
     841              : 
     842            1 :     ScanKeyInit(&key[0],
     843              :                 Anum_pg_depend_refclassid,
     844              :                 BTEqualStrategyNumber, F_OIDEQ,
     845              :                 ObjectIdGetDatum(ExtensionRelationId));
     846            1 :     ScanKeyInit(&key[1],
     847              :                 Anum_pg_depend_refobjid,
     848              :                 BTEqualStrategyNumber, F_OIDEQ,
     849              :                 ObjectIdGetDatum(extensionOid));
     850            1 :     ScanKeyInit(&key[2],
     851              :                 Anum_pg_depend_refobjsubid,
     852              :                 BTEqualStrategyNumber, F_INT4EQ,
     853              :                 Int32GetDatum(0));
     854              : 
     855            1 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
     856              :                               NULL, 3, key);
     857              : 
     858            1 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     859              :     {
     860            1 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     861              : 
     862            1 :         if (depform->classid == TypeRelationId &&
     863            1 :             depform->deptype == DEPENDENCY_EXTENSION)
     864              :         {
     865            1 :             Oid         typoid = depform->objid;
     866              :             HeapTuple   typtup;
     867              : 
     868            1 :             typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid));
     869            1 :             if (!HeapTupleIsValid(typtup))
     870            0 :                 continue;       /* should we throw an error? */
     871            1 :             if (strcmp(NameStr(((Form_pg_type) GETSTRUCT(typtup))->typname),
     872              :                        typname) == 0)
     873              :             {
     874            1 :                 result = typoid;
     875            1 :                 ReleaseSysCache(typtup);
     876            1 :                 break;          /* no need to keep searching */
     877              :             }
     878            0 :             ReleaseSysCache(typtup);
     879              :         }
     880              :     }
     881              : 
     882            1 :     systable_endscan(scan);
     883              : 
     884            1 :     table_close(depRel, AccessShareLock);
     885              : 
     886            1 :     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          441 : sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
     902              : {
     903          441 :     bool        ret = false;
     904              :     Relation    depRel;
     905              :     ScanKeyData key[2];
     906              :     SysScanDesc scan;
     907              :     HeapTuple   tup;
     908              : 
     909          441 :     depRel = table_open(DependRelationId, AccessShareLock);
     910              : 
     911          441 :     ScanKeyInit(&key[0],
     912              :                 Anum_pg_depend_classid,
     913              :                 BTEqualStrategyNumber, F_OIDEQ,
     914              :                 ObjectIdGetDatum(RelationRelationId));
     915          441 :     ScanKeyInit(&key[1],
     916              :                 Anum_pg_depend_objid,
     917              :                 BTEqualStrategyNumber, F_OIDEQ,
     918              :                 ObjectIdGetDatum(seqId));
     919              : 
     920          441 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
     921              :                               NULL, 2, key);
     922              : 
     923          884 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     924              :     {
     925          449 :         Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
     926              : 
     927          449 :         if (depform->refclassid == RelationRelationId &&
     928            6 :             depform->deptype == deptype)
     929              :         {
     930            6 :             *tableId = depform->refobjid;
     931            6 :             *colId = depform->refobjsubid;
     932            6 :             ret = true;
     933            6 :             break;              /* no need to keep scanning */
     934              :         }
     935              :     }
     936              : 
     937          441 :     systable_endscan(scan);
     938              : 
     939          441 :     table_close(depRel, AccessShareLock);
     940              : 
     941          441 :     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          372 : getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)
     951              : {
     952          372 :     List       *result = NIL;
     953              :     Relation    depRel;
     954              :     ScanKeyData key[3];
     955              :     SysScanDesc scan;
     956              :     HeapTuple   tup;
     957              : 
     958          372 :     depRel = table_open(DependRelationId, AccessShareLock);
     959              : 
     960          372 :     ScanKeyInit(&key[0],
     961              :                 Anum_pg_depend_refclassid,
     962              :                 BTEqualStrategyNumber, F_OIDEQ,
     963              :                 ObjectIdGetDatum(RelationRelationId));
     964          372 :     ScanKeyInit(&key[1],
     965              :                 Anum_pg_depend_refobjid,
     966              :                 BTEqualStrategyNumber, F_OIDEQ,
     967              :                 ObjectIdGetDatum(relid));
     968          372 :     if (attnum)
     969          322 :         ScanKeyInit(&key[2],
     970              :                     Anum_pg_depend_refobjsubid,
     971              :                     BTEqualStrategyNumber, F_INT4EQ,
     972              :                     Int32GetDatum(attnum));
     973              : 
     974          372 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
     975              :                               NULL, attnum ? 3 : 2, key);
     976              : 
     977         1248 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     978              :     {
     979          876 :         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          876 :         if (deprec->classid == RelationRelationId &&
     987          377 :             deprec->objsubid == 0 &&
     988          377 :             deprec->refobjsubid != 0 &&
     989          724 :             (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
     990          362 :             get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
     991              :         {
     992          360 :             if (!deptype || deprec->deptype == deptype)
     993          357 :                 result = lappend_oid(result, deprec->objid);
     994              :         }
     995              :     }
     996              : 
     997          372 :     systable_endscan(scan);
     998              : 
     999          372 :     table_close(depRel, AccessShareLock);
    1000              : 
    1001          372 :     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           50 : getOwnedSequences(Oid relid)
    1010              : {
    1011           50 :     return getOwnedSequences_internal(relid, 0, 0);
    1012              : }
    1013              : 
    1014              : /*
    1015              :  * Get owned identity sequence, error if not exactly one.
    1016              :  */
    1017              : Oid
    1018          322 : getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
    1019              : {
    1020          322 :     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          322 :     if (RelationGetForm(rel)->relispartition)
    1028              :     {
    1029           27 :         List       *ancestors = get_partition_ancestors(relid);
    1030           27 :         const char *attname = get_attname(relid, attnum, false);
    1031              : 
    1032           27 :         relid = llast_oid(ancestors);
    1033           27 :         attnum = get_attnum(relid, attname);
    1034           27 :         if (attnum == InvalidAttrNumber)
    1035            0 :             elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
    1036              :                  attname, relid);
    1037           27 :         list_free(ancestors);
    1038              :     }
    1039              : 
    1040          322 :     seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL);
    1041          322 :     if (list_length(seqlist) > 1)
    1042            0 :         elog(ERROR, "more than one owned sequence found");
    1043          322 :     else if (seqlist == NIL)
    1044              :     {
    1045            6 :         if (missing_ok)
    1046            6 :             return InvalidOid;
    1047              :         else
    1048            0 :             elog(ERROR, "no owned sequence found");
    1049              :     }
    1050              : 
    1051          316 :     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         7117 : get_index_constraint(Oid indexId)
    1062              : {
    1063         7117 :     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         7117 :     depRel = table_open(DependRelationId, AccessShareLock);
    1071              : 
    1072         7117 :     ScanKeyInit(&key[0],
    1073              :                 Anum_pg_depend_classid,
    1074              :                 BTEqualStrategyNumber, F_OIDEQ,
    1075              :                 ObjectIdGetDatum(RelationRelationId));
    1076         7117 :     ScanKeyInit(&key[1],
    1077              :                 Anum_pg_depend_objid,
    1078              :                 BTEqualStrategyNumber, F_OIDEQ,
    1079              :                 ObjectIdGetDatum(indexId));
    1080         7117 :     ScanKeyInit(&key[2],
    1081              :                 Anum_pg_depend_objsubid,
    1082              :                 BTEqualStrategyNumber, F_INT4EQ,
    1083              :                 Int32GetDatum(0));
    1084              : 
    1085         7117 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
    1086              :                               NULL, 3, key);
    1087              : 
    1088         8633 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
    1089              :     {
    1090         2433 :         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         2433 :         if (deprec->refclassid == ConstraintRelationId &&
    1097          917 :             deprec->refobjsubid == 0 &&
    1098          917 :             deprec->deptype == DEPENDENCY_INTERNAL)
    1099              :         {
    1100          917 :             constraintId = deprec->refobjid;
    1101          917 :             break;
    1102              :         }
    1103              :     }
    1104              : 
    1105         7117 :     systable_endscan(scan);
    1106         7117 :     table_close(depRel, AccessShareLock);
    1107              : 
    1108         7117 :     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          267 : get_index_ref_constraints(Oid indexId)
    1118              : {
    1119          267 :     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          267 :     depRel = table_open(DependRelationId, AccessShareLock);
    1127              : 
    1128          267 :     ScanKeyInit(&key[0],
    1129              :                 Anum_pg_depend_refclassid,
    1130              :                 BTEqualStrategyNumber, F_OIDEQ,
    1131              :                 ObjectIdGetDatum(RelationRelationId));
    1132          267 :     ScanKeyInit(&key[1],
    1133              :                 Anum_pg_depend_refobjid,
    1134              :                 BTEqualStrategyNumber, F_OIDEQ,
    1135              :                 ObjectIdGetDatum(indexId));
    1136          267 :     ScanKeyInit(&key[2],
    1137              :                 Anum_pg_depend_refobjsubid,
    1138              :                 BTEqualStrategyNumber, F_INT4EQ,
    1139              :                 Int32GetDatum(0));
    1140              : 
    1141          267 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
    1142              :                               NULL, 3, key);
    1143              : 
    1144          276 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
    1145              :     {
    1146            9 :         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            9 :         if (deprec->classid == ConstraintRelationId &&
    1153            9 :             deprec->objsubid == 0 &&
    1154            9 :             deprec->deptype == DEPENDENCY_NORMAL)
    1155              :         {
    1156            9 :             result = lappend_oid(result, deprec->objid);
    1157              :         }
    1158              :     }
    1159              : 
    1160          267 :     systable_endscan(scan);
    1161          267 :     table_close(depRel, AccessShareLock);
    1162              : 
    1163          267 :     return result;
    1164              : }
        

Generated by: LCOV version 2.0-1