]> granicus.if.org Git - postgresql/blobdiff - src/backend/catalog/dependency.c
Refactor the handling of the various DropStmt variants so that when multiple
[postgresql] / src / backend / catalog / dependency.c
index 44b674b00221b2648f21361e85a13b7897404d66..793071b0f055757e16c3a640f12d1f04e0c5a3ef 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.70 2008/03/26 21:10:37 alvherre Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.76 2008/06/14 18:04:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,6 +16,7 @@
 
 #include "access/genam.h"
 #include "access/heapam.h"
+#include "access/sysattr.h"
 #include "access/xact.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
@@ -29,6 +30,7 @@
 #include "catalog/pg_cast.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_conversion.h"
+#include "catalog/pg_conversion_fn.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_depend.h"
 #include "catalog/pg_language.h"
 #include "optimizer/clauses.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteRemove.h"
+#include "storage/lmgr.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/guc.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 #include "utils/tqual.h"
 
 
+/*
+ * Deletion processing requires additional state for each ObjectAddress that
+ * it's planning to delete.  For simplicity and code-sharing we make the
+ * ObjectAddresses code support arrays with or without this extra state.
+ */
+typedef struct
+{
+       int                     flags;                  /* bitmask, see bit definitions below */
+       ObjectAddress dependee;         /* object whose deletion forced this one */
+} ObjectAddressExtra;
+
+/* ObjectAddressExtra flag bits */
+#define DEPFLAG_ORIGINAL       0x0001          /* an original deletion target */
+#define DEPFLAG_NORMAL         0x0002          /* reached via normal dependency */
+#define DEPFLAG_AUTO           0x0004          /* reached via auto dependency */
+#define DEPFLAG_INTERNAL       0x0008          /* reached via internal dependency */
+
+
 /* expansible list of ObjectAddresses */
 struct ObjectAddresses
 {
        ObjectAddress *refs;            /* => palloc'd array */
+       ObjectAddressExtra *extras;     /* => palloc'd array, or NULL if not used */
        int                     numrefs;                /* current number of references */
-       int                     maxrefs;                /* current size of palloc'd array */
+       int                     maxrefs;                /* current size of palloc'd array(s) */
 };
 
 /* typedef ObjectAddresses appears in dependency.h */
 
+/* threaded list of ObjectAddresses, for recursion detection */
+typedef struct ObjectAddressStack
+{
+       const ObjectAddress *object; /* object being visited */
+       int                     flags;                  /* its current flag bits */
+       struct ObjectAddressStack *next; /* next outer stack level */
+} ObjectAddressStack;
+
 /* for find_expr_references_walker */
 typedef struct
 {
@@ -112,33 +143,32 @@ static const Oid object_classes[MAX_OCLASS] = {
 };
 
 
-static void performDeletionWithList(const ObjectAddress *object,
-                                               ObjectAddresses *oktodelete,
-                                               DropBehavior behavior,
-                                               ObjectAddresses *alreadyDeleted);
-static void findAutoDeletableObjects(const ObjectAddress *object,
-                                                ObjectAddresses *oktodelete,
-                                                Relation depRel, bool addself);
-static bool recursiveDeletion(const ObjectAddress *object,
-                                 DropBehavior behavior,
-                                 int msglevel,
-                                 const ObjectAddress *callingObject,
-                                 ObjectAddresses *oktodelete,
-                                 Relation depRel,
-                                 ObjectAddresses *alreadyDeleted);
-static bool deleteDependentObjects(const ObjectAddress *object,
-                                          const char *objDescription,
+static void findDependentObjects(const ObjectAddress *object,
+                                        int flags,
+                                        ObjectAddressStack *stack,
+                                        ObjectAddresses *targetObjects,
+                                        const ObjectAddresses *pendingObjects,
+                                        Relation depRel);
+static void reportDependentObjects(const ObjectAddresses *targetObjects,
                                           DropBehavior behavior,
                                           int msglevel,
-                                          ObjectAddresses *oktodelete,
-                                          Relation depRel);
+                                          const ObjectAddress *origObject);
+static void deleteOneObject(const ObjectAddress *object, Relation depRel);
 static void doDeletion(const ObjectAddress *object);
+static void AcquireDeletionLock(const ObjectAddress *object);
+static void ReleaseDeletionLock(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 add_object_address(ObjectClass oclass, Oid objectId, int32 subId,
                                   ObjectAddresses *addrs);
+static void add_exact_object_address_extra(const ObjectAddress *object,
+                                                          const ObjectAddressExtra *extra,
+                                                          ObjectAddresses *addrs);
+static bool object_address_present_add_flags(const ObjectAddress *object,
+                                                                                        int flags,
+                                                                                        ObjectAddresses *addrs);
 static void getRelationDescription(StringInfo buffer, Oid relid);
 static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
 
@@ -151,21 +181,17 @@ static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
  * according to the dependency type.
  *
  * This is the outer control routine for all forms of DROP that drop objects
- * that can participate in dependencies.
+ * that can participate in dependencies.  Note that the next two routines
+ * are variants on the same theme; if you change anything here you'll likely
+ * need to fix them too.
  */
 void
 performDeletion(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);
+       ObjectAddresses *targetObjects;
+       int                     i;
 
        /*
         * We save some cycles by opening pg_depend just once and passing the
@@ -174,83 +200,50 @@ performDeletion(const ObjectAddress *object,
        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.
+        * Acquire deletion lock on the target object.  (Ideally the caller has
+        * done this already, but many places are sloppy about it.)
         */
-       oktodelete = new_object_addresses();
-
-       findAutoDeletableObjects(object, oktodelete, depRel, true);
-
-       if (!recursiveDeletion(object, behavior, NOTICE,
-                                                  NULL, oktodelete, depRel, NULL))
-               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.")));
-
-       free_object_addresses(oktodelete);
-
-       heap_close(depRel, RowExclusiveLock);
-
-       pfree(objDescription);
-}
-
-
-/*
- * performDeletionWithList: As above, but the oktodelete list may have already
- * filled with some objects.  Also, the deleted objects are saved in the
- * alreadyDeleted list.
- *
- * XXX performDeletion could be refactored to be a thin wrapper around this
- * function.
- */
-static void
-performDeletionWithList(const ObjectAddress *object,
-                                               ObjectAddresses *oktodelete,
-                                               DropBehavior behavior,
-                                               ObjectAddresses *alreadyDeleted)
-{
-       char       *objDescription;
-       Relation        depRel;
+       AcquireDeletionLock(object);
 
        /*
-        * Get object description for possible use in failure message. Must do
-        * this before deleting it ...
+        * Construct a list of objects to delete (ie, the given object plus
+        * everything directly or indirectly dependent on it).
         */
-       objDescription = getObjectDescription(object);
+       targetObjects = new_object_addresses();
+
+       findDependentObjects(object,
+                                                DEPFLAG_ORIGINAL,
+                                                NULL,  /* empty stack */
+                                                targetObjects,
+                                                NULL,  /* no pendingObjects */
+                                                depRel);
 
        /*
-        * We save some cycles by opening pg_depend just once and passing the
-        * Relation pointer down to all the recursive deletion steps.
+        * Check if deletion is allowed, and report about cascaded deletes.
         */
-       depRel = heap_open(DependRelationId, RowExclusiveLock);
+       reportDependentObjects(targetObjects,
+                                                  behavior,
+                                                  NOTICE,
+                                                  object);
 
        /*
-        * 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.
+        * Delete all the objects in the proper order.
         */
-       findAutoDeletableObjects(object, oktodelete, depRel, true);
+       for (i = 0; i < targetObjects->numrefs; i++)
+       {
+               ObjectAddress *thisobj = targetObjects->refs + i;
 
-       if (!recursiveDeletion(object, behavior, NOTICE,
-                                                  NULL, oktodelete, depRel, alreadyDeleted))
-               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.")));
+               deleteOneObject(thisobj, depRel);
+       }
 
-       heap_close(depRel, RowExclusiveLock);
+       /* And clean up */
+       free_object_addresses(targetObjects);
 
-       pfree(objDescription);
+       heap_close(depRel, RowExclusiveLock);
 }
 
 /*
- * performMultipleDeletion: Similar to performDeletion, but act on multiple
+ * performMultipleDeletions: Similar to performDeletion, but act on multiple
  * objects at once.
  *
  * The main difference from issuing multiple performDeletion calls is that the
@@ -262,67 +255,73 @@ void
 performMultipleDeletions(const ObjectAddresses *objects,
                                                 DropBehavior behavior)
 {
-       ObjectAddresses *implicit;
-       ObjectAddresses *alreadyDeleted;
        Relation        depRel;
+       ObjectAddresses *targetObjects;
        int                     i;
 
-       implicit = new_object_addresses();
-       alreadyDeleted = new_object_addresses();
+       /* No work if no objects... */
+       if (objects->numrefs <= 0)
+               return;
 
+       /*
+        * 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);
 
        /*
-        * Get the list of all objects that would be deleted after deleting the
-        * whole "objects" list.  We do this by creating a list of all implicit
-        * (INTERNAL and AUTO) dependencies for each object we collected above.
-        * Note that we must exclude the objects themselves from this list!
+        * Construct a list of objects to delete (ie, the given objects plus
+        * everything directly or indirectly dependent on them).  Note that
+        * because we pass the whole objects list as pendingObjects context,
+        * we won't get a failure from trying to delete an object that is
+        * internally dependent on another one in the list; we'll just skip
+        * that object and delete it when we reach its owner.
         */
+       targetObjects = new_object_addresses();
+
        for (i = 0; i < objects->numrefs; i++)
        {
-               ObjectAddress obj = objects->refs[i];
-
-               /*
-                * If it's in the implicit list, we don't need to delete it explicitly
-                * nor follow the dependencies, because that was already done in a
-                * previous iteration.
-                */
-               if (object_address_present(&obj, implicit))
-                       continue;
+               const ObjectAddress *thisobj = objects->refs + i;
 
                /*
-                * Add the objects dependent on this one to the global list of
-                * implicit objects.
+                * Acquire deletion lock on each target object.  (Ideally the caller
+                * has done this already, but many places are sloppy about it.)
                 */
-               findAutoDeletableObjects(&obj, implicit, depRel, false);
+               AcquireDeletionLock(thisobj);
+
+               findDependentObjects(thisobj,
+                                                        DEPFLAG_ORIGINAL,
+                                                        NULL,  /* empty stack */
+                                                        targetObjects,
+                                                        objects,
+                                                        depRel);
        }
 
-       /* Do the deletion. */
-       for (i = 0; i < objects->numrefs; i++)
-       {
-               ObjectAddress obj = objects->refs[i];
-
-               /*
-                * Skip this object if it was already deleted in a previous iteration.
-                */
-               if (object_address_present(&obj, alreadyDeleted))
-                       continue;
+       /*
+        * Check if deletion is allowed, and report about cascaded deletes.
+        *
+        * If there's exactly one object being deleted, report it the same
+        * way as in performDeletion(), else we have to be vaguer.
+        */
+       reportDependentObjects(targetObjects,
+                                                  behavior,
+                                                  NOTICE,
+                                                  (objects->numrefs == 1 ? objects->refs : NULL));
 
-               /*
-                * Skip this object if it's also present in the list of implicit
-                * objects --- it will be deleted later.
-                */
-               if (object_address_present(&obj, implicit))
-                       continue;
+       /*
+        * Delete all the objects in the proper order.
+        */
+       for (i = 0; i < targetObjects->numrefs; i++)
+       {
+               ObjectAddress *thisobj = targetObjects->refs + i;
 
-               /* delete it */
-               performDeletionWithList(&obj, implicit, behavior, alreadyDeleted);
+               deleteOneObject(thisobj, depRel);
        }
 
-       heap_close(depRel, RowExclusiveLock);
+       /* And clean up */
+       free_object_addresses(targetObjects);
 
-       free_object_addresses(implicit);
-       free_object_addresses(alreadyDeleted);
+       heap_close(depRel, RowExclusiveLock);
 }
 
 /*
@@ -338,14 +337,9 @@ void
 deleteWhatDependsOn(const ObjectAddress *object,
                                        bool showNotices)
 {
-       char       *objDescription;
        Relation        depRel;
-       ObjectAddresses *oktodelete;
-
-       /*
-        * Get object description for possible use in failure messages
-        */
-       objDescription = getObjectDescription(object);
+       ObjectAddresses *targetObjects;
+       int                     i;
 
        /*
         * We save some cycles by opening pg_depend just once and passing the
@@ -354,213 +348,155 @@ deleteWhatDependsOn(const ObjectAddress *object,
        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.
+        * Acquire deletion lock on the target object.  (Ideally the caller has
+        * done this already, but many places are sloppy about it.)
         */
-       oktodelete = new_object_addresses();
-
-       findAutoDeletableObjects(object, oktodelete, depRel, true);
+       AcquireDeletionLock(object);
 
        /*
-        * Now invoke only step 2 of recursiveDeletion: just recurse to the stuff
-        * dependent on the given object.
+        * Construct a list of objects to delete (ie, the given object plus
+        * everything directly or indirectly dependent on it).
         */
-       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.
-        */
-
-       free_object_addresses(oktodelete);
-
-       heap_close(depRel, RowExclusiveLock);
-
-       pfree(objDescription);
-}
+       targetObjects = new_object_addresses();
 
-
-/*
- * findAutoDeletableObjects: find all objects that are reachable by AUTO or
- * INTERNAL dependency paths from the given object.  Add them all to the
- * oktodelete list.  If addself is true, the originally given object will also
- * be added to the list.
- *
- * depRel is the already-open pg_depend relation.
- */
-static void
-findAutoDeletableObjects(const ObjectAddress *object,
-                                                ObjectAddresses *oktodelete,
-                                                Relation depRel, bool addself)
-{
-       ScanKeyData key[3];
-       int                     nkeys;
-       SysScanDesc scan;
-       HeapTuple       tup;
-       ObjectAddress otherObject;
+       findDependentObjects(object,
+                                                DEPFLAG_ORIGINAL,
+                                                NULL,  /* empty stack */
+                                                targetObjects,
+                                                NULL,  /* no pendingObjects */
+                                                depRel);
 
        /*
-        * 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.
+        * Check if deletion is allowed, and report about cascaded deletes.
         */
-       if (object_address_present(object, oktodelete))
-               return;
-       if (addself)
-               add_exact_object_address(object, oktodelete);
+       reportDependentObjects(targetObjects,
+                                                  DROP_CASCADE,
+                                                  showNotices ? NOTICE : DEBUG2,
+                                                  object);
 
        /*
-        * 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.
+        * Delete all the objects in the proper order, except we skip the original
+        * object.
         */
-       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)))
+       for (i = 0; i < targetObjects->numrefs; i++)
        {
-               Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
+               ObjectAddress *thisobj = targetObjects->refs + i;
+               ObjectAddressExtra *thisextra = targetObjects->extras + i;
 
-               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, true);
-                               break;
-                       case DEPENDENCY_PIN:
+               if (thisextra->flags & DEPFLAG_ORIGINAL)
+                       continue;
 
-                               /*
-                                * 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;
-               }
+               deleteOneObject(thisobj, depRel);
        }
 
-       systable_endscan(scan);
-}
+       /* And clean up */
+       free_object_addresses(targetObjects);
 
+       heap_close(depRel, RowExclusiveLock);
+}
 
 /*
- * recursiveDeletion: delete a single object for performDeletion, plus
- * (recursively) anything that depends on it.
+ * findDependentObjects - find all objects that depend on 'object'
  *
- * 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.
+ * For every object that depends on the starting object, acquire a deletion
+ * lock on the object, add it to targetObjects (if not already there),
+ * and recursively find objects that depend on it.  An object's dependencies
+ * will be placed into targetObjects before the object itself; this means
+ * that the finished list's order represents a safe deletion order.
  *
+ * The caller must already have a deletion lock on 'object' itself,
+ * but must not have added it to targetObjects.  (Note: there are corner
+ * cases where we won't add the object either, and will also release the
+ * caller-taken lock.  This is a bit ugly, but the API is set up this way
+ * to allow easy rechecking of an object's liveness after we lock it.  See
+ * notes within the function.)
  *
- * 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.)
+ * When dropping a whole object (subId = 0), we find dependencies for
+ * its sub-objects too.
  *
- * 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.
+ *     object: the object to add to targetObjects and find dependencies on
+ *     flags: flags to be ORed into the object's targetObjects entry
+ *     stack: list of objects being visited in current recursion; topmost item
+ *                     is the object that we recursed from (NULL for external callers)
+ *     targetObjects: list of objects that are scheduled to be deleted
+ *     pendingObjects: list of other objects slated for destruction, but
+ *                     not necessarily in targetObjects yet (can be NULL if none)
+ *     depRel: already opened pg_depend relation
  */
-static bool
-recursiveDeletion(const ObjectAddress *object,
-                                 DropBehavior behavior,
-                                 int msglevel,
-                                 const ObjectAddress *callingObject,
-                                 ObjectAddresses *oktodelete,
-                                 Relation depRel,
-                                 ObjectAddresses *alreadyDeleted)
+static void
+findDependentObjects(const ObjectAddress *object,
+                                        int flags,
+                                        ObjectAddressStack *stack,
+                                        ObjectAddresses *targetObjects,
+                                        const ObjectAddresses *pendingObjects,
+                                        Relation depRel)
 {
-       bool            ok = true;
-       char       *objDescription;
        ScanKeyData key[3];
        int                     nkeys;
        SysScanDesc scan;
        HeapTuple       tup;
        ObjectAddress otherObject;
-       ObjectAddress owningObject;
-       bool            amOwned = false;
+       ObjectAddressStack mystack;
+       ObjectAddressExtra extra;
+       ObjectAddressStack *stackptr;
 
        /*
-        * Get object description for possible use in messages.  Must do this
-        * before deleting it ...
+        * If the target object is already being visited in an outer recursion
+        * level, just report the current flags back to that level and exit.
+        * This is needed to avoid infinite recursion in the face of circular
+        * dependencies.
+        *
+        * The stack check alone would result in dependency loops being broken at
+        * an arbitrary point, ie, the first member object of the loop to be
+        * visited is the last one to be deleted.  This is obviously unworkable.
+        * However, the check for internal dependency below guarantees that we
+        * will not break a loop at an internal dependency: if we enter the loop
+        * at an "owned" object we will switch and start at the "owning" object
+        * instead.  We could probably hack something up to avoid breaking at an
+        * auto dependency, too, if we had to.  However there are no known cases
+        * where that would be necessary.
         */
-       objDescription = getObjectDescription(object);
+       for (stackptr = stack; stackptr; stackptr = stackptr->next)
+       {
+               if (object->classId == stackptr->object->classId &&
+                       object->objectId == stackptr->object->objectId)
+               {
+                       if (object->objectSubId == stackptr->object->objectSubId)
+                       {
+                               stackptr->flags |= flags;
+                               return;
+                       }
+                       /*
+                        * Could visit column with whole table already on stack; this is
+                        * the same case noted in object_address_present_add_flags().
+                        * (It's not clear this can really happen, but we might as well
+                        * check.)
+                        */
+                       if (stackptr->object->objectSubId == 0)
+                               return;
+               }
+       }
 
        /*
-        * 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.
+        * It's also possible that the target object has already been completely
+        * processed and put into targetObjects.  If so, again we just add the
+        * specified flags to its entry and return.
         *
-        * When dropping a whole object (subId = 0), remove all pg_depend records
-        * for its sub-objects too.
+        * (Note: in these early-exit cases we could release the caller-taken
+        * lock, since the object is presumably now locked multiple times;
+        * but it seems not worth the cycles.)
+        */
+       if (object_address_present_add_flags(object, flags, targetObjects))
+               return;
+
+       /*
+        * The target object might be internally dependent on some other object
+        * (its "owner").  If so, and if we aren't recursing from the owning
+        * object, we have to transform this deletion request into a deletion
+        * request of the owning object.  (We'll eventually recurse back to this
+        * object, but the owning object has to be visited first so it will be
+        * deleted after.)  The way to find out about this is to scan the
+        * pg_depend entries that show what this object depends on.
         */
        ScanKeyInit(&key[0],
                                Anum_pg_depend_classid,
@@ -606,16 +542,28 @@ recursiveDeletion(const ObjectAddress *object,
                                 *
                                 * 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.)
+                                * dependencies are likely to be interesting.)  However, if
+                                * the other object is listed in pendingObjects, just release
+                                * the caller's lock and return; we'll eventually complete
+                                * the DROP when we reach that entry in the pending list.
                                 */
-                               if (callingObject == NULL)
+                               if (stack == NULL)
                                {
-                                       char       *otherObjDesc = getObjectDescription(&otherObject);
+                                       char       *otherObjDesc;
 
+                                       if (object_address_present(&otherObject, pendingObjects))
+                                       {
+                                               systable_endscan(scan);
+                                               /* need to release caller's lock; see notes below */
+                                               ReleaseDeletionLock(object);
+                                               return;
+                                       }
+                                       otherObjDesc = getObjectDescription(&otherObject);
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
                                                         errmsg("cannot drop %s because %s requires it",
-                                                                       objDescription, otherObjDesc),
+                                                                       getObjectDescription(object),
+                                                                       otherObjDesc),
                                                         errhint("You can drop %s instead.",
                                                                         otherObjDesc)));
                                }
@@ -626,27 +574,52 @@ recursiveDeletion(const ObjectAddress *object,
                                 * recursing from a whole object that includes the nominal
                                 * other end as a component, too.
                                 */
-                               if (callingObject->classId == otherObject.classId &&
-                                       callingObject->objectId == otherObject.objectId &&
-                                       (callingObject->objectSubId == otherObject.objectSubId ||
-                                        callingObject->objectSubId == 0))
+                               if (stack->object->classId == otherObject.classId &&
+                                       stack->object->objectId == otherObject.objectId &&
+                                       (stack->object->objectSubId == otherObject.objectSubId ||
+                                        stack->object->objectSubId == 0))
                                        break;
 
                                /*
                                 * 3. When recursing from anyplace else, transform this
-                                * deletion request into a delete of the other object. (This
-                                * will be an error condition iff RESTRICT mode.) In this case
-                                * we finish deleting my dependencies except for the INTERNAL
-                                * link, which will be needed to cause the owning object to
-                                * recurse back to me.
+                                * deletion request into a delete of the other object.
+                                *
+                                * First, release caller's lock on this object and get
+                                * deletion lock on the other object.  (We must release
+                                * caller's lock to avoid deadlock against a concurrent
+                                * deletion of the other object.)
                                 */
-                               if (amOwned)    /* shouldn't happen */
-                                       elog(ERROR, "multiple INTERNAL dependencies for %s",
-                                                objDescription);
-                               owningObject = otherObject;
-                               amOwned = true;
-                               /* "continue" bypasses the simple_heap_delete call below */
-                               continue;
+                               ReleaseDeletionLock(object);
+                               AcquireDeletionLock(&otherObject);
+
+                               /*
+                                * The other object might have been deleted while we waited
+                                * to lock it; if so, neither it nor the current object are
+                                * interesting anymore.  We test this by checking the
+                                * pg_depend entry (see notes below).
+                                */
+                               if (!systable_recheck_tuple(scan, tup))
+                               {
+                                       systable_endscan(scan);
+                                       ReleaseDeletionLock(&otherObject);
+                                       return;
+                               }
+
+                               /*
+                                * Okay, recurse to the other object instead of proceeding.
+                                * We treat this exactly as if the original reference had
+                                * linked to that object instead of this one; hence, pass
+                                * through the same flags and stack.
+                                */
+                               findDependentObjects(&otherObject,
+                                                                        flags,
+                                                                        stack,
+                                                                        targetObjects,
+                                                                        pendingObjects,
+                                                                        depRel);
+                               /* And we're done here. */
+                               systable_endscan(scan);
+                               return;
                        case DEPENDENCY_PIN:
 
                                /*
@@ -654,172 +627,334 @@ recursiveDeletion(const ObjectAddress *object,
                                 * the depender fields...
                                 */
                                elog(ERROR, "incorrect use of PIN dependency with %s",
-                                        objDescription);
+                                        getObjectDescription(object));
                                break;
                        default:
                                elog(ERROR, "unrecognized dependency type '%c' for %s",
-                                        foundDep->deptype, objDescription);
+                                        foundDep->deptype, getObjectDescription(object));
                                break;
                }
-
-               /* delete the pg_depend tuple */
-               simple_heap_delete(depRel, &tup->t_self);
        }
 
        systable_endscan(scan);
 
        /*
-        * CommandCounterIncrement here to ensure that preceding changes are all
-        * visible; in particular, that the above deletions of pg_depend entries
-        * are visible.  That prevents infinite recursion in case of a dependency
-        * loop (which is perfectly legal).
+        * Now recurse to any dependent objects.  We must visit them first
+        * since they have to be deleted before the current object.
         */
-       CommandCounterIncrement();
+       mystack.object = object;        /* set up a new stack level */
+       mystack.flags = flags;
+       mystack.next = stack;
 
-       /*
-        * If we found we are owned by another object, ask it to delete itself
-        * instead of proceeding.  Complain if RESTRICT mode, unless the other
-        * object is in oktodelete.
-        */
-       if (amOwned)
+       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)
        {
-               if (object_address_present(&owningObject, oktodelete))
-                       ereport(DEBUG2,
-                                       (errmsg("drop auto-cascades to %s",
-                                                       getObjectDescription(&owningObject))));
-               else if (behavior == DROP_RESTRICT)
+               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);
+               int             subflags;
+
+               otherObject.classId = foundDep->classid;
+               otherObject.objectId = foundDep->objid;
+               otherObject.objectSubId = foundDep->objsubid;
+
+               /*
+                * Must lock the dependent object before recursing to it.
+                */
+               AcquireDeletionLock(&otherObject);
+
+               /*
+                * The dependent object might have been deleted while we waited
+                * to lock it; if so, we don't need to do anything more with it.
+                * We can test this cheaply and independently of the object's type
+                * by seeing if the pg_depend tuple we are looking at is still live.
+                * (If the object got deleted, the tuple would have been deleted too.)
+                */
+               if (!systable_recheck_tuple(scan, tup))
                {
-                       ereport(msglevel,
-                                       (errmsg("%s depends on %s",
-                                                       getObjectDescription(&owningObject),
-                                                       objDescription)));
-                       ok = false;
+                       /* release the now-useless lock */
+                       ReleaseDeletionLock(&otherObject);
+                       /* and continue scanning for dependencies */
+                       continue;
                }
-               else
-                       ereport(msglevel,
-                                       (errmsg("drop cascades to %s",
-                                                       getObjectDescription(&owningObject))));
 
-               if (!recursiveDeletion(&owningObject, behavior, msglevel,
-                                                          object, oktodelete, depRel, alreadyDeleted))
-                       ok = false;
+               /* Recurse, passing flags indicating the dependency type */
+               switch (foundDep->deptype)
+               {
+                       case DEPENDENCY_NORMAL:
+                               subflags = DEPFLAG_NORMAL;
+                               break;
+                       case DEPENDENCY_AUTO:
+                               subflags = DEPFLAG_AUTO;
+                               break;
+                       case DEPENDENCY_INTERNAL:
+                               subflags = DEPFLAG_INTERNAL;
+                               break;
+                       case DEPENDENCY_PIN:
 
-               pfree(objDescription);
+                               /*
+                                * For a PIN dependency we just ereport immediately; there
+                                * won't be any others to report.
+                                */
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+                                                errmsg("cannot drop %s because it is required by the database system",
+                                                               getObjectDescription(object))));
+                               subflags = 0;   /* keep compiler quiet */
+                               break;
+                       default:
+                               elog(ERROR, "unrecognized dependency type '%c' for %s",
+                                        foundDep->deptype, getObjectDescription(object));
+                               subflags = 0;   /* keep compiler quiet */
+                               break;
+               }
 
-               return ok;
+               findDependentObjects(&otherObject,
+                                                        subflags,
+                                                        &mystack,
+                                                        targetObjects,
+                                                        pendingObjects,
+                                                        depRel);
        }
 
-       /*
-        * Step 2: scan pg_depend records that link to this object, showing the
-        * things that depend on it.  Recursively delete those things. Note it's
-        * important to delete the dependent objects before the referenced one,
-        * since the deletion routines might do things like try to update the
-        * pg_class record when deleting a check constraint.
-        */
-       if (!deleteDependentObjects(object, objDescription,
-                                                               behavior, msglevel,
-                                                               oktodelete, depRel))
-               ok = false;
+       systable_endscan(scan);
 
        /*
-        * We do not need CommandCounterIncrement here, since if step 2 did
-        * anything then each recursive call will have ended with one.
+        * Finally, we can add the target object to targetObjects.  Be careful
+        * to include any flags that were passed back down to us from inner
+        * recursion levels.
         */
+       extra.flags = mystack.flags;
+       if (stack)
+               extra.dependee = *stack->object;
+       else
+               memset(&extra.dependee, 0, sizeof(extra.dependee));
+       add_exact_object_address_extra(object, &extra, targetObjects);
+}
 
-       /*
-        * Step 3: delete the object itself, and save it to the list of deleted
-        * objects if appropiate.
-        */
-       doDeletion(object);
-       if (alreadyDeleted != NULL)
-       {
-               if (!object_address_present(object, alreadyDeleted))
-                       add_exact_object_address(object, alreadyDeleted);
-       }
+/*
+ * reportDependentObjects - report about dependencies, and fail if RESTRICT
+ *
+ * Tell the user about dependent objects that we are going to delete
+ * (or would need to delete, but are prevented by RESTRICT mode);
+ * then error out if there are any and it's not CASCADE mode.
+ *
+ *     targetObjects: list of objects that are scheduled to be deleted
+ *     behavior: RESTRICT or CASCADE
+ *     msglevel: elog level for non-error report messages
+ *     origObject: base object of deletion, or NULL if not available
+ *             (the latter case occurs in DROP OWNED)
+ */
+static void
+reportDependentObjects(const ObjectAddresses *targetObjects,
+                                          DropBehavior behavior,
+                                          int msglevel,
+                                          const ObjectAddress *origObject)
+{
+       bool            ok = true;
+       StringInfoData clientdetail;
+       StringInfoData logdetail;
+       int                     numReportedClient = 0;
+       int                     numNotReportedClient = 0;
+       int                     i;
 
        /*
-        * Delete any comments associated with this object.  (This is a convenient
-        * place to do it instead of having every object type know to do it.)
+        * If no error is to be thrown, and the msglevel is too low to be shown
+        * to either client or server log, there's no need to do any of the work.
+        *
+        * Note: this code doesn't know all there is to be known about elog
+        * levels, but it works for NOTICE and DEBUG2, which are the only values
+        * msglevel can currently have.  We also assume we are running in a normal
+        * operating environment.
         */
-       DeleteComments(object->objectId, object->classId, object->objectSubId);
+       if (behavior == DROP_CASCADE &&
+               msglevel < client_min_messages &&
+               (msglevel < log_min_messages || log_min_messages == LOG))
+               return;
 
        /*
-        * Delete shared dependency references related to this object. Sub-objects
-        * (columns) don't have dependencies on global objects, so skip them.
+        * We limit the number of dependencies reported to the client to
+        * MAX_REPORTED_DEPS, since client software may not deal well with
+        * enormous error strings.      The server log always gets a full report.
         */
-       if (object->objectSubId == 0)
-               deleteSharedDependencyRecordsFor(object->classId, object->objectId);
+#define MAX_REPORTED_DEPS 100
 
-       /*
-        * CommandCounterIncrement here to ensure that preceding changes are all
-        * visible.
-        */
-       CommandCounterIncrement();
+       initStringInfo(&clientdetail);
+       initStringInfo(&logdetail);
 
        /*
-        * And we're done!
+        * We process the list back to front (ie, in dependency order not deletion
+        * order), since this makes for a more understandable display.
         */
-       pfree(objDescription);
+       for (i = targetObjects->numrefs - 1; i >= 0; i--)
+       {
+               const ObjectAddress *obj = &targetObjects->refs[i];
+               const ObjectAddressExtra *extra = &targetObjects->extras[i];
+               char       *objDesc;
 
-       return ok;
-}
+               /* Ignore the original deletion target(s) */
+               if (extra->flags & DEPFLAG_ORIGINAL)
+                       continue;
+
+               objDesc = getObjectDescription(obj);
+
+               /*
+                * If, at any stage of the recursive search, we reached the object
+                * via an AUTO or INTERNAL dependency, then it's okay to delete it
+                * even in RESTRICT mode.
+                */
+               if (extra->flags & (DEPFLAG_AUTO | DEPFLAG_INTERNAL))
+               {
+                       /*
+                        * auto-cascades are reported at DEBUG2, not msglevel.  We
+                        * don't try to combine them with the regular message because
+                        * the results are too confusing when client_min_messages and
+                        * log_min_messages are different.
+                        */
+                       ereport(DEBUG2,
+                                       (errmsg("drop auto-cascades to %s",
+                                                       objDesc)));
+               }
+               else if (behavior == DROP_RESTRICT)
+               {
+                       char   *otherDesc = getObjectDescription(&extra->dependee);
+
+                       if (numReportedClient < MAX_REPORTED_DEPS)
+                       {
+                               /* separate entries with a newline */
+                               if (clientdetail.len != 0)
+                                       appendStringInfoChar(&clientdetail, '\n');
+                               appendStringInfo(&clientdetail, _("%s depends on %s"),
+                                                                objDesc, otherDesc);
+                               numReportedClient++;
+                       }
+                       else
+                               numNotReportedClient++;
+                       /* separate entries with a newline */
+                       if (logdetail.len != 0)
+                               appendStringInfoChar(&logdetail, '\n');
+                       appendStringInfo(&logdetail, _("%s depends on %s"),
+                                                        objDesc, otherDesc);
+                       pfree(otherDesc);
+                       ok = false;
+               }
+               else
+               {
+                       if (numReportedClient < MAX_REPORTED_DEPS)
+                       {
+                               /* separate entries with a newline */
+                               if (clientdetail.len != 0)
+                                       appendStringInfoChar(&clientdetail, '\n');
+                               appendStringInfo(&clientdetail, _("drop cascades to %s"),
+                                                                objDesc);
+                               numReportedClient++;
+                       }
+                       else
+                               numNotReportedClient++;
+                       /* separate entries with a newline */
+                       if (logdetail.len != 0)
+                               appendStringInfoChar(&logdetail, '\n');
+                       appendStringInfo(&logdetail, _("drop cascades to %s"),
+                                                        objDesc);
+               }
+
+               pfree(objDesc);
+       }
+
+       if (numNotReportedClient > 0)
+               appendStringInfo(&clientdetail, _("\nand %d other objects "
+                                                                                 "(see server log for list)"),
+                                                numNotReportedClient);
+
+       if (!ok)
+       {
+               if (origObject)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+                                        errmsg("cannot drop %s because other objects depend on it",
+                                                       getObjectDescription(origObject)),
+                                        errdetail("%s", clientdetail.data),
+                                        errdetail_log("%s", logdetail.data),
+                                        errhint("Use DROP ... CASCADE to drop the dependent objects too.")));
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+                                        errmsg("cannot drop desired object(s) because other objects depend on them"),
+                                        errdetail("%s", clientdetail.data),
+                                        errdetail_log("%s", logdetail.data),
+                                        errhint("Use DROP ... CASCADE to drop the dependent objects too.")));
+       }
+       else if (numReportedClient > 1)
+       {
+               ereport(msglevel,
+                               /* translator: %d always has a value larger than 1 */
+                               (errmsg("drop cascades to %d other objects",
+                                               numReportedClient + numNotReportedClient),
+                                errdetail("%s", clientdetail.data),
+                                errdetail_log("%s", logdetail.data)));
+       }
+       else if (numReportedClient == 1)
+       {
+               /* we just use the single item as-is */
+               ereport(msglevel,
+                               (errmsg_internal("%s", clientdetail.data)));
+       }
 
+       pfree(clientdetail.data);
+       pfree(logdetail.data);
+}
 
 /*
- * deleteDependentObjects - find and delete objects that depend on 'object'
- *
- * Scan pg_depend records that link to the given object, showing
- * the things that depend on it.  Recursively delete those things. (We
- * don't delete the pg_depend records here, as the recursive call will
- * do that.)  Note it's important to delete the dependent objects
- * before the referenced one, since the deletion routines might do
- * things like try to update the pg_class record when deleting a check
- * constraint.
- *
- * When dropping a whole object (subId = 0), find pg_depend records for
- * its sub-objects too.
+ * deleteOneObject: delete a single object for performDeletion.
  *
- *     object: the object to find dependencies on
- *     objDescription: description of object (only used for error messages)
- *     behavior: desired drop behavior
- *     oktodelete: stuff that's AUTO-deletable
- *     depRel: already opened pg_depend relation
- *
- * Returns TRUE if all is well, false if any problem found.
- *
- * NOTE: because we are using SnapshotNow, if a recursive call deletes
- * any pg_depend tuples that our scan hasn't yet visited, we will not
- * see them as good when we do visit them.     This is essential for
- * correct behavior if there are multiple dependency paths between two
- * objects --- else we might try to delete an already-deleted object.
+ * depRel is the already-open pg_depend relation.
  */
-static bool
-deleteDependentObjects(const ObjectAddress *object,
-                                          const char *objDescription,
-                                          DropBehavior behavior,
-                                          int msglevel,
-                                          ObjectAddresses *oktodelete,
-                                          Relation depRel)
+static void
+deleteOneObject(const ObjectAddress *object, Relation depRel)
 {
-       bool            ok = true;
        ScanKeyData key[3];
        int                     nkeys;
        SysScanDesc scan;
        HeapTuple       tup;
-       ObjectAddress otherObject;
 
+       /*
+        * First remove any pg_depend records that link from this object to
+        * others.  (Any records linking to this object should be gone already.)
+        *
+        * When dropping a whole object (subId = 0), remove all pg_depend records
+        * for its sub-objects too.
+        */
        ScanKeyInit(&key[0],
-                               Anum_pg_depend_refclassid,
+                               Anum_pg_depend_classid,
                                BTEqualStrategyNumber, F_OIDEQ,
                                ObjectIdGetDatum(object->classId));
        ScanKeyInit(&key[1],
-                               Anum_pg_depend_refobjid,
+                               Anum_pg_depend_objid,
                                BTEqualStrategyNumber, F_OIDEQ,
                                ObjectIdGetDatum(object->objectId));
        if (object->objectSubId != 0)
        {
                ScanKeyInit(&key[2],
-                                       Anum_pg_depend_refobjsubid,
+                                       Anum_pg_depend_objsubid,
                                        BTEqualStrategyNumber, F_INT4EQ,
                                        Int32GetDatum(object->objectSubId));
                nkeys = 3;
@@ -827,88 +962,45 @@ deleteDependentObjects(const ObjectAddress *object,
        else
                nkeys = 2;
 
-       scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+       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->classid;
-               otherObject.objectId = foundDep->objid;
-               otherObject.objectSubId = foundDep->objsubid;
-
-               switch (foundDep->deptype)
-               {
-                       case DEPENDENCY_NORMAL:
-
-                               /*
-                                * Perhaps there was another dependency path that would have
-                                * allowed silent deletion of the otherObject, had we only
-                                * taken that path first. In that case, act like this link is
-                                * AUTO, too.
-                                */
-                               if (object_address_present(&otherObject, oktodelete))
-                                       ereport(DEBUG2,
-                                                       (errmsg("drop auto-cascades to %s",
-                                                                       getObjectDescription(&otherObject))));
-                               else if (behavior == DROP_RESTRICT)
-                               {
-                                       ereport(msglevel,
-                                                       (errmsg("%s depends on %s",
-                                                                       getObjectDescription(&otherObject),
-                                                                       objDescription)));
-                                       ok = false;
-                               }
-                               else
-                                       ereport(msglevel,
-                                                       (errmsg("drop cascades to %s",
-                                                                       getObjectDescription(&otherObject))));
+               simple_heap_delete(depRel, &tup->t_self);
+       }
 
-                               if (!recursiveDeletion(&otherObject, behavior, msglevel,
-                                                                          object, oktodelete, depRel, NULL))
-                                       ok = false;
-                               break;
-                       case DEPENDENCY_AUTO:
-                       case DEPENDENCY_INTERNAL:
+       systable_endscan(scan);
 
-                               /*
-                                * We propagate the DROP without complaint even in the
-                                * RESTRICT case.  (However, normal dependencies on the
-                                * component object could still cause failure.)
-                                */
-                               ereport(DEBUG2,
-                                               (errmsg("drop auto-cascades to %s",
-                                                               getObjectDescription(&otherObject))));
+       /*
+        * Now delete the object itself, in an object-type-dependent way.
+        */
+       doDeletion(object);
 
-                               if (!recursiveDeletion(&otherObject, behavior, msglevel,
-                                                                          object, oktodelete, depRel, NULL))
-                                       ok = false;
-                               break;
-                       case DEPENDENCY_PIN:
+       /*
+        * Delete any comments associated with this object.  (This is a convenient
+        * place to do it instead of having every object type know to do it.)
+        */
+       DeleteComments(object->objectId, object->classId, object->objectSubId);
 
-                               /*
-                                * For a PIN dependency we just ereport immediately; there
-                                * won't be any others to report.
-                                */
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
-                                                errmsg("cannot drop %s because it is required by the database system",
-                                                               objDescription)));
-                               break;
-                       default:
-                               elog(ERROR, "unrecognized dependency type '%c' for %s",
-                                        foundDep->deptype, objDescription);
-                               break;
-               }
-       }
+       /*
+        * Delete shared dependency references related to this object. Sub-objects
+        * (columns) don't have dependencies on global objects, so skip them.
+        */
+       if (object->objectSubId == 0)
+               deleteSharedDependencyRecordsFor(object->classId, object->objectId);
 
-       systable_endscan(scan);
+       /*
+        * CommandCounterIncrement here to ensure that preceding changes are all
+        * visible to the next deletion step.
+        */
+       CommandCounterIncrement();
 
-       return ok;
+       /*
+        * And we're done!
+        */
 }
 
-
 /*
  * doDeletion: actually delete a single object
  */
@@ -1021,6 +1113,38 @@ doDeletion(const ObjectAddress *object)
        }
 }
 
+/*
+ * AcquireDeletionLock - acquire a suitable lock for deleting an object
+ *
+ * We use LockRelation for relations, LockDatabaseObject for everything
+ * else.  Note that dependency.c is not concerned with deleting any kind of
+ * shared-across-databases object, so we have no need for LockSharedObject.
+ */
+static void
+AcquireDeletionLock(const ObjectAddress *object)
+{
+       if (object->classId == RelationRelationId)
+               LockRelationOid(object->objectId, AccessExclusiveLock);
+       else
+               /* assume we should lock the whole object not a sub-object */
+               LockDatabaseObject(object->classId, object->objectId, 0,
+                                                  AccessExclusiveLock);
+}
+
+/*
+ * ReleaseDeletionLock - release an object deletion lock
+ */
+static void
+ReleaseDeletionLock(const ObjectAddress *object)
+{
+       if (object->classId == RelationRelationId)
+               UnlockRelationOid(object->objectId, AccessExclusiveLock);
+       else
+               /* assume we should lock the whole object not a sub-object */
+               UnlockDatabaseObject(object->classId, object->objectId, 0,
+                                                        AccessExclusiveLock);
+}
+
 /*
  * recordDependencyOnExpr - find expression dependencies
  *
@@ -1115,15 +1239,12 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
                                thisobj->objectId == relId)
                        {
                                /* Move this ref into self_addrs */
-                               add_object_address(OCLASS_CLASS, relId, thisobj->objectSubId,
-                                                                  self_addrs);
+                               add_exact_object_address(thisobj, self_addrs);
                        }
                        else
                        {
                                /* Keep it in context.addrs */
-                               outobj->classId = thisobj->classId;
-                               outobj->objectId = thisobj->objectId;
-                               outobj->objectSubId = thisobj->objectSubId;
+                               *outobj = *thisobj;
                                outobj++;
                                outrefs++;
                        }
@@ -1474,6 +1595,13 @@ eliminate_duplicate_dependencies(ObjectAddresses *addrs)
        int                     oldref,
                                newrefs;
 
+       /*
+        * We can't sort if the array has "extra" data, because there's no way
+        * to keep it in sync.  Fortunately that combination of features is
+        * not needed.
+        */
+       Assert(!addrs->extras);
+
        if (addrs->numrefs <= 1)
                return;                                 /* nothing to do */
 
@@ -1510,9 +1638,7 @@ eliminate_duplicate_dependencies(ObjectAddresses *addrs)
                }
                /* Not identical, so add thisobj to output set */
                priorobj++;
-               priorobj->classId = thisobj->classId;
-               priorobj->objectId = thisobj->objectId;
-               priorobj->objectSubId = thisobj->objectSubId;
+               *priorobj = *thisobj;
                newrefs++;
        }
 
@@ -1564,6 +1690,7 @@ new_object_addresses(void)
        addrs->maxrefs = 32;
        addrs->refs = (ObjectAddress *)
                palloc(addrs->maxrefs * sizeof(ObjectAddress));
+       addrs->extras = NULL;           /* until/unless needed */
 
        return addrs;
 }
@@ -1586,6 +1713,7 @@ add_object_address(ObjectClass oclass, Oid objectId, int32 subId,
                addrs->maxrefs *= 2;
                addrs->refs = (ObjectAddress *)
                        repalloc(addrs->refs, addrs->maxrefs * sizeof(ObjectAddress));
+               Assert(!addrs->extras);
        }
        /* record this item */
        item = addrs->refs + addrs->numrefs;
@@ -1612,10 +1740,46 @@ add_exact_object_address(const ObjectAddress *object,
                addrs->maxrefs *= 2;
                addrs->refs = (ObjectAddress *)
                        repalloc(addrs->refs, addrs->maxrefs * sizeof(ObjectAddress));
+               Assert(!addrs->extras);
+       }
+       /* record this item */
+       item = addrs->refs + addrs->numrefs;
+       *item = *object;
+       addrs->numrefs++;
+}
+
+/*
+ * Add an entry to an ObjectAddresses array.
+ *
+ * As above, but specify entry exactly and provide some "extra" data too.
+ */
+static void
+add_exact_object_address_extra(const ObjectAddress *object,
+                                                          const ObjectAddressExtra *extra,
+                                                          ObjectAddresses *addrs)
+{
+       ObjectAddress *item;
+       ObjectAddressExtra *itemextra;
+
+       /* allocate extra space if first time */
+       if (!addrs->extras)
+               addrs->extras = (ObjectAddressExtra *)
+                       palloc(addrs->maxrefs * sizeof(ObjectAddressExtra));
+
+       /* enlarge array if needed */
+       if (addrs->numrefs >= addrs->maxrefs)
+       {
+               addrs->maxrefs *= 2;
+               addrs->refs = (ObjectAddress *)
+                       repalloc(addrs->refs, addrs->maxrefs * sizeof(ObjectAddress));
+               addrs->extras = (ObjectAddressExtra *)
+                 repalloc(addrs->extras, addrs->maxrefs * sizeof(ObjectAddressExtra));
        }
        /* record this item */
        item = addrs->refs + addrs->numrefs;
        *item = *object;
+       itemextra = addrs->extras + addrs->numrefs;
+       *itemextra = *extra;
        addrs->numrefs++;
 }
 
@@ -1626,13 +1790,13 @@ add_exact_object_address(const ObjectAddress *object,
  */
 bool
 object_address_present(const ObjectAddress *object,
-                                          ObjectAddresses *addrs)
+                                          const ObjectAddresses *addrs)
 {
        int                     i;
 
        for (i = addrs->numrefs - 1; i >= 0; i--)
        {
-               ObjectAddress *thisobj = addrs->refs + i;
+               const ObjectAddress *thisobj = addrs->refs + i;
 
                if (object->classId == thisobj->classId &&
                        object->objectId == thisobj->objectId)
@@ -1646,6 +1810,47 @@ object_address_present(const ObjectAddress *object,
        return false;
 }
 
+/*
+ * As above, except that if the object is present then also OR the given
+ * flags into its associated extra data (which must exist).
+ */
+static bool
+object_address_present_add_flags(const ObjectAddress *object,
+                                                                int flags,
+                                                                ObjectAddresses *addrs)
+{
+       int                     i;
+
+       for (i = addrs->numrefs - 1; i >= 0; i--)
+       {
+               ObjectAddress *thisobj = addrs->refs + i;
+
+               if (object->classId == thisobj->classId &&
+                       object->objectId == thisobj->objectId)
+               {
+                       if (object->objectSubId == thisobj->objectSubId)
+                       {
+                               ObjectAddressExtra *thisextra = addrs->extras + i;
+
+                               thisextra->flags |= flags;
+                               return true;
+                       }
+                       if (thisobj->objectSubId == 0)
+                       {
+                               /*
+                                * We get here if we find a need to delete a column after
+                                * having already decided to drop its whole table.  Obviously
+                                * we no longer need to drop the column.  But don't plaster
+                                * its flags on the table.
+                                */
+                               return true;
+                       }
+               }
+       }
+
+       return false;
+}
+
 /*
  * Record multiple dependencies from an ObjectAddresses array, after first
  * removing any duplicates.
@@ -1668,6 +1873,8 @@ void
 free_object_addresses(ObjectAddresses *addrs)
 {
        pfree(addrs->refs);
+       if (addrs->extras)
+               pfree(addrs->extras);
        pfree(addrs);
 }
 
@@ -2003,6 +2210,7 @@ getObjectDescription(const ObjectAddress *object)
                                SysScanDesc amscan;
                                HeapTuple       tup;
                                Form_pg_amop amopForm;
+                               StringInfoData opfam;
 
                                amopDesc = heap_open(AccessMethodOperatorRelationId,
                                                                         AccessShareLock);
@@ -2023,10 +2231,18 @@ getObjectDescription(const ObjectAddress *object)
 
                                amopForm = (Form_pg_amop) GETSTRUCT(tup);
 
-                               appendStringInfo(&buffer, _("operator %d %s of "),
+                               initStringInfo(&opfam);
+                               getOpFamilyDescription(&opfam, amopForm->amopfamily);
+                               /*
+                                * translator: %d is the operator strategy (a number), the
+                                * first %s is the textual form of the operator, and the second
+                                * %s is the description of the operator family.
+                                */
+                               appendStringInfo(&buffer, _("operator %d %s of %s"),
                                                                 amopForm->amopstrategy,
-                                                                format_operator(amopForm->amopopr));
-                               getOpFamilyDescription(&buffer, amopForm->amopfamily);
+                                                                format_operator(amopForm->amopopr),
+                                                                opfam.data);
+                               pfree(opfam.data);
 
                                systable_endscan(amscan);
                                heap_close(amopDesc, AccessShareLock);
@@ -2040,6 +2256,7 @@ getObjectDescription(const ObjectAddress *object)
                                SysScanDesc amscan;
                                HeapTuple       tup;
                                Form_pg_amproc amprocForm;
+                               StringInfoData opfam;
 
                                amprocDesc = heap_open(AccessMethodProcedureRelationId,
                                                                           AccessShareLock);
@@ -2060,10 +2277,18 @@ getObjectDescription(const ObjectAddress *object)
 
                                amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
 
-                               appendStringInfo(&buffer, _("function %d %s of "),
+                               initStringInfo(&opfam);
+                               getOpFamilyDescription(&opfam, amprocForm->amprocfamily);
+                               /*
+                                * translator: %d is the function number, the first %s is the
+                                * textual form of the function with arguments, and the second
+                                * %s is the description of the operator family.
+                                */
+                               appendStringInfo(&buffer, _("function %d %s of %s"),
                                                                 amprocForm->amprocnum,
-                                                                format_procedure(amprocForm->amproc));
-                               getOpFamilyDescription(&buffer, amprocForm->amprocfamily);
+                                                                format_procedure(amprocForm->amproc),
+                                                                opfam.data);
+                               pfree(opfam.data);
 
                                systable_endscan(amscan);
                                heap_close(amprocDesc, AccessShareLock);