dependency.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,809 行 · 第 1/4 页

C
1,809
字号
/*------------------------------------------------------------------------- * * dependency.c *	  Routines to support inter-object dependencies. * * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.31 2003/08/11 23:04:49 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "catalog/catname.h"#include "catalog/dependency.h"#include "catalog/heap.h"#include "catalog/index.h"#include "catalog/indexing.h"#include "catalog/namespace.h"#include "catalog/pg_attrdef.h"#include "catalog/pg_cast.h"#include "catalog/pg_constraint.h"#include "catalog/pg_conversion.h"#include "catalog/pg_depend.h"#include "catalog/pg_language.h"#include "catalog/pg_opclass.h"#include "catalog/pg_rewrite.h"#include "catalog/pg_trigger.h"#include "commands/comment.h"#include "commands/defrem.h"#include "commands/proclang.h"#include "commands/schemacmds.h"#include "commands/trigger.h"#include "commands/typecmds.h"#include "lib/stringinfo.h"#include "miscadmin.h"#include "optimizer/clauses.h"#include "parser/parsetree.h"#include "rewrite/rewriteRemove.h"#include "utils/builtins.h"#include "utils/fmgroids.h"#include "utils/lsyscache.h"#include "utils/syscache.h"/* This enum covers all system catalogs whose OIDs can appear in classid. */typedef enum ObjectClasses{	OCLASS_CLASS,				/* pg_class */	OCLASS_PROC,				/* pg_proc */	OCLASS_TYPE,				/* pg_type */	OCLASS_CAST,				/* pg_cast */	OCLASS_CONSTRAINT,			/* pg_constraint */	OCLASS_CONVERSION,			/* pg_conversion */	OCLASS_DEFAULT,				/* pg_attrdef */	OCLASS_LANGUAGE,			/* pg_language */	OCLASS_OPERATOR,			/* pg_operator */	OCLASS_OPCLASS,				/* pg_opclass */	OCLASS_REWRITE,				/* pg_rewrite */	OCLASS_TRIGGER,				/* pg_trigger */	OCLASS_SCHEMA,				/* pg_namespace */	MAX_OCLASS					/* MUST BE LAST */} ObjectClasses;/* expansible list of ObjectAddresses */typedef struct{	ObjectAddress *refs;		/* => palloc'd array */	int			numrefs;		/* current number of references */	int			maxrefs;		/* current size of palloc'd array */} ObjectAddresses;/* for find_expr_references_walker */typedef struct{	ObjectAddresses addrs;		/* addresses being accumulated */	List	   *rtables;		/* list of rangetables to resolve Vars */} find_expr_references_context;/* * Because not all system catalogs have predetermined OIDs, we build a table * mapping between ObjectClasses and OIDs.	This is done at most once per * backend run, to minimize lookup overhead. */static bool object_classes_initialized = false;static Oid	object_classes[MAX_OCLASS];static void findAutoDeletableObjects(const ObjectAddress *object,						 ObjectAddresses *oktodelete,						 Relation depRel);static bool recursiveDeletion(const ObjectAddress *object,				  DropBehavior behavior,				  int msglevel,				  const ObjectAddress *callingObject,				  ObjectAddresses *oktodelete,				  Relation depRel);static bool deleteDependentObjects(const ObjectAddress *object,					   const char *objDescription,					   DropBehavior behavior,					   int msglevel,					   ObjectAddresses *oktodelete,					   Relation depRel);static void doDeletion(const ObjectAddress *object);static bool find_expr_references_walker(Node *node,							find_expr_references_context *context);static void eliminate_duplicate_dependencies(ObjectAddresses *addrs);static int	object_address_comparator(const void *a, const void *b);static void init_object_addresses(ObjectAddresses *addrs);static void add_object_address(ObjectClasses oclass, Oid objectId, int32 subId,				   ObjectAddresses *addrs);static void add_exact_object_address(const ObjectAddress *object,						 ObjectAddresses *addrs);static bool object_address_present(const ObjectAddress *object,					   ObjectAddresses *addrs);static void term_object_addresses(ObjectAddresses *addrs);static void init_object_classes(void);static ObjectClasses getObjectClass(const ObjectAddress *object);static char *getObjectDescription(const ObjectAddress *object);static void getRelationDescription(StringInfo buffer, Oid relid);/* * performDeletion: attempt to drop the specified object.  If CASCADE * behavior is specified, also drop any dependent objects (recursively). * If RESTRICT behavior is specified, error out if there are any dependent * objects, except for those that should be implicitly dropped anyway * according to the dependency type. * * This is the outer control routine for all forms of DROP that drop objects * that can participate in dependencies. */voidperformDeletion(const ObjectAddress *object,				DropBehavior behavior){	char	   *objDescription;	Relation	depRel;	ObjectAddresses oktodelete;	/*	 * Get object description for possible use in failure message. Must do	 * this before deleting it ...	 */	objDescription = getObjectDescription(object);	/*	 * We save some cycles by opening pg_depend just once and passing the	 * Relation pointer down to all the recursive deletion steps.	 */	depRel = heap_openr(DependRelationName, RowExclusiveLock);	/*	 * Construct a list of objects that are reachable by AUTO or INTERNAL	 * dependencies from the target object.  These should be deleted	 * silently, even if the actual deletion pass first reaches one of	 * them via a non-auto dependency.	 */	init_object_addresses(&oktodelete);	findAutoDeletableObjects(object, &oktodelete, depRel);	if (!recursiveDeletion(object, behavior, NOTICE,						   NULL, &oktodelete, depRel))		ereport(ERROR,				(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),			  errmsg("cannot drop %s because other objects depend on it",					 objDescription),				 errhint("Use DROP ... CASCADE to drop the dependent objects too.")));	term_object_addresses(&oktodelete);	heap_close(depRel, RowExclusiveLock);	pfree(objDescription);}/* * deleteWhatDependsOn: attempt to drop everything that depends on the * specified object, though not the object itself.	Behavior is always * CASCADE. * * This is currently used only to clean out the contents of a schema * (namespace): the passed object is a namespace.  We normally want this * to be done silently, so there's an option to suppress NOTICE messages. */voiddeleteWhatDependsOn(const ObjectAddress *object,					bool showNotices){	char	   *objDescription;	Relation	depRel;	ObjectAddresses oktodelete;	/*	 * Get object description for possible use in failure messages	 */	objDescription = getObjectDescription(object);	/*	 * We save some cycles by opening pg_depend just once and passing the	 * Relation pointer down to all the recursive deletion steps.	 */	depRel = heap_openr(DependRelationName, RowExclusiveLock);	/*	 * Construct a list of objects that are reachable by AUTO or INTERNAL	 * dependencies from the target object.  These should be deleted	 * silently, even if the actual deletion pass first reaches one of	 * them via a non-auto dependency.	 */	init_object_addresses(&oktodelete);	findAutoDeletableObjects(object, &oktodelete, depRel);	/*	 * Now invoke only step 2 of recursiveDeletion: just recurse to the	 * stuff dependent on the given object.	 */	if (!deleteDependentObjects(object, objDescription,								DROP_CASCADE,								showNotices ? NOTICE : DEBUG2,								&oktodelete, depRel))		ereport(ERROR,				(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),				 errmsg("failed to drop all objects depending on %s",						objDescription)));	/*	 * We do not need CommandCounterIncrement here, since if step 2 did	 * anything then each recursive call will have ended with one.	 */	term_object_addresses(&oktodelete);	heap_close(depRel, RowExclusiveLock);	pfree(objDescription);}/* * findAutoDeletableObjects: find all objects that are reachable by AUTO or * INTERNAL dependency paths from the given object.  Add them all to the * oktodelete list.  Note that the originally given object will also be * added to the list. * * depRel is the already-open pg_depend relation. */static voidfindAutoDeletableObjects(const ObjectAddress *object,						 ObjectAddresses *oktodelete,						 Relation depRel){	ScanKeyData key[3];	int			nkeys;	SysScanDesc scan;	HeapTuple	tup;	ObjectAddress otherObject;	/*	 * If this object is already in oktodelete, then we already visited	 * it; don't do so again (this prevents infinite recursion if there's	 * a loop in pg_depend).  Otherwise, add it.	 */	if (object_address_present(object, oktodelete))		return;	add_exact_object_address(object, oktodelete);	/*	 * Scan pg_depend records that link to this object, showing the things	 * that depend on it.  For each one that is AUTO or INTERNAL, visit	 * the referencing object.	 *	 * When dropping a whole object (subId = 0), find pg_depend records for	 * its sub-objects too.	 */	ScanKeyEntryInitialize(&key[0], 0x0,						   Anum_pg_depend_refclassid, F_OIDEQ,						   ObjectIdGetDatum(object->classId));	ScanKeyEntryInitialize(&key[1], 0x0,						   Anum_pg_depend_refobjid, F_OIDEQ,						   ObjectIdGetDatum(object->objectId));	if (object->objectSubId != 0)	{		ScanKeyEntryInitialize(&key[2], 0x0,							   Anum_pg_depend_refobjsubid, F_INT4EQ,							   Int32GetDatum(object->objectSubId));		nkeys = 3;	}	else		nkeys = 2;	scan = systable_beginscan(depRel, DependReferenceIndex, true,							  SnapshotNow, nkeys, key);	while (HeapTupleIsValid(tup = systable_getnext(scan)))	{		Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);		switch (foundDep->deptype)		{			case DEPENDENCY_NORMAL:				/* ignore */				break;			case DEPENDENCY_AUTO:			case DEPENDENCY_INTERNAL:				/* recurse */				otherObject.classId = foundDep->classid;				otherObject.objectId = foundDep->objid;				otherObject.objectSubId = foundDep->objsubid;				findAutoDeletableObjects(&otherObject, oktodelete, depRel);				break;			case DEPENDENCY_PIN:				/*				 * For a PIN dependency we just ereport immediately; there				 * won't be any others to examine, and we aren't ever				 * going to let the user delete it.				 */				ereport(ERROR,						(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),						 errmsg("cannot drop %s because it is required by the database system",								getObjectDescription(object))));				break;			default:				elog(ERROR, "unrecognized dependency type '%c' for %s",					 foundDep->deptype, getObjectDescription(object));				break;		}	}	systable_endscan(scan);}/* * recursiveDeletion: delete a single object for performDeletion, plus * (recursively) anything that depends on it. * * Returns TRUE if successful, FALSE if not. * * callingObject is NULL at the outer level, else identifies the object that * we recursed from (the reference object that someone else needs to delete). * * oktodelete is a list of objects verified deletable (ie, reachable by one * or more AUTO or INTERNAL dependencies from the original target). * * depRel is the already-open pg_depend relation. * * * In RESTRICT mode, we perform all the deletions anyway, but ereport a message * and return FALSE if we find a restriction violation.  performDeletion * will then abort the transaction to nullify the deletions.  We have to * do it this way to (a) report all the direct and indirect dependencies * while (b) not going into infinite recursion if there's a cycle. * * This is even more complex than one could wish, because it is possible for * the same pair of objects to be related by both NORMAL and AUTO/INTERNAL * dependencies.  Also, we might have a situation where we've been asked to * delete object A, and objects B and C both have AUTO dependencies on A, * but B also has a NORMAL dependency on C.  (Since any of these paths might * be indirect, we can't prevent these scenarios, but must cope instead.) * If we visit C before B then we would mistakenly decide that the B->C link * should prevent the restricted drop from occurring.  To handle this, we make * a pre-scan to find all the objects that are auto-deletable from A.  If we * visit C first, but B is present in the oktodelete list, then we make no * complaint but recurse to delete B anyway.  (Note that in general we must * delete B before deleting C; the drop routine for B may try to access C.) * * Note: in the case where the path to B is traversed first, we will not * see the NORMAL dependency when we reach C, because of the pg_depend * removals done in step 1.  The oktodelete list is necessary just * to make the behavior independent of the order in which pg_depend * entries are visited. */static boolrecursiveDeletion(const ObjectAddress *object,				  DropBehavior behavior,				  int msglevel,				  const ObjectAddress *callingObject,				  ObjectAddresses *oktodelete,				  Relation depRel){	bool		ok = true;	char	   *objDescription;	ScanKeyData key[3];	int			nkeys;	SysScanDesc scan;	HeapTuple	tup;	ObjectAddress otherObject;	ObjectAddress owningObject;	bool		amOwned = false;	/*	 * Get object description for possible use in messages.  Must do this	 * before deleting it ...	 */	objDescription = getObjectDescription(object);	/*	 * Step 1: find and remove pg_depend records that link from this	 * object to others.  We have to do this anyway, and doing it first	 * ensures that we avoid infinite recursion in the case of cycles.	 * Also, some dependency types require extra processing here.	 *	 * When dropping a whole object (subId = 0), remove all pg_depend records	 * for its sub-objects too.	 */	ScanKeyEntryInitialize(&key[0], 0x0,						   Anum_pg_depend_classid, F_OIDEQ,						   ObjectIdGetDatum(object->classId));	ScanKeyEntryInitialize(&key[1], 0x0,						   Anum_pg_depend_objid, F_OIDEQ,						   ObjectIdGetDatum(object->objectId));	if (object->objectSubId != 0)	{		ScanKeyEntryInitialize(&key[2], 0x0,							   Anum_pg_depend_objsubid, F_INT4EQ,							   Int32GetDatum(object->objectSubId));		nkeys = 3;	}	else		nkeys = 2;	scan = systable_beginscan(depRel, DependDependerIndex, true,							  SnapshotNow, nkeys, key);	while (HeapTupleIsValid(tup = systable_getnext(scan)))	{		Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);		otherObject.classId = foundDep->refclassid;		otherObject.objectId = foundDep->refobjid;		otherObject.objectSubId = foundDep->refobjsubid;		switch (foundDep->deptype)		{			case DEPENDENCY_NORMAL:			case DEPENDENCY_AUTO:				/* no problem */				break;			case DEPENDENCY_INTERNAL:				/*				 * This object is part of the internal implementation of

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?