Refactor the handling of the various DropStmt variants so that when multiple
authorTom Lane
Sat, 14 Jun 2008 18:04:34 +0000 (18:04 +0000)
committerTom Lane
Sat, 14 Jun 2008 18:04:34 +0000 (18:04 +0000)
objects are specified, we drop them all in a single performMultipleDeletions
call.  This makes the RESTRICT/CASCADE checks more relaxed: it's not counted
as a cascade if one of the later objects has a dependency on an earlier one.
NOTICE messages about such cases go away, too.

In passing, fix the permissions check for DROP CONVERSION, which for some
reason was never made role-aware, and omitted the namespace-owner exemption
too.

Alex Hunsaker, with further fiddling by me.

20 files changed:
src/backend/catalog/dependency.c
src/backend/catalog/pg_conversion.c
src/backend/commands/conversioncmds.c
src/backend/commands/indexcmds.c
src/backend/commands/schemacmds.c
src/backend/commands/tablecmds.c
src/backend/commands/tsearchcmds.c
src/backend/commands/typecmds.c
src/backend/commands/view.c
src/backend/tcop/utility.c
src/include/catalog/pg_conversion_fn.h
src/include/commands/conversioncmds.h
src/include/commands/defrem.h
src/include/commands/schemacmds.h
src/include/commands/tablecmds.h
src/include/commands/typecmds.h
src/include/commands/view.h
src/test/regress/expected/foreign_key.out
src/test/regress/expected/truncate.out
src/test/regress/sql/foreign_key.sql

index 8ca95a3fd65aa3a710ef40c9e6f328250e8d4408..793071b0f055757e16c3a640f12d1f04e0c5a3ef 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.75 2008/06/11 21:53:48 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.76 2008/06/14 18:04:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -259,6 +259,10 @@ performMultipleDeletions(const ObjectAddresses *objects,
    ObjectAddresses *targetObjects;
    int         i;
 
+   /* No work if no objects... */
+   if (objects->numrefs <= 0)
+       return;
+
    /*
     * We save some cycles by opening pg_depend just once and passing the
     * Relation pointer down to all the recursive deletion steps.
@@ -295,11 +299,14 @@ performMultipleDeletions(const ObjectAddresses *objects,
 
    /*
     * Check if deletion is allowed, and report about cascaded deletes.
+    *
+    * If there's exactly one object being deleted, report it the same
+    * way as in performDeletion(), else we have to be vaguer.
     */
    reportDependentObjects(targetObjects,
                           behavior,
                           NOTICE,
-                          NULL);
+                          (objects->numrefs == 1 ? objects->refs : NULL));
 
    /*
     * Delete all the objects in the proper order.
index e8f28a7d873315da98e5b92c3b025a276fb42a2f..753acb133df114b50b8e9b933c060eee487f2e84 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.43 2008/05/12 00:00:47 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.44 2008/06/14 18:04:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,6 @@
 #include "access/sysattr.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
-#include "catalog/namespace.h"
 #include "catalog/pg_conversion.h"
 #include "catalog/pg_conversion_fn.h"
 #include "catalog/pg_namespace.h"
@@ -138,40 +137,6 @@ ConversionCreate(const char *conname, Oid connamespace,
    return oid;
 }
 
-/*
- * ConversionDrop
- *
- * Drop a conversion after doing permission checks.
- */
-void
-ConversionDrop(Oid conversionOid, DropBehavior behavior)
-{
-   HeapTuple   tuple;
-   ObjectAddress object;
-
-   tuple = SearchSysCache(CONVOID,
-                          ObjectIdGetDatum(conversionOid),
-                          0, 0, 0);
-   if (!HeapTupleIsValid(tuple))
-       elog(ERROR, "cache lookup failed for conversion %u", conversionOid);
-
-   if (!superuser() &&
-       ((Form_pg_conversion) GETSTRUCT(tuple))->conowner != GetUserId())
-       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
-                 NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname));
-
-   ReleaseSysCache(tuple);
-
-   /*
-    * Do the deletion
-    */
-   object.classId = ConversionRelationId;
-   object.objectId = conversionOid;
-   object.objectSubId = 0;
-
-   performDeletion(&object, behavior);
-}
-
 /*
  * RemoveConversionById
  *
index 43f5329b0367c9edd0fed1ebb98445ec091f3c3d..c789dc94467a2818701d37b15fd7759baa970f09 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.33 2008/03/27 03:57:33 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.34 2008/06/14 18:04:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -99,31 +99,73 @@ CreateConversionCommand(CreateConversionStmt *stmt)
  * DROP CONVERSION
  */
 void
-DropConversionCommand(List *name, DropBehavior behavior, bool missing_ok)
+DropConversionsCommand(DropStmt *drop)
 {
-   Oid         conversionOid;
+   ObjectAddresses *objects;
+   ListCell *cell;
 
-   conversionOid = FindConversionByName(name);
-   if (!OidIsValid(conversionOid))
+   /*
+    * First we identify all the conversions, then we delete them in a single
+    * performMultipleDeletions() call.  This is to avoid unwanted
+    * DROP RESTRICT errors if one of the conversions depends on another.
+    * (Not that that is very likely, but we may as well do this consistently.)
+    */
+   objects = new_object_addresses();
+
+   foreach(cell, drop->objects)
    {
-       if (!missing_ok)
-       {
-           ereport(ERROR,
-                   (errcode(ERRCODE_UNDEFINED_OBJECT),
-                    errmsg("conversion \"%s\" does not exist",
-                           NameListToString(name))));
-       }
-       else
+       List        *name = (List *) lfirst(cell);
+       Oid         conversionOid;
+       HeapTuple   tuple;
+       Form_pg_conversion con;
+       ObjectAddress object;
+
+       conversionOid = FindConversionByName(name);
+
+       if (!OidIsValid(conversionOid))
        {
-           ereport(NOTICE,
-                   (errmsg("conversion \"%s\" does not exist, skipping",
-                           NameListToString(name))));
+           if (!drop->missing_ok)
+           {
+               ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                        errmsg("conversion \"%s\" does not exist",
+                               NameListToString(name))));
+           }
+           else
+           {
+               ereport(NOTICE,
+                       (errmsg("conversion \"%s\" does not exist, skipping",
+                               NameListToString(name))));
+           }
+           continue;
        }
 
-       return;
+       tuple = SearchSysCache(CONVOID,
+                              ObjectIdGetDatum(conversionOid),
+                              0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+           elog(ERROR, "cache lookup failed for conversion %u",
+                conversionOid);
+       con = (Form_pg_conversion) GETSTRUCT(tuple);
+
+       /* Permission check: must own conversion or its namespace */
+       if (!pg_conversion_ownercheck(conversionOid, GetUserId()) &&
+           !pg_namespace_ownercheck(con->connamespace, GetUserId()))
+           aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
+                          NameStr(con->conname));
+
+       object.classId = ConversionRelationId;
+       object.objectId = conversionOid;
+       object.objectSubId = 0;
+
+       add_exact_object_address(&object, objects);
+
+       ReleaseSysCache(tuple);
    }
 
-   ConversionDrop(conversionOid, behavior);
+   performMultipleDeletions(objects, drop->behavior);
+
+   free_object_addresses(objects);
 }
 
 /*
index 05eb55e86991eab931a898071c07766b39cce4e4..a8a78b561fb11e679320e265413ffbefe92fdaa3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.176 2008/05/12 20:01:59 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.177 2008/06/14 18:04:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,7 +21,6 @@
 #include "access/transam.h"
 #include "access/xact.h"
 #include "catalog/catalog.h"
-#include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/indexing.h"
@@ -1256,33 +1255,6 @@ relationHasPrimaryKey(Relation rel)
    return result;
 }
 
-
-/*
- * RemoveIndex
- *     Deletes an index.
- */
-void
-RemoveIndex(RangeVar *relation, DropBehavior behavior)
-{
-   Oid         indOid;
-   char        relkind;
-   ObjectAddress object;
-
-   indOid = RangeVarGetRelid(relation, false);
-   relkind = get_rel_relkind(indOid);
-   if (relkind != RELKIND_INDEX)
-       ereport(ERROR,
-               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                errmsg("\"%s\" is not an index",
-                       relation->relname)));
-
-   object.classId = RelationRelationId;
-   object.objectId = indOid;
-   object.objectSubId = 0;
-
-   performDeletion(&object, behavior);
-}
-
 /*
  * ReindexIndex
  *     Recreate a specific index.
index d77294ccc225913ea9e58673ba65dbbe41f4a037..8029013e2cce60b8d12c9fce5812f8970dfd8ef5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.49 2008/01/03 21:23:15 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.50 2008/06/14 18:04:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -148,57 +148,76 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
 
 
 /*
- * RemoveSchema
- *     Removes a schema.
+ * RemoveSchemas
+ *     Implements DROP SCHEMA.
  */
 void
-RemoveSchema(List *names, DropBehavior behavior, bool missing_ok)
+RemoveSchemas(DropStmt *drop)
 {
-   char       *namespaceName;
-   Oid         namespaceId;
-   ObjectAddress object;
+   ObjectAddresses *objects;
+   ListCell        *cell;
 
-   if (list_length(names) != 1)
-       ereport(ERROR,
-               (errcode(ERRCODE_SYNTAX_ERROR),
-                errmsg("schema name cannot be qualified")));
-   namespaceName = strVal(linitial(names));
-
-   namespaceId = GetSysCacheOid(NAMESPACENAME,
-                                CStringGetDatum(namespaceName),
-                                0, 0, 0);
-   if (!OidIsValid(namespaceId))
+   /*
+    * First we identify all the schemas, then we delete them in a single
+    * performMultipleDeletions() call.  This is to avoid unwanted
+    * DROP RESTRICT errors if one of the schemas depends on another.
+    */
+   objects = new_object_addresses();
+
+   foreach(cell, drop->objects)
    {
-       if (!missing_ok)
-       {
+       List       *names = (List *) lfirst(cell);
+       char       *namespaceName;
+       Oid         namespaceId;
+       ObjectAddress object;
+
+       if (list_length(names) != 1)
            ereport(ERROR,
-                   (errcode(ERRCODE_UNDEFINED_SCHEMA),
-                    errmsg("schema \"%s\" does not exist", namespaceName)));
-       }
-       else
+                   (errcode(ERRCODE_SYNTAX_ERROR),
+                    errmsg("schema name cannot be qualified")));
+       namespaceName = strVal(linitial(names));
+
+       namespaceId = GetSysCacheOid(NAMESPACENAME,
+                                    CStringGetDatum(namespaceName),
+                                    0, 0, 0);
+
+       if (!OidIsValid(namespaceId))
        {
-           ereport(NOTICE,
-                   (errmsg("schema \"%s\" does not exist, skipping",
-                           namespaceName)));
+           if (!drop->missing_ok)
+           {
+               ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_SCHEMA),
+                        errmsg("schema \"%s\" does not exist",
+                               namespaceName)));
+           }
+           else
+           {
+               ereport(NOTICE,
+                       (errmsg("schema \"%s\" does not exist, skipping",
+                               namespaceName)));
+           }
+           continue;
        }
 
-       return;
-   }
+       /* Permission check */
+       if (!pg_namespace_ownercheck(namespaceId, GetUserId()))
+           aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
+                          namespaceName);
 
-   /* Permission check */
-   if (!pg_namespace_ownercheck(namespaceId, GetUserId()))
-       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
-                      namespaceName);
+       object.classId = NamespaceRelationId;
+       object.objectId = namespaceId;
+       object.objectSubId = 0;
+
+       add_exact_object_address(&object, objects);
+   }
 
    /*
-    * Do the deletion.  Objects contained in the schema are removed by means
-    * of their dependency links to the schema.
+    * Do the deletions.  Objects contained in the schema(s) are removed by
+    * means of their dependency links to the schema.
     */
-   object.classId = NamespaceRelationId;
-   object.objectId = namespaceId;
-   object.objectSubId = 0;
+   performMultipleDeletions(objects, drop->behavior);
 
-   performDeletion(&object, behavior);
+   free_object_addresses(objects);
 }
 
 
index 58f0b01b0982c158ac9971b915ec80b898f6e4fe..7f7102858b119fc19df54cff885b7be3aaf5dcbd 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.255 2008/05/19 04:14:24 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.256 2008/06/14 18:04:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,6 +61,7 @@
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/bufmgr.h"
+#include "storage/lmgr.h"
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
@@ -168,6 +169,53 @@ typedef struct NewColumnValue
    ExprState  *exprstate;      /* execution state */
 } NewColumnValue;
 
+/*
+ * Error-reporting support for RemoveRelations
+ */
+struct dropmsgstrings
+{
+   char        kind;
+   int         nonexistent_code;
+   const char *nonexistent_msg;
+   const char *skipping_msg;
+   const char *nota_msg;
+   const char *drophint_msg;
+};
+
+static const struct dropmsgstrings dropmsgstringarray[] = {
+   {RELKIND_RELATION,
+    ERRCODE_UNDEFINED_TABLE,
+    gettext_noop("table \"%s\" does not exist"),
+    gettext_noop("table \"%s\" does not exist, skipping"),
+    gettext_noop("\"%s\" is not a table"),
+    gettext_noop("Use DROP TABLE to remove a table.")},
+   {RELKIND_SEQUENCE,
+    ERRCODE_UNDEFINED_TABLE,
+    gettext_noop("sequence \"%s\" does not exist"),
+    gettext_noop("sequence \"%s\" does not exist, skipping"),
+    gettext_noop("\"%s\" is not a sequence"),
+    gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
+   {RELKIND_VIEW,
+    ERRCODE_UNDEFINED_TABLE,
+    gettext_noop("view \"%s\" does not exist"),
+    gettext_noop("view \"%s\" does not exist, skipping"),
+    gettext_noop("\"%s\" is not a view"),
+    gettext_noop("Use DROP VIEW to remove a view.")},
+   {RELKIND_INDEX,
+    ERRCODE_UNDEFINED_OBJECT,
+    gettext_noop("index \"%s\" does not exist"),
+    gettext_noop("index \"%s\" does not exist, skipping"),
+    gettext_noop("\"%s\" is not an index"),
+    gettext_noop("Use DROP INDEX to remove an index.")},
+   {RELKIND_COMPOSITE_TYPE,
+    ERRCODE_UNDEFINED_OBJECT,
+    gettext_noop("type \"%s\" does not exist"),
+    gettext_noop("type \"%s\" does not exist, skipping"),
+    gettext_noop("\"%s\" is not a type"),
+    gettext_noop("Use DROP TYPE to remove a type.")},
+   {'\0', 0, NULL, NULL, NULL, NULL}
+};
+
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, bool istemp,
@@ -497,22 +545,175 @@ DefineRelation(CreateStmt *stmt, char relkind)
 }
 
 /*
- * RemoveRelation
- *     Deletes a relation.
+ * Emit the right error or warning message for a "DROP" command issued on a
+ * non-existent relation
+ */
+static void
+DropErrorMsgNonExistent(const char *relname, char rightkind, bool missing_ok)
+{
+   const struct dropmsgstrings *rentry;
+
+   for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
+   {
+       if (rentry->kind == rightkind)
+       {
+           if (!missing_ok)
+           {
+               ereport(ERROR,
+                       (errcode(rentry->nonexistent_code),
+                        errmsg(rentry->nonexistent_msg, relname)));
+           }
+           else
+           {
+               ereport(NOTICE, (errmsg(rentry->skipping_msg, relname)));
+               break;
+           }
+       }
+   }
+
+   Assert(rentry->kind != '\0');       /* Should be impossible */
+}
+
+/*
+ * Emit the right error message for a "DROP" command issued on a
+ * relation of the wrong type
+ */
+static void
+DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
+{
+   const struct dropmsgstrings *rentry;
+   const struct dropmsgstrings *wentry;
+
+   for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
+       if (rentry->kind == rightkind)
+           break;
+   Assert(rentry->kind != '\0');
+
+   for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
+       if (wentry->kind == wrongkind)
+           break;
+   /* wrongkind could be something we don't have in our table... */
+
+   ereport(ERROR,
+           (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+            errmsg(rentry->nota_msg, relname),
+            (wentry->kind != '\0') ? errhint(wentry->drophint_msg) : 0));
+}
+
+/*
+ * RemoveRelations
+ *     Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW
  */
 void
-RemoveRelation(const RangeVar *relation, DropBehavior behavior)
+RemoveRelations(DropStmt *drop)
 {
-   Oid         relOid;
-   ObjectAddress object;
+   ObjectAddresses *objects;
+   char        relkind;
+   ListCell   *cell;
 
-   relOid = RangeVarGetRelid(relation, false);
+   /*
+    * First we identify all the relations, then we delete them in a single
+    * performMultipleDeletions() call.  This is to avoid unwanted
+    * DROP RESTRICT errors if one of the relations depends on another.
+    */
 
-   object.classId = RelationRelationId;
-   object.objectId = relOid;
-   object.objectSubId = 0;
+   /* Determine required relkind */
+   switch (drop->removeType)
+   {
+       case OBJECT_TABLE:
+           relkind = RELKIND_RELATION;
+           break;
 
-   performDeletion(&object, behavior);
+       case OBJECT_INDEX:
+           relkind = RELKIND_INDEX;
+           break;
+
+       case OBJECT_SEQUENCE:
+           relkind = RELKIND_SEQUENCE;
+           break;
+
+       case OBJECT_VIEW:
+           relkind = RELKIND_VIEW;
+           break;
+
+       default:
+           elog(ERROR, "unrecognized drop object type: %d",
+                (int) drop->removeType);
+           relkind = 0;    /* keep compiler quiet */
+           break;
+   }
+
+   /* Lock and validate each relation; build a list of object addresses */
+   objects = new_object_addresses();
+
+   foreach(cell, drop->objects)
+   {
+       RangeVar   *rel = makeRangeVarFromNameList((List *) lfirst(cell));
+       Oid         relOid;
+       HeapTuple   tuple;
+       Form_pg_class classform;
+       ObjectAddress obj;
+
+       /*
+        * These next few steps are a great deal like relation_openrv, but we
+        * don't bother building a relcache entry since we don't need it.
+        *
+        * Check for shared-cache-inval messages before trying to access the
+        * relation.  This is needed to cover the case where the name
+        * identifies a rel that has been dropped and recreated since the
+        * start of our transaction: if we don't flush the old syscache entry,
+        * then we'll latch onto that entry and suffer an error later.
+        */
+       AcceptInvalidationMessages();
+
+       /* Look up the appropriate relation using namespace search */
+       relOid = RangeVarGetRelid(rel, true);
+
+       /* Not there? */
+       if (!OidIsValid(relOid))
+       {
+           DropErrorMsgNonExistent(rel->relname, relkind, drop->missing_ok);
+           continue;
+       }
+
+       /* Get the lock before trying to fetch the syscache entry */
+       LockRelationOid(relOid, AccessExclusiveLock);
+
+       tuple = SearchSysCache(RELOID,
+                              ObjectIdGetDatum(relOid),
+                              0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+           elog(ERROR, "cache lookup failed for relation %u", relOid);
+       classform = (Form_pg_class) GETSTRUCT(tuple);
+
+       if (classform->relkind != relkind)
+           DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
+
+       /* Allow DROP to either table owner or schema owner */
+       if (!pg_class_ownercheck(relOid, GetUserId()) &&
+           !pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
+           aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+                          rel->relname);
+
+       if (!allowSystemTableMods && IsSystemClass(classform))
+           ereport(ERROR,
+                   (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                    errmsg("permission denied: \"%s\" is a system catalog",
+                           rel->relname)));
+
+       /* OK, we're ready to delete this one */
+       obj.classId = RelationRelationId;
+       obj.objectId = relOid;
+       obj.objectSubId = 0;
+
+       add_exact_object_address(&obj, objects);
+
+       ReleaseSysCache(tuple);
+   }
+
+   performMultipleDeletions(objects, drop->behavior);
+
+   free_object_addresses(objects);
 }
 
 /*
index e3d877e03f90e2962c78f8d1efde0ab3185969cb..ceb5fba0e6a65eb320825f7201a15a2a8080265f 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.11 2008/03/26 21:10:38 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.12 2008/06/14 18:04:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -271,40 +271,59 @@ DefineTSParser(List *names, List *parameters)
  * DROP TEXT SEARCH PARSER
  */
 void
-RemoveTSParser(List *names, DropBehavior behavior, bool missing_ok)
+RemoveTSParsers(DropStmt *drop)
 {
-   Oid         prsOid;
-   ObjectAddress object;
+   ObjectAddresses *objects;
+   ListCell        *cell;
 
    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("must be superuser to drop text search parsers")));
 
-   prsOid = TSParserGetPrsid(names, true);
-   if (!OidIsValid(prsOid))
+   /*
+    * First we identify all the objects, then we delete them in a single
+    * performMultipleDeletions() call.  This is to avoid unwanted
+    * DROP RESTRICT errors if one of the objects depends on another.
+    */
+   objects = new_object_addresses();
+
+   foreach(cell, drop->objects)
    {
-       if (!missing_ok)
+       List        *names = (List *) lfirst(cell);
+       Oid         prsOid;
+       ObjectAddress object;
+
+       prsOid = TSParserGetPrsid(names, true);
+
+       if (!OidIsValid(prsOid))
        {
-           ereport(ERROR,
-                   (errcode(ERRCODE_UNDEFINED_OBJECT),
-                    errmsg("text search parser \"%s\" does not exist",
+           if (!drop->missing_ok)
+           {
+               ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                        errmsg("text search parser \"%s\" does not exist",
+                               NameListToString(names))));
+           }
+           else
+           {
+               ereport(NOTICE,
+                   (errmsg("text search parser \"%s\" does not exist, skipping",
                            NameListToString(names))));
+           }
+           continue;
        }
-       else
-       {
-           ereport(NOTICE,
-               (errmsg("text search parser \"%s\" does not exist, skipping",
-                       NameListToString(names))));
-       }
-       return;
+
+       object.classId = TSParserRelationId;
+       object.objectId = prsOid;
+       object.objectSubId = 0;
+
+       add_exact_object_address(&object, objects);
    }
 
-   object.classId = TSParserRelationId;
-   object.objectId = prsOid;
-   object.objectSubId = 0;
+   performMultipleDeletions(objects, drop->behavior);
 
-   performDeletion(&object, behavior);
+   free_object_addresses(objects);
 }
 
 /*
@@ -613,54 +632,72 @@ RenameTSDictionary(List *oldname, const char *newname)
  * DROP TEXT SEARCH DICTIONARY
  */
 void
-RemoveTSDictionary(List *names, DropBehavior behavior, bool missing_ok)
+RemoveTSDictionaries(DropStmt *drop)
 {
-   Oid         dictOid;
-   ObjectAddress object;
-   HeapTuple   tup;
-   Oid         namespaceId;
+   ObjectAddresses *objects;
+   ListCell        *cell;
+
+   /*
+    * First we identify all the objects, then we delete them in a single
+    * performMultipleDeletions() call.  This is to avoid unwanted
+    * DROP RESTRICT errors if one of the objects depends on another.
+    */
+   objects = new_object_addresses();
 
-   dictOid = TSDictionaryGetDictid(names, true);
-   if (!OidIsValid(dictOid))
+   foreach(cell, drop->objects)
    {
-       if (!missing_ok)
-       {
-           ereport(ERROR,
-                   (errcode(ERRCODE_UNDEFINED_OBJECT),
-                    errmsg("text search dictionary \"%s\" does not exist",
-                           NameListToString(names))));
-       }
-       else
+       List        *names = (List *) lfirst(cell);
+       Oid         dictOid;
+       ObjectAddress object;
+       HeapTuple   tup;
+       Oid         namespaceId;
+
+       dictOid = TSDictionaryGetDictid(names, true);
+
+       if (!OidIsValid(dictOid))
        {
-           ereport(NOTICE,
-           (errmsg("text search dictionary \"%s\" does not exist, skipping",
-                   NameListToString(names))));
+           if (!drop->missing_ok)
+           {
+               ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                        errmsg("text search dictionary \"%s\" does not exist",
+                               NameListToString(names))));
+           }
+           else
+           {
+               ereport(NOTICE,
+               (errmsg("text search dictionary \"%s\" does not exist, skipping",
+                       NameListToString(names))));
+           }
+           continue;
        }
-       return;
-   }
 
-   tup = SearchSysCache(TSDICTOID,
-                        ObjectIdGetDatum(dictOid),
-                        0, 0, 0);
+       tup = SearchSysCache(TSDICTOID,
+                            ObjectIdGetDatum(dictOid),
+                            0, 0, 0);
+       if (!HeapTupleIsValid(tup)) /* should not happen */
+           elog(ERROR, "cache lookup failed for text search dictionary %u",
+                dictOid);
 
-   if (!HeapTupleIsValid(tup)) /* should not happen */
-       elog(ERROR, "cache lookup failed for text search dictionary %u",
-            dictOid);
+       /* Permission check: must own dictionary or its namespace */
+       namespaceId = ((Form_pg_ts_dict) GETSTRUCT(tup))->dictnamespace;
+       if (!pg_ts_dict_ownercheck(dictOid, GetUserId()) &&
+           !pg_namespace_ownercheck(namespaceId, GetUserId()))
+           aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
+                          NameListToString(names));
 
-   /* Permission check: must own dictionary or its namespace */
-   namespaceId = ((Form_pg_ts_dict) GETSTRUCT(tup))->dictnamespace;
-   if (!pg_ts_dict_ownercheck(dictOid, GetUserId()) &&
-       !pg_namespace_ownercheck(namespaceId, GetUserId()))
-       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
-                      NameListToString(names));
+       object.classId = TSDictionaryRelationId;
+       object.objectId = dictOid;
+       object.objectSubId = 0;
 
-   ReleaseSysCache(tup);
+       add_exact_object_address(&object, objects);
 
-   object.classId = TSDictionaryRelationId;
-   object.objectId = dictOid;
-   object.objectSubId = 0;
+       ReleaseSysCache(tup);
+   }
+
+   performMultipleDeletions(objects, drop->behavior);
 
-   performDeletion(&object, behavior);
+   free_object_addresses(objects);
 }
 
 /*
@@ -1086,40 +1123,59 @@ RenameTSTemplate(List *oldname, const char *newname)
  * DROP TEXT SEARCH TEMPLATE
  */
 void
-RemoveTSTemplate(List *names, DropBehavior behavior, bool missing_ok)
+RemoveTSTemplates(DropStmt *drop)
 {
-   Oid         tmplOid;
-   ObjectAddress object;
+   ObjectAddresses *objects;
+   ListCell        *cell;
 
    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("must be superuser to drop text search templates")));
 
-   tmplOid = TSTemplateGetTmplid(names, true);
-   if (!OidIsValid(tmplOid))
+   /*
+    * First we identify all the objects, then we delete them in a single
+    * performMultipleDeletions() call.  This is to avoid unwanted
+    * DROP RESTRICT errors if one of the objects depends on another.
+    */
+   objects = new_object_addresses();
+
+   foreach(cell, drop->objects)
    {
-       if (!missing_ok)
-       {
-           ereport(ERROR,
-                   (errcode(ERRCODE_UNDEFINED_OBJECT),
-                    errmsg("text search template \"%s\" does not exist",
-                           NameListToString(names))));
-       }
-       else
+       List        *names = (List *) lfirst(cell);
+       Oid         tmplOid;
+       ObjectAddress object;
+
+       tmplOid = TSTemplateGetTmplid(names, true);
+
+       if (!OidIsValid(tmplOid))
        {
-           ereport(NOTICE,
-             (errmsg("text search template \"%s\" does not exist, skipping",
-                     NameListToString(names))));
+           if (!drop->missing_ok)
+           {
+               ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                        errmsg("text search template \"%s\" does not exist",
+                               NameListToString(names))));
+           }
+           else
+           {
+               ereport(NOTICE,
+                       (errmsg("text search template \"%s\" does not exist, skipping",
+                               NameListToString(names))));
+           }
+           continue;
        }
-       return;
+
+       object.classId = TSTemplateRelationId;
+       object.objectId = tmplOid;
+       object.objectSubId = 0;
+
+       add_exact_object_address(&object, objects);
    }
 
-   object.classId = TSTemplateRelationId;
-   object.objectId = tmplOid;
-   object.objectSubId = 0;
+   performMultipleDeletions(objects, drop->behavior);
 
-   performDeletion(&object, behavior);
+   free_object_addresses(objects);
 }
 
 /*
@@ -1474,48 +1530,66 @@ RenameTSConfiguration(List *oldname, const char *newname)
  * DROP TEXT SEARCH CONFIGURATION
  */
 void
-RemoveTSConfiguration(List *names, DropBehavior behavior, bool missing_ok)
+RemoveTSConfigurations(DropStmt *drop)
 {
-   Oid         cfgOid;
-   Oid         namespaceId;
-   ObjectAddress object;
-   HeapTuple   tup;
+   ObjectAddresses *objects;
+   ListCell        *cell;
 
-   tup = GetTSConfigTuple(names);
+   /*
+    * First we identify all the objects, then we delete them in a single
+    * performMultipleDeletions() call.  This is to avoid unwanted
+    * DROP RESTRICT errors if one of the objects depends on another.
+    */
+   objects = new_object_addresses();
 
-   if (!HeapTupleIsValid(tup))
+   foreach(cell, drop->objects)
    {
-       if (!missing_ok)
-       {
-           ereport(ERROR,
-                   (errcode(ERRCODE_UNDEFINED_OBJECT),
-                    errmsg("text search configuration \"%s\" does not exist",
-                           NameListToString(names))));
-       }
-       else
+       List        *names = (List *) lfirst(cell);
+       Oid         cfgOid;
+       Oid         namespaceId;
+       ObjectAddress object;
+       HeapTuple   tup;
+
+       tup = GetTSConfigTuple(names);
+
+       if (!HeapTupleIsValid(tup))
        {
-           ereport(NOTICE,
-                   (errmsg("text search configuration \"%s\" does not exist, skipping",
-                           NameListToString(names))));
+           if (!drop->missing_ok)
+           {
+               ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                        errmsg("text search configuration \"%s\" does not exist",
+                               NameListToString(names))));
+           }
+           else
+           {
+               ereport(NOTICE,
+                       (errmsg("text search configuration \"%s\" does not exist, skipping",
+                               NameListToString(names))));
+           }
+           continue;
        }
-       return;
-   }
 
-   /* Permission check: must own configuration or its namespace */
-   cfgOid = HeapTupleGetOid(tup);
-   namespaceId = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgnamespace;
-   if (!pg_ts_config_ownercheck(cfgOid, GetUserId()) &&
-       !pg_namespace_ownercheck(namespaceId, GetUserId()))
-       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
-                      NameListToString(names));
+       /* Permission check: must own configuration or its namespace */
+       cfgOid = HeapTupleGetOid(tup);
+       namespaceId = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgnamespace;
+       if (!pg_ts_config_ownercheck(cfgOid, GetUserId()) &&
+           !pg_namespace_ownercheck(namespaceId, GetUserId()))
+           aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
+                          NameListToString(names));
 
-   ReleaseSysCache(tup);
+       object.classId = TSConfigRelationId;
+       object.objectId = cfgOid;
+       object.objectSubId = 0;
+
+       add_exact_object_address(&object, objects);
+
+       ReleaseSysCache(tup);
+   }
 
-   object.classId = TSConfigRelationId;
-   object.objectId = cfgOid;
-   object.objectSubId = 0;
+   performMultipleDeletions(objects, drop->behavior);
 
-   performDeletion(&object, behavior);
+   free_object_addresses(objects);
 }
 
 /*
index 880788fd22bbad04160f3582cef42922aca7e299..793e9262e3e475a2b94e4ed2cced52710466cf8a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.118 2008/05/09 23:32:04 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.119 2008/06/14 18:04:33 tgl Exp $
  *
  * DESCRIPTION
  *   The "DefineFoo" routines take the parse tree and pick out the
@@ -483,66 +483,93 @@ DefineType(List *names, List *parameters)
 
 
 /*
- * RemoveType
- *     Removes a datatype.
+ * RemoveTypes
+ *     Implements DROP TYPE and DROP DOMAIN
+ *
+ * Note: if DOMAIN is specified, we enforce that each type is a domain, but
+ * we don't enforce the converse for DROP TYPE
  */
 void
-RemoveType(List *names, DropBehavior behavior, bool missing_ok)
+RemoveTypes(DropStmt *drop)
 {
-   TypeName   *typename;
-   Oid         typeoid;
-   HeapTuple   tup;
-   ObjectAddress object;
-   Form_pg_type typ;
+   ObjectAddresses *objects;
+   ListCell        *cell;
 
-   /* Make a TypeName so we can use standard type lookup machinery */
-   typename = makeTypeNameFromNameList(names);
+   /*
+    * First we identify all the types, then we delete them in a single
+    * performMultipleDeletions() call.  This is to avoid unwanted
+    * DROP RESTRICT errors if one of the types depends on another.
+    */
+   objects = new_object_addresses();
 
-   /* Use LookupTypeName here so that shell types can be removed. */
-   tup = LookupTypeName(NULL, typename, NULL);
-   if (tup == NULL)
+   foreach(cell, drop->objects)
    {
-       if (!missing_ok)
+       List       *names = (List *) lfirst(cell);
+       TypeName   *typename;
+       Oid         typeoid;
+       HeapTuple   tup;
+       ObjectAddress object;
+       Form_pg_type typ;
+
+       /* Make a TypeName so we can use standard type lookup machinery */
+       typename = makeTypeNameFromNameList(names);
+
+       /* Use LookupTypeName here so that shell types can be removed. */
+       tup = LookupTypeName(NULL, typename, NULL);
+       if (tup == NULL)
        {
-           ereport(ERROR,
-                   (errcode(ERRCODE_UNDEFINED_OBJECT),
-                    errmsg("type \"%s\" does not exist",
-                           TypeNameToString(typename))));
+           if (!drop->missing_ok)
+           {
+               ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                        errmsg("type \"%s\" does not exist",
+                               TypeNameToString(typename))));
+           }
+           else
+           {
+               ereport(NOTICE,
+                       (errmsg("type \"%s\" does not exist, skipping",
+                               TypeNameToString(typename))));
+           }
+           continue;
        }
-       else
+
+       typeoid = typeTypeId(tup);
+       typ = (Form_pg_type) GETSTRUCT(tup);
+
+       /* Permission check: must own type or its namespace */
+       if (!pg_type_ownercheck(typeoid, GetUserId()) &&
+           !pg_namespace_ownercheck(typ->typnamespace, GetUserId()))
+           aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+                          TypeNameToString(typename));
+
+       if (drop->removeType == OBJECT_DOMAIN)
        {
-           ereport(NOTICE,
-                   (errmsg("type \"%s\" does not exist, skipping",
-                           TypeNameToString(typename))));
+           /* Check that this is actually a domain */
+           if (typ->typtype != TYPTYPE_DOMAIN)
+               ereport(ERROR,
+                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                        errmsg("\"%s\" is not a domain",
+                               TypeNameToString(typename))));
        }
 
-       return;
-   }
-
-   typeoid = typeTypeId(tup);
-   typ = (Form_pg_type) GETSTRUCT(tup);
+       /*
+        * Note: we need no special check for array types here, as the normal
+        * treatment of internal dependencies handles it just fine
+        */
 
-   /* Permission check: must own type or its namespace */
-   if (!pg_type_ownercheck(typeoid, GetUserId()) &&
-       !pg_namespace_ownercheck(typ->typnamespace, GetUserId()))
-       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
-                      TypeNameToString(typename));
+       object.classId = TypeRelationId;
+       object.objectId = typeoid;
+       object.objectSubId = 0;
 
-   /*
-    * Note: we need no special check for array types here, as the normal
-    * treatment of internal dependencies handles it just fine
-    */
+       add_exact_object_address(&object, objects);
 
-   ReleaseSysCache(tup);
+       ReleaseSysCache(tup);
+   }
 
-   /*
-    * Do the deletion
-    */
-   object.classId = TypeRelationId;
-   object.objectId = typeoid;
-   object.objectSubId = 0;
+   performMultipleDeletions(objects, drop->behavior);
 
-   performDeletion(&object, behavior);
+   free_object_addresses(objects);
 }
 
 
@@ -923,75 +950,6 @@ DefineDomain(CreateDomainStmt *stmt)
 }
 
 
-/*
- * RemoveDomain
- *     Removes a domain.
- *
- * This is identical to RemoveType except we insist it be a domain.
- */
-void
-RemoveDomain(List *names, DropBehavior behavior, bool missing_ok)
-{
-   TypeName   *typename;
-   Oid         typeoid;
-   HeapTuple   tup;
-   char        typtype;
-   ObjectAddress object;
-
-   /* Make a TypeName so we can use standard type lookup machinery */
-   typename = makeTypeNameFromNameList(names);
-
-   /* Use LookupTypeName here so that shell types can be removed. */
-   tup = LookupTypeName(NULL, typename, NULL);
-   if (tup == NULL)
-   {
-       if (!missing_ok)
-       {
-           ereport(ERROR,
-                   (errcode(ERRCODE_UNDEFINED_OBJECT),
-                    errmsg("type \"%s\" does not exist",
-                           TypeNameToString(typename))));
-       }
-       else
-       {
-           ereport(NOTICE,
-                   (errmsg("type \"%s\" does not exist, skipping",
-                           TypeNameToString(typename))));
-       }
-
-       return;
-   }
-
-   typeoid = typeTypeId(tup);
-
-   /* Permission check: must own type or its namespace */
-   if (!pg_type_ownercheck(typeoid, GetUserId()) &&
-     !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
-                              GetUserId()))
-       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
-                      TypeNameToString(typename));
-
-   /* Check that this is actually a domain */
-   typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
-
-   if (typtype != TYPTYPE_DOMAIN)
-       ereport(ERROR,
-               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                errmsg("\"%s\" is not a domain",
-                       TypeNameToString(typename))));
-
-   ReleaseSysCache(tup);
-
-   /*
-    * Do the deletion
-    */
-   object.classId = TypeRelationId;
-   object.objectId = typeoid;
-   object.objectSubId = 0;
-
-   performDeletion(&object, behavior);
-}
-
 /*
  * DefineEnum
  *     Registers a new enum.
index 0a2e3a6e4659694a843dc86b4a340652b2173461..8814cf8dc1b9b95a8199bc4cccc2228d55c528c0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.104 2008/01/01 19:45:49 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.105 2008/06/14 18:04:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,7 +16,6 @@
 
 #include "access/heapam.h"
 #include "access/xact.h"
-#include "catalog/dependency.h"
 #include "catalog/namespace.h"
 #include "commands/defrem.h"
 #include "commands/tablecmds.h"
@@ -446,26 +445,3 @@ DefineView(ViewStmt *stmt, const char *queryString)
     */
    DefineViewRules(viewOid, viewParse, stmt->replace);
 }
-
-/*
- * RemoveView
- *
- * Remove a view given its name
- *
- * We just have to drop the relation; the associated rules will be
- * cleaned up automatically.
- */
-void
-RemoveView(const RangeVar *view, DropBehavior behavior)
-{
-   Oid         viewOid;
-   ObjectAddress object;
-
-   viewOid = RangeVarGetRelid(view, false);
-
-   object.classId = RelationRelationId;
-   object.objectId = viewOid;
-   object.objectSubId = 0;
-
-   performDeletion(&object, behavior);
-}
index 1bebfec182bed21acdb72612e2c64f284252f318..6c839650b43cf8f751a147947e9907f73046f1f3 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.292 2008/06/05 15:47:32 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.293 2008/06/14 18:04:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/syscache.h"
 
 
-/*
- * Error-checking support for DROP commands
- */
-
-struct msgstrings
-{
-   char        kind;
-   int         nonexistent_code;
-   const char *nonexistent_msg;
-   const char *skipping_msg;
-   const char *nota_msg;
-   const char *drophint_msg;
-};
-
-static const struct msgstrings msgstringarray[] = {
-   {RELKIND_RELATION,
-       ERRCODE_UNDEFINED_TABLE,
-       gettext_noop("table \"%s\" does not exist"),
-       gettext_noop("table \"%s\" does not exist, skipping"),
-       gettext_noop("\"%s\" is not a table"),
-   gettext_noop("Use DROP TABLE to remove a table.")},
-   {RELKIND_SEQUENCE,
-       ERRCODE_UNDEFINED_TABLE,
-       gettext_noop("sequence \"%s\" does not exist"),
-       gettext_noop("sequence \"%s\" does not exist, skipping"),
-       gettext_noop("\"%s\" is not a sequence"),
-   gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
-   {RELKIND_VIEW,
-       ERRCODE_UNDEFINED_TABLE,
-       gettext_noop("view \"%s\" does not exist"),
-       gettext_noop("view \"%s\" does not exist, skipping"),
-       gettext_noop("\"%s\" is not a view"),
-   gettext_noop("Use DROP VIEW to remove a view.")},
-   {RELKIND_INDEX,
-       ERRCODE_UNDEFINED_OBJECT,
-       gettext_noop("index \"%s\" does not exist"),
-       gettext_noop("index \"%s\" does not exist, skipping"),
-       gettext_noop("\"%s\" is not an index"),
-   gettext_noop("Use DROP INDEX to remove an index.")},
-   {RELKIND_COMPOSITE_TYPE,
-       ERRCODE_UNDEFINED_OBJECT,
-       gettext_noop("type \"%s\" does not exist"),
-       gettext_noop("type \"%s\" does not exist, skipping"),
-       gettext_noop("\"%s\" is not a type"),
-   gettext_noop("Use DROP TYPE to remove a type.")},
-   {'\0', 0, NULL, NULL, NULL}
-};
-
-
-/*
- * Emit the right error message for a "DROP" command issued on a
- * relation of the wrong type
- */
-static void
-DropErrorMsgWrongType(char *relname, char wrongkind, char rightkind)
-{
-   const struct msgstrings *rentry;
-   const struct msgstrings *wentry;
-
-   for (rentry = msgstringarray; rentry->kind != '\0'; rentry++)
-       if (rentry->kind == rightkind)
-           break;
-   Assert(rentry->kind != '\0');
-
-   for (wentry = msgstringarray; wentry->kind != '\0'; wentry++)
-       if (wentry->kind == wrongkind)
-           break;
-   /* wrongkind could be something we don't have in our table... */
-
-   ereport(ERROR,
-           (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-            errmsg(rentry->nota_msg, relname),
-            (wentry->kind != '\0') ? errhint(wentry->drophint_msg) : 0));
-}
-
-/*
- * Emit the right error message for a "DROP" command issued on a
- * non-existent relation
- */
-static void
-DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
-{
-   const struct msgstrings *rentry;
-
-   for (rentry = msgstringarray; rentry->kind != '\0'; rentry++)
-   {
-       if (rentry->kind == rightkind)
-       {
-           if (!missing_ok)
-           {
-               ereport(ERROR,
-                       (errcode(rentry->nonexistent_code),
-                        errmsg(rentry->nonexistent_msg, rel->relname)));
-           }
-           else
-           {
-               ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
-               break;
-           }
-       }
-   }
-
-   Assert(rentry->kind != '\0');       /* Should be impossible */
-}
-
-/*
- * returns false if missing_ok is true and the object does not exist,
- * true if object exists and permissions are OK,
- * errors otherwise
- *
- */
-
-static bool
-CheckDropPermissions(RangeVar *rel, char rightkind, bool missing_ok)
-{
-   Oid         relOid;
-   HeapTuple   tuple;
-   Form_pg_class classform;
-
-   relOid = RangeVarGetRelid(rel, true);
-   if (!OidIsValid(relOid))
-   {
-       DropErrorMsgNonExistent(rel, rightkind, missing_ok);
-       return false;
-   }
-
-   tuple = SearchSysCache(RELOID,
-                          ObjectIdGetDatum(relOid),
-                          0, 0, 0);
-   if (!HeapTupleIsValid(tuple))
-       elog(ERROR, "cache lookup failed for relation %u", relOid);
-
-   classform = (Form_pg_class) GETSTRUCT(tuple);
-
-   if (classform->relkind != rightkind)
-       DropErrorMsgWrongType(rel->relname, classform->relkind,
-                             rightkind);
-
-   /* Allow DROP to either table owner or schema owner */
-   if (!pg_class_ownercheck(relOid, GetUserId()) &&
-       !pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
-       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
-                      rel->relname);
-
-   if (!allowSystemTableMods && IsSystemClass(classform))
-       ereport(ERROR,
-               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                errmsg("permission denied: \"%s\" is a system catalog",
-                       rel->relname)));
-
-   ReleaseSysCache(tuple);
-
-   return true;
-}
-
 /*
  * Verify user has ownership of specified relation, else ereport.
  *
@@ -603,96 +448,49 @@ ProcessUtility(Node *parsetree,
        case T_DropStmt:
            {
                DropStmt   *stmt = (DropStmt *) parsetree;
-               ListCell   *arg;
 
-               foreach(arg, stmt->objects)
+               switch (stmt->removeType)
                {
-                   List       *names = (List *) lfirst(arg);
-                   RangeVar   *rel;
+                   case OBJECT_TABLE:
+                   case OBJECT_SEQUENCE:
+                   case OBJECT_VIEW:
+                   case OBJECT_INDEX:
+                       RemoveRelations(stmt);
+                       break;
 
-                   switch (stmt->removeType)
-                   {
-                       case OBJECT_TABLE:
-                           rel = makeRangeVarFromNameList(names);
-                           if (CheckDropPermissions(rel, RELKIND_RELATION,
-                                                    stmt->missing_ok))
-                               RemoveRelation(rel, stmt->behavior);
-                           break;
-
-                       case OBJECT_SEQUENCE:
-                           rel = makeRangeVarFromNameList(names);
-                           if (CheckDropPermissions(rel, RELKIND_SEQUENCE,
-                                                    stmt->missing_ok))
-                               RemoveRelation(rel, stmt->behavior);
-                           break;
-
-                       case OBJECT_VIEW:
-                           rel = makeRangeVarFromNameList(names);
-                           if (CheckDropPermissions(rel, RELKIND_VIEW,
-                                                    stmt->missing_ok))
-                               RemoveView(rel, stmt->behavior);
-                           break;
-
-                       case OBJECT_INDEX:
-                           rel = makeRangeVarFromNameList(names);
-                           if (CheckDropPermissions(rel, RELKIND_INDEX,
-                                                    stmt->missing_ok))
-                               RemoveIndex(rel, stmt->behavior);
-                           break;
-
-                       case OBJECT_TYPE:
-                           /* RemoveType does its own permissions checks */
-                           RemoveType(names, stmt->behavior,
-                                      stmt->missing_ok);
-                           break;
-
-                       case OBJECT_DOMAIN:
-                           /* RemoveDomain does its own permissions checks */
-                           RemoveDomain(names, stmt->behavior,
-                                        stmt->missing_ok);
-                           break;
-
-                       case OBJECT_CONVERSION:
-                           DropConversionCommand(names, stmt->behavior,
-                                                 stmt->missing_ok);
-                           break;
-
-                       case OBJECT_SCHEMA:
-                           /* RemoveSchema does its own permissions checks */
-                           RemoveSchema(names, stmt->behavior,
-                                        stmt->missing_ok);
-                           break;
-
-                       case OBJECT_TSPARSER:
-                           RemoveTSParser(names, stmt->behavior,
-                                          stmt->missing_ok);
-                           break;
-
-                       case OBJECT_TSDICTIONARY:
-                           RemoveTSDictionary(names, stmt->behavior,
-                                              stmt->missing_ok);
-                           break;
-
-                       case OBJECT_TSTEMPLATE:
-                           RemoveTSTemplate(names, stmt->behavior,
-                                            stmt->missing_ok);
-                           break;
-
-                       case OBJECT_TSCONFIGURATION:
-                           RemoveTSConfiguration(names, stmt->behavior,
-                                                 stmt->missing_ok);
-                           break;
-
-                       default:
-                           elog(ERROR, "unrecognized drop object type: %d",
-                                (int) stmt->removeType);
-                           break;
-                   }
+                   case OBJECT_TYPE:
+                   case OBJECT_DOMAIN:
+                       RemoveTypes(stmt);
+                       break;
+
+                   case OBJECT_CONVERSION:
+                       DropConversionsCommand(stmt);
+                       break;
+
+                   case OBJECT_SCHEMA:
+                       RemoveSchemas(stmt);
+                       break;
+
+                   case OBJECT_TSPARSER:
+                       RemoveTSParsers(stmt);
+                       break;
+
+                   case OBJECT_TSDICTIONARY:
+                       RemoveTSDictionaries(stmt);
+                       break;
+
+                   case OBJECT_TSTEMPLATE:
+                       RemoveTSTemplates(stmt);
+                       break;
 
-                   /*
-                    * We used to need to do CommandCounterIncrement() here,
-                    * but now it's done inside performDeletion().
-                    */
+                   case OBJECT_TSCONFIGURATION:
+                       RemoveTSConfigurations(stmt);
+                       break;
+
+                   default:
+                       elog(ERROR, "unrecognized drop object type: %d",
+                            (int) stmt->removeType);
+                       break;
                }
            }
            break;
index 8706012197a5db4b50f54e458287a50ad7735552..6df77b3bf0629b88d770af67c9b3da8dfb73e78b 100644 (file)
@@ -7,20 +7,17 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_conversion_fn.h,v 1.1 2008/03/27 03:57:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_conversion_fn.h,v 1.2 2008/06/14 18:04:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef PG_CONVERSION_FN_H
 #define PG_CONVERSION_FN_H
 
-#include "nodes/parsenodes.h"
-
 extern Oid ConversionCreate(const char *conname, Oid connamespace,
                 Oid conowner,
                 int32 conforencoding, int32 contoencoding,
                 Oid conproc, bool def);
-extern void ConversionDrop(Oid conversionOid, DropBehavior behavior);
 extern void RemoveConversionById(Oid conversionOid);
 extern Oid FindConversion(const char *conname, Oid connamespace);
 extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding);
index 570cb7962240a52a471087e3ee1292359a686c7c..815bbce329423d74cd98352c9279c72dda31010a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/conversioncmds.h,v 1.16 2008/01/01 19:45:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/conversioncmds.h,v 1.17 2008/06/14 18:04:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,8 +18,7 @@
 #include "nodes/parsenodes.h"
 
 extern void CreateConversionCommand(CreateConversionStmt *parsetree);
-extern void DropConversionCommand(List *conversion_name,
-                     DropBehavior behavior, bool missing_ok);
+extern void DropConversionsCommand(DropStmt *drop);
 extern void RenameConversion(List *name, const char *newname);
 extern void AlterConversionOwner(List *name, Oid newOwnerId);
 extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
index 1270a7a5138ad8a8ff0ad5031baf49a3c3757d2b..90e3a19f6716946b861e4478edb2b43571062e3c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.88 2008/01/01 19:45:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.89 2008/06/14 18:04:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,6 @@ extern void DefineIndex(RangeVar *heapRelation,
            bool skip_build,
            bool quiet,
            bool concurrent);
-extern void RemoveIndex(RangeVar *relation, DropBehavior behavior);
 extern void ReindexIndex(RangeVar *indexRelation);
 extern void ReindexTable(RangeVar *relation);
 extern void ReindexDatabase(const char *databaseName,
@@ -94,28 +93,24 @@ extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwn
 /* commands/tsearchcmds.c */
 extern void DefineTSParser(List *names, List *parameters);
 extern void RenameTSParser(List *oldname, const char *newname);
-extern void RemoveTSParser(List *names, DropBehavior behavior,
-              bool missing_ok);
+extern void RemoveTSParsers(DropStmt *drop);
 extern void RemoveTSParserById(Oid prsId);
 
 extern void DefineTSDictionary(List *names, List *parameters);
 extern void RenameTSDictionary(List *oldname, const char *newname);
-extern void RemoveTSDictionary(List *names, DropBehavior behavior,
-                  bool missing_ok);
+extern void RemoveTSDictionaries(DropStmt *drop);
 extern void RemoveTSDictionaryById(Oid dictId);
 extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
 extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
 
 extern void DefineTSTemplate(List *names, List *parameters);
 extern void RenameTSTemplate(List *oldname, const char *newname);
-extern void RemoveTSTemplate(List *names, DropBehavior behavior,
-                bool missing_ok);
+extern void RemoveTSTemplates(DropStmt *stmt);
 extern void RemoveTSTemplateById(Oid tmplId);
 
 extern void DefineTSConfiguration(List *names, List *parameters);
 extern void RenameTSConfiguration(List *oldname, const char *newname);
-extern void RemoveTSConfiguration(List *names, DropBehavior behavior,
-                     bool missing_ok);
+extern void RemoveTSConfigurations(DropStmt *stmt);
 extern void RemoveTSConfigurationById(Oid cfgId);
 extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
 extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
index f31407757b85e960897865194c340b663a471661..e0df980c368d31d5c26ce551872d4d10b116564b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/schemacmds.h,v 1.18 2008/01/01 19:45:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/schemacmds.h,v 1.19 2008/06/14 18:04:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,7 @@
 extern void CreateSchemaCommand(CreateSchemaStmt *parsetree,
                    const char *queryString);
 
-extern void RemoveSchema(List *names, DropBehavior behavior, bool missing_ok);
+extern void RemoveSchemas(DropStmt *drop);
 extern void RemoveSchemaById(Oid schemaOid);
 
 extern void RenameSchema(const char *oldname, const char *newname);
index 700c1bd42eff52728c472d613dbf3e79d85e9c4b..cdb80240169abe2d7ec530bf27417c3778198aac 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.38 2008/03/19 18:38:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.39 2008/06/14 18:04:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,7 @@
 
 extern Oid DefineRelation(CreateStmt *stmt, char relkind);
 
-extern void RemoveRelation(const RangeVar *relation, DropBehavior behavior);
+extern void RemoveRelations(DropStmt *drop);
 
 extern void AlterTable(AlterTableStmt *stmt);
 
index ce807b56d08c7992c6ec77f00af443e91bbc6912..ed0048c6c8e71afe4d864b5e9bdb39fb993f4ff8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.23 2008/03/19 18:38:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.24 2008/06/14 18:04:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #define DEFAULT_TYPDELIM       ','
 
 extern void DefineType(List *names, List *parameters);
-extern void RemoveType(List *names, DropBehavior behavior, bool missing_ok);
+extern void RemoveTypes(DropStmt *drop);
 extern void RemoveTypeById(Oid typeOid);
 extern void DefineDomain(CreateDomainStmt *stmt);
-extern void RemoveDomain(List *names, DropBehavior behavior, bool missing_ok);
 extern void DefineEnum(CreateEnumStmt *stmt);
 extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
 
index daefb644d143d2cb0608c1faa3ce7ed1806c6517..7046e67ec9fd77aa3428bc6d306a402d411673ec 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/view.h,v 1.26 2008/01/01 19:45:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/view.h,v 1.27 2008/06/14 18:04:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,5 @@
 #include "nodes/parsenodes.h"
 
 extern void DefineView(ViewStmt *stmt, const char *queryString);
-extern void RemoveView(const RangeVar *view, DropBehavior behavior);
 
 #endif   /* VIEW_H */
index e3086d2e08565e34098cbb60eec2c27442d09bc4..c805322b6d994ba3cdd9603afed0564a2e1dda25 100644 (file)
@@ -1086,8 +1086,7 @@ INSERT INTO fktable VALUES (100, 200);
 COMMIT;
 ERROR:  insert or update on table "fktable" violates foreign key constraint "fktable_fk_fkey"
 DETAIL:  Key (fk)=(200) is not present in table "pktable".
-DROP TABLE pktable, fktable CASCADE;
-NOTICE:  drop cascades to constraint fktable_fk_fkey on table fktable
+DROP TABLE pktable, fktable;
 -- test notice about expensive referential integrity checks,
 -- where the index cannot be used because of type incompatibilities.
 CREATE TEMP TABLE pktable (
@@ -1156,17 +1155,7 @@ ALTER TABLE fktable ADD CONSTRAINT fk_241_132
 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 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
+DROP TABLE pktable, 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 520db9d9b171b122d0217c59f022473263b23a04..c9134ee233ce69f6a2c55e1bffdca09609f33a25 100644 (file)
@@ -141,12 +141,6 @@ SELECT * FROM trunc_e;
 (0 rows)
 
 DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE;
-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 519a5056af04906e37eec9ea69c6faeff9241452..cc7b23f113a7a9646c695fd2a1f3405bf7412702 100644 (file)
@@ -724,7 +724,7 @@ INSERT INTO fktable VALUES (100, 200);
 -- error here on commit
 COMMIT;
 
-DROP TABLE pktable, fktable CASCADE;
+DROP TABLE pktable, fktable;
 
 -- test notice about expensive referential integrity checks,
 -- where the index cannot be used because of type incompatibilities.
@@ -799,7 +799,7 @@ FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1);
 ALTER TABLE fktable ADD CONSTRAINT fk_241_132
 FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2);
 
-DROP TABLE pktable, fktable CASCADE;
+DROP TABLE pktable, 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