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 04:46:38 -0000 *************** *** 56,69 **** #include "utils/syscache.h" - /* 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 { --- 56,61 ---- *************** 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, --- 84,96 ---- }; 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 bool find_expr_references_walker( *** 112,125 **** 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 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); --- 102,109 ---- *************** 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", --- 145,154 ---- */ 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 *** 178,183 **** --- 162,214 ---- pfree(objDescription); } + /* + * As above, but the oktodelete list may have already filled with some + * objects. Also, the deleted objects are saved in the alreadyDeleted + * list. + */ + 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); + } + /* * deleteWhatDependsOn: attempt to drop everything that depends on the *************** 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 --- 246,252 ---- */ 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; --- 277,291 ---- /* * 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. */ ! 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 --- 300,307 ---- */ 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: --- 348,354 ---- 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; --- 419,426 ---- 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); --- 586,592 ---- 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 --- 612,626 ---- */ /* ! * 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: --- 751,757 ---- 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: --- 767,773 ---- getObjectDescription(&otherObject)))); if (!recursiveDeletion(&otherObject, behavior, msglevel, ! object, oktodelete, depRel, NULL)) ok = false; break; case DEPENDENCY_PIN: *************** object_address_comparator(const void *a, *** 1334,1340 **** * * init_object_addresses: initialize an ObjectAddresses array. */ ! static void init_object_addresses(ObjectAddresses *addrs) { /* Initialize array to empty */ --- 1373,1379 ---- * * init_object_addresses: initialize an ObjectAddresses array. */ ! void init_object_addresses(ObjectAddresses *addrs) { /* Initialize array to empty */ *************** 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) { --- 1415,1421 ---- * * 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) { --- 1439,1445 ---- * * 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,1431 **** /* * Clean up when done with an ObjectAddresses array. */ ! static void term_object_addresses(ObjectAddresses *addrs) { pfree(addrs->refs); --- 1464,1470 ---- /* * Clean up when done with an ObjectAddresses array. */ ! void term_object_addresses(ObjectAddresses *addrs) { pfree(addrs->refs); 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 04:40:13 -0000 *************** *** 22,27 **** --- 22,28 ---- #include "catalog/pg_authid.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" + #include "catalog/pg_depend.h" #include "catalog/pg_language.h" #include "catalog/pg_namespace.h" #include "catalog/pg_operator.h" *************** void *** 1060,1068 **** --- 1061,1079 ---- shdepDropOwned(List *roleids, DropBehavior behavior) { Relation sdepRel; + Relation depRel; ListCell *cell; + int i; + ObjectAddresses deleteobjs; + ObjectAddresses implicitobjs; + ObjectAddresses alreadyDeleted; + + init_object_addresses(&deleteobjs); + init_object_addresses(&implicitobjs); + init_object_addresses(&alreadyDeleted); sdepRel = heap_open(SharedDependRelationId, AccessExclusiveLock); + depRel = heap_open(DependRelationId, RowExclusiveLock); /* * For each role, find the dependent objects and drop them using the *************** shdepDropOwned(List *roleids, DropBehavi *** 1086,1094 **** ereport(ERROR, (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), ! errmsg("cannot drop objects owned by %s because they are " ! "required by the database system", ! getObjectDescription(&obj)))); } ScanKeyInit(&key[0], --- 1097,1105 ---- ereport(ERROR, (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), ! errmsg("cannot drop objects owned by %s because they are " ! "required by the database system", ! getObjectDescription(&obj)))); } ScanKeyInit(&key[0], *************** shdepDropOwned(List *roleids, DropBehavi *** 1105,1110 **** --- 1116,1124 ---- 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"); --- 1127,1133 ---- 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; --- 1136,1160 ---- 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; } } --- 1188,1199 ---- 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 **** --- 1201,1265 ---- systable_endscan(scan); } + /* + * Get the list of all objects that would be deleted after deleting the + * whole "deleteobjs" 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 < deleteobjs.numrefs; i++) + { + ObjectAddress obj = deleteobjs.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, &implicitobjs)) + continue; + + /* + * Add the objects dependent on this one to the global list of + * implicit objects. + */ + findAutoDeletableObjects(&obj, &implicitobjs, depRel, false); + } + + /* + * Do the deletion. On each iteration, skip deleting objects that were + * already deleted, and those that will be deleted in the future + * because of being in the implicit list. + */ + for (i = 0; i < deleteobjs.numrefs; i++) + { + ObjectAddress obj = deleteobjs.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, &implicitobjs)) + continue; + + /* delete the objects that remain */ + performDeletionWithList(&obj, &implicitobjs, behavior, &alreadyDeleted); + } + heap_close(sdepRel, AccessExclusiveLock); + heap_close(depRel, RowExclusiveLock); + + term_object_addresses(&deleteobjs); + term_object_addresses(&implicitobjs); + term_object_addresses(&alreadyDeleted); } /* 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 03:54:36 -0000 *************** *** 15,20 **** --- 15,21 ---- #define DEPENDENCY_H #include "nodes/parsenodes.h" /* for DropBehavior */ + #include "utils/rel.h" /*---------- *************** typedef struct ObjectAddress *** 112,117 **** --- 113,126 ---- int32 objectSubId; /* Subitem within the object (column of table) */ } ObjectAddress; + /* expansible list of ObjectAddresses */ + typedef struct ObjectAddresses + { + ObjectAddress *refs; /* => palloc'd array */ + int numrefs; /* current number of references */ + int maxrefs; /* current size of palloc'd array */ + } ObjectAddresses; + /* * This enum covers all system catalogs whose OIDs can appear in *************** typedef enum ObjectClass *** 141,149 **** --- 150,167 ---- /* in dependency.c */ + extern void findAutoDeletableObjects(const ObjectAddress *object, + ObjectAddresses *oktodelete, + Relation depRel, bool addself); + extern void performDeletion(const ObjectAddress *object, DropBehavior behavior); + extern void performDeletionWithList(const ObjectAddress *object, + ObjectAddresses *oktodelete, + DropBehavior behavior, + ObjectAddresses *alreadyDeleted); + extern void deleteWhatDependsOn(const ObjectAddress *object, bool showNotices); *************** extern ObjectClass getObjectClass(const *** 160,165 **** --- 178,193 ---- extern char *getObjectDescription(const ObjectAddress *object); + extern void init_object_addresses(ObjectAddresses *addrs); + + 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,