* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
*
* 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)
*/
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.
{
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)
(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);
}
/*
(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
--
-- 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
(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;
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
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;
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
-- 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
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
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);
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
(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');
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;
(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,
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;