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 + -
显示快捷键?