LCOV - code coverage report
Current view: top level - src/backend/commands - dropcmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 191 229 83.4 %
Date: 2024-04-22 21:11:44 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-2024, 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        8140 : RemoveObjects(DropStmt *stmt)
      54             : {
      55             :     ObjectAddresses *objects;
      56             :     ListCell   *cell1;
      57             : 
      58        8140 :     objects = new_object_addresses();
      59             : 
      60       16056 :     foreach(cell1, stmt->objects)
      61             :     {
      62             :         ObjectAddress address;
      63        8424 :         Node       *object = lfirst(cell1);
      64        8424 :         Relation    relation = NULL;
      65             :         Oid         namespaceId;
      66             : 
      67             :         /* Get an ObjectAddress for the object. */
      68        8424 :         address = get_object_address(stmt->removeType,
      69             :                                      object,
      70             :                                      &relation,
      71             :                                      AccessExclusiveLock,
      72        8424 :                                      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        7994 :         if (!OidIsValid(address.objectId))
      80             :         {
      81             :             Assert(stmt->missing_ok);
      82         384 :             does_not_exist_skipping(stmt->removeType, object);
      83         384 :             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        7610 :         if (stmt->removeType == OBJECT_FUNCTION)
      92             :         {
      93        3304 :             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        7610 :         namespaceId = get_object_namespace(&address);
     103        7610 :         if (!OidIsValid(namespaceId) ||
     104        5242 :             !object_ownercheck(NamespaceRelationId, namespaceId, GetUserId()))
     105        2524 :             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        7532 :         if (OidIsValid(namespaceId) && isTempNamespace(namespaceId))
     113         256 :             MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
     114             : 
     115             :         /* Release any relcache reference count, but keep lock until commit. */
     116        7532 :         if (relation)
     117         996 :             table_close(relation, NoLock);
     118             : 
     119        7532 :         add_exact_object_address(&address, objects);
     120             :     }
     121             : 
     122             :     /* Here we really delete them. */
     123        7632 :     performMultipleDeletions(objects, stmt->behavior, 0);
     124             : 
     125        7482 :     free_object_addresses(objects);
     126        7482 : }
     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          48 : owningrel_does_not_exist_skipping(List *object, const char **msg, char **name)
     140             : {
     141             :     List       *parent_object;
     142             :     RangeVar   *parent_rel;
     143             : 
     144          48 :     parent_object = list_copy_head(object, list_length(object) - 1);
     145             : 
     146          48 :     if (schema_does_not_exist_skipping(parent_object, msg, name))
     147          24 :         return true;
     148             : 
     149          24 :     parent_rel = makeRangeVarFromNameList(parent_object);
     150             : 
     151          24 :     if (!OidIsValid(RangeVarGetRelid(parent_rel, NoLock, true)))
     152             :     {
     153          12 :         *msg = gettext_noop("relation \"%s\" does not exist, skipping");
     154          12 :         *name = NameListToString(parent_object);
     155             : 
     156          12 :         return true;
     157             :     }
     158             : 
     159          12 :     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         350 : schema_does_not_exist_skipping(List *object, const char **msg, char **name)
     175             : {
     176             :     RangeVar   *rel;
     177             : 
     178         350 :     rel = makeRangeVarFromNameList(object);
     179             : 
     180         488 :     if (rel->schemaname != NULL &&
     181         138 :         !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
     182             :     {
     183         138 :         *msg = gettext_noop("schema \"%s\" does not exist, skipping");
     184         138 :         *name = rel->schemaname;
     185             : 
     186         138 :         return true;
     187             :     }
     188             : 
     189         212 :     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         142 : type_in_list_does_not_exist_skipping(List *typenames, const char **msg,
     207             :                                      char **name)
     208             : {
     209             :     ListCell   *l;
     210             : 
     211         212 :     foreach(l, typenames)
     212             :     {
     213         138 :         TypeName   *typeName = lfirst_node(TypeName, l);
     214             : 
     215         138 :         if (typeName != NULL)
     216             :         {
     217         132 :             if (!OidIsValid(LookupTypeNameOid(NULL, typeName, true)))
     218             :             {
     219             :                 /* type doesn't exist, try to find why */
     220          68 :                 if (schema_does_not_exist_skipping(typeName->names, msg, name))
     221          68 :                     return true;
     222             : 
     223          32 :                 *msg = gettext_noop("type \"%s\" does not exist, skipping");
     224          32 :                 *name = TypeNameToString(typeName);
     225             : 
     226          32 :                 return true;
     227             :             }
     228             :         }
     229             :     }
     230             : 
     231          74 :     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         384 : does_not_exist_skipping(ObjectType objtype, Node *object)
     244             : {
     245         384 :     const char *msg = NULL;
     246         384 :     char       *name = NULL;
     247         384 :     char       *args = NULL;
     248             : 
     249         384 :     switch (objtype)
     250             :     {
     251           6 :         case OBJECT_ACCESS_METHOD:
     252           6 :             msg = gettext_noop("access method \"%s\" does not exist, skipping");
     253           6 :             name = strVal(object);
     254           6 :             break;
     255          26 :         case OBJECT_TYPE:
     256             :         case OBJECT_DOMAIN:
     257             :             {
     258          26 :                 TypeName   *typ = castNode(TypeName, object);
     259             : 
     260          26 :                 if (!schema_does_not_exist_skipping(typ->names, &msg, &name))
     261             :                 {
     262          14 :                     msg = gettext_noop("type \"%s\" does not exist, skipping");
     263          14 :                     name = TypeNameToString(typ);
     264             :                 }
     265             :             }
     266          26 :             break;
     267          18 :         case OBJECT_COLLATION:
     268          18 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     269             :             {
     270          12 :                 msg = gettext_noop("collation \"%s\" does not exist, skipping");
     271          12 :                 name = NameListToString(castNode(List, object));
     272             :             }
     273          18 :             break;
     274          12 :         case OBJECT_CONVERSION:
     275          12 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     276             :             {
     277           6 :                 msg = gettext_noop("conversion \"%s\" does not exist, skipping");
     278           6 :                 name = NameListToString(castNode(List, object));
     279             :             }
     280          12 :             break;
     281          12 :         case OBJECT_SCHEMA:
     282          12 :             msg = gettext_noop("schema \"%s\" does not exist, skipping");
     283          12 :             name = strVal(object);
     284          12 :             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          12 :         case OBJECT_TSPARSER:
     293          12 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     294             :             {
     295           6 :                 msg = gettext_noop("text search parser \"%s\" does not exist, skipping");
     296           6 :                 name = NameListToString(castNode(List, object));
     297             :             }
     298          12 :             break;
     299          12 :         case OBJECT_TSDICTIONARY:
     300          12 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     301             :             {
     302           6 :                 msg = gettext_noop("text search dictionary \"%s\" does not exist, skipping");
     303           6 :                 name = NameListToString(castNode(List, object));
     304             :             }
     305          12 :             break;
     306          12 :         case OBJECT_TSTEMPLATE:
     307          12 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     308             :             {
     309           6 :                 msg = gettext_noop("text search template \"%s\" does not exist, skipping");
     310           6 :                 name = NameListToString(castNode(List, object));
     311             :             }
     312          12 :             break;
     313          12 :         case OBJECT_TSCONFIGURATION:
     314          12 :             if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
     315             :             {
     316           6 :                 msg = gettext_noop("text search configuration \"%s\" does not exist, skipping");
     317           6 :                 name = NameListToString(castNode(List, object));
     318             :             }
     319          12 :             break;
     320          12 :         case OBJECT_EXTENSION:
     321          12 :             msg = gettext_noop("extension \"%s\" does not exist, skipping");
     322          12 :             name = strVal(object);
     323          12 :             break;
     324          46 :         case OBJECT_FUNCTION:
     325             :             {
     326          46 :                 ObjectWithArgs *owa = castNode(ObjectWithArgs, object);
     327             : 
     328          46 :                 if (!schema_does_not_exist_skipping(owa->objname, &msg, &name) &&
     329          40 :                     !type_in_list_does_not_exist_skipping(owa->objargs, &msg, &name))
     330             :                 {
     331          28 :                     msg = gettext_noop("function %s(%s) does not exist, skipping");
     332          28 :                     name = NameListToString(owa->objname);
     333          28 :                     args = TypeNameListToString(owa->objargs);
     334             :                 }
     335          46 :                 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          30 :         case OBJECT_AGGREGATE:
     364             :             {
     365          30 :                 ObjectWithArgs *owa = castNode(ObjectWithArgs, object);
     366             : 
     367          30 :                 if (!schema_does_not_exist_skipping(owa->objname, &msg, &name) &&
     368          24 :                     !type_in_list_does_not_exist_skipping(owa->objargs, &msg, &name))
     369             :                 {
     370          12 :                     msg = gettext_noop("aggregate %s(%s) does not exist, skipping");
     371          12 :                     name = NameListToString(owa->objname);
     372          12 :                     args = TypeNameListToString(owa->objargs);
     373             :                 }
     374          30 :                 break;
     375             :             }
     376          30 :         case OBJECT_OPERATOR:
     377             :             {
     378          30 :                 ObjectWithArgs *owa = castNode(ObjectWithArgs, object);
     379             : 
     380          30 :                 if (!schema_does_not_exist_skipping(owa->objname, &msg, &name) &&
     381          24 :                     !type_in_list_does_not_exist_skipping(owa->objargs, &msg, &name))
     382             :                 {
     383           6 :                     msg = gettext_noop("operator %s does not exist, skipping");
     384           6 :                     name = NameListToString(owa->objname);
     385             :                 }
     386          30 :                 break;
     387             :             }
     388           6 :         case OBJECT_LANGUAGE:
     389           6 :             msg = gettext_noop("language \"%s\" does not exist, skipping");
     390           6 :             name = strVal(object);
     391           6 :             break;
     392          30 :         case OBJECT_CAST:
     393             :             {
     394          30 :                 if (!type_in_list_does_not_exist_skipping(list_make1(linitial(castNode(List, object))), &msg, &name) &&
     395          18 :                     !type_in_list_does_not_exist_skipping(list_make1(lsecond(castNode(List, object))), &msg, &name))
     396             :                 {
     397             :                     /* XXX quote or no quote? */
     398           6 :                     msg = gettext_noop("cast from type %s to type %s does not exist, skipping");
     399           6 :                     name = TypeNameToString(linitial_node(TypeName, castNode(List, object)));
     400           6 :                     args = TypeNameToString(lsecond_node(TypeName, castNode(List, object)));
     401             :                 }
     402             :             }
     403          30 :             break;
     404           6 :         case OBJECT_TRANSFORM:
     405           6 :             if (!type_in_list_does_not_exist_skipping(list_make1(linitial(castNode(List, object))), &msg, &name))
     406             :             {
     407           4 :                 msg = gettext_noop("transform for type %s language \"%s\" does not exist, skipping");
     408           4 :                 name = TypeNameToString(linitial_node(TypeName, castNode(List, object)));
     409           4 :                 args = strVal(lsecond(castNode(List, object)));
     410             :             }
     411           6 :             break;
     412          24 :         case OBJECT_TRIGGER:
     413          24 :             if (!owningrel_does_not_exist_skipping(castNode(List, object), &msg, &name))
     414             :             {
     415           6 :                 msg = gettext_noop("trigger \"%s\" for relation \"%s\" does not exist, skipping");
     416           6 :                 name = strVal(llast(castNode(List, object)));
     417           6 :                 args = NameListToString(list_copy_head(castNode(List, object),
     418           6 :                                                        list_length(castNode(List, object)) - 1));
     419             :             }
     420          24 :             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           6 :         case OBJECT_EVENT_TRIGGER:
     431           6 :             msg = gettext_noop("event trigger \"%s\" does not exist, skipping");
     432           6 :             name = strVal(object);
     433           6 :             break;
     434          24 :         case OBJECT_RULE:
     435          24 :             if (!owningrel_does_not_exist_skipping(castNode(List, object), &msg, &name))
     436             :             {
     437           6 :                 msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping");
     438           6 :                 name = strVal(llast(castNode(List, object)));
     439           6 :                 args = NameListToString(list_copy_head(castNode(List, object),
     440           6 :                                                        list_length(castNode(List, object)) - 1));
     441             :             }
     442          24 :             break;
     443          12 :         case OBJECT_FDW:
     444          12 :             msg = gettext_noop("foreign-data wrapper \"%s\" does not exist, skipping");
     445          12 :             name = strVal(object);
     446          12 :             break;
     447          12 :         case OBJECT_FOREIGN_SERVER:
     448          12 :             msg = gettext_noop("server \"%s\" does not exist, skipping");
     449          12 :             name = strVal(object);
     450          12 :             break;
     451          12 :         case OBJECT_OPCLASS:
     452             :             {
     453          12 :                 List       *opcname = list_copy_tail(castNode(List, object), 1);
     454             : 
     455          12 :                 if (!schema_does_not_exist_skipping(opcname, &msg, &name))
     456             :                 {
     457           6 :                     msg = gettext_noop("operator class \"%s\" does not exist for access method \"%s\", skipping");
     458           6 :                     name = NameListToString(opcname);
     459           6 :                     args = strVal(linitial(castNode(List, object)));
     460             :                 }
     461             :             }
     462          12 :             break;
     463          12 :         case OBJECT_OPFAMILY:
     464             :             {
     465          12 :                 List       *opfname = list_copy_tail(castNode(List, object), 1);
     466             : 
     467          12 :                 if (!schema_does_not_exist_skipping(opfname, &msg, &name))
     468             :                 {
     469           6 :                     msg = gettext_noop("operator family \"%s\" does not exist for access method \"%s\", skipping");
     470           6 :                     name = NameListToString(opfname);
     471           6 :                     args = strVal(linitial(castNode(List, object)));
     472             :                 }
     473             :             }
     474          12 :             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         384 :     if (!msg)
     518           0 :         elog(ERROR, "unrecognized object type: %d", (int) objtype);
     519             : 
     520         384 :     if (!args)
     521         310 :         ereport(NOTICE, (errmsg(msg, name)));
     522             :     else
     523          74 :         ereport(NOTICE, (errmsg(msg, name, args)));
     524         384 : }

Generated by: LCOV version 1.14