]> granicus.if.org Git - postgresql/commitdiff
Improve reporting of dependencies in DROP to work like the scheme that we
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 11 Jun 2008 21:53:49 +0000 (21:53 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 11 Jun 2008 21:53:49 +0000 (21:53 +0000)
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
src/test/regress/expected/alter_table.out
src/test/regress/expected/create_view.out
src/test/regress/expected/domain.out
src/test/regress/expected/foreign_key.out
src/test/regress/expected/inherit.out
src/test/regress/expected/namespace.out
src/test/regress/expected/sequence.out
src/test/regress/expected/truncate.out
src/test/regress/output/tablespace.source

index 12b654504d7c1c3615c09eab4acf40b178d7881c..8ca95a3fd65aa3a710ef40c9e6f328250e8d4408 100644 (file)
@@ -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);
 }
 
 /*
index e7aef8bf59d5c53dbbfdd549ac2392cf9cc9704a..daf9482c4add2cead65202eb07ad24f5548d59a0 100644 (file)
@@ -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
index 3eae7e90ccdfc9bc102576894c734f065e158618..cbee9dceed88f57108bcc441331caede784164e9 100644 (file)
@@ -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;
index 179c8c63471fec8acb7e0d6407cc53f4c93fc925..fd88b16ccee0553aba2bbcdbb1650fa0de327c87 100644 (file)
@@ -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
index 87e6591ea35d274b02d23e0205edf9ffbe653700..e3086d2e08565e34098cbb60eec2c27442d09bc4 100644 (file)
@@ -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
index 7c7ac0ef3388decef5eb17711574969bb5d382dc..100edb35396dc63c7e62e20565b910eb68c24f30 100644 (file)
@@ -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
index 94b3e5e99b0ef66ff22cf3286a9c6855529d59ad..58a0c4dc81d6a98e655a8c156edf8fc2fc7b25b3 100644 (file)
@@ -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');
index 84ece98357b60084a94882488e4107b74d68a1a3..823039ae9558212f78a95ee64a8ba4af0b43075b 100644 (file)
@@ -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;
index d0a99a554a62a77b244872337a13f8d83f258012..520db9d9b171b122d0217c59f022473263b23a04 100644 (file)
@@ -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,
index 6337798c6258043bb58010a1dc0970684e2183c7..8065505be95414b3411af4813ef794e8c49a50d4 100644 (file)
@@ -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;