+
+ findDependentObjects(&otherObject,
+ subflags,
+ &mystack,
+ targetObjects,
+ pendingObjects,
+ depRel);
+ }
+
+ systable_endscan(scan);
+
+ /*
+ * Finally, we can add the target object to targetObjects. Be careful
+ * to include any flags that were passed back down to us from inner
+ * recursion levels.
+ */
+ extra.flags = mystack.flags;
+ if (stack)
+ extra.dependee = *stack->object;
+ else
+ memset(&extra.dependee, 0, sizeof(extra.dependee));
+ add_exact_object_address_extra(object, &extra, targetObjects);
+}
+
+/*
+ * reportDependentObjects - report about dependencies, and fail if RESTRICT
+ *
+ * Tell the user about dependent objects that we are going to delete
+ * (or would need to delete, but are prevented by RESTRICT mode);
+ * then error out if there are any and it's not CASCADE mode.
+ *
+ * targetObjects: list of objects that are scheduled to be deleted
+ * behavior: RESTRICT or CASCADE
+ * msglevel: elog level for non-error report messages
+ * origObject: base object of deletion, or NULL if not available
+ * (the latter case occurs in DROP OWNED)
+ */
+static void
+reportDependentObjects(const ObjectAddresses *targetObjects,
+ DropBehavior behavior,
+ int msglevel,
+ const ObjectAddress *origObject)
+{
+ bool ok = true;
+ StringInfoData clientdetail;
+ StringInfoData logdetail;
+ int numReportedClient = 0;
+ int numNotReportedClient = 0;
+ int i;
+
+ /*
+ * If no error is to be thrown, and the msglevel is too low to be shown
+ * to either client or server log, there's no need to do any of the work.
+ *
+ * Note: this code doesn't know all there is to be known about elog
+ * levels, but it works for NOTICE and DEBUG2, which are the only values
+ * msglevel can currently have. We also assume we are running in a normal
+ * operating environment.
+ */
+ if (behavior == DROP_CASCADE &&
+ msglevel < client_min_messages &&
+ (msglevel < log_min_messages || log_min_messages == LOG))
+ return;
+
+ /*
+ * We limit the number of dependencies reported to the client to
+ * MAX_REPORTED_DEPS, since client software may not deal well with
+ * enormous error strings. The server log always gets a full report.
+ */
+#define MAX_REPORTED_DEPS 100
+
+ initStringInfo(&clientdetail);
+ initStringInfo(&logdetail);
+
+ /*
+ * We process the list back to front (ie, in dependency order not deletion
+ * order), since this makes for a more understandable display.
+ */
+ for (i = targetObjects->numrefs - 1; i >= 0; i--)
+ {
+ const ObjectAddress *obj = &targetObjects->refs[i];
+ const ObjectAddressExtra *extra = &targetObjects->extras[i];
+ char *objDesc;
+
+ /* Ignore the original deletion target(s) */
+ if (extra->flags & DEPFLAG_ORIGINAL)
+ continue;
+
+ objDesc = getObjectDescription(obj);
+
+ /*
+ * If, at any stage of the recursive search, we reached the object
+ * via an AUTO or INTERNAL dependency, then it's okay to delete it
+ * even in RESTRICT mode.
+ */
+ if (extra->flags & (DEPFLAG_AUTO | DEPFLAG_INTERNAL))
+ {
+ /*
+ * auto-cascades are reported at DEBUG2, not msglevel. We
+ * don't try to combine them with the regular message because
+ * the results are too confusing when client_min_messages and
+ * log_min_messages are different.
+ */
+ ereport(DEBUG2,
+ (errmsg("drop auto-cascades to %s",
+ objDesc)));
+ }
+ else if (behavior == DROP_RESTRICT)
+ {
+ char *otherDesc = getObjectDescription(&extra->dependee);
+
+ if (numReportedClient < MAX_REPORTED_DEPS)
+ {
+ /* separate entries with a newline */
+ if (clientdetail.len != 0)
+ appendStringInfoChar(&clientdetail, '\n');
+ appendStringInfo(&clientdetail, _("%s depends on %s"),
+ objDesc, otherDesc);
+ numReportedClient++;
+ }
+ else
+ numNotReportedClient++;
+ /* separate entries with a newline */
+ if (logdetail.len != 0)
+ appendStringInfoChar(&logdetail, '\n');
+ appendStringInfo(&logdetail, _("%s depends on %s"),
+ objDesc, otherDesc);
+ pfree(otherDesc);
+ ok = false;
+ }
+ else
+ {
+ if (numReportedClient < MAX_REPORTED_DEPS)
+ {
+ /* separate entries with a newline */
+ if (clientdetail.len != 0)
+ appendStringInfoChar(&clientdetail, '\n');
+ appendStringInfo(&clientdetail, _("drop cascades to %s"),
+ objDesc);
+ numReportedClient++;
+ }
+ else
+ numNotReportedClient++;
+ /* separate entries with a newline */
+ if (logdetail.len != 0)
+ appendStringInfoChar(&logdetail, '\n');
+ appendStringInfo(&logdetail, _("drop cascades to %s"),
+ objDesc);
+ }
+
+ pfree(objDesc);
+ }
+
+ if (numNotReportedClient > 0)
+ appendStringInfo(&clientdetail, ngettext("\nand %d other object "
+ "(see server log for list)",
+ "\nand %d other objects "
+ "(see server log for list)",
+ numNotReportedClient),
+ numNotReportedClient);
+
+ if (!ok)
+ {
+ if (origObject)
+ ereport(ERROR,
+ (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+ errmsg("cannot drop %s because other objects depend on it",
+ getObjectDescription(origObject)),
+ errdetail("%s", clientdetail.data),
+ errdetail_log("%s", logdetail.data),
+ errhint("Use DROP ... CASCADE to drop the dependent objects too.")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+ errmsg("cannot drop desired object(s) because other objects depend on them"),
+ errdetail("%s", clientdetail.data),
+ errdetail_log("%s", logdetail.data),
+ errhint("Use DROP ... CASCADE to drop the dependent objects too.")));
+ }
+ else if (numReportedClient > 1)
+ {
+ ereport(msglevel,
+ /* translator: %d always has a value larger than 1 */
+ (errmsg_plural("drop cascades to %d other object",
+ "drop cascades to %d other objects",
+ numReportedClient + numNotReportedClient,
+ numReportedClient + numNotReportedClient),
+ errdetail("%s", clientdetail.data),
+ errdetail_log("%s", logdetail.data)));
+ }
+ else if (numReportedClient == 1)
+ {
+ /* we just use the single item as-is */
+ ereport(msglevel,
+ (errmsg_internal("%s", clientdetail.data)));
+ }
+
+ pfree(clientdetail.data);
+ pfree(logdetail.data);
+}
+
+/*
+ * deleteOneObject: delete a single object for performDeletion.
+ *
+ * depRel is the already-open pg_depend relation.
+ */
+static void
+deleteOneObject(const ObjectAddress *object, Relation depRel)
+{
+ ScanKeyData key[3];
+ int nkeys;
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ /*
+ * First remove any pg_depend records that link from this object to
+ * others. (Any records linking to this object should be gone already.)
+ *
+ * When dropping a whole object (subId = 0), remove all pg_depend records
+ * for its sub-objects too.
+ */
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ if (object->objectSubId != 0)
+ {
+ ScanKeyInit(&key[2],
+ Anum_pg_depend_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+ nkeys = 3;
+ }
+ else
+ nkeys = 2;
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ SnapshotNow, nkeys, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ simple_heap_delete(depRel, &tup->t_self);