⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pg_shdepend.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * 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 + -