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.
This commit is contained in:
parent
00f1a41ab2
commit
3acf422316
|
@ -8,7 +8,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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,
|
const ObjectAddress *callingObject,
|
||||||
ObjectAddresses *oktodelete,
|
ObjectAddresses *oktodelete,
|
||||||
Relation depRel);
|
Relation depRel);
|
||||||
|
static bool deleteDependentObjects(const ObjectAddress *object,
|
||||||
|
const char *objDescription,
|
||||||
|
DropBehavior behavior,
|
||||||
|
ObjectAddresses *oktodelete,
|
||||||
|
Relation depRel);
|
||||||
static void doDeletion(const ObjectAddress *object);
|
static void doDeletion(const ObjectAddress *object);
|
||||||
static bool find_expr_references_walker(Node *node,
|
static bool find_expr_references_walker(Node *node,
|
||||||
find_expr_references_context *context);
|
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
|
* findAutoDeletableObjects: find all objects that are reachable by AUTO or
|
||||||
* INTERNAL dependency paths from the given object. Add them all to the
|
* 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
|
* Step 2: scan pg_depend records that link to this object, showing
|
||||||
* the things that depend on it. Recursively delete those things. (We
|
* the things that depend on it. Recursively delete those things.
|
||||||
* don't delete the pg_depend records here, as the recursive call will
|
* Note it's important to delete the dependent objects
|
||||||
* do that.) Note it's important to delete the dependent objects
|
|
||||||
* before the referenced one, since the deletion routines might do
|
* before the referenced one, since the deletion routines might do
|
||||||
* things like try to update the pg_class record when deleting a check
|
* things like try to update the pg_class record when deleting a check
|
||||||
* constraint.
|
* 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,
|
ScanKeyEntryInitialize(&key[0], 0x0,
|
||||||
Anum_pg_depend_refclassid, F_OIDEQ,
|
Anum_pg_depend_refclassid, F_OIDEQ,
|
||||||
ObjectIdGetDatum(object->classId));
|
ObjectIdGetDatum(object->classId));
|
||||||
|
@ -581,34 +712,6 @@ recursiveDeletion(const ObjectAddress *object,
|
||||||
|
|
||||||
systable_endscan(scan);
|
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;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,21 +13,17 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "postgres.h"
|
||||||
|
|
||||||
#include "access/heapam.h"
|
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/catalog.h"
|
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
#include "catalog/dependency.h"
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/heap.h"
|
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_conversion.h"
|
#include "catalog/pg_conversion.h"
|
||||||
#include "catalog/pg_inherits.h"
|
|
||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_operator.h"
|
#include "catalog/pg_operator.h"
|
||||||
|
@ -42,10 +38,9 @@
|
||||||
#include "utils/acl.h"
|
#include "utils/acl.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/catcache.h"
|
#include "utils/catcache.h"
|
||||||
#include "utils/fmgroids.h"
|
|
||||||
#include "utils/guc.h"
|
|
||||||
#include "utils/inval.h"
|
#include "utils/inval.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -1691,50 +1686,19 @@ AtEOXact_Namespace(bool isCommit)
|
||||||
static void
|
static void
|
||||||
RemoveTempRelations(Oid tempNamespaceId)
|
RemoveTempRelations(Oid tempNamespaceId)
|
||||||
{
|
{
|
||||||
Relation pgclass;
|
|
||||||
HeapScanDesc scan;
|
|
||||||
HeapTuple tuple;
|
|
||||||
ScanKeyData key;
|
|
||||||
ObjectAddress object;
|
ObjectAddress object;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scan pg_class to find all the relations in the target namespace.
|
* We want to get rid of everything in the target namespace, but not
|
||||||
* Ignore indexes, though, on the assumption that they'll go away when
|
* the namespace itself (deleting it only to recreate it later would be
|
||||||
* their tables are deleted.
|
* a waste of cycles). We do this by finding everything that has a
|
||||||
*
|
* dependency on the namespace.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
ScanKeyEntryInitialize(&key, 0x0,
|
object.classId = get_system_catalog_relid(NamespaceRelationName);
|
||||||
Anum_pg_class_relnamespace,
|
object.objectId = tempNamespaceId;
|
||||||
F_OIDEQ,
|
object.objectSubId = 0;
|
||||||
ObjectIdGetDatum(tempNamespaceId));
|
|
||||||
|
|
||||||
pgclass = heap_openr(RelationRelationName, AccessShareLock);
|
deleteWhatDependsOn(&object);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
heap_endscan(scan);
|
|
||||||
heap_close(pgclass, AccessShareLock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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,
|
extern void performDeletion(const ObjectAddress *object,
|
||||||
DropBehavior behavior);
|
DropBehavior behavior);
|
||||||
|
|
||||||
|
extern void deleteWhatDependsOn(const ObjectAddress *object);
|
||||||
|
|
||||||
extern void recordDependencyOnExpr(const ObjectAddress *depender,
|
extern void recordDependencyOnExpr(const ObjectAddress *depender,
|
||||||
Node *expr, List *rtable,
|
Node *expr, List *rtable,
|
||||||
DependencyType behavior);
|
DependencyType behavior);
|
||||||
|
|
Loading…
Reference in New Issue