Revise mechanism for getting rid of temp tables at backend shutdown.
authorTom Lane
Fri, 7 Feb 2003 01:33:06 +0000 (01:33 +0000)
committerTom Lane
Fri, 7 Feb 2003 01:33:06 +0000 (01:33 +0000)
Instead of grovelling through pg_class to find them, make use of the
handy dandy dependency mechanism: just delete everything that depends
on our temp schema.  Unlike the pg_class scan, the dependency mechanism
is smart enough to delete things in an order that doesn't fall foul of
any dependency restrictions.  Fixes problem reported by David Heggie:
a temp table with a serial column may cause a backend FATAL exit at
shutdown time, if it chances to try to delete the temp sequence first.

src/backend/catalog/dependency.c
src/backend/catalog/namespace.c
src/include/catalog/dependency.h

index 688d4fb114b04b6b0d9e965d7b0be0e4d9d541bc..856f4ce0d546886b46bd9b30c8cbc10f6a1c8c48 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.19 2003/01/10 21:08:07 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.20 2003/02/07 01:33:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -100,6 +100,11 @@ static bool recursiveDeletion(const ObjectAddress *object,
                  const ObjectAddress *callingObject,
                  ObjectAddresses *oktodelete,
                  Relation depRel);
+static bool deleteDependentObjects(const ObjectAddress *object,
+                                  const char *objDescription,
+                                  DropBehavior behavior,
+                                  ObjectAddresses *oktodelete,
+                                  Relation depRel);
 static void doDeletion(const ObjectAddress *object);
 static bool find_expr_references_walker(Node *node,
                            find_expr_references_context *context);
@@ -172,6 +177,64 @@ performDeletion(const ObjectAddress *object,
 }
 
 
+/*
+ * deleteWhatDependsOn: attempt to drop everything that depends on the
+ * specified object, though not the object itself.  Behavior is always
+ * CASCADE.
+ *
+ * This is currently used only to clean out the contents of a schema
+ * (namespace): the passed object is a namespace.
+ */
+void
+deleteWhatDependsOn(const ObjectAddress *object)
+{
+   char       *objDescription;
+   Relation    depRel;
+   ObjectAddresses oktodelete;
+
+   /*
+    * Get object description for possible use in failure messages
+    */
+   objDescription = getObjectDescription(object);
+
+   /*
+    * We save some cycles by opening pg_depend just once and passing the
+    * Relation pointer down to all the recursive deletion steps.
+    */
+   depRel = heap_openr(DependRelationName, RowExclusiveLock);
+
+   /*
+    * Construct a list of objects that are reachable by AUTO or INTERNAL
+    * dependencies from the target object.  These should be deleted silently,
+    * even if the actual deletion pass first reaches one of them via a
+    * non-auto dependency.
+    */
+   init_object_addresses(&oktodelete);
+
+   findAutoDeletableObjects(object, &oktodelete, depRel);
+
+   /*
+    * Now invoke only step 2 of recursiveDeletion: just recurse to the
+    * stuff dependent on the given object.
+    */
+   if (!deleteDependentObjects(object, objDescription,
+                               DROP_CASCADE, &oktodelete, depRel))
+       elog(ERROR, "Failed to drop all objects depending on %s",
+            objDescription);
+
+   /*
+    * We do not need CommandCounterIncrement here, since if step 2 did
+    * anything then each recursive call will have ended with one.
+    */
+
+   term_object_addresses(&oktodelete);
+
+   heap_close(depRel, RowExclusiveLock);
+
+   pfree(objDescription);
+}
+
+
 /*
  * findAutoDeletableObjects: find all objects that are reachable by AUTO or
  * INTERNAL dependency paths from the given object.  Add them all to the
@@ -476,22 +539,90 @@ recursiveDeletion(const ObjectAddress *object,
 
    /*
     * Step 2: scan pg_depend records that link to this 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
+    * 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.
-    *
-    * Again, when dropping a whole object (subId = 0), find pg_depend
-    * records for its sub-objects too.
-    *
-    * 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.
     */
+   if (!deleteDependentObjects(object, objDescription,
+                               behavior, 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.
+    */
+   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);
+
+   /*
+    * CommandCounterIncrement here to ensure that preceding changes are
+    * all visible.
+    */
+   CommandCounterIncrement();
+
+   /*
+    * And we're done!
+    */
+   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,
+                      ObjectAddresses *oktodelete,
+                      Relation depRel)
+{
+   bool        ok = true;
+   ScanKeyData key[3];
+   int         nkeys;
+   SysScanDesc scan;
+   HeapTuple   tup;
+   ObjectAddress otherObject;
+
    ScanKeyEntryInitialize(&key[0], 0x0,
                           Anum_pg_depend_refclassid, F_OIDEQ,
                           ObjectIdGetDatum(object->classId));
@@ -581,34 +712,6 @@ recursiveDeletion(const ObjectAddress *object,
 
    systable_endscan(scan);
 
-   /*
-    * 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.
-    */
-   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);
-
-   /*
-    * CommandCounterIncrement here to ensure that preceding changes are
-    * all visible.
-    */
-   CommandCounterIncrement();
-
-   /*
-    * And we're done!
-    */
-   pfree(objDescription);
-
    return ok;
 }
 
index 723355ac898d77bf2657234ade5b890c2fb0c8a7..7f7bf8570a430fe47dc21965d420a0a33bcc0de1 100644 (file)
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.45 2003/01/12 18:19:37 petere Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.46 2003/02/07 01:33:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-#include "access/heapam.h"
 #include "access/xact.h"
-#include "catalog/catalog.h"
 #include "catalog/catname.h"
 #include "catalog/dependency.h"
-#include "catalog/heap.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_conversion.h"
-#include "catalog/pg_inherits.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/catcache.h"
-#include "utils/fmgroids.h"
-#include "utils/guc.h"
 #include "utils/inval.h"
 #include "utils/lsyscache.h"
+#include "utils/memutils.h"
 #include "utils/syscache.h"
 
 
@@ -1691,50 +1686,19 @@ AtEOXact_Namespace(bool isCommit)
 static void
 RemoveTempRelations(Oid tempNamespaceId)
 {
-   Relation    pgclass;
-   HeapScanDesc scan;
-   HeapTuple   tuple;
-   ScanKeyData key;
    ObjectAddress object;
 
    /*
-    * Scan pg_class to find all the relations in the target namespace.
-    * Ignore indexes, though, on the assumption that they'll go away when
-    * their tables are deleted.
-    *
-    * NOTE: if there are deletion constraints between temp relations, then
-    * our CASCADE delete call may cause as-yet-unvisited objects to go
-    * away.  This is okay because we are using SnapshotNow; when the scan
-    * does reach those pg_class tuples, they'll be ignored as already
-    * deleted.
+    * We want to get rid of everything in the target namespace, but not
+    * the namespace itself (deleting it only to recreate it later would be
+    * a waste of cycles).  We do this by finding everything that has a
+    * dependency on the namespace.
     */
-   ScanKeyEntryInitialize(&key, 0x0,
-                          Anum_pg_class_relnamespace,
-                          F_OIDEQ,
-                          ObjectIdGetDatum(tempNamespaceId));
-
-   pgclass = heap_openr(RelationRelationName, AccessShareLock);
-   scan = heap_beginscan(pgclass, SnapshotNow, 1, &key);
-
-   while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
-   {
-       switch (((Form_pg_class) GETSTRUCT(tuple))->relkind)
-       {
-           case RELKIND_RELATION:
-           case RELKIND_SEQUENCE:
-           case RELKIND_VIEW:
-               object.classId = RelOid_pg_class;
-               object.objectId = HeapTupleGetOid(tuple);
-               object.objectSubId = 0;
-               performDeletion(&object, DROP_CASCADE);
-               break;
-           default:
-               break;
-       }
-   }
+   object.classId = get_system_catalog_relid(NamespaceRelationName);
+   object.objectId = tempNamespaceId;
+   object.objectSubId = 0;
 
-   heap_endscan(scan);
-   heap_close(pgclass, AccessShareLock);
+   deleteWhatDependsOn(&object);
 }
 
 /*
index d0c18b1c86e6da48cf044aea102c3134d7c1720c..4cc4e995418d4073d1ec62d4dff4f4456438ac3f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: dependency.h,v 1.5 2002/09/04 20:31:37 momjian Exp $
+ * $Id: dependency.h,v 1.6 2003/02/07 01:33:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -84,6 +84,8 @@ typedef struct ObjectAddress
 extern void performDeletion(const ObjectAddress *object,
                DropBehavior behavior);
 
+extern void deleteWhatDependsOn(const ObjectAddress *object);
+
 extern void recordDependencyOnExpr(const ObjectAddress *depender,
                       Node *expr, List *rtable,
                       DependencyType behavior);