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