📄 pg_shdepend.c
字号:
/*------------------------------------------------------------------------- * * pg_shdepend.c * routines to support manipulation of the pg_shdepend relation * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.3.2.1 2005/11/22 18:23:06 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "catalog/dependency.h"#include "catalog/indexing.h"#include "catalog/pg_authid.h"#include "catalog/pg_database.h"#include "catalog/pg_shdepend.h"#include "lib/stringinfo.h"#include "miscadmin.h"#include "utils/fmgroids.h"#include "utils/inval.h"#include "utils/syscache.h"typedef enum{ LOCAL_OBJECT, SHARED_OBJECT, REMOTE_OBJECT} objectType;static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff);static Oid classIdGetDbId(Oid classId);static void shdepLockAndCheckObject(Oid classId, Oid objectId);static void shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, Oid refclassid, Oid refobjid, SharedDependencyType deptype);static void shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId, Oid refclassId, Oid refobjId, SharedDependencyType deptype);static void shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId, Oid refclassId, Oid refobjId, SharedDependencyType deptype);static void storeObjectDescription(StringInfo descs, objectType type, ObjectAddress *object, SharedDependencyType deptype, int count);static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel);/* * recordSharedDependencyOn * * Record a dependency between 2 objects via their respective ObjectAddresses. * The first argument is the dependent object, the second the one it * references (which must be a shared object). * * This locks the referenced object and makes sure it still exists. * Then it creates an entry in pg_shdepend. The lock is kept until * the end of the transaction. * * Dependencies on pinned objects are not recorded. */voidrecordSharedDependencyOn(ObjectAddress *depender, ObjectAddress *referenced, SharedDependencyType deptype){ Relation sdepRel; /* * Objects in pg_shdepend can't have SubIds. */ Assert(depender->objectSubId == 0); Assert(referenced->objectSubId == 0); /* * During bootstrap, do nothing since pg_shdepend may not exist yet. * initdb will fill in appropriate pg_shdepend entries after bootstrap. */ if (IsBootstrapProcessingMode()) return; sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); /* If the referenced object is pinned, do nothing. */ if (!isSharedObjectPinned(referenced->classId, referenced->objectId, sdepRel)) { shdepAddDependency(sdepRel, depender->classId, depender->objectId, referenced->classId, referenced->objectId, deptype); } heap_close(sdepRel, RowExclusiveLock);}/* * recordDependencyOnOwner * * A convenient wrapper of recordSharedDependencyOn -- register the specified * user as owner of the given object. * * Note: it's the caller's responsibility to ensure that there isn't an owner * entry for the object already. */voidrecordDependencyOnOwner(Oid classId, Oid objectId, Oid owner){ ObjectAddress myself, referenced; myself.classId = classId; myself.objectId = objectId; myself.objectSubId = 0; referenced.classId = AuthIdRelationId; referenced.objectId = owner; referenced.objectSubId = 0; recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER);}/* * shdepChangeDep * * Update shared dependency records to account for an updated referenced * object. This is an internal workhorse for operations such as changing * an object's owner. * * There must be no more than one existing entry for the given dependent * object and dependency type! So in practice this can only be used for * updating SHARED_DEPENDENCY_OWNER entries, which should have that property. * * If there is no previous entry, we assume it was referencing a PINned * object, so we create a new entry. If the new referenced object is * PINned, we don't create an entry (and drop the old one, if any). * * sdepRel must be the pg_shdepend relation, already opened and suitably * locked. */static voidshdepChangeDep(Relation sdepRel, Oid classid, Oid objid, Oid refclassid, Oid refobjid, SharedDependencyType deptype){ Oid dbid = classIdGetDbId(classid); HeapTuple oldtup = NULL; HeapTuple scantup; ScanKeyData key[3]; SysScanDesc scan; /* * Make sure the new referenced object doesn't go away while we record the * dependency. */ shdepLockAndCheckObject(refclassid, refobjid); /* * Look for a previous entry */ ScanKeyInit(&key[0], Anum_pg_shdepend_dbid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(dbid)); ScanKeyInit(&key[1], Anum_pg_shdepend_classid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(classid)); ScanKeyInit(&key[2], Anum_pg_shdepend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(objid)); scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true, SnapshotNow, 3, key); while ((scantup = systable_getnext(scan)) != NULL) { /* Ignore if not of the target dependency type */ if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype) continue; /* Caller screwed up if multiple matches */ if (oldtup) elog(ERROR, "multiple pg_shdepend entries for object %u/%u deptype %c", classid, objid, deptype); oldtup = heap_copytuple(scantup); } systable_endscan(scan); if (isSharedObjectPinned(refclassid, refobjid, sdepRel)) { /* No new entry needed, so just delete existing entry if any */ if (oldtup) simple_heap_delete(sdepRel, &oldtup->t_self); } else if (oldtup) { /* Need to update existing entry */ Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup); /* Since oldtup is a copy, we can just modify it in-memory */ shForm->refclassid = refclassid; shForm->refobjid = refobjid; simple_heap_update(sdepRel, &oldtup->t_self, oldtup); /* keep indexes current */ CatalogUpdateIndexes(sdepRel, oldtup); } else { /* Need to insert new entry */ Datum values[Natts_pg_shdepend]; bool nulls[Natts_pg_shdepend]; memset(nulls, 0, sizeof(nulls)); values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid); values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid); values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid); values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid); values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid); values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype); /* * we are reusing oldtup just to avoid declaring a new variable, but * it's certainly a new tuple */ oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls); simple_heap_insert(sdepRel, oldtup); /* keep indexes current */ CatalogUpdateIndexes(sdepRel, oldtup); } if (oldtup) heap_freetuple(oldtup);}/* * changeDependencyOnOwner * * Update the shared dependencies to account for the new owner. */voidchangeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId){ Relation sdepRel; sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); /* Adjust the SHARED_DEPENDENCY_OWNER entry */ shdepChangeDep(sdepRel, classId, objectId, AuthIdRelationId, newOwnerId, SHARED_DEPENDENCY_OWNER); /*---------- * There should never be a SHARED_DEPENDENCY_ACL entry for the owner, * so get rid of it if there is one. This can happen if the new owner * was previously granted some rights to the object. * * This step is analogous to aclnewowner's removal of duplicate entries * in the ACL. We have to do it to handle this scenario: * A grants some rights on an object to B * ALTER OWNER changes the object's owner to B * ALTER OWNER changes the object's owner to C * The third step would remove all mention of B from the object's ACL, * but we'd still have a SHARED_DEPENDENCY_ACL for B if we did not do * things this way. * * The rule against having a SHARED_DEPENDENCY_ACL entry for the owner * allows us to fix things up in just this one place, without having * to make the various ALTER OWNER routines each know about it. *---------- */ shdepDropDependency(sdepRel, classId, objectId, AuthIdRelationId, newOwnerId, SHARED_DEPENDENCY_ACL); heap_close(sdepRel, RowExclusiveLock);}/* * getOidListDiff * Helper for updateAclDependencies. * * Takes two Oid arrays and returns elements from the first not found in the * second. We assume both arrays are sorted and de-duped, and that the * second array does not contain any values not found in the first. * * NOTE: Both input arrays are pfreed. */static intgetOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff){ Oid *result; int i, j, k = 0; AssertArg(nlist1 >= nlist2 && nlist2 >= 0); result = palloc(sizeof(Oid) * (nlist1 - nlist2)); *diff = result; for (i = 0, j = 0; i < nlist1 && j < nlist2;) { if (list1[i] == list2[j]) { i++; j++; } else if (list1[i] < list2[j]) { result[k++] = list1[i]; i++; } else { /* can't happen */ elog(WARNING, "invalid element %u in shorter list", list2[j]); j++; } } for (; i < nlist1; i++) result[k++] = list1[i]; /* We should have copied the exact number of elements */ AssertState(k == (nlist1 - nlist2)); if (list1) pfree(list1); if (list2) pfree(list2); return k;}/* * updateAclDependencies * Update the pg_shdepend info for an object's ACL during GRANT/REVOKE. * * classId, objectId: identify the object whose ACL this is * ownerId: role owning the object * isGrant: are we adding or removing ACL entries? * noldmembers, oldmembers: array of roleids appearing in old ACL * nnewmembers, newmembers: array of roleids appearing in new ACL * * We calculate the difference between the new and old lists of roles, * and then insert (if it's a grant) or delete (if it's a revoke) from * pg_shdepend as appropiate. * * Note that we can't insert blindly at grant, because we would end up with * duplicate registered dependencies. We could check for existence of the * tuple before inserting, but that seems to be more expensive than what we are * doing now. On the other hand, we can't just delete the tuples blindly at * revoke, because the user may still have other privileges. * * NOTE: Both input arrays must be sorted and de-duped. They are pfreed * before return. */voidupdateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant, int noldmembers, Oid *oldmembers, int nnewmembers, Oid *newmembers){ Relation sdepRel; Oid *diff; int ndiff, i; /* * Calculate the differences between the old and new lists. */ if (isGrant) ndiff = getOidListDiff(newmembers, nnewmembers, oldmembers, noldmembers, &diff); else ndiff = getOidListDiff(oldmembers, noldmembers, newmembers, nnewmembers, &diff); if (ndiff > 0) { sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); /* Add or drop the respective dependency */ for (i = 0; i < ndiff; i++) { Oid roleid = diff[i]; /* * Skip the owner: he has an OWNER shdep entry instead. (This is * not just a space optimization; it makes ALTER OWNER easier. See * notes in changeDependencyOnOwner.) */ if (roleid == ownerId) continue; /* Skip pinned roles */ if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel)) continue; if (isGrant) shdepAddDependency(sdepRel, classId, objectId, AuthIdRelationId, roleid, SHARED_DEPENDENCY_ACL); else shdepDropDependency(sdepRel, classId, objectId, AuthIdRelationId, roleid, SHARED_DEPENDENCY_ACL); } heap_close(sdepRel, RowExclusiveLock); } pfree(diff);}/* * A struct to keep track of dependencies found in other databases. */typedef struct{ Oid dbOid; int count;} remoteDep;/* * checkSharedDependencies * * Check whether there are shared dependency entries for a given shared * object. Returns a string containing a newline-separated list of object * descriptions that depend on the shared object, or NULL if none is found. * * We can find three different kinds of dependencies: dependencies on objects * of the current database; dependencies on shared objects; and dependencies * on objects local to other databases. We can (and do) provide descriptions * of the two former kinds of objects, but we can't do that for "remote" * objects, so we just provide a count of them. * * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early. */char *checkSharedDependencies(Oid classId, Oid objectId){ Relation sdepRel; ScanKeyData key[2]; SysScanDesc scan; HeapTuple tup; int totalDeps = 0; int numLocalDeps = 0; int numSharedDeps = 0; List *remDeps = NIL; ListCell *cell; ObjectAddress object; StringInfoData descs; /* * We try to limit the number of reported dependencies to something sane, * both for the user's sake and to avoid blowing out memory. */#define MAX_REPORTED_DEPS 100 initStringInfo(&descs); sdepRel = heap_open(SharedDependRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_shdepend_refclassid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(classId)); ScanKeyInit(&key[1], Anum_pg_shdepend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(objectId)); scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true, SnapshotNow, 2, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) { Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup); /* This case can be dispatched quickly */ if (sdepForm->deptype == SHARED_DEPENDENCY_PIN) { object.classId = classId; object.objectId = objectId; object.objectSubId = 0; ereport(ERROR, (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), errmsg("cannot drop %s because it is required by the database system", getObjectDescription(&object)))); } object.classId = sdepForm->classid; object.objectId = sdepForm->objid; object.objectSubId = 0; /* * If it's a dependency local to this database or it's a shared * object, describe it. * * If it's a remote dependency, keep track of it so we can report the * number of them later. */ if (sdepForm->dbid == MyDatabaseId) { numLocalDeps++; if (++totalDeps <= MAX_REPORTED_DEPS) storeObjectDescription(&descs, LOCAL_OBJECT, &object,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -