📄 dependency.c
字号:
/*------------------------------------------------------------------------- * * dependency.c * Routines to support inter-object dependencies. * * * 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/dependency.c,v 1.47.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/heap.h"#include "catalog/index.h"#include "catalog/indexing.h"#include "catalog/namespace.h"#include "catalog/pg_attrdef.h"#include "catalog/pg_authid.h"#include "catalog/pg_cast.h"#include "catalog/pg_constraint.h"#include "catalog/pg_conversion.h"#include "catalog/pg_database.h"#include "catalog/pg_depend.h"#include "catalog/pg_language.h"#include "catalog/pg_namespace.h"#include "catalog/pg_opclass.h"#include "catalog/pg_operator.h"#include "catalog/pg_proc.h"#include "catalog/pg_rewrite.h"#include "catalog/pg_tablespace.h"#include "catalog/pg_trigger.h"#include "catalog/pg_type.h"#include "commands/comment.h"#include "commands/dbcommands.h"#include "commands/defrem.h"#include "commands/proclang.h"#include "commands/schemacmds.h"#include "commands/tablespace.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"/* 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;/* * This constant table maps ObjectClasses to the corresponding catalog OIDs. * See also getObjectClass(). */static const Oid object_classes[MAX_OCLASS] = { RelationRelationId, /* OCLASS_CLASS */ ProcedureRelationId, /* OCLASS_PROC */ TypeRelationId, /* OCLASS_TYPE */ CastRelationId, /* OCLASS_CAST */ ConstraintRelationId, /* OCLASS_CONSTRAINT */ ConversionRelationId, /* OCLASS_CONVERSION */ AttrDefaultRelationId, /* OCLASS_DEFAULT */ LanguageRelationId, /* OCLASS_LANGUAGE */ OperatorRelationId, /* OCLASS_OPERATOR */ OperatorClassRelationId, /* OCLASS_OPCLASS */ RewriteRelationId, /* OCLASS_REWRITE */ TriggerRelationId, /* OCLASS_TRIGGER */ NamespaceRelationId /* OCLASS_SCHEMA */};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(ObjectClass 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 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_open(DependRelationId, 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_open(DependRelationId, 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. */ ScanKeyInit(&key[0], Anum_pg_depend_refclassid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(object->classId)); ScanKeyInit(&key[1], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(object->objectId)); if (object->objectSubId != 0) { ScanKeyInit(&key[2], Anum_pg_depend_refobjsubid, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(object->objectSubId)); nkeys = 3; } else nkeys = 2; scan = systable_beginscan(depRel, DependReferenceIndexId, 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. */ ScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(object->classId)); ScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(object->objectId)); if (object->objectSubId != 0) { ScanKeyInit(&key[2], Anum_pg_depend_objsubid, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(object->objectSubId)); nkeys = 3; } else nkeys = 2; scan = systable_beginscan(depRel, DependDependerIndexId, 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 * another object. We have three cases: * * 1. At the outermost recursion level, disallow the DROP. (We * just ereport here, rather than proceeding, since no other * dependencies are likely to be interesting.) */ if (callingObject == NULL) { char *otherObjDesc = getObjectDescription(&otherObject); ereport(ERROR, (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -