struct ObjectAddressStack *next; /* next outer stack level */
} ObjectAddressStack;
+/* temporary storage in findDependentObjects */
+typedef struct
+{
+ ObjectAddress obj; /* object to be deleted --- MUST BE FIRST */
+ int subflags; /* flags to pass down when recursing to obj */
+} ObjectAddressAndFlags;
+
/* for find_expr_references_walker */
typedef struct
{
SysScanDesc scan;
HeapTuple tup;
ObjectAddress otherObject;
+ ObjectAddressAndFlags *dependentObjects;
+ int numDependentObjects;
+ int maxDependentObjects;
ObjectAddressStack mystack;
ObjectAddressExtra extra;
systable_endscan(scan);
/*
- * Now recurse to any dependent objects. We must visit them first since
- * they have to be deleted before the current object.
+ * Next, identify all objects that directly depend on the current object.
+ * To ensure predictable deletion order, we collect them up in
+ * dependentObjects and sort the list before actually recursing. (The
+ * deletion order would be valid in any case, but doing this ensures
+ * consistent output from DROP CASCADE commands, which is helpful for
+ * regression testing.)
*/
- mystack.object = object; /* set up a new stack level */
- mystack.flags = objflags;
- mystack.next = stack;
+ maxDependentObjects = 128; /* arbitrary initial allocation */
+ dependentObjects = (ObjectAddressAndFlags *)
+ palloc(maxDependentObjects * sizeof(ObjectAddressAndFlags));
+ numDependentObjects = 0;
ScanKeyInit(&key[0],
Anum_pg_depend_refclassid,
continue;
}
- /* Recurse, passing objflags indicating the dependency type */
+ /*
+ * We do need to delete it, so identify objflags to be passed down,
+ * which depend on the dependency type.
+ */
switch (foundDep->deptype)
{
case DEPENDENCY_NORMAL:
break;
}
- findDependentObjects(&otherObject,
- subflags,
+ /* And add it to the pending-objects list */
+ if (numDependentObjects >= maxDependentObjects)
+ {
+ /* enlarge array if needed */
+ maxDependentObjects *= 2;
+ dependentObjects = (ObjectAddressAndFlags *)
+ repalloc(dependentObjects,
+ maxDependentObjects * sizeof(ObjectAddressAndFlags));
+ }
+
+ dependentObjects[numDependentObjects].obj = otherObject;
+ dependentObjects[numDependentObjects].subflags = subflags;
+ numDependentObjects++;
+ }
+
+ systable_endscan(scan);
+
+ /*
+ * Now we can sort the dependent objects into a stable visitation order.
+ * It's safe to use object_address_comparator here since the obj field is
+ * first within ObjectAddressAndFlags.
+ */
+ if (numDependentObjects > 1)
+ qsort((void *) dependentObjects, numDependentObjects,
+ sizeof(ObjectAddressAndFlags),
+ object_address_comparator);
+
+ /*
+ * Now recurse to the dependent objects. We must visit them first since
+ * they have to be deleted before the current object.
+ */
+ mystack.object = object; /* set up a new stack level */
+ mystack.flags = objflags;
+ mystack.next = stack;
+
+ for (int i = 0; i < numDependentObjects; i++)
+ {
+ ObjectAddressAndFlags *depObj = dependentObjects + i;
+
+ findDependentObjects(&depObj->obj,
+ depObj->subflags,
flags,
&mystack,
targetObjects,
depRel);
}
- systable_endscan(scan);
+ pfree(dependentObjects);
/*
* Finally, we can add the target object to targetObjects. Be careful to
const ObjectAddress *obja = (const ObjectAddress *) a;
const ObjectAddress *objb = (const ObjectAddress *) b;
- if (obja->classId < objb->classId)
+ /*
+ * Primary sort key is OID descending. Most of the time, this will result
+ * in putting newer objects before older ones, which is likely to be the
+ * right order to delete in.
+ */
+ if (obja->objectId > objb->objectId)
return -1;
- if (obja->classId > objb->classId)
- return 1;
if (obja->objectId < objb->objectId)
+ return 1;
+
+ /*
+ * Next sort on catalog ID, in case identical OIDs appear in different
+ * catalogs. Sort direction is pretty arbitrary here.
+ */
+ if (obja->classId < objb->classId)
return -1;
- if (obja->objectId > objb->objectId)
+ if (obja->classId > objb->classId)
return 1;
/*
- * We sort the subId as an unsigned int so that 0 will come first. See
- * logic in eliminate_duplicate_dependencies.
+ * Last, sort on object subId.
+ *
+ * We sort the subId as an unsigned int so that 0 (the whole object) will
+ * come first. This is essential for eliminate_duplicate_dependencies,
+ * and is also the best order for findDependentObjects.
*/
if ((unsigned int) obja->objectSubId < (unsigned int) objb->objectSubId)
return -1;
ERROR: function base_fn_out(opaque) does not exist
DROP TYPE base_type; -- error
ERROR: cannot drop type base_type because other objects depend on it
-DETAIL: function base_fn_out(base_type) depends on type base_type
-function base_fn_in(cstring) depends on type base_type
+DETAIL: function base_fn_in(cstring) depends on type base_type
+function base_fn_out(base_type) depends on type base_type
HINT: Use DROP ... CASCADE to drop the dependent objects too.
DROP TYPE base_type CASCADE;
NOTICE: drop cascades to 2 other objects
-DETAIL: drop cascades to function base_fn_out(base_type)
-drop cascades to function base_fn_in(cstring)
+DETAIL: drop cascades to function base_fn_in(cstring)
+drop cascades to function base_fn_out(base_type)
-- Check usage of typmod with a user-defined type
-- (we have borrowed numeric's typmod functions)
CREATE TEMP TABLE mytab (foo widget(42,13,7)); -- should fail
DROP TABLE mvtest_t;
ERROR: cannot drop table mvtest_t because other objects depend on it
DETAIL: view mvtest_tv depends on table mvtest_t
+materialized view mvtest_mvschema.mvtest_tvm depends on view mvtest_tv
+materialized view mvtest_tvmm depends on materialized view mvtest_mvschema.mvtest_tvm
view mvtest_tvv depends on view mvtest_tv
materialized view mvtest_tvvm depends on view mvtest_tvv
view mvtest_tvvmv depends on materialized view mvtest_tvvm
materialized view mvtest_bb depends on view mvtest_tvvmv
-materialized view mvtest_mvschema.mvtest_tvm depends on view mvtest_tv
-materialized view mvtest_tvmm depends on materialized view mvtest_mvschema.mvtest_tvm
materialized view mvtest_tm depends on table mvtest_t
materialized view mvtest_tmm depends on materialized view mvtest_tm
HINT: Use DROP ... CASCADE to drop the dependent objects too.
DROP TABLE mvtest_t CASCADE;
NOTICE: drop cascades to 9 other objects
DETAIL: drop cascades to view mvtest_tv
+drop cascades to materialized view mvtest_mvschema.mvtest_tvm
+drop cascades to materialized view mvtest_tvmm
drop cascades to view mvtest_tvv
drop cascades to materialized view mvtest_tvvm
drop cascades to view mvtest_tvvmv
drop cascades to materialized view mvtest_bb
-drop cascades to materialized view mvtest_mvschema.mvtest_tvm
-drop cascades to materialized view mvtest_tvmm
drop cascades to materialized view mvtest_tm
drop cascades to materialized view mvtest_tmm
ROLLBACK;
drop cascades to view ro_view17
drop cascades to view ro_view2
drop cascades to view ro_view3
+drop cascades to view ro_view4
drop cascades to view ro_view5
drop cascades to view ro_view6
drop cascades to view ro_view7
drop cascades to view ro_view9
drop cascades to view ro_view11
drop cascades to view ro_view13
+drop cascades to view rw_view14
drop cascades to view rw_view15
drop cascades to view rw_view16
drop cascades to view ro_view20
-drop cascades to view ro_view4
-drop cascades to view rw_view14
DROP VIEW ro_view10, ro_view12, ro_view18;
DROP SEQUENCE uv_seq CASCADE;
NOTICE: drop cascades to view ro_view19