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

Generated by: LCOV version 1.11