]> granicus.if.org Git - postgresql/commitdiff
Rewrite DROP's dependency traversal algorithm into an honest two-pass
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 8 Jun 2008 22:41:04 +0000 (22:41 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 8 Jun 2008 22:41:04 +0000 (22:41 +0000)
algorithm, replacing the original intention of a one-pass search, which
had been hacked up over time to be partially two-pass in hopes of handling
various corner cases better.  It still wasn't quite there, especially as
regards emitting unwanted NOTICE messages.  More importantly, this approach
lets us fix a number of open bugs concerning concurrent DROP scenarios,
because we can take locks during the first pass and avoid traversing to
dependent objects that were just deleted by someone else.

There is more that can be done here, but I'll go ahead and commit the
base patch before working on the options.

15 files changed:
src/backend/access/index/genam.c
src/backend/catalog/dependency.c
src/include/access/genam.h
src/include/catalog/dependency.h
src/test/regress/expected/alter_table.out
src/test/regress/expected/create_view.out
src/test/regress/expected/domain.out
src/test/regress/expected/foreign_key.out
src/test/regress/expected/inherit.out
src/test/regress/expected/namespace.out
src/test/regress/expected/plancache.out
src/test/regress/expected/privileges.out
src/test/regress/expected/subselect.out
src/test/regress/expected/truncate.out
src/test/regress/output/tablespace.source

index e8958abca2b4538052d2ed19be3f7e3e1bdcaf01..301062951b6dca8d0fe91191193e90e926c6a118 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.68 2008/04/13 19:18:14 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.69 2008/06/08 22:41:04 tgl Exp $
  *
  * NOTES
  *       many of the old access method routines have been turned into
@@ -251,6 +251,47 @@ systable_getnext(SysScanDesc sysscan)
        return htup;
 }
 
+/*
+ * systable_recheck_tuple --- recheck visibility of most-recently-fetched tuple
+ *
+ * This is useful to test whether an object was deleted while we waited to
+ * acquire lock on it.
+ *
+ * Note: we don't actually *need* the tuple to be passed in, but it's a
+ * good crosscheck that the caller is interested in the right tuple.
+ */
+bool
+systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
+{
+       bool            result;
+
+       if (sysscan->irel)
+       {
+               IndexScanDesc scan = sysscan->iscan;
+
+               Assert(tup == &scan->xs_ctup);
+               Assert(BufferIsValid(scan->xs_cbuf));
+               /* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
+               LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
+               result = HeapTupleSatisfiesVisibility(tup, scan->xs_snapshot,
+                                                                                         scan->xs_cbuf);
+               LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
+       }
+       else
+       {
+               HeapScanDesc scan = sysscan->scan;
+
+               Assert(tup == &scan->rs_ctup);
+               Assert(BufferIsValid(scan->rs_cbuf));
+               /* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
+               LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+               result = HeapTupleSatisfiesVisibility(tup, scan->rs_snapshot,
+                                                                                         scan->rs_cbuf);
+               LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+       }
+       return result;
+}
+
 /*
  * systable_endscan --- close scan, release resources
  *
index 24423d2837e646349803de709feff6a78b99a421..12b654504d7c1c3615c09eab4acf40b178d7881c 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.73 2008/06/05 15:04:39 alvherre Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.74 2008/06/08 22:41:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,6 +59,7 @@
 #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/lsyscache.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
 {
@@ -114,33 +142,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);
 
@@ -153,21 +180,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
@@ -176,83 +199,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
@@ -264,67 +254,66 @@ 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();
-
+       /*
+        * 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];
+               const ObjectAddress *thisobj = 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.
+                * Acquire deletion lock on each target object.  (Ideally the caller
+                * has done this already, but many places are sloppy about it.)
                 */
-               if (object_address_present(&obj, implicit))
-                       continue;
-
-               /*
-                * Add the objects dependent on this one to the global list of
-                * implicit objects.
-                */
-               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.
+        */
+       reportDependentObjects(targetObjects,
+                                                  behavior,
+                                                  NOTICE,
+                                                  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);
 }
 
 /*
@@ -340,14 +329,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
@@ -356,213 +340,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);
-
-       /*
-        * 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)));
+       AcquireDeletionLock(object);
 
        /*
-        * We do not need CommandCounterIncrement here, since if step 2 did
-        * anything then each recursive call will have ended with one.
+        * Construct a list of objects to delete (ie, the given object plus
+        * everything directly or indirectly dependent on it).
         */
+       targetObjects = new_object_addresses();
 
-       free_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.  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)
+       for (i = 0; i < targetObjects->numrefs; i++)
        {
-               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);
+               ObjectAddress *thisobj = targetObjects->refs + i;
+               ObjectAddressExtra *thisextra = targetObjects->extras + i;
 
-       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, 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.
- *
- * Returns TRUE if successful, FALSE if not.
+ * findDependentObjects - find all objects that depend on 'object'
  *
- * callingObject is NULL at the outer level, else identifies the object that
- * we recursed from (the reference object that someone else needs to delete).
+ * 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.
  *
- * 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.
+ * 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.)
  *
+ * When dropping a whole object (subId = 0), we find dependencies for
+ * its sub-objects too.
  *
- * 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.
+ *     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,
@@ -608,16 +534,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)));
                                }
@@ -628,27 +566,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:
 
                                /*
@@ -656,159 +619,24 @@ 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).
-        */
-       CommandCounterIncrement();
-
-       /*
-        * 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)
-       {
-               if (object_address_present(&owningObject, oktodelete))
-                       ereport(DEBUG2,
-                                       (errmsg("drop auto-cascades to %s",
-                                                       getObjectDescription(&owningObject))));
-               else if (behavior == DROP_RESTRICT)
-               {
-                       ereport(msglevel,
-                                       (errmsg("%s depends on %s",
-                                                       getObjectDescription(&owningObject),
-                                                       objDescription)));
-                       ok = false;
-               }
-               else
-                       ereport(msglevel,
-                                       (errmsg("drop cascades to %s",
-                                                       getObjectDescription(&owningObject))));
-
-               if (!recursiveDeletion(&owningObject, behavior, msglevel,
-                                                          object, oktodelete, depRel, alreadyDeleted))
-                       ok = false;
-
-               pfree(objDescription);
-
-               return ok;
-       }
-
-       /*
-        * 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;
-
-       /*
-        * We do not need CommandCounterIncrement here, since if step 2 did
-        * anything then each recursive call will have ended with one.
-        */
-
-       /*
-        * 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);
-       }
-
-       /*
-        * 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);
-
-       /*
-        * 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);
-
-       /*
-        * CommandCounterIncrement here to ensure that preceding changes are all
-        * visible.
-        */
-       CommandCounterIncrement();
-
-       /*
-        * And we're done!
+        * Now recurse to any dependent objects.  We must visit them first
+        * since they have to be deleted before the current object.
         */
-       pfree(objDescription);
-
-       return ok;
-}
-
-
-/*
- * 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.
- *
- *     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.
- */
-static bool
-deleteDependentObjects(const ObjectAddress *object,
-                                          const char *objDescription,
-                                          DropBehavior behavior,
-                                          int msglevel,
-                                          ObjectAddresses *oktodelete,
-                                          Relation depRel)
-{
-       bool            ok = true;
-       ScanKeyData key[3];
-       int                     nkeys;
-       SysScanDesc scan;
-       HeapTuple       tup;
-       ObjectAddress otherObject;
+       mystack.object = object;        /* set up a new stack level */
+       mystack.flags = flags;
+       mystack.next = stack;
 
        ScanKeyInit(&key[0],
                                Anum_pg_depend_refclassid,
@@ -835,57 +663,43 @@ deleteDependentObjects(const ObjectAddress *object,
        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))
+               {
+                       /* release the now-useless lock */
+                       ReleaseDeletionLock(&otherObject);
+                       /* and continue scanning for dependencies */
+                       continue;
+               }
+
+               /* Recurse, passing flags indicating the dependency type */
                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))));
-
-                               if (!recursiveDeletion(&otherObject, behavior, msglevel,
-                                                                          object, oktodelete, depRel, NULL))
-                                       ok = false;
+                               subflags = DEPFLAG_NORMAL;
                                break;
                        case DEPENDENCY_AUTO:
+                               subflags = DEPFLAG_AUTO;
+                               break;
                        case DEPENDENCY_INTERNAL:
-
-                               /*
-                                * 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))));
-
-                               if (!recursiveDeletion(&otherObject, behavior, msglevel,
-                                                                          object, oktodelete, depRel, NULL))
-                                       ok = false;
+                               subflags = DEPFLAG_INTERNAL;
                                break;
                        case DEPENDENCY_PIN:
 
@@ -896,20 +710,190 @@ deleteDependentObjects(const ObjectAddress *object,
                                ereport(ERROR,
                                                (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
                                                 errmsg("cannot drop %s because it is required by the database system",
-                                                               objDescription)));
+                                                               getObjectDescription(object))));
+                               subflags = 0;   /* keep compiler quiet */
                                break;
                        default:
                                elog(ERROR, "unrecognized dependency type '%c' for %s",
-                                        foundDep->deptype, objDescription);
+                                        foundDep->deptype, getObjectDescription(object));
+                               subflags = 0;   /* keep compiler quiet */
                                break;
                }
+
+               findDependentObjects(&otherObject,
+                                                        subflags,
+                                                        &mystack,
+                                                        targetObjects,
+                                                        pendingObjects,
+                                                        depRel);
        }
 
        systable_endscan(scan);
 
-       return ok;
+       /*
+        * 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);
 }
 
+/*
+ * 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-debug notice 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;
+       int                     i;
+
+       /*
+        * We process the list back to front (ie, in dependency order not deletion
+        * order), since this makes for a more understandable display.
+        */
+       for (i = targetObjects->numrefs - 1; i >= 0; i--)
+       {
+               const ObjectAddress *obj = &targetObjects->refs[i];
+               const ObjectAddressExtra *extra = &targetObjects->extras[i];
+
+               /* Ignore the original deletion target(s) */
+               if (extra->flags & DEPFLAG_ORIGINAL)
+                       continue;
+
+               /*
+                * 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))
+                       ereport(DEBUG2,
+                                       (errmsg("drop auto-cascades to %s",
+                                                       getObjectDescription(obj))));
+               else if (behavior == DROP_RESTRICT)
+               {
+                       ereport(msglevel,
+                                       (errmsg("%s depends on %s",
+                                                       getObjectDescription(obj),
+                                                       getObjectDescription(&extra->dependee))));
+                       ok = false;
+               }
+               else
+                       ereport(msglevel,
+                                       (errmsg("drop cascades to %s",
+                                                       getObjectDescription(obj))));
+       }
+
+       if (!ok)
+       {
+               if (origObject)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+                                        errmsg("cannot drop %s because other objects depend on it",
+                                                       getObjectDescription(origObject)),
+                                        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"),
+                                        errhint("Use DROP ... CASCADE to drop the dependent objects too.")));
+       }
+}
+
+/*
+ * deleteOneObject: delete a single object for performDeletion.
+ *
+ * depRel is the already-open pg_depend relation.
+ */
+static void
+deleteOneObject(const ObjectAddress *object, Relation depRel)
+{
+       ScanKeyData key[3];
+       int                     nkeys;
+       SysScanDesc scan;
+       HeapTuple       tup;
+
+       /*
+        * 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_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)))
+       {
+               simple_heap_delete(depRel, &tup->t_self);
+       }
+
+       systable_endscan(scan);
+
+       /*
+        * Now delete the object itself, in an object-type-dependent way.
+        */
+       doDeletion(object);
+
+       /*
+        * 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);
+
+       /*
+        * 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);
+
+       /*
+        * CommandCounterIncrement here to ensure that preceding changes are all
+        * visible to the next deletion step.
+        */
+       CommandCounterIncrement();
+
+       /*
+        * And we're done!
+        */
+}
 
 /*
  * doDeletion: actually delete a single object
@@ -1023,6 +1007,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
  *
@@ -1117,15 +1133,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++;
                        }
@@ -1476,6 +1489,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 */
 
@@ -1512,9 +1532,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++;
        }
 
@@ -1566,6 +1584,7 @@ new_object_addresses(void)
        addrs->maxrefs = 32;
        addrs->refs = (ObjectAddress *)
                palloc(addrs->maxrefs * sizeof(ObjectAddress));
+       addrs->extras = NULL;           /* until/unless needed */
 
        return addrs;
 }
@@ -1588,6 +1607,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;
@@ -1614,6 +1634,7 @@ 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;
@@ -1621,6 +1642,41 @@ add_exact_object_address(const ObjectAddress *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++;
+}
+
 /*
  * Test whether an object is present in an ObjectAddresses array.
  *
@@ -1628,13 +1684,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)
@@ -1648,6 +1704,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.
@@ -1670,6 +1767,8 @@ void
 free_object_addresses(ObjectAddresses *addrs)
 {
        pfree(addrs->refs);
+       if (addrs->extras)
+               pfree(addrs->extras);
        pfree(addrs);
 }
 
index 21374f17d3da66e04cca740fc72b1a8beb1a16fe..c59c3b9d52ff56c2bfddd50dd7a0478a89dac3b7 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.72 2008/05/12 00:00:53 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.73 2008/06/08 22:41:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -138,6 +138,7 @@ extern SysScanDesc systable_beginscan(Relation heapRelation,
                                   Snapshot snapshot,
                                   int nkeys, ScanKey key);
 extern HeapTuple systable_getnext(SysScanDesc sysscan);
+extern bool systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup);
 extern void systable_endscan(SysScanDesc sysscan);
 extern SysScanDesc systable_beginscan_ordered(Relation heapRelation,
                                                                                          Relation indexRelation,
index e192c1ed288aa00e933184db1874c6eec05d66ac..8929be6a3426a985338950bfbdaf95c9dc856cfe 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.35 2008/05/16 23:36:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.36 2008/06/08 22:41:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,7 +17,7 @@
 #include "nodes/parsenodes.h"  /* for DropBehavior */
 
 
-/*----------
+/*
  * Precise semantics of a dependency relationship are specified by the
  * DependencyType code (which is stored in a "char" field in pg_depend,
  * so we assign ASCII-code values to the enumeration members).
@@ -56,7 +56,6 @@
  * contain zeroes.
  *
  * Other dependency flavors may be needed in future.
- *----------
  */
 
 typedef enum DependencyType
@@ -178,7 +177,7 @@ extern void add_exact_object_address(const ObjectAddress *object,
                                                 ObjectAddresses *addrs);
 
 extern bool object_address_present(const ObjectAddress *object,
-                                          ObjectAddresses *addrs);
+                                          const ObjectAddresses *addrs);
 
 extern void record_object_address_dependencies(const ObjectAddress *depender,
                                                                   ObjectAddresses *referenced,
index b5af16c558ec65b75539b008b8b6cdd47d83310b..e3aebc630d8660eef2f5640d27bbbac47bc1a620 100644 (file)
@@ -376,7 +376,6 @@ select test2 from atacc2;
 
 drop table atacc2 cascade;
 NOTICE:  drop cascades to table atacc3
-NOTICE:  drop cascades to constraint foo on table atacc3
 drop table atacc1;
 -- adding only to a parent is disallowed as of 8.4
 create table atacc1 (test int);
@@ -1251,7 +1250,6 @@ select * from p1;
 
 drop table p1 cascade;
 NOTICE:  drop cascades to table c1
-NOTICE:  drop cascades to constraint p1_a1_check on table c1
 -- test that operations with a dropped column do not try to reference
 -- its datatype
 create domain mytype as text;
@@ -1470,12 +1468,8 @@ select alter2.plus1(41);
 
 -- clean up
 drop schema alter2 cascade;
-NOTICE:  drop cascades to type alter2.ctype
-NOTICE:  drop cascades to type alter2.posint
-NOTICE:  drop cascades to function alter2.plus1(integer)
-NOTICE:  drop cascades to view alter2.v1
-NOTICE:  drop cascades to rule _RETURN on view alter2.v1
-NOTICE:  drop cascades to sequence alter2.t1_f1_seq
-NOTICE:  drop cascades to default for table alter2.t1 column f1
 NOTICE:  drop cascades to table alter2.t1
-NOTICE:  drop cascades to constraint t1_f2_check on table alter2.t1
+NOTICE:  drop cascades to view alter2.v1
+NOTICE:  drop cascades to function alter2.plus1(integer)
+NOTICE:  drop cascades to type alter2.posint
+NOTICE:  drop cascades to type alter2.ctype
index 0ef4bffe0b5805d34cfaf43cf9c775612051dba6..3eae7e90ccdfc9bc102576894c734f065e158618 100644 (file)
@@ -237,72 +237,43 @@ And relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%')
 (1 row)
 
 DROP SCHEMA temp_view_test CASCADE;
-NOTICE:  drop cascades to view temp_view_test.v9
-NOTICE:  drop cascades to rule _RETURN on view temp_view_test.v9
-NOTICE:  drop cascades to sequence temp_view_test.seq1
-NOTICE:  drop cascades to view temp_view_test.v8
-NOTICE:  drop cascades to rule _RETURN on view temp_view_test.v8
-NOTICE:  drop cascades to view temp_view_test.v7
-NOTICE:  drop cascades to rule _RETURN on view temp_view_test.v7
-NOTICE:  drop cascades to view temp_view_test.v6
-NOTICE:  drop cascades to rule _RETURN on view temp_view_test.v6
-NOTICE:  drop cascades to view temp_view_test.v5
-NOTICE:  drop cascades to rule _RETURN on view temp_view_test.v5
-NOTICE:  drop cascades to view temp_view_test.v4
-NOTICE:  drop cascades to rule _RETURN on view temp_view_test.v4
-NOTICE:  drop cascades to view temp_view_test.v3
-NOTICE:  drop cascades to rule _RETURN on view temp_view_test.v3
-NOTICE:  drop cascades to view temp_view_test.v2
-NOTICE:  drop cascades to rule _RETURN on view temp_view_test.v2
-NOTICE:  drop cascades to view temp_view_test.v1
-NOTICE:  drop cascades to rule _RETURN on view temp_view_test.v1
-NOTICE:  drop cascades to table temp_view_test.base_table2
-NOTICE:  drop cascades to rule _RETURN on view v5_temp
-NOTICE:  drop cascades to view v5_temp
 NOTICE:  drop cascades to table temp_view_test.base_table
-NOTICE:  drop cascades to rule _RETURN on view v9_temp
-NOTICE:  drop cascades to view v9_temp
-NOTICE:  drop cascades to rule _RETURN on view v8_temp
-NOTICE:  drop cascades to view v8_temp
-NOTICE:  drop cascades to rule _RETURN on view v6_temp
-NOTICE:  drop cascades to view v6_temp
-NOTICE:  drop cascades to rule _RETURN on view v4_temp
-NOTICE:  drop cascades to view v4_temp
-NOTICE:  drop cascades to rule _RETURN on view v2_temp
-NOTICE:  drop cascades to view v2_temp
-NOTICE:  drop cascades to rule _RETURN on view v11_temp
-NOTICE:  drop cascades to view v11_temp
-NOTICE:  drop cascades to rule _RETURN on view v12_temp
-NOTICE:  drop cascades to view v12_temp
-NOTICE:  drop cascades to rule _RETURN on view v7_temp
 NOTICE:  drop cascades to view v7_temp
-NOTICE:  drop cascades to rule _RETURN on view v10_temp
 NOTICE:  drop cascades to view v10_temp
+NOTICE:  drop cascades to view v11_temp
+NOTICE:  drop cascades to view v12_temp
+NOTICE:  drop cascades to view v2_temp
+NOTICE:  drop cascades to view v4_temp
+NOTICE:  drop cascades to view v6_temp
+NOTICE:  drop cascades to view v8_temp
+NOTICE:  drop cascades to view v9_temp
+NOTICE:  drop cascades to table temp_view_test.base_table2
+NOTICE:  drop cascades to view v5_temp
+NOTICE:  drop cascades to view temp_view_test.v1
+NOTICE:  drop cascades to view temp_view_test.v2
+NOTICE:  drop cascades to view temp_view_test.v3
+NOTICE:  drop cascades to view temp_view_test.v4
+NOTICE:  drop cascades to view temp_view_test.v5
+NOTICE:  drop cascades to view temp_view_test.v6
+NOTICE:  drop cascades to view temp_view_test.v7
+NOTICE:  drop cascades to view temp_view_test.v8
+NOTICE:  drop cascades to sequence temp_view_test.seq1
+NOTICE:  drop cascades to view temp_view_test.v9
 DROP SCHEMA testviewschm2 CASCADE;
-NOTICE:  drop cascades to view pubview
-NOTICE:  drop cascades to rule _RETURN on view pubview
-NOTICE:  drop cascades to table tbl4
-NOTICE:  drop cascades to rule _RETURN on view mytempview
-NOTICE:  drop cascades to view mytempview
-NOTICE:  drop cascades to table tbl3
-NOTICE:  drop cascades to table tbl2
-NOTICE:  drop cascades to table tbl1
-NOTICE:  drop cascades to view nontemp4
-NOTICE:  drop cascades to rule _RETURN on view nontemp4
-NOTICE:  drop cascades to view nontemp3
-NOTICE:  drop cascades to rule _RETURN on view nontemp3
-NOTICE:  drop cascades to view nontemp2
-NOTICE:  drop cascades to rule _RETURN on view nontemp2
-NOTICE:  drop cascades to view nontemp1
-NOTICE:  drop cascades to rule _RETURN on view nontemp1
-NOTICE:  drop cascades to table t2
 NOTICE:  drop cascades to table t1
-NOTICE:  drop cascades to rule _RETURN on view temporal4
-NOTICE:  drop cascades to view temporal4
-NOTICE:  drop cascades to rule _RETURN on view temporal3
-NOTICE:  drop cascades to view temporal3
-NOTICE:  drop cascades to rule _RETURN on view temporal2
-NOTICE:  drop cascades to view temporal2
-NOTICE:  drop cascades to rule _RETURN on view temporal1
 NOTICE:  drop cascades to view temporal1
+NOTICE:  drop cascades to view temporal2
+NOTICE:  drop cascades to view temporal3
+NOTICE:  drop cascades to view temporal4
+NOTICE:  drop cascades to table t2
+NOTICE:  drop cascades to view nontemp1
+NOTICE:  drop cascades to view nontemp2
+NOTICE:  drop cascades to view nontemp3
+NOTICE:  drop cascades to view nontemp4
+NOTICE:  drop cascades to table tbl1
+NOTICE:  drop cascades to table tbl2
+NOTICE:  drop cascades to table tbl3
+NOTICE:  drop cascades to table tbl4
+NOTICE:  drop cascades to view mytempview
+NOTICE:  drop cascades to view pubview
 SET search_path to public;
index a80eae7d590ba69e8b07d4d61b931f23143f13bc..179c8c63471fec8acb7e0d6407cc53f4c93fc925 100644 (file)
@@ -266,8 +266,8 @@ ERROR:  domain dnotnulltest does not allow null values
 alter domain dnotnulltest drop not null;
 update domnotnull set col1 = null;
 drop domain dnotnulltest cascade;
-NOTICE:  drop cascades to table domnotnull column col2
 NOTICE:  drop cascades to table domnotnull column col1
+NOTICE:  drop cascades to table domnotnull column col2
 -- Test ALTER DOMAIN .. DEFAULT ..
 create table domdeftest (col1 ddef1);
 insert into domdeftest default values;
index e24ceb51a87f480507abb2328cb983c5727107f2..87e6591ea35d274b02d23e0205edf9ffbe653700 100644 (file)
@@ -1157,15 +1157,15 @@ FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2);
 ERROR:  foreign key constraint "fk_241_132" cannot be implemented
 DETAIL:  Key columns "x2" and "id1" are of incompatible types: character varying and integer.
 DROP TABLE pktable, fktable CASCADE;
-NOTICE:  drop cascades to constraint fk_253_213 on table fktable
-NOTICE:  drop cascades to constraint fk_213_213 on table fktable
-NOTICE:  drop cascades to constraint fk_123_123 on table fktable
-NOTICE:  drop cascades to constraint fk_5_1 on table fktable
-NOTICE:  drop cascades to constraint fktable_x1_fkey on table fktable
-NOTICE:  drop cascades to constraint fk_4_2 on table fktable
-NOTICE:  drop cascades to constraint fktable_x2_fkey on table fktable
-NOTICE:  drop cascades to constraint fk_1_3 on table fktable
 NOTICE:  drop cascades to constraint fktable_x3_fkey on table fktable
+NOTICE:  drop cascades to constraint fk_1_3 on table fktable
+NOTICE:  drop cascades to constraint fktable_x2_fkey on table fktable
+NOTICE:  drop cascades to constraint fk_4_2 on table fktable
+NOTICE:  drop cascades to constraint fktable_x1_fkey on table fktable
+NOTICE:  drop cascades to constraint fk_5_1 on table fktable
+NOTICE:  drop cascades to constraint fk_123_123 on table fktable
+NOTICE:  drop cascades to constraint fk_213_213 on table fktable
+NOTICE:  drop cascades to constraint fk_253_213 on table fktable
 -- test a tricky case: we can elide firing the FK check trigger during
 -- an UPDATE if the UPDATE did not change the foreign key
 -- field. However, we can't do this if our transaction was the one that
index e3acd03c17db689f6e5d4d30aa7c2b059a686be9..7c7ac0ef3388decef5eb17711574969bb5d382dc 100644 (file)
@@ -818,9 +818,9 @@ create table c2(f3 int) inherits(p1,p2);
       Table "public.c2"
  Column |  Type   | Modifiers 
 --------+---------+-----------
- f1     | integer |          
- f2     | integer |          
- f3     | integer |          
+ f1     | integer | 
+ f2     | integer | 
+ f3     | integer | 
 Check constraints:
     "p2_f2_check" CHECK (f2 > 0)
 Inherits: p1,
@@ -834,22 +834,19 @@ NOTICE:  merging multiple inherited definitions of column "f3"
       Table "public.c3"
  Column |  Type   | Modifiers 
 --------+---------+-----------
- f1     | integer |          
- f2     | integer |          
- f3     | integer |          
- f4     | integer |          
+ f1     | integer | 
+ f2     | integer | 
+ f3     | integer | 
+ f4     | integer | 
 Check constraints:
     "p2_f2_check" CHECK (f2 > 0)
 Inherits: c1,
           c2
 
 drop table p1 cascade;
+NOTICE:  drop cascades to table c1
 NOTICE:  drop cascades to table c2
 NOTICE:  drop cascades to table c3
-NOTICE:  drop cascades to constraint p2_f2_check on table c3
-NOTICE:  drop cascades to constraint p2_f2_check on table c2
-NOTICE:  drop cascades to table c1
-NOTICE:  drop cascades to constraint p2_f2_check on table c1
 drop table p2 cascade;
 create table pp1 (f1 int);
 create table cc1 (f2 text, f3 int) inherits (pp1);
@@ -858,10 +855,10 @@ alter table pp1 add column a1 int check (a1 > 0);
       Table "public.cc1"
  Column |  Type   | Modifiers 
 --------+---------+-----------
- f1     | integer |          
- f2     | text    |          
- f3     | integer |          
- a1     | integer |          
+ f1     | integer | 
+ f2     | text    | 
+ f3     | integer | 
+ a1     | integer | 
 Check constraints:
     "pp1_a1_check" CHECK (a1 > 0)
 Inherits: pp1
@@ -873,11 +870,11 @@ NOTICE:  merging multiple inherited definitions of column "a1"
           Table "public.cc2"
  Column |       Type       | Modifiers 
 --------+------------------+-----------
- f1     | integer          |          
- a1     | integer          |          
- f2     | text             |          
- f3     | integer          |          
- f4     | double precision |          
+ f1     | integer          | 
+ a1     | integer          | 
+ f2     | text             | 
+ f3     | integer          | 
+ f4     | double precision | 
 Check constraints:
     "pp1_a1_check" CHECK (a1 > 0)
 Inherits: pp1,
@@ -890,12 +887,12 @@ NOTICE:  merging constraint "pp1_a2_check" with inherited definition
           Table "public.cc2"
  Column |       Type       | Modifiers 
 --------+------------------+-----------
- f1     | integer          |          
- a1     | integer          |          
- f2     | text             |          
- f3     | integer          |          
- f4     | double precision |          
- a2     | integer          |          
+ f1     | integer          | 
+ a1     | integer          | 
+ f2     | text             | 
+ f3     | integer          | 
+ f4     | double precision | 
+ a2     | integer          | 
 Check constraints:
     "pp1_a1_check" CHECK (a1 > 0)
     "pp1_a2_check" CHECK (a2 > 0)
@@ -903,9 +900,5 @@ Inherits: pp1,
           cc1
 
 drop table pp1 cascade;
-NOTICE:  drop cascades to table cc2
-NOTICE:  drop cascades to constraint pp1_a1_check on table cc2
-NOTICE:  drop cascades to constraint pp1_a2_check on table cc2
 NOTICE:  drop cascades to table cc1
-NOTICE:  drop cascades to constraint pp1_a1_check on table cc1
-NOTICE:  drop cascades to constraint pp1_a2_check on table cc1
+NOTICE:  drop cascades to table cc2
index a9f3e4ab92e7aac3754cc3795c27fe5183b1f36e..94b3e5e99b0ef66ff22cf3286a9c6855529d59ad 100644 (file)
@@ -39,10 +39,8 @@ SELECT * FROM test_schema_1.abc_view;
 (3 rows)
 
 DROP SCHEMA test_schema_1 CASCADE;
-NOTICE:  drop cascades to view test_schema_1.abc_view
-NOTICE:  drop cascades to rule _RETURN on view test_schema_1.abc_view
 NOTICE:  drop cascades to table test_schema_1.abc
-NOTICE:  drop cascades to default for table test_schema_1.abc column a
+NOTICE:  drop cascades to view test_schema_1.abc_view
 -- verify that the objects were dropped
 SELECT COUNT(*) FROM pg_class WHERE relnamespace =
     (SELECT oid FROM pg_namespace WHERE nspname = 'test_schema_1');
index 7ee11921c25994d0fdea2f285cbf8be3a5523595..c0681d26e38e3fc6f99cfc43c6c181702ef150fd 100644 (file)
@@ -245,9 +245,6 @@ NOTICE:  3
 (1 row)
 
 select cachebug();
-NOTICE:  drop cascades to rule _RETURN on view vv
-CONTEXT:  SQL statement "drop table if exists temptable cascade"
-PL/pgSQL function "cachebug" line 3 at SQL statement
 NOTICE:  drop cascades to view vv
 CONTEXT:  SQL statement "drop table if exists temptable cascade"
 PL/pgSQL function "cachebug" line 3 at SQL statement
index bcb2ea54aed0714218dc2c5eeb5233dcd1b365ad..54d6613f28a22707ef2a1e7b2550ee57e581733a 100644 (file)
@@ -589,7 +589,6 @@ DROP VIEW atestv1;
 DROP VIEW atestv2;
 -- this should cascade to drop atestv4
 DROP VIEW atestv3 CASCADE;
-NOTICE:  drop cascades to rule _RETURN on view atestv4
 NOTICE:  drop cascades to view atestv4
 -- this should complain "does not exist"
 DROP VIEW atestv4;
index 723dca70079929d2cfca3bd3d970d2687f239da5..4bd0b4afbb58bd36e5f0641eaceecb67b002102c 100644 (file)
@@ -332,7 +332,6 @@ SELECT * FROM orders_view;
 (11 rows)
 
 DROP TABLE orderstest cascade;
-NOTICE:  drop cascades to rule _RETURN on view orders_view
 NOTICE:  drop cascades to view orders_view
 --
 -- Test cases to catch situations where rule rewriter fails to propagate
index 929aba4da706b468e1ff56fc841a3bcbfd6e3327..d0a99a554a62a77b244872337a13f8d83f258012 100644 (file)
@@ -141,10 +141,10 @@ SELECT * FROM trunc_e;
 (0 rows)
 
 DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE;
-NOTICE:  drop cascades to constraint trunc_e_a_fkey on table trunc_e
 NOTICE:  drop cascades to constraint trunc_b_a_fkey on table trunc_b
-NOTICE:  drop cascades to constraint trunc_e_b_fkey on table trunc_e
+NOTICE:  drop cascades to constraint trunc_e_a_fkey on table trunc_e
 NOTICE:  drop cascades to constraint trunc_d_a_fkey on table trunc_d
+NOTICE:  drop cascades to constraint trunc_e_b_fkey on table trunc_e
 -- Test ON TRUNCATE triggers
 CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text);
 CREATE TABLE trunc_trigger_log (tgop text, tglevel text, tgwhen text,
index 607232f5353bf8b17a2cf3fe51e87388135c672e..6337798c6258043bb58010a1dc0970684e2183c7 100644 (file)
@@ -65,9 +65,9 @@ ERROR:  tablespace "nosuchspace" does not exist
 DROP TABLESPACE testspace;
 ERROR:  tablespace "testspace" is not empty
 DROP SCHEMA testschema CASCADE;
-NOTICE:  drop cascades to table testschema.atable
-NOTICE:  drop cascades to table testschema.asexecute
-NOTICE:  drop cascades to table testschema.asselect
 NOTICE:  drop cascades to table testschema.foo
+NOTICE:  drop cascades to table testschema.asselect
+NOTICE:  drop cascades to table testschema.asexecute
+NOTICE:  drop cascades to table testschema.atable
 -- Should succeed
 DROP TABLESPACE testspace;