Index: src/backend/catalog/dependency.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/dependency.c,v retrieving revision 1.58 diff -c -p -r1.58 dependency.c *** src/backend/catalog/dependency.c 14 Jul 2006 14:52:17 -0000 1.58 --- src/backend/catalog/dependency.c 20 Aug 2006 17:29:03 -0000 *************** *** 57,68 **** /* expansible list of ObjectAddresses */ ! typedef struct { ObjectAddress *refs; /* => palloc'd array */ int numrefs; /* current number of references */ int maxrefs; /* current size of palloc'd array */ ! } ObjectAddresses; /* for find_expr_references_walker */ typedef struct --- 57,70 ---- /* expansible list of ObjectAddresses */ ! struct ObjectAddresses { ObjectAddress *refs; /* => palloc'd array */ int numrefs; /* current number of references */ int maxrefs; /* current size of palloc'd array */ ! bool alloc; /* ObjectAddresses itself is palloc'ed? */ ! }; ! /* typedef ObjectAddresses appears in dependency.h */ /* for find_expr_references_walker */ typedef struct *************** static const Oid object_classes[MAX_OCLA *** 92,106 **** }; static void findAutoDeletableObjects(const ObjectAddress *object, ObjectAddresses *oktodelete, ! Relation depRel); static bool recursiveDeletion(const ObjectAddress *object, DropBehavior behavior, int msglevel, const ObjectAddress *callingObject, ObjectAddresses *oktodelete, ! Relation depRel); static bool deleteDependentObjects(const ObjectAddress *object, const char *objDescription, DropBehavior behavior, --- 94,113 ---- }; + 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, DropBehavior behavior, *************** static int object_address_comparator(con *** 115,125 **** static void init_object_addresses(ObjectAddresses *addrs); static void add_object_address(ObjectClass oclass, Oid objectId, int32 subId, ObjectAddresses *addrs); - static void add_exact_object_address(const ObjectAddress *object, - ObjectAddresses *addrs); - static bool object_address_present(const ObjectAddress *object, - ObjectAddresses *addrs); - static void term_object_addresses(ObjectAddresses *addrs); static void getRelationDescription(StringInfo buffer, Oid relid); --- 122,127 ---- *************** performDeletion(const ObjectAddress *obj *** 161,170 **** */ init_object_addresses(&oktodelete); ! findAutoDeletableObjects(object, &oktodelete, depRel); if (!recursiveDeletion(object, behavior, NOTICE, ! NULL, &oktodelete, depRel)) ereport(ERROR, (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), errmsg("cannot drop %s because other objects depend on it", --- 163,172 ---- */ init_object_addresses(&oktodelete); ! 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", *************** performDeletion(const ObjectAddress *obj *** 180,185 **** --- 182,313 ---- /* + * 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 to this + * function. + */ + static void + performDeletionWithList(const ObjectAddress *object, + ObjectAddresses *oktodelete, + DropBehavior behavior, + ObjectAddresses *alreadyDeleted) + { + char *objDescription; + Relation depRel; + + /* + * Get object description for possible use in failure message. Must do + * this before deleting it ... + */ + 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_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. + */ + findAutoDeletableObjects(object, oktodelete, depRel, true); + + 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."))); + + heap_close(depRel, RowExclusiveLock); + + pfree(objDescription); + } + + /* + * performMultipleDeletion: Similar to performDeletion, but act on multiple + * objects at once. + * + * The main difference from issuing multiple performDeletion calls is that the + * list of objects that would be implicitly dropped, for each object to be + * dropped, is the union of all lists for all objects. + */ + void + performMultipleDeletions(const ObjectAddresses *objects, + DropBehavior behavior) + { + ObjectAddresses implicit; + ObjectAddresses alreadyDeleted; + Relation depRel; + int i; + + init_object_addresses(&implicit); + init_object_addresses(&alreadyDeleted); + + 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! + */ + for (i = 0; i < objects->numrefs; i++) + { + ObjectAddress obj = 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. + */ + 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); + } + + /* 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; + + /* + * 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 it */ + performDeletionWithList(&obj, &implicit, behavior, &alreadyDeleted); + } + + heap_close(depRel, RowExclusiveLock); + + term_object_addresses(&implicit); + term_object_addresses(&alreadyDeleted); + } + + /* * deleteWhatDependsOn: attempt to drop everything that depends on the * specified object, though not the object itself. Behavior is always * CASCADE. *************** deleteWhatDependsOn(const ObjectAddress *** 215,221 **** */ init_object_addresses(&oktodelete); ! findAutoDeletableObjects(object, &oktodelete, depRel); /* * Now invoke only step 2 of recursiveDeletion: just recurse to the stuff --- 343,349 ---- */ init_object_addresses(&oktodelete); ! findAutoDeletableObjects(object, &oktodelete, depRel, true); /* * Now invoke only step 2 of recursiveDeletion: just recurse to the stuff *************** deleteWhatDependsOn(const ObjectAddress *** 246,260 **** /* * findAutoDeletableObjects: find all objects that are reachable by AUTO or * INTERNAL dependency paths from the given object. Add them all to the ! * oktodelete list. Note that 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) { ScanKeyData key[3]; int nkeys; --- 374,388 ---- /* * 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; *************** findAutoDeletableObjects(const ObjectAdd *** 269,275 **** */ if (object_address_present(object, oktodelete)) return; ! add_exact_object_address(object, oktodelete); /* * Scan pg_depend records that link to this object, showing the things --- 397,404 ---- */ if (object_address_present(object, oktodelete)) return; ! if (addself) ! add_exact_object_address(object, oktodelete); /* * Scan pg_depend records that link to this object, showing the things *************** findAutoDeletableObjects(const ObjectAdd *** 316,322 **** otherObject.classId = foundDep->classid; otherObject.objectId = foundDep->objid; otherObject.objectSubId = foundDep->objsubid; ! findAutoDeletableObjects(&otherObject, oktodelete, depRel); break; case DEPENDENCY_PIN: --- 445,451 ---- otherObject.classId = foundDep->classid; otherObject.objectId = foundDep->objid; otherObject.objectSubId = foundDep->objsubid; ! findAutoDeletableObjects(&otherObject, oktodelete, depRel, true); break; case DEPENDENCY_PIN: *************** recursiveDeletion(const ObjectAddress *o *** 387,393 **** int msglevel, const ObjectAddress *callingObject, ObjectAddresses *oktodelete, ! Relation depRel) { bool ok = true; char *objDescription; --- 516,523 ---- int msglevel, const ObjectAddress *callingObject, ObjectAddresses *oktodelete, ! Relation depRel, ! ObjectAddresses *alreadyDeleted) { bool ok = true; char *objDescription; *************** recursiveDeletion(const ObjectAddress *o *** 553,559 **** getObjectDescription(&owningObject)))); if (!recursiveDeletion(&owningObject, behavior, msglevel, ! object, oktodelete, depRel)) ok = false; pfree(objDescription); --- 683,689 ---- getObjectDescription(&owningObject)))); if (!recursiveDeletion(&owningObject, behavior, msglevel, ! object, oktodelete, depRel, alreadyDeleted)) ok = false; pfree(objDescription); *************** recursiveDeletion(const ObjectAddress *o *** 579,587 **** */ /* ! * Step 3: delete the object itself. */ doDeletion(object); /* * Delete any comments associated with this object. (This is a convenient --- 709,723 ---- */ /* ! * 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 *************** deleteDependentObjects(const ObjectAddre *** 712,718 **** getObjectDescription(&otherObject)))); if (!recursiveDeletion(&otherObject, behavior, msglevel, ! object, oktodelete, depRel)) ok = false; break; case DEPENDENCY_AUTO: --- 848,854 ---- getObjectDescription(&otherObject)))); if (!recursiveDeletion(&otherObject, behavior, msglevel, ! object, oktodelete, depRel, NULL)) ok = false; break; case DEPENDENCY_AUTO: *************** deleteDependentObjects(const ObjectAddre *** 728,734 **** getObjectDescription(&otherObject)))); if (!recursiveDeletion(&otherObject, behavior, msglevel, ! object, oktodelete, depRel)) ok = false; break; case DEPENDENCY_PIN: --- 864,870 ---- getObjectDescription(&otherObject)))); if (!recursiveDeletion(&otherObject, behavior, msglevel, ! object, oktodelete, depRel, NULL)) ok = false; break; case DEPENDENCY_PIN: *************** object_address_comparator(const void *a, *** 1332,1337 **** --- 1468,1492 ---- /* * Routines for handling an expansible array of ObjectAddress items. * + * new_object_addresses: create a new ObjectAddresses array. + */ + ObjectAddresses * + new_object_addresses(void) + { + ObjectAddresses *addrs; + + addrs = palloc(sizeof(ObjectAddresses)); + + addrs->numrefs = 0; + addrs->maxrefs = 32; + addrs->refs = (ObjectAddress *) + palloc(addrs->maxrefs * sizeof(ObjectAddress)); + addrs->alloc = true; + + return addrs; + } + + /* * init_object_addresses: initialize an ObjectAddresses array. */ static void *************** init_object_addresses(ObjectAddresses *a *** 1342,1347 **** --- 1497,1503 ---- addrs->maxrefs = 32; /* arbitrary initial array size */ addrs->refs = (ObjectAddress *) palloc(addrs->maxrefs * sizeof(ObjectAddress)); + addrs->alloc = false; } /* *************** add_object_address(ObjectClass oclass, O *** 1376,1382 **** * * As above, but specify entry exactly. */ ! static void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs) { --- 1532,1538 ---- * * As above, but specify entry exactly. */ ! void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs) { *************** add_exact_object_address(const ObjectAdd *** 1400,1406 **** * * We return "true" if object is a subobject of something in the array, too. */ ! static bool object_address_present(const ObjectAddress *object, ObjectAddresses *addrs) { --- 1556,1562 ---- * * We return "true" if object is a subobject of something in the array, too. */ ! bool object_address_present(const ObjectAddress *object, ObjectAddresses *addrs) { *************** object_address_present(const ObjectAddre *** 1425,1434 **** /* * Clean up when done with an ObjectAddresses array. */ ! static void term_object_addresses(ObjectAddresses *addrs) { pfree(addrs->refs); } /* --- 1581,1592 ---- /* * Clean up when done with an ObjectAddresses array. */ ! void term_object_addresses(ObjectAddresses *addrs) { pfree(addrs->refs); + if (addrs->alloc) + pfree(addrs); } /* Index: src/backend/catalog/pg_shdepend.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/pg_shdepend.c,v retrieving revision 1.12 diff -c -p -r1.12 pg_shdepend.c *** src/backend/catalog/pg_shdepend.c 14 Jul 2006 14:52:18 -0000 1.12 --- src/backend/catalog/pg_shdepend.c 20 Aug 2006 17:26:58 -0000 *************** shdepDropOwned(List *roleids, DropBehavi *** 1061,1066 **** --- 1061,1069 ---- { Relation sdepRel; ListCell *cell; + ObjectAddresses *deleteobjs; + + deleteobjs = new_object_addresses(); sdepRel = heap_open(SharedDependRelationId, AccessExclusiveLock); *************** shdepDropOwned(List *roleids, DropBehavi *** 1105,1110 **** --- 1108,1116 ---- while ((tuple = systable_getnext(scan)) != NULL) { + ObjectAddress obj; + GrantObjectType objtype; + InternalGrant istmt; Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple); /* We only operate on objects on the current database */ *************** shdepDropOwned(List *roleids, DropBehavi *** 1113,1123 **** switch (sdepForm->deptype) { ! ObjectAddress obj; ! GrantObjectType objtype; ! InternalGrant istmt; ! ! /* Shouldn't happen */ case SHARED_DEPENDENCY_PIN: case SHARED_DEPENDENCY_INVALID: elog(ERROR, "unexpected dependency type"); --- 1119,1125 ---- switch (sdepForm->deptype) { ! /* Shouldn't happen */ case SHARED_DEPENDENCY_PIN: case SHARED_DEPENDENCY_INVALID: elog(ERROR, "unexpected dependency type"); *************** shdepDropOwned(List *roleids, DropBehavi *** 1126,1150 **** switch (sdepForm->classid) { case RelationRelationId: ! { ! /* is it a sequence or non-sequence? */ ! Form_pg_class pg_class_tuple; ! HeapTuple tuple; ! ! tuple = SearchSysCache(RELOID, ! ObjectIdGetDatum(sdepForm->objid), ! 0, 0, 0); ! if (!HeapTupleIsValid(tuple)) ! elog(ERROR, "cache lookup failed for relation %u", ! sdepForm->objid); ! pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple); ! if (pg_class_tuple->relkind == RELKIND_SEQUENCE) ! istmt.objtype = ACL_OBJECT_SEQUENCE; ! else ! istmt.objtype = ACL_OBJECT_RELATION; ! ReleaseSysCache(tuple); break; - } case DatabaseRelationId: istmt.objtype = ACL_OBJECT_DATABASE; break; --- 1128,1152 ---- switch (sdepForm->classid) { case RelationRelationId: ! { ! /* is it a sequence or non-sequence? */ ! Form_pg_class pg_class_tuple; ! HeapTuple tuple; ! ! tuple = SearchSysCache(RELOID, ! ObjectIdGetDatum(sdepForm->objid), ! 0, 0, 0); ! if (!HeapTupleIsValid(tuple)) ! elog(ERROR, "cache lookup failed for relation %u", ! sdepForm->objid); ! pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple); ! if (pg_class_tuple->relkind == RELKIND_SEQUENCE) ! istmt.objtype = ACL_OBJECT_SEQUENCE; ! else ! istmt.objtype = ACL_OBJECT_RELATION; ! ReleaseSysCache(tuple); ! } break; case DatabaseRelationId: istmt.objtype = ACL_OBJECT_DATABASE; break; *************** shdepDropOwned(List *roleids, DropBehavi *** 1178,1197 **** ExecGrantStmt_oids(&istmt); break; case SHARED_DEPENDENCY_OWNER: ! ! /* ! * If there's a regular (non-shared) dependency on this ! * object marked with DEPENDENCY_INTERNAL, skip this ! * object. We will drop the referencer object instead. ! */ ! if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid)) ! continue; ! ! /* Drop the object */ obj.classId = sdepForm->classid; obj.objectId = sdepForm->objid; obj.objectSubId = 0; ! performDeletion(&obj, behavior); break; } } --- 1180,1191 ---- ExecGrantStmt_oids(&istmt); break; case SHARED_DEPENDENCY_OWNER: ! /* Save it for later deleting it */ obj.classId = sdepForm->classid; obj.objectId = sdepForm->objid; obj.objectSubId = 0; ! ! add_exact_object_address(&obj, deleteobjs); break; } } *************** shdepDropOwned(List *roleids, DropBehavi *** 1199,1205 **** --- 1193,1204 ---- systable_endscan(scan); } + /* the dependency mechanism does the actual work */ + performMultipleDeletions(deleteobjs, behavior); + heap_close(sdepRel, AccessExclusiveLock); + + term_object_addresses(deleteobjs); } /* Index: src/include/catalog/dependency.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/dependency.h,v retrieving revision 1.25 diff -c -p -r1.25 dependency.h *** src/include/catalog/dependency.h 27 Jun 2006 18:35:05 -0000 1.25 --- src/include/catalog/dependency.h 20 Aug 2006 17:30:05 -0000 *************** typedef struct ObjectAddress *** 112,117 **** --- 112,119 ---- int32 objectSubId; /* Subitem within the object (column of table) */ } ObjectAddress; + /* expansible list of ObjectAddresses */ + typedef struct ObjectAddresses ObjectAddresses; /* * This enum covers all system catalogs whose OIDs can appear in *************** typedef enum ObjectClass *** 144,149 **** --- 146,154 ---- extern void performDeletion(const ObjectAddress *object, DropBehavior behavior); + extern void performMultipleDeletions(const ObjectAddresses *objects, + DropBehavior behavior); + extern void deleteWhatDependsOn(const ObjectAddress *object, bool showNotices); *************** extern ObjectClass getObjectClass(const *** 160,165 **** --- 165,180 ---- extern char *getObjectDescription(const ObjectAddress *object); + extern ObjectAddresses *new_object_addresses(void); + + extern void add_exact_object_address(const ObjectAddress *object, + ObjectAddresses *addrs); + + extern bool object_address_present(const ObjectAddress *object, + ObjectAddresses *addrs); + + extern void term_object_addresses(ObjectAddresses *addrs); + /* in pg_depend.c */ extern void recordDependencyOn(const ObjectAddress *depender,