summaryrefslogtreecommitdiffstats
path: root/src/backend/catalog/pg_depend.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:46:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:46:48 +0000
commit311bcfc6b3acdd6fd152798c7f287ddf74fa2a98 (patch)
tree0ec307299b1dada3701e42f4ca6eda57d708261e /src/backend/catalog/pg_depend.c
parentInitial commit. (diff)
downloadpostgresql-15-upstream.tar.xz
postgresql-15-upstream.zip
Adding upstream version 15.4.upstream/15.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/catalog/pg_depend.c')
-rw-r--r--src/backend/catalog/pg_depend.c1071
1 files changed, 1071 insertions, 0 deletions
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
new file mode 100644
index 0000000..89bbb5c
--- /dev/null
+++ b/src/backend/catalog/pg_depend.c
@@ -0,0 +1,1071 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_depend.c
+ * routines to support manipulation of the pg_depend relation
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_depend.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/table.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_constraint.h"
+#include "catalog/pg_depend.h"
+#include "catalog/pg_extension.h"
+#include "commands/extension.h"
+#include "miscadmin.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+
+
+static bool isObjectPinned(const ObjectAddress *object);
+
+
+/*
+ * Record a dependency between 2 objects via their respective objectAddress.
+ * The first argument is the dependent object, the second the one it
+ * references.
+ *
+ * This simply creates an entry in pg_depend, without any other processing.
+ */
+void
+recordDependencyOn(const ObjectAddress *depender,
+ const ObjectAddress *referenced,
+ DependencyType behavior)
+{
+ recordMultipleDependencies(depender, referenced, 1, behavior);
+}
+
+/*
+ * Record multiple dependencies (of the same kind) for a single dependent
+ * object. This has a little less overhead than recording each separately.
+ */
+void
+recordMultipleDependencies(const ObjectAddress *depender,
+ const ObjectAddress *referenced,
+ int nreferenced,
+ DependencyType behavior)
+{
+ Relation dependDesc;
+ CatalogIndexState indstate;
+ TupleTableSlot **slot;
+ int i,
+ max_slots,
+ slot_init_count,
+ slot_stored_count;
+
+ if (nreferenced <= 0)
+ return; /* nothing to do */
+
+ /*
+ * During bootstrap, do nothing since pg_depend may not exist yet.
+ *
+ * Objects created during bootstrap are most likely pinned, and the few
+ * that are not do not have dependencies on each other, so that there
+ * would be no need to make a pg_depend entry anyway.
+ */
+ if (IsBootstrapProcessingMode())
+ return;
+
+ dependDesc = table_open(DependRelationId, RowExclusiveLock);
+
+ /*
+ * Allocate the slots to use, but delay costly initialization until we
+ * know that they will be used.
+ */
+ max_slots = Min(nreferenced,
+ MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
+ slot = palloc(sizeof(TupleTableSlot *) * max_slots);
+
+ /* Don't open indexes unless we need to make an update */
+ indstate = NULL;
+
+ /* number of slots currently storing tuples */
+ slot_stored_count = 0;
+ /* number of slots currently initialized */
+ slot_init_count = 0;
+ for (i = 0; i < nreferenced; i++, referenced++)
+ {
+ /*
+ * If the referenced object is pinned by the system, there's no real
+ * need to record dependencies on it. This saves lots of space in
+ * pg_depend, so it's worth the time taken to check.
+ */
+ if (isObjectPinned(referenced))
+ continue;
+
+ if (slot_init_count < max_slots)
+ {
+ slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
+ &TTSOpsHeapTuple);
+ slot_init_count++;
+ }
+
+ ExecClearTuple(slot[slot_stored_count]);
+
+ /*
+ * Record the dependency. Note we don't bother to check for duplicate
+ * dependencies; there's no harm in them.
+ */
+ slot[slot_stored_count]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
+ slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
+ slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
+ slot[slot_stored_count]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
+ slot[slot_stored_count]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
+ slot[slot_stored_count]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
+ slot[slot_stored_count]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
+
+ memset(slot[slot_stored_count]->tts_isnull, false,
+ slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
+
+ ExecStoreVirtualTuple(slot[slot_stored_count]);
+ slot_stored_count++;
+
+ /* If slots are full, insert a batch of tuples */
+ if (slot_stored_count == max_slots)
+ {
+ /* fetch index info only when we know we need it */
+ if (indstate == NULL)
+ indstate = CatalogOpenIndexes(dependDesc);
+
+ CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
+ indstate);
+ slot_stored_count = 0;
+ }
+ }
+
+ /* Insert any tuples left in the buffer */
+ if (slot_stored_count > 0)
+ {
+ /* fetch index info only when we know we need it */
+ if (indstate == NULL)
+ indstate = CatalogOpenIndexes(dependDesc);
+
+ CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
+ indstate);
+ }
+
+ if (indstate != NULL)
+ CatalogCloseIndexes(indstate);
+
+ table_close(dependDesc, RowExclusiveLock);
+
+ /* Drop only the number of slots used */
+ for (i = 0; i < slot_init_count; i++)
+ ExecDropSingleTupleTableSlot(slot[i]);
+ pfree(slot);
+}
+
+/*
+ * If we are executing a CREATE EXTENSION operation, mark the given object
+ * as being a member of the extension, or check that it already is one.
+ * Otherwise, do nothing.
+ *
+ * This must be called during creation of any user-definable object type
+ * that could be a member of an extension.
+ *
+ * isReplace must be true if the object already existed, and false if it is
+ * newly created. In the former case we insist that it already be a member
+ * of the current extension. In the latter case we can skip checking whether
+ * it is already a member of any extension.
+ *
+ * Note: isReplace = true is typically used when updating an object in
+ * CREATE OR REPLACE and similar commands. We used to allow the target
+ * object to not already be an extension member, instead silently absorbing
+ * it into the current extension. However, this was both error-prone
+ * (extensions might accidentally overwrite free-standing objects) and
+ * a security hazard (since the object would retain its previous ownership).
+ */
+void
+recordDependencyOnCurrentExtension(const ObjectAddress *object,
+ bool isReplace)
+{
+ /* Only whole objects can be extension members */
+ Assert(object->objectSubId == 0);
+
+ if (creating_extension)
+ {
+ ObjectAddress extension;
+
+ /* Only need to check for existing membership if isReplace */
+ if (isReplace)
+ {
+ Oid oldext;
+
+ /*
+ * Side note: these catalog lookups are safe only because the
+ * object is a pre-existing one. In the not-isReplace case, the
+ * caller has most likely not yet done a CommandCounterIncrement
+ * that would make the new object visible.
+ */
+ oldext = getExtensionOfObject(object->classId, object->objectId);
+ if (OidIsValid(oldext))
+ {
+ /* If already a member of this extension, nothing to do */
+ if (oldext == CurrentExtensionObject)
+ return;
+ /* Already a member of some other extension, so reject */
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("%s is already a member of extension \"%s\"",
+ getObjectDescription(object, false),
+ get_extension_name(oldext))));
+ }
+ /* It's a free-standing object, so reject */
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("%s is not a member of extension \"%s\"",
+ getObjectDescription(object, false),
+ get_extension_name(CurrentExtensionObject)),
+ errdetail("An extension is not allowed to replace an object that it does not own.")));
+ }
+
+ /* OK, record it as a member of CurrentExtensionObject */
+ extension.classId = ExtensionRelationId;
+ extension.objectId = CurrentExtensionObject;
+ extension.objectSubId = 0;
+
+ recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
+ }
+}
+
+/*
+ * If we are executing a CREATE EXTENSION operation, check that the given
+ * object is a member of the extension, and throw an error if it isn't.
+ * Otherwise, do nothing.
+ *
+ * This must be called whenever a CREATE IF NOT EXISTS operation (for an
+ * object type that can be an extension member) has found that an object of
+ * the desired name already exists. It is insecure for an extension to use
+ * IF NOT EXISTS except when the conflicting object is already an extension
+ * member; otherwise a hostile user could substitute an object with arbitrary
+ * properties.
+ */
+void
+checkMembershipInCurrentExtension(const ObjectAddress *object)
+{
+ /*
+ * This is actually the same condition tested in
+ * recordDependencyOnCurrentExtension; but we want to issue a
+ * differently-worded error, and anyway it would be pretty confusing to
+ * call recordDependencyOnCurrentExtension in these circumstances.
+ */
+
+ /* Only whole objects can be extension members */
+ Assert(object->objectSubId == 0);
+
+ if (creating_extension)
+ {
+ Oid oldext;
+
+ oldext = getExtensionOfObject(object->classId, object->objectId);
+ /* If already a member of this extension, OK */
+ if (oldext == CurrentExtensionObject)
+ return;
+ /* Else complain */
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("%s is not a member of extension \"%s\"",
+ getObjectDescription(object, false),
+ get_extension_name(CurrentExtensionObject)),
+ errdetail("An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.")));
+ }
+}
+
+/*
+ * deleteDependencyRecordsFor -- delete all records with given depender
+ * classId/objectId. Returns the number of records deleted.
+ *
+ * This is used when redefining an existing object. Links leading to the
+ * object do not change, and links leading from it will be recreated
+ * (possibly with some differences from before).
+ *
+ * If skipExtensionDeps is true, we do not delete any dependencies that
+ * show that the given object is a member of an extension. This avoids
+ * needing a lot of extra logic to fetch and recreate that dependency.
+ */
+long
+deleteDependencyRecordsFor(Oid classId, Oid objectId,
+ bool skipExtensionDeps)
+{
+ long count = 0;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = table_open(DependRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 2, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ if (skipExtensionDeps &&
+ ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
+ continue;
+
+ CatalogTupleDelete(depRel, &tup->t_self);
+ count++;
+ }
+
+ systable_endscan(scan);
+
+ table_close(depRel, RowExclusiveLock);
+
+ return count;
+}
+
+/*
+ * deleteDependencyRecordsForClass -- delete all records with given depender
+ * classId/objectId, dependee classId, and deptype.
+ * Returns the number of records deleted.
+ *
+ * This is a variant of deleteDependencyRecordsFor, useful when revoking
+ * an object property that is expressed by a dependency record (such as
+ * extension membership).
+ */
+long
+deleteDependencyRecordsForClass(Oid classId, Oid objectId,
+ Oid refclassId, char deptype)
+{
+ long count = 0;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = table_open(DependRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 2, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (depform->refclassid == refclassId && depform->deptype == deptype)
+ {
+ CatalogTupleDelete(depRel, &tup->t_self);
+ count++;
+ }
+ }
+
+ systable_endscan(scan);
+
+ table_close(depRel, RowExclusiveLock);
+
+ return count;
+}
+
+/*
+ * deleteDependencyRecordsForSpecific -- delete all records with given depender
+ * classId/objectId, dependee classId/objectId, of the given deptype.
+ * Returns the number of records deleted.
+ */
+long
+deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype,
+ Oid refclassId, Oid refobjectId)
+{
+ long count = 0;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = table_open(DependRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 2, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (depform->refclassid == refclassId &&
+ depform->refobjid == refobjectId &&
+ depform->deptype == deptype)
+ {
+ CatalogTupleDelete(depRel, &tup->t_self);
+ count++;
+ }
+ }
+
+ systable_endscan(scan);
+
+ table_close(depRel, RowExclusiveLock);
+
+ return count;
+}
+
+/*
+ * Adjust dependency record(s) to point to a different object of the same type
+ *
+ * classId/objectId specify the referencing object.
+ * refClassId/oldRefObjectId specify the old referenced object.
+ * newRefObjectId is the new referenced object (must be of class refClassId).
+ *
+ * Note the lack of objsubid parameters. If there are subobject references
+ * they will all be readjusted. Also, there is an expectation that we are
+ * dealing with NORMAL dependencies: if we have to replace an (implicit)
+ * dependency on a pinned object with an explicit dependency on an unpinned
+ * one, the new one will be NORMAL.
+ *
+ * Returns the number of records updated -- zero indicates a problem.
+ */
+long
+changeDependencyFor(Oid classId, Oid objectId,
+ Oid refClassId, Oid oldRefObjectId,
+ Oid newRefObjectId)
+{
+ long count = 0;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+ ObjectAddress objAddr;
+ ObjectAddress depAddr;
+ bool oldIsPinned;
+ bool newIsPinned;
+
+ /*
+ * Check to see if either oldRefObjectId or newRefObjectId is pinned.
+ * Pinned objects should not have any dependency entries pointing to them,
+ * so in these cases we should add or remove a pg_depend entry, or do
+ * nothing at all, rather than update an entry as in the normal case.
+ */
+ objAddr.classId = refClassId;
+ objAddr.objectId = oldRefObjectId;
+ objAddr.objectSubId = 0;
+
+ oldIsPinned = isObjectPinned(&objAddr);
+
+ objAddr.objectId = newRefObjectId;
+
+ newIsPinned = isObjectPinned(&objAddr);
+
+ if (oldIsPinned)
+ {
+ /*
+ * If both are pinned, we need do nothing. However, return 1 not 0,
+ * else callers will think this is an error case.
+ */
+ if (newIsPinned)
+ return 1;
+
+ /*
+ * There is no old dependency record, but we should insert a new one.
+ * Assume a normal dependency is wanted.
+ */
+ depAddr.classId = classId;
+ depAddr.objectId = objectId;
+ depAddr.objectSubId = 0;
+ recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
+
+ return 1;
+ }
+
+ depRel = table_open(DependRelationId, RowExclusiveLock);
+
+ /* There should be existing dependency record(s), so search. */
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 2, key);
+
+ while (HeapTupleIsValid((tup = systable_getnext(scan))))
+ {
+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (depform->refclassid == refClassId &&
+ depform->refobjid == oldRefObjectId)
+ {
+ if (newIsPinned)
+ CatalogTupleDelete(depRel, &tup->t_self);
+ else
+ {
+ /* make a modifiable copy */
+ tup = heap_copytuple(tup);
+ depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ depform->refobjid = newRefObjectId;
+
+ CatalogTupleUpdate(depRel, &tup->t_self, tup);
+
+ heap_freetuple(tup);
+ }
+
+ count++;
+ }
+ }
+
+ systable_endscan(scan);
+
+ table_close(depRel, RowExclusiveLock);
+
+ return count;
+}
+
+/*
+ * Adjust all dependency records to come from a different object of the same type
+ *
+ * classId/oldObjectId specify the old referencing object.
+ * newObjectId is the new referencing object (must be of class classId).
+ *
+ * Returns the number of records updated.
+ */
+long
+changeDependenciesOf(Oid classId, Oid oldObjectId,
+ Oid newObjectId)
+{
+ long count = 0;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = table_open(DependRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(oldObjectId));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 2, key);
+
+ while (HeapTupleIsValid((tup = systable_getnext(scan))))
+ {
+ Form_pg_depend depform;
+
+ /* make a modifiable copy */
+ tup = heap_copytuple(tup);
+ depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ depform->objid = newObjectId;
+
+ CatalogTupleUpdate(depRel, &tup->t_self, tup);
+
+ heap_freetuple(tup);
+
+ count++;
+ }
+
+ systable_endscan(scan);
+
+ table_close(depRel, RowExclusiveLock);
+
+ return count;
+}
+
+/*
+ * Adjust all dependency records to point to a different object of the same type
+ *
+ * refClassId/oldRefObjectId specify the old referenced object.
+ * newRefObjectId is the new referenced object (must be of class refClassId).
+ *
+ * Returns the number of records updated.
+ */
+long
+changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
+ Oid newRefObjectId)
+{
+ long count = 0;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+ ObjectAddress objAddr;
+ bool newIsPinned;
+
+ depRel = table_open(DependRelationId, RowExclusiveLock);
+
+ /*
+ * If oldRefObjectId is pinned, there won't be any dependency entries on
+ * it --- we can't cope in that case. (This isn't really worth expending
+ * code to fix, in current usage; it just means you can't rename stuff out
+ * of pg_catalog, which would likely be a bad move anyway.)
+ */
+ objAddr.classId = refClassId;
+ objAddr.objectId = oldRefObjectId;
+ objAddr.objectSubId = 0;
+
+ if (isObjectPinned(&objAddr))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot remove dependency on %s because it is a system object",
+ getObjectDescription(&objAddr, false))));
+
+ /*
+ * We can handle adding a dependency on something pinned, though, since
+ * that just means deleting the dependency entry.
+ */
+ objAddr.objectId = newRefObjectId;
+
+ newIsPinned = isObjectPinned(&objAddr);
+
+ /* Now search for dependency records */
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_refclassid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(refClassId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_refobjid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(oldRefObjectId));
+
+ scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+ NULL, 2, key);
+
+ while (HeapTupleIsValid((tup = systable_getnext(scan))))
+ {
+ if (newIsPinned)
+ CatalogTupleDelete(depRel, &tup->t_self);
+ else
+ {
+ Form_pg_depend depform;
+
+ /* make a modifiable copy */
+ tup = heap_copytuple(tup);
+ depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ depform->refobjid = newRefObjectId;
+
+ CatalogTupleUpdate(depRel, &tup->t_self, tup);
+
+ heap_freetuple(tup);
+ }
+
+ count++;
+ }
+
+ systable_endscan(scan);
+
+ table_close(depRel, RowExclusiveLock);
+
+ return count;
+}
+
+/*
+ * isObjectPinned()
+ *
+ * Test if an object is required for basic database functionality.
+ *
+ * The passed subId, if any, is ignored; we assume that only whole objects
+ * are pinned (and that this implies pinning their components).
+ */
+static bool
+isObjectPinned(const ObjectAddress *object)
+{
+ return IsPinnedObject(object->classId, object->objectId);
+}
+
+
+/*
+ * Various special-purpose lookups and manipulations of pg_depend.
+ */
+
+
+/*
+ * Find the extension containing the specified object, if any
+ *
+ * Returns the OID of the extension, or InvalidOid if the object does not
+ * belong to any extension.
+ *
+ * Extension membership is marked by an EXTENSION dependency from the object
+ * to the extension. Note that the result will be indeterminate if pg_depend
+ * contains links from this object to more than one extension ... but that
+ * should never happen.
+ */
+Oid
+getExtensionOfObject(Oid classId, Oid objectId)
+{
+ Oid result = InvalidOid;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = table_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 2, key);
+
+ while (HeapTupleIsValid((tup = systable_getnext(scan))))
+ {
+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (depform->refclassid == ExtensionRelationId &&
+ depform->deptype == DEPENDENCY_EXTENSION)
+ {
+ result = depform->refobjid;
+ break; /* no need to keep scanning */
+ }
+ }
+
+ systable_endscan(scan);
+
+ table_close(depRel, AccessShareLock);
+
+ return result;
+}
+
+/*
+ * Return (possibly NIL) list of extensions that the given object depends on
+ * in DEPENDENCY_AUTO_EXTENSION mode.
+ */
+List *
+getAutoExtensionsOfObject(Oid classId, Oid objectId)
+{
+ List *result = NIL;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = table_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 2, key);
+
+ while (HeapTupleIsValid((tup = systable_getnext(scan))))
+ {
+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (depform->refclassid == ExtensionRelationId &&
+ depform->deptype == DEPENDENCY_AUTO_EXTENSION)
+ result = lappend_oid(result, depform->refobjid);
+ }
+
+ systable_endscan(scan);
+
+ table_close(depRel, AccessShareLock);
+
+ return result;
+}
+
+/*
+ * Detect whether a sequence is marked as "owned" by a column
+ *
+ * An ownership marker is an AUTO or INTERNAL dependency from the sequence to the
+ * column. If we find one, store the identity of the owning column
+ * into *tableId and *colId and return true; else return false.
+ *
+ * Note: if there's more than one such pg_depend entry then you get
+ * a random one of them returned into the out parameters. This should
+ * not happen, though.
+ */
+bool
+sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
+{
+ bool ret = false;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = table_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(seqId));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 2, key);
+
+ while (HeapTupleIsValid((tup = systable_getnext(scan))))
+ {
+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (depform->refclassid == RelationRelationId &&
+ depform->deptype == deptype)
+ {
+ *tableId = depform->refobjid;
+ *colId = depform->refobjsubid;
+ ret = true;
+ break; /* no need to keep scanning */
+ }
+ }
+
+ systable_endscan(scan);
+
+ table_close(depRel, AccessShareLock);
+
+ return ret;
+}
+
+/*
+ * Collect a list of OIDs of all sequences owned by the specified relation,
+ * and column if specified. If deptype is not zero, then only find sequences
+ * with the specified dependency type.
+ */
+static List *
+getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)
+{
+ List *result = NIL;
+ Relation depRel;
+ ScanKeyData key[3];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = table_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_refclassid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_refobjid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(relid));
+ if (attnum)
+ ScanKeyInit(&key[2],
+ Anum_pg_depend_refobjsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(attnum));
+
+ scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+ NULL, attnum ? 3 : 2, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
+
+ /*
+ * We assume any auto or internal dependency of a sequence on a column
+ * must be what we are looking for. (We need the relkind test because
+ * indexes can also have auto dependencies on columns.)
+ */
+ if (deprec->classid == RelationRelationId &&
+ deprec->objsubid == 0 &&
+ deprec->refobjsubid != 0 &&
+ (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
+ get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
+ {
+ if (!deptype || deprec->deptype == deptype)
+ result = lappend_oid(result, deprec->objid);
+ }
+ }
+
+ systable_endscan(scan);
+
+ table_close(depRel, AccessShareLock);
+
+ return result;
+}
+
+/*
+ * Collect a list of OIDs of all sequences owned (identity or serial) by the
+ * specified relation.
+ */
+List *
+getOwnedSequences(Oid relid)
+{
+ return getOwnedSequences_internal(relid, 0, 0);
+}
+
+/*
+ * Get owned identity sequence, error if not exactly one.
+ */
+Oid
+getIdentitySequence(Oid relid, AttrNumber attnum, bool missing_ok)
+{
+ List *seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL);
+
+ if (list_length(seqlist) > 1)
+ elog(ERROR, "more than one owned sequence found");
+ else if (list_length(seqlist) < 1)
+ {
+ if (missing_ok)
+ return InvalidOid;
+ else
+ elog(ERROR, "no owned sequence found");
+ }
+
+ return linitial_oid(seqlist);
+}
+
+/*
+ * get_index_constraint
+ * Given the OID of an index, return the OID of the owning unique,
+ * primary-key, or exclusion constraint, or InvalidOid if there
+ * is no owning constraint.
+ */
+Oid
+get_index_constraint(Oid indexId)
+{
+ Oid constraintId = InvalidOid;
+ Relation depRel;
+ ScanKeyData key[3];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ /* Search the dependency table for the index */
+ depRel = table_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(indexId));
+ ScanKeyInit(&key[2],
+ Anum_pg_depend_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(0));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ NULL, 3, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
+
+ /*
+ * We assume any internal dependency on a constraint must be what we
+ * are looking for.
+ */
+ if (deprec->refclassid == ConstraintRelationId &&
+ deprec->refobjsubid == 0 &&
+ deprec->deptype == DEPENDENCY_INTERNAL)
+ {
+ constraintId = deprec->refobjid;
+ break;
+ }
+ }
+
+ systable_endscan(scan);
+ table_close(depRel, AccessShareLock);
+
+ return constraintId;
+}
+
+/*
+ * get_index_ref_constraints
+ * Given the OID of an index, return the OID of all foreign key
+ * constraints which reference the index.
+ */
+List *
+get_index_ref_constraints(Oid indexId)
+{
+ List *result = NIL;
+ Relation depRel;
+ ScanKeyData key[3];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ /* Search the dependency table for the index */
+ depRel = table_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_refclassid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_refobjid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(indexId));
+ ScanKeyInit(&key[2],
+ Anum_pg_depend_refobjsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(0));
+
+ scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+ NULL, 3, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
+
+ /*
+ * We assume any normal dependency from a constraint must be what we
+ * are looking for.
+ */
+ if (deprec->classid == ConstraintRelationId &&
+ deprec->objsubid == 0 &&
+ deprec->deptype == DEPENDENCY_NORMAL)
+ {
+ result = lappend_oid(result, deprec->objid);
+ }
+ }
+
+ systable_endscan(scan);
+ table_close(depRel, AccessShareLock);
+
+ return result;
+}