From: Tom Lane Date: Fri, 7 Feb 2003 01:33:06 +0000 (+0000) Subject: Revise mechanism for getting rid of temp tables at backend shutdown. X-Git-Tag: REL7_4_BETA1~1100 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3acf42231634513e8ba3b67d1ea7cb275d8e5fb3;p=postgresql Revise mechanism for getting rid of temp tables at backend shutdown. 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. --- diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 688d4fb114..856f4ce0d5 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 - * $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; } diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 723355ac89..7f7bf8570a 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,21 +13,17 @@ * 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" @@ -42,10 +38,9 @@ #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); } /* diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index d0c18b1c86..4cc4e99541 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -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);