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

Generated by: LCOV version 2.0-1