diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index e8958abca2b4538052d2ed19be3f7e3e1bdcaf01..301062951b6dca8d0fe91191193e90e926c6a118 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -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
  *
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 24423d2837e646349803de709feff6a78b99a421..12b654504d7c1c3615c09eab4acf40b178d7881c 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -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"
@@ -66,16 +67,43 @@
 #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);
 }
 
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 21374f17d3da66e04cca740fc72b1a8beb1a16fe..c59c3b9d52ff56c2bfddd50dd7a0478a89dac3b7 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -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,
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index e192c1ed288aa00e933184db1874c6eec05d66ac..8929be6a3426a985338950bfbdaf95c9dc856cfe 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -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,
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index b5af16c558ec65b75539b008b8b6cdd47d83310b..e3aebc630d8660eef2f5640d27bbbac47bc1a620 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -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
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index 0ef4bffe0b5805d34cfaf43cf9c775612051dba6..3eae7e90ccdfc9bc102576894c734f065e158618 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -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;
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index a80eae7d590ba69e8b07d4d61b931f23143f13bc..179c8c63471fec8acb7e0d6407cc53f4c93fc925 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -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;
diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out
index e24ceb51a87f480507abb2328cb983c5727107f2..87e6591ea35d274b02d23e0205edf9ffbe653700 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -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
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index e3acd03c17db689f6e5d4d30aa7c2b059a686be9..7c7ac0ef3388decef5eb17711574969bb5d382dc 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -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
diff --git a/src/test/regress/expected/namespace.out b/src/test/regress/expected/namespace.out
index a9f3e4ab92e7aac3754cc3795c27fe5183b1f36e..94b3e5e99b0ef66ff22cf3286a9c6855529d59ad 100644
--- a/src/test/regress/expected/namespace.out
+++ b/src/test/regress/expected/namespace.out
@@ -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');
diff --git a/src/test/regress/expected/plancache.out b/src/test/regress/expected/plancache.out
index 7ee11921c25994d0fdea2f285cbf8be3a5523595..c0681d26e38e3fc6f99cfc43c6c181702ef150fd 100644
--- a/src/test/regress/expected/plancache.out
+++ b/src/test/regress/expected/plancache.out
@@ -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
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index bcb2ea54aed0714218dc2c5eeb5233dcd1b365ad..54d6613f28a22707ef2a1e7b2550ee57e581733a 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -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;
diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out
index 723dca70079929d2cfca3bd3d970d2687f239da5..4bd0b4afbb58bd36e5f0641eaceecb67b002102c 100644
--- a/src/test/regress/expected/subselect.out
+++ b/src/test/regress/expected/subselect.out
@@ -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
diff --git a/src/test/regress/expected/truncate.out b/src/test/regress/expected/truncate.out
index 929aba4da706b468e1ff56fc841a3bcbfd6e3327..d0a99a554a62a77b244872337a13f8d83f258012 100644
--- a/src/test/regress/expected/truncate.out
+++ b/src/test/regress/expected/truncate.out
@@ -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,
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index 607232f5353bf8b17a2cf3fe51e87388135c672e..6337798c6258043bb58010a1dc0970684e2183c7 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -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;