From c4f2a0458dc029d1214f013f1434f70f5194e56d Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Wed, 11 Jun 2008 21:53:49 +0000 Subject: [PATCH] Improve reporting of dependencies in DROP to work like the scheme that we devised for pg_shdepend, namely the individual dependencies are reported as DETAIL lines rather than coming out as separate NOTICEs. The client-side report is capped at 100 lines, but the server log always gets a full report. --- src/backend/catalog/dependency.c | 119 ++++++++++++++++++++-- src/test/regress/expected/alter_table.out | 16 +-- src/test/regress/expected/create_view.out | 78 +++++++------- src/test/regress/expected/domain.out | 12 ++- src/test/regress/expected/foreign_key.out | 21 ++-- src/test/regress/expected/inherit.out | 12 ++- src/test/regress/expected/namespace.out | 5 +- src/test/regress/expected/sequence.out | 4 +- src/test/regress/expected/truncate.out | 10 +- src/test/regress/output/tablespace.source | 9 +- 10 files changed, 199 insertions(+), 87 deletions(-) diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 12b654504d7..8ca95a3fd65 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.74 2008/06/08 22:41:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.75 2008/06/11 21:53:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -62,6 +62,7 @@ #include "storage/lmgr.h" #include "utils/builtins.h" #include "utils/fmgroids.h" +#include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -752,7 +753,7 @@ findDependentObjects(const ObjectAddress *object, * * targetObjects: list of objects that are scheduled to be deleted * behavior: RESTRICT or CASCADE - * msglevel: elog level for non-debug notice messages + * 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) */ @@ -763,8 +764,36 @@ reportDependentObjects(const ObjectAddresses *targetObjects, 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. @@ -773,34 +802,82 @@ reportDependentObjects(const ObjectAddresses *targetObjects, { 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", - getObjectDescription(obj)))); + objDesc))); + } else if (behavior == DROP_RESTRICT) { - ereport(msglevel, - (errmsg("%s depends on %s", - getObjectDescription(obj), - getObjectDescription(&extra->dependee)))); + 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 - ereport(msglevel, - (errmsg("drop cascades to %s", - getObjectDescription(obj)))); + { + 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, _("\nand %d other objects " + "(see server log for list)"), + numNotReportedClient); + if (!ok) { if (origObject) @@ -808,13 +885,35 @@ reportDependentObjects(const ObjectAddresses *targetObjects, (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("drop cascades to %d other objects", + 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); } /* diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index e7aef8bf59d..daf9482c4ad 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -1161,8 +1161,9 @@ order by relname, attnum; (8 rows) drop table p1, p2 cascade; -NOTICE: drop cascades to table c1 -NOTICE: drop cascades to table gc1 +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table c1 +drop cascades to table gc1 -- -- Test the ALTER TABLE WITHOUT OIDS command -- @@ -1469,8 +1470,9 @@ select alter2.plus1(41); -- clean up drop schema alter2 cascade; -NOTICE: drop cascades to table alter2.t1 -NOTICE: drop cascades to view alter2.v1 -NOTICE: drop cascades to function alter2.plus1(integer) -NOTICE: drop cascades to type alter2.posint -NOTICE: drop cascades to type alter2.ctype +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to table alter2.t1 +drop cascades to view alter2.v1 +drop cascades to function alter2.plus1(integer) +drop cascades to type alter2.posint +drop cascades to type alter2.ctype diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index 3eae7e90ccd..cbee9dceed8 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -237,43 +237,45 @@ And relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%') (1 row) DROP SCHEMA temp_view_test CASCADE; -NOTICE: drop cascades to table temp_view_test.base_table -NOTICE: drop cascades to view v7_temp -NOTICE: drop cascades to view v10_temp -NOTICE: drop cascades to view v11_temp -NOTICE: drop cascades to view v12_temp -NOTICE: drop cascades to view v2_temp -NOTICE: drop cascades to view v4_temp -NOTICE: drop cascades to view v6_temp -NOTICE: drop cascades to view v8_temp -NOTICE: drop cascades to view v9_temp -NOTICE: drop cascades to table temp_view_test.base_table2 -NOTICE: drop cascades to view v5_temp -NOTICE: drop cascades to view temp_view_test.v1 -NOTICE: drop cascades to view temp_view_test.v2 -NOTICE: drop cascades to view temp_view_test.v3 -NOTICE: drop cascades to view temp_view_test.v4 -NOTICE: drop cascades to view temp_view_test.v5 -NOTICE: drop cascades to view temp_view_test.v6 -NOTICE: drop cascades to view temp_view_test.v7 -NOTICE: drop cascades to view temp_view_test.v8 -NOTICE: drop cascades to sequence temp_view_test.seq1 -NOTICE: drop cascades to view temp_view_test.v9 +NOTICE: drop cascades to 22 other objects +DETAIL: drop cascades to table temp_view_test.base_table +drop cascades to view v7_temp +drop cascades to view v10_temp +drop cascades to view v11_temp +drop cascades to view v12_temp +drop cascades to view v2_temp +drop cascades to view v4_temp +drop cascades to view v6_temp +drop cascades to view v8_temp +drop cascades to view v9_temp +drop cascades to table temp_view_test.base_table2 +drop cascades to view v5_temp +drop cascades to view temp_view_test.v1 +drop cascades to view temp_view_test.v2 +drop cascades to view temp_view_test.v3 +drop cascades to view temp_view_test.v4 +drop cascades to view temp_view_test.v5 +drop cascades to view temp_view_test.v6 +drop cascades to view temp_view_test.v7 +drop cascades to view temp_view_test.v8 +drop cascades to sequence temp_view_test.seq1 +drop cascades to view temp_view_test.v9 DROP SCHEMA testviewschm2 CASCADE; -NOTICE: drop cascades to table t1 -NOTICE: drop cascades to view temporal1 -NOTICE: drop cascades to view temporal2 -NOTICE: drop cascades to view temporal3 -NOTICE: drop cascades to view temporal4 -NOTICE: drop cascades to table t2 -NOTICE: drop cascades to view nontemp1 -NOTICE: drop cascades to view nontemp2 -NOTICE: drop cascades to view nontemp3 -NOTICE: drop cascades to view nontemp4 -NOTICE: drop cascades to table tbl1 -NOTICE: drop cascades to table tbl2 -NOTICE: drop cascades to table tbl3 -NOTICE: drop cascades to table tbl4 -NOTICE: drop cascades to view mytempview -NOTICE: drop cascades to view pubview +NOTICE: drop cascades to 16 other objects +DETAIL: drop cascades to table t1 +drop cascades to view temporal1 +drop cascades to view temporal2 +drop cascades to view temporal3 +drop cascades to view temporal4 +drop cascades to table t2 +drop cascades to view nontemp1 +drop cascades to view nontemp2 +drop cascades to view nontemp3 +drop cascades to view nontemp4 +drop cascades to table tbl1 +drop cascades to table tbl2 +drop cascades to table tbl3 +drop cascades to table tbl4 +drop cascades to view mytempview +drop cascades to view pubview SET search_path to public; diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index 179c8c63471..fd88b16ccee 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -7,8 +7,8 @@ comment on domain domaindroptest is 'About to drop this..'; create domain dependenttypetest domaindroptest; -- fail because of dependent type drop domain domaindroptest; -NOTICE: type dependenttypetest depends on type domaindroptest ERROR: cannot drop type domaindroptest because other objects depend on it +DETAIL: type dependenttypetest depends on type domaindroptest HINT: Use DROP ... CASCADE to drop the dependent objects too. drop domain domaindroptest cascade; NOTICE: drop cascades to type dependenttypetest @@ -266,8 +266,9 @@ ERROR: domain dnotnulltest does not allow null values alter domain dnotnulltest drop not null; update domnotnull set col1 = null; drop domain dnotnulltest cascade; -NOTICE: drop cascades to table domnotnull column col1 -NOTICE: drop cascades to table domnotnull column col2 +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table domnotnull column col1 +drop cascades to table domnotnull column col2 -- Test ALTER DOMAIN .. DEFAULT .. create table domdeftest (col1 ddef1); insert into domdeftest default values; @@ -395,8 +396,9 @@ insert into dtest values('xz23'); -- fail ERROR: value for domain dtop violates check constraint "dtop_check" drop table dtest; drop domain vchar4 cascade; -NOTICE: drop cascades to type dinter -NOTICE: drop cascades to type dtop +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to type dinter +drop cascades to type dtop -- Make sure that constraints of newly-added domain columns are -- enforced correctly, even if there's no default value for the new -- column. Per bug #1433 diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 87e6591ea35..e3086d2e085 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -257,8 +257,8 @@ SELECT * FROM FKTABLE; -- this should fail for lack of CASCADE DROP TABLE PKTABLE; -NOTICE: constraint constrname2 on table fktable depends on table pktable ERROR: cannot drop table pktable because other objects depend on it +DETAIL: constraint constrname2 on table fktable depends on table pktable HINT: Use DROP ... CASCADE to drop the dependent objects too. DROP TABLE PKTABLE CASCADE; NOTICE: drop cascades to constraint constrname2 on table fktable @@ -1157,15 +1157,16 @@ FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2); ERROR: foreign key constraint "fk_241_132" cannot be implemented DETAIL: Key columns "x2" and "id1" are of incompatible types: character varying and integer. DROP TABLE pktable, fktable CASCADE; -NOTICE: drop cascades to constraint fktable_x3_fkey on table fktable -NOTICE: drop cascades to constraint fk_1_3 on table fktable -NOTICE: drop cascades to constraint fktable_x2_fkey on table fktable -NOTICE: drop cascades to constraint fk_4_2 on table fktable -NOTICE: drop cascades to constraint fktable_x1_fkey on table fktable -NOTICE: drop cascades to constraint fk_5_1 on table fktable -NOTICE: drop cascades to constraint fk_123_123 on table fktable -NOTICE: drop cascades to constraint fk_213_213 on table fktable -NOTICE: drop cascades to constraint fk_253_213 on table fktable +NOTICE: drop cascades to 9 other objects +DETAIL: drop cascades to constraint fktable_x3_fkey on table fktable +drop cascades to constraint fk_1_3 on table fktable +drop cascades to constraint fktable_x2_fkey on table fktable +drop cascades to constraint fk_4_2 on table fktable +drop cascades to constraint fktable_x1_fkey on table fktable +drop cascades to constraint fk_5_1 on table fktable +drop cascades to constraint fk_123_123 on table fktable +drop cascades to constraint fk_213_213 on table fktable +drop cascades to constraint fk_253_213 on table fktable -- test a tricky case: we can elide firing the FK check trigger during -- an UPDATE if the UPDATE did not change the foreign key -- field. However, we can't do this if our transaction was the one that diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 7c7ac0ef338..100edb35396 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -844,9 +844,10 @@ Inherits: c1, c2 drop table p1 cascade; -NOTICE: drop cascades to table c1 -NOTICE: drop cascades to table c2 -NOTICE: drop cascades to table c3 +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to table c1 +drop cascades to table c2 +drop cascades to table c3 drop table p2 cascade; create table pp1 (f1 int); create table cc1 (f2 text, f3 int) inherits (pp1); @@ -900,5 +901,6 @@ Inherits: pp1, cc1 drop table pp1 cascade; -NOTICE: drop cascades to table cc1 -NOTICE: drop cascades to table cc2 +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table cc1 +drop cascades to table cc2 diff --git a/src/test/regress/expected/namespace.out b/src/test/regress/expected/namespace.out index 94b3e5e99b0..58a0c4dc81d 100644 --- a/src/test/regress/expected/namespace.out +++ b/src/test/regress/expected/namespace.out @@ -39,8 +39,9 @@ SELECT * FROM test_schema_1.abc_view; (3 rows) DROP SCHEMA test_schema_1 CASCADE; -NOTICE: drop cascades to table test_schema_1.abc -NOTICE: drop cascades to view test_schema_1.abc_view +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table test_schema_1.abc +drop cascades to view test_schema_1.abc_view -- verify that the objects were dropped SELECT COUNT(*) FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'test_schema_1'); diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 84ece98357b..823039ae955 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -148,12 +148,12 @@ CREATE TEMP TABLE t1 ( NOTICE: CREATE TABLE will create implicit sequence "t1_f1_seq" for serial column "t1.f1" -- Both drops should fail, but with different error messages: DROP SEQUENCE t1_f1_seq; -NOTICE: default for table t1 column f1 depends on sequence t1_f1_seq ERROR: cannot drop sequence t1_f1_seq because other objects depend on it +DETAIL: default for table t1 column f1 depends on sequence t1_f1_seq HINT: Use DROP ... CASCADE to drop the dependent objects too. DROP SEQUENCE myseq2; -NOTICE: default for table t1 column f2 depends on sequence myseq2 ERROR: cannot drop sequence myseq2 because other objects depend on it +DETAIL: default for table t1 column f2 depends on sequence myseq2 HINT: Use DROP ... CASCADE to drop the dependent objects too. -- This however will work: DROP SEQUENCE myseq3; diff --git a/src/test/regress/expected/truncate.out b/src/test/regress/expected/truncate.out index d0a99a554a6..520db9d9b17 100644 --- a/src/test/regress/expected/truncate.out +++ b/src/test/regress/expected/truncate.out @@ -141,10 +141,12 @@ SELECT * FROM trunc_e; (0 rows) DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE; -NOTICE: drop cascades to constraint trunc_b_a_fkey on table trunc_b -NOTICE: drop cascades to constraint trunc_e_a_fkey on table trunc_e -NOTICE: drop cascades to constraint trunc_d_a_fkey on table trunc_d -NOTICE: drop cascades to constraint trunc_e_b_fkey on table trunc_e +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to constraint trunc_b_a_fkey on table trunc_b +drop cascades to constraint trunc_e_a_fkey on table trunc_e +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to constraint trunc_d_a_fkey on table trunc_d +drop cascades to constraint trunc_e_b_fkey on table trunc_e -- Test ON TRUNCATE triggers CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text); CREATE TABLE trunc_trigger_log (tgop text, tglevel text, tgwhen text, diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source index 6337798c625..8065505be95 100644 --- a/src/test/regress/output/tablespace.source +++ b/src/test/regress/output/tablespace.source @@ -65,9 +65,10 @@ ERROR: tablespace "nosuchspace" does not exist DROP TABLESPACE testspace; ERROR: tablespace "testspace" is not empty DROP SCHEMA testschema CASCADE; -NOTICE: drop cascades to table testschema.foo -NOTICE: drop cascades to table testschema.asselect -NOTICE: drop cascades to table testschema.asexecute -NOTICE: drop cascades to table testschema.atable +NOTICE: drop cascades to 4 other objects +DETAIL: drop cascades to table testschema.foo +drop cascades to table testschema.asselect +drop cascades to table testschema.asexecute +drop cascades to table testschema.atable -- Should succeed DROP TABLESPACE testspace; -- GitLab