LCOV - code coverage report
Current view: top level - src/backend/commands - dropcmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 194 229 84.7 %
Date: 2020-06-01 08:06:25 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * dropcmds.c
       4             :  *    handle various "DROP" operations
       5             :  *
       6             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/commands/dropcmds.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/htup_details.h"
      18             : #include "access/table.h"
      19             : #include "access/xact.h"
      20             : #include "catalog/dependency.h"
      21             : #include "catalog/namespace.h"
      22             : #include "catalog/objectaddress.h"
      23             : #include "catalog/pg_class.h"
      24             : #include "catalog/pg_proc.h"
      25             : #include "commands/defrem.h"
      26             : #include "miscadmin.h"
      27             : #include "nodes/makefuncs.h"
      28             : #include "parser/parse_type.h"
      29             : #include "utils/acl.h"
      30             : #include "utils/builtins.h"
      31             : #include "utils/lsyscache.h"
      32             : #include "utils/syscache.h"
      33             : 
      34             : 
      35             : static void does_not_exist_skipping(ObjectType objtype,
      36             :                                     Node *object);
      37             : static bool owningrel_does_not_exist_skipping(List *object,
      38             :                                               const char **msg, char **name);
      39             : static bool schema_does_not_exist_skipping(List *object,
      40             :                                            const char **msg, char **name);
      41             : static bool type_in_list_does_not_exist_skipping(List *typenames,
      42             :                                                  const char **msg, char **name);
      43             : 
      44             : 
      45             : /*
      46             :  * Drop one or more objects.
      47             :  *
      48             :  * We don't currently handle all object types here.  Relations, for example,
      49             :  * require special handling, because (for example) indexes have additional
      50             :  * locking requirements.
      51             :  *
      52             :  * We look up all the objects first, and then delete them in a single
      53             :  * performMultipleDeletions() call.  This avoids unnecessary DROP RESTRICT
      54             :  * errors if there are dependencies between them.
      55             :  */
      56             : void
      57        4374 : RemoveObjects(DropStmt *stmt)
      58             : {
      59             :     ObjectAddresses *objects;
      60             :     ListCell   *cell1;
      61             : 
      62        4374 :     objects = new_object_addresses();
      63             : 
      64        8526 :     foreach(cell1, stmt->objects)
      65             :     {
      66             :         ObjectAddress address;
      67        4460 :         Node       *object = lfirst(cell1);
      68        4460 :         Relation    relation = NULL;
      69             :         Oid         namespaceId;
      70             : 
      71             :         /* Get an ObjectAddress for the object. */
      72        4460 :         address = get_object_address(stmt->removeType,
      73             :                                      object,
      74             :                                      &relation,
      75             :                                      AccessExclusiveLock,
      76        4460 :                                      stmt->missing_ok);
      77             : 
      78             :         /*
      79             :          * Issue NOTICE if supplied object was not found.  Note this is only
      80             :          * relevant in the missing_ok case, because otherwise
      81             :          * get_object_address would have thrown an error.
      82             :          */
      83        4200 :         if (!OidIsValid(address.objectId))
      84             :         {
      85             :             Assert(stmt->missing_ok);
      86         248 :             does_not_exist_skipping(stmt->removeType, object);
      87         248 :             continue;
      88             :         }
      89             : 
      90             :         /*
      91             :          * Although COMMENT ON FUNCTION, SECURITY LABEL ON FUNCTION, etc. are
      92             :          * happy to operate on an aggregate as on any other function, we have
      93             :          * historically not allowed this for DROP FUNCTION.
      94             :          */
      95        3952 :         if (stmt->removeType == OBJECT_FUNCTION)
      96             :         {
      97        1738 :             if (get_func_prokind(address.objectId) == PROKIND_AGGREGATE)
      98           0 :                 ereport(ERROR,
      99             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     100             :                          errmsg("\"%s\" is an aggregate function",
     101             :                                 NameListToString(castNode(ObjectWithArgs, object)->objname)),
     102             :                          errhint("Use DROP AGGREGATE to drop aggregate functions.")));
     103             :         }
     104             : 
     105             :         /* Check permissions. */
     106        3952 :         namespaceId = get_object_namespace(&address);
     107        3952 :         if (!OidIsValid(namespaceId) ||
     108        2734 :             !pg_namespace_ownercheck(namespaceId, GetUserId()))
     109        1306 :             check_object_ownership(GetUserId(), stmt->removeType, address,
     110             :                                    object, relation);
     111             : 
     112             :         /*
     113             :          * Make note if a temporary namespace has been accessed in this
     114             :          * transaction.
     115             :          */
     116        3904 :         if (OidIsValid(namespaceId) && isTempNamespace(namespaceId))
     117         254 :             MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
     118             : 
     119             :         /* Release any relcache reference count, but keep lock until commit. */
     120        3904 :         if (relation)
     121         658 :             table_close(relation, NoLock);
     122             : 
     123        3904 :         add_exact_object_address(&address, objects);
     124             :     }
     125             : 
     126             :     /* Here we really delete them. */
     127        4066 :     performMultipleDeletions(objects, stmt->behavior, 0);
     128             : 
     129        3968 :     free_object_addresses(objects);
     130        3968 : }
     131             : 
     132             : /*
     133             :  * owningrel_does_not_exist_skipping
     134             :  *      Subroutine for RemoveObjects
     135             :  *
     136             :  * After determining that a specification for a rule or trigger returns that
     137             :  * the specified object does not exist, test whether its owning relation, and
     138             :  * its schema, exist or not; if they do, return false --- the trigger or rule
     139             :  * itself is missing instead.  If the owning relation or its schema do not
     140             :  * exist, fill the error message format string and name, and return true.
     141             :  */
     142             : static bool
     143          32 : owningrel_does_not_exist_skipping(List *object, const char **msg, char **name)
     144             : {
     145             :     List       *parent_object;
     146             :     RangeVar   *parent_rel;
     147             : 
     148          32 :     parent_object = list_truncate(list_copy(object),
     149          32 :                                   list_length(object) - 1);
     150             : 
     151          32 :     if (schema_does_not_exist_skipping(parent_object, msg, name))
     152          16 :         return true;
     153             : 
     154          16 :     parent_rel = makeRangeVarFromNameList(parent_object);
     155             : 
     156          16 :     if (!OidIsValid(RangeVarGetRelid(parent_rel, NoLock, true)))
     157             :     {
     158           8 :         *msg = gettext_noop("relation \"%s\" does not exist, skipping");
     159           8 :         *name = NameListToString(parent_object);
     160             : 
     161           8 :         return true;
     162             :     }
     163             : 
     164           8 :     return false;
     165             : }
     166             : 
     167             : /*
     168             :  * schema_does_not_exist_skipping
     169             :  *      Subroutine for RemoveObjects
     170             :  *
     171             :  * After determining that a specification for a schema-qualifiable object
     172             :  * refers to an object that does not exist, test whether the specified schema
     173             :  * exists or not.  If no schema was specified, or if the schema does exist,
     174             :  * return false -- the object itself is missing instead.  If the specified
     175             :  * schema does not exist, fill the error message format string and the
     176             :  * specified schema name, and return true.
     177             :  */
     178             : static bool
     179         222 : schema_does_not_exist_skipping(List *object, const char **msg, char **name)
     180             : {
     181             :     RangeVar   *rel;
     182             : 
     183         222 :     rel = makeRangeVarFromNameList(object);
     184             : 
     185         314 :     if (rel->schemaname != NULL &&
     186          92 :         !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
     187             :     {
     188          92 :         *msg = gettext_noop("schema \"%s\" does not exist, skipping");
     189          92 :         *name = rel->schemaname;
     190             : 
     191          92 :         return true;
     192             :     }
     193             : 
     194         130 :     return false;
     195             : }
     196             : 
     197             : /*
     198             :  * type_in_list_does_not_exist_skipping
     199             :  *      Subroutine for RemoveObjects
     200             :  *
     201             :  * After determining that a specification for a function, cast, aggregate or
     202             :  * operator returns that the specified object does not exist, test whether the
     203             :  * involved datatypes, and their schemas, exist or not; if they do, return
     204             :  * false --- the original object itself is missing instead.  If the datatypes
     205             :  * or schemas do not exist, fill the error message format string and the
     206             :  * missing name, and return true.
     207             :  *
     208             :  * First parameter is a list of TypeNames.
     209             :  */
     210             : static bool
     211          88 : type_in_list_does_not_exist_skipping(List *typenames, const char **msg,
     212             :                                      char **name)
     213             : {
     214             :     ListCell   *l;
     215             : 
     216         138 :     foreach(l, typenames)
     217             :     {
     218          96 :         TypeName   *typeName = lfirst_node(TypeName, l);
     219             : 
     220          96 :         if (typeName != NULL)
     221             :         {
     222          92 :             if (!OidIsValid(LookupTypeNameOid(NULL, typeName, true)))
     223             :             {
     224             :                 /* type doesn't exist, try to find why */
     225          46 :                 if (schema_does_not_exist_skipping(typeName->names, msg, name))
     226          46 :                     return true;
     227             : 
     228          22 :                 *msg = gettext_noop("type \"%s\" does not exist, skipping");
     229          22 :                 *name = TypeNameToString(typeName);
     230             : 
     231          22 :                 return true;
     232             :             }
     233             :         }
     234             :     }
     235             : 
     236          42 :     return false;
     237             : }
     238             : 
     239             : /*
     240             :  * does_not_exist_skipping
     241             :  *      Subroutine for RemoveObjects
     242             :  *
     243             :  * Generate a NOTICE stating that the named object was not found, and is
     244             :  * being skipped.  This is only relevant when "IF EXISTS" is used; otherwise,
     245             :  * get_object_address() in RemoveObjects would have thrown an ERROR.
     246             :  */
     247             : static void
     248         248 : does_not_exist_skipping(ObjectType objtype, Node *object)
     249             : {
     250         248 :     const char *msg = NULL;
     251         248 :     char       *name = NULL;
     252         248 :     char       *args = NULL;
     253             : 
     254         248 :     switch (objtype)
     255             :     {
     256           4 :         case OBJECT_ACCESS_METHOD:
     257           4 :             msg = gettext_noop("access method \"%s\" does not exist, skipping");
     258           4 :             name = strVal((Value *) object);
     259           4 :             break;
     260          18 :         case OBJECT_TYPE:
     261             :         case OBJECT_DOMAIN:
     262           8 :             {
     263          18 :                 TypeName   *typ = castNode(TypeName, object);
     264             : 
     265          18 :                 if (!schema_does_not_exist_skipping(typ->names, &msg, &name))
     266             :                 {
     267          10 :                     msg = gettext_noop("type \"%s\" does not exist, skipping");
     268          10 :                     name = TypeNameToString(typ);
     269             :                 }
     270             :             }
     271          18 :             break;
     272           8 :         case OBJECT_COLLATION:
     273           8 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     274             :             {
     275           4 :                 msg = gettext_noop("collation \"%s\" does not exist, skipping");
     276           4 :                 name = NameListToString(castNode(List, object));
     277             :             }
     278           8 :             break;
     279           8 :         case OBJECT_CONVERSION:
     280           8 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     281             :             {
     282           4 :                 msg = gettext_noop("conversion \"%s\" does not exist, skipping");
     283           4 :                 name = NameListToString(castNode(List, object));
     284             :             }
     285           8 :             break;
     286           8 :         case OBJECT_SCHEMA:
     287           8 :             msg = gettext_noop("schema \"%s\" does not exist, skipping");
     288           8 :             name = strVal((Value *) object);
     289           8 :             break;
     290           0 :         case OBJECT_STATISTIC_EXT:
     291           0 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     292             :             {
     293           0 :                 msg = gettext_noop("statistics object \"%s\" does not exist, skipping");
     294           0 :                 name = NameListToString(castNode(List, object));
     295             :             }
     296           0 :             break;
     297           8 :         case OBJECT_TSPARSER:
     298           8 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     299             :             {
     300           4 :                 msg = gettext_noop("text search parser \"%s\" does not exist, skipping");
     301           4 :                 name = NameListToString(castNode(List, object));
     302             :             }
     303           8 :             break;
     304           8 :         case OBJECT_TSDICTIONARY:
     305           8 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     306             :             {
     307           4 :                 msg = gettext_noop("text search dictionary \"%s\" does not exist, skipping");
     308           4 :                 name = NameListToString(castNode(List, object));
     309             :             }
     310           8 :             break;
     311           8 :         case OBJECT_TSTEMPLATE:
     312           8 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     313             :             {
     314           4 :                 msg = gettext_noop("text search template \"%s\" does not exist, skipping");
     315           4 :                 name = NameListToString(castNode(List, object));
     316             :             }
     317           8 :             break;
     318           8 :         case OBJECT_TSCONFIGURATION:
     319           8 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     320             :             {
     321           4 :                 msg = gettext_noop("text search configuration \"%s\" does not exist, skipping");
     322           4 :                 name = NameListToString(castNode(List, object));
     323             :             }
     324           8 :             break;
     325          10 :         case OBJECT_EXTENSION:
     326          10 :             msg = gettext_noop("extension \"%s\" does not exist, skipping");
     327          10 :             name = strVal((Value *) object);
     328          10 :             break;
     329          22 :         case OBJECT_FUNCTION:
     330             :             {
     331          22 :                 ObjectWithArgs *owa = castNode(ObjectWithArgs, object);
     332             : 
     333          22 :                 if (!schema_does_not_exist_skipping(owa->objname, &msg, &name) &&
     334          18 :                     !type_in_list_does_not_exist_skipping(owa->objargs, &msg, &name))
     335             :                 {
     336          10 :                     msg = gettext_noop("function %s(%s) does not exist, skipping");
     337          10 :                     name = NameListToString(owa->objname);
     338          10 :                     args = TypeNameListToString(owa->objargs);
     339             :                 }
     340          22 :                 break;
     341             :             }
     342           0 :         case OBJECT_PROCEDURE:
     343             :             {
     344           0 :                 ObjectWithArgs *owa = castNode(ObjectWithArgs, object);
     345             : 
     346           0 :                 if (!schema_does_not_exist_skipping(owa->objname, &msg, &name) &&
     347           0 :                     !type_in_list_does_not_exist_skipping(owa->objargs, &msg, &name))
     348             :                 {
     349           0 :                     msg = gettext_noop("procedure %s(%s) does not exist, skipping");
     350           0 :                     name = NameListToString(owa->objname);
     351           0 :                     args = TypeNameListToString(owa->objargs);
     352             :                 }
     353           0 :                 break;
     354             :             }
     355           0 :         case OBJECT_ROUTINE:
     356             :             {
     357           0 :                 ObjectWithArgs *owa = castNode(ObjectWithArgs, object);
     358             : 
     359           0 :                 if (!schema_does_not_exist_skipping(owa->objname, &msg, &name) &&
     360           0 :                     !type_in_list_does_not_exist_skipping(owa->objargs, &msg, &name))
     361             :                 {
     362           0 :                     msg = gettext_noop("routine %s(%s) does not exist, skipping");
     363           0 :                     name = NameListToString(owa->objname);
     364           0 :                     args = TypeNameListToString(owa->objargs);
     365             :                 }
     366           0 :                 break;
     367             :             }
     368          20 :         case OBJECT_AGGREGATE:
     369             :             {
     370          20 :                 ObjectWithArgs *owa = castNode(ObjectWithArgs, object);
     371             : 
     372          20 :                 if (!schema_does_not_exist_skipping(owa->objname, &msg, &name) &&
     373          16 :                     !type_in_list_does_not_exist_skipping(owa->objargs, &msg, &name))
     374             :                 {
     375           8 :                     msg = gettext_noop("aggregate %s(%s) does not exist, skipping");
     376           8 :                     name = NameListToString(owa->objname);
     377           8 :                     args = TypeNameListToString(owa->objargs);
     378             :                 }
     379          20 :                 break;
     380             :             }
     381          20 :         case OBJECT_OPERATOR:
     382             :             {
     383          20 :                 ObjectWithArgs *owa = castNode(ObjectWithArgs, object);
     384             : 
     385          20 :                 if (!schema_does_not_exist_skipping(owa->objname, &msg, &name) &&
     386          16 :                     !type_in_list_does_not_exist_skipping(owa->objargs, &msg, &name))
     387             :                 {
     388           4 :                     msg = gettext_noop("operator %s does not exist, skipping");
     389           4 :                     name = NameListToString(owa->objname);
     390             :                 }
     391          20 :                 break;
     392             :             }
     393           4 :         case OBJECT_LANGUAGE:
     394           4 :             msg = gettext_noop("language \"%s\" does not exist, skipping");
     395           4 :             name = strVal((Value *) object);
     396           4 :             break;
     397          20 :         case OBJECT_CAST:
     398             :             {
     399          20 :                 if (!type_in_list_does_not_exist_skipping(list_make1(linitial(castNode(List, object))), &msg, &name) &&
     400          12 :                     !type_in_list_does_not_exist_skipping(list_make1(lsecond(castNode(List, object))), &msg, &name))
     401             :                 {
     402             :                     /* XXX quote or no quote? */
     403           4 :                     msg = gettext_noop("cast from type %s to type %s does not exist, skipping");
     404           4 :                     name = TypeNameToString(linitial_node(TypeName, castNode(List, object)));
     405           4 :                     args = TypeNameToString(lsecond_node(TypeName, castNode(List, object)));
     406             :                 }
     407             :             }
     408          20 :             break;
     409           6 :         case OBJECT_TRANSFORM:
     410           6 :             if (!type_in_list_does_not_exist_skipping(list_make1(linitial(castNode(List, object))), &msg, &name))
     411             :             {
     412           4 :                 msg = gettext_noop("transform for type %s language \"%s\" does not exist, skipping");
     413           4 :                 name = TypeNameToString(linitial_node(TypeName, castNode(List, object)));
     414           4 :                 args = strVal(lsecond(castNode(List, object)));
     415             :             }
     416           6 :             break;
     417          16 :         case OBJECT_TRIGGER:
     418          16 :             if (!owningrel_does_not_exist_skipping(castNode(List, object), &msg, &name))
     419             :             {
     420           4 :                 msg = gettext_noop("trigger \"%s\" for relation \"%s\" does not exist, skipping");
     421           4 :                 name = strVal(llast(castNode(List, object)));
     422           4 :                 args = NameListToString(list_truncate(list_copy(castNode(List, object)),
     423           4 :                                                       list_length(castNode(List, object)) - 1));
     424             :             }
     425          16 :             break;
     426           0 :         case OBJECT_POLICY:
     427           0 :             if (!owningrel_does_not_exist_skipping(castNode(List, object), &msg, &name))
     428             :             {
     429           0 :                 msg = gettext_noop("policy \"%s\" for relation \"%s\" does not exist, skipping");
     430           0 :                 name = strVal(llast(castNode(List, object)));
     431           0 :                 args = NameListToString(list_truncate(list_copy(castNode(List, object)),
     432           0 :                                                       list_length(castNode(List, object)) - 1));
     433             :             }
     434           0 :             break;
     435           4 :         case OBJECT_EVENT_TRIGGER:
     436           4 :             msg = gettext_noop("event trigger \"%s\" does not exist, skipping");
     437           4 :             name = strVal((Value *) object);
     438           4 :             break;
     439          16 :         case OBJECT_RULE:
     440          16 :             if (!owningrel_does_not_exist_skipping(castNode(List, object), &msg, &name))
     441             :             {
     442           4 :                 msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping");
     443           4 :                 name = strVal(llast(castNode(List, object)));
     444           4 :                 args = NameListToString(list_truncate(list_copy(castNode(List, object)),
     445           4 :                                                       list_length(castNode(List, object)) - 1));
     446             :             }
     447          16 :             break;
     448           8 :         case OBJECT_FDW:
     449           8 :             msg = gettext_noop("foreign-data wrapper \"%s\" does not exist, skipping");
     450           8 :             name = strVal((Value *) object);
     451           8 :             break;
     452           8 :         case OBJECT_FOREIGN_SERVER:
     453           8 :             msg = gettext_noop("server \"%s\" does not exist, skipping");
     454           8 :             name = strVal((Value *) object);
     455           8 :             break;
     456           8 :         case OBJECT_OPCLASS:
     457           4 :             {
     458           8 :                 List       *opcname = list_copy_tail(castNode(List, object), 1);
     459             : 
     460           8 :                 if (!schema_does_not_exist_skipping(opcname, &msg, &name))
     461             :                 {
     462           4 :                     msg = gettext_noop("operator class \"%s\" does not exist for access method \"%s\", skipping");
     463           4 :                     name = NameListToString(opcname);
     464           4 :                     args = strVal(linitial(castNode(List, object)));
     465             :                 }
     466             :             }
     467           8 :             break;
     468           8 :         case OBJECT_OPFAMILY:
     469           4 :             {
     470           8 :                 List       *opfname = list_copy_tail(castNode(List, object), 1);
     471             : 
     472           8 :                 if (!schema_does_not_exist_skipping(opfname, &msg, &name))
     473             :                 {
     474           4 :                     msg = gettext_noop("operator family \"%s\" does not exist for access method \"%s\", skipping");
     475           4 :                     name = NameListToString(opfname);
     476           4 :                     args = strVal(linitial(castNode(List, object)));
     477             :                 }
     478             :             }
     479           8 :             break;
     480           0 :         case OBJECT_PUBLICATION:
     481           0 :             msg = gettext_noop("publication \"%s\" does not exist, skipping");
     482           0 :             name = strVal((Value *) object);
     483           0 :             break;
     484           0 :         default:
     485           0 :             elog(ERROR, "unrecognized object type: %d", (int) objtype);
     486             :             break;
     487             :     }
     488             : 
     489         248 :     if (!args)
     490         206 :         ereport(NOTICE, (errmsg(msg, name)));
     491             :     else
     492          42 :         ereport(NOTICE, (errmsg(msg, name, args)));
     493         248 : }

Generated by: LCOV version 1.13