/src/postgres/src/backend/catalog/dependency.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * dependency.c |
4 | | * Routines to support inter-object dependencies. |
5 | | * |
6 | | * |
7 | | * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group |
8 | | * Portions Copyright (c) 1994, Regents of the University of California |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/catalog/dependency.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | #include "postgres.h" |
16 | | |
17 | | #include "access/genam.h" |
18 | | #include "access/htup_details.h" |
19 | | #include "access/table.h" |
20 | | #include "access/xact.h" |
21 | | #include "catalog/catalog.h" |
22 | | #include "catalog/dependency.h" |
23 | | #include "catalog/heap.h" |
24 | | #include "catalog/index.h" |
25 | | #include "catalog/objectaccess.h" |
26 | | #include "catalog/pg_am.h" |
27 | | #include "catalog/pg_amop.h" |
28 | | #include "catalog/pg_amproc.h" |
29 | | #include "catalog/pg_attrdef.h" |
30 | | #include "catalog/pg_authid.h" |
31 | | #include "catalog/pg_auth_members.h" |
32 | | #include "catalog/pg_cast.h" |
33 | | #include "catalog/pg_collation.h" |
34 | | #include "catalog/pg_constraint.h" |
35 | | #include "catalog/pg_conversion.h" |
36 | | #include "catalog/pg_database.h" |
37 | | #include "catalog/pg_default_acl.h" |
38 | | #include "catalog/pg_depend.h" |
39 | | #include "catalog/pg_event_trigger.h" |
40 | | #include "catalog/pg_extension.h" |
41 | | #include "catalog/pg_foreign_data_wrapper.h" |
42 | | #include "catalog/pg_foreign_server.h" |
43 | | #include "catalog/pg_init_privs.h" |
44 | | #include "catalog/pg_language.h" |
45 | | #include "catalog/pg_largeobject.h" |
46 | | #include "catalog/pg_namespace.h" |
47 | | #include "catalog/pg_opclass.h" |
48 | | #include "catalog/pg_operator.h" |
49 | | #include "catalog/pg_opfamily.h" |
50 | | #include "catalog/pg_parameter_acl.h" |
51 | | #include "catalog/pg_policy.h" |
52 | | #include "catalog/pg_proc.h" |
53 | | #include "catalog/pg_publication.h" |
54 | | #include "catalog/pg_publication_namespace.h" |
55 | | #include "catalog/pg_publication_rel.h" |
56 | | #include "catalog/pg_rewrite.h" |
57 | | #include "catalog/pg_statistic_ext.h" |
58 | | #include "catalog/pg_subscription.h" |
59 | | #include "catalog/pg_tablespace.h" |
60 | | #include "catalog/pg_transform.h" |
61 | | #include "catalog/pg_trigger.h" |
62 | | #include "catalog/pg_ts_config.h" |
63 | | #include "catalog/pg_ts_dict.h" |
64 | | #include "catalog/pg_ts_parser.h" |
65 | | #include "catalog/pg_ts_template.h" |
66 | | #include "catalog/pg_type.h" |
67 | | #include "catalog/pg_user_mapping.h" |
68 | | #include "commands/comment.h" |
69 | | #include "commands/defrem.h" |
70 | | #include "commands/event_trigger.h" |
71 | | #include "commands/extension.h" |
72 | | #include "commands/policy.h" |
73 | | #include "commands/publicationcmds.h" |
74 | | #include "commands/seclabel.h" |
75 | | #include "commands/sequence.h" |
76 | | #include "commands/trigger.h" |
77 | | #include "commands/typecmds.h" |
78 | | #include "funcapi.h" |
79 | | #include "miscadmin.h" |
80 | | #include "nodes/nodeFuncs.h" |
81 | | #include "parser/parsetree.h" |
82 | | #include "rewrite/rewriteRemove.h" |
83 | | #include "storage/lmgr.h" |
84 | | #include "utils/fmgroids.h" |
85 | | #include "utils/lsyscache.h" |
86 | | #include "utils/syscache.h" |
87 | | |
88 | | |
89 | | /* |
90 | | * Deletion processing requires additional state for each ObjectAddress that |
91 | | * it's planning to delete. For simplicity and code-sharing we make the |
92 | | * ObjectAddresses code support arrays with or without this extra state. |
93 | | */ |
94 | | typedef struct |
95 | | { |
96 | | int flags; /* bitmask, see bit definitions below */ |
97 | | ObjectAddress dependee; /* object whose deletion forced this one */ |
98 | | } ObjectAddressExtra; |
99 | | |
100 | | /* ObjectAddressExtra flag bits */ |
101 | 0 | #define DEPFLAG_ORIGINAL 0x0001 /* an original deletion target */ |
102 | 0 | #define DEPFLAG_NORMAL 0x0002 /* reached via normal dependency */ |
103 | 0 | #define DEPFLAG_AUTO 0x0004 /* reached via auto dependency */ |
104 | 0 | #define DEPFLAG_INTERNAL 0x0008 /* reached via internal dependency */ |
105 | 0 | #define DEPFLAG_PARTITION 0x0010 /* reached via partition dependency */ |
106 | 0 | #define DEPFLAG_EXTENSION 0x0020 /* reached via extension dependency */ |
107 | 0 | #define DEPFLAG_REVERSE 0x0040 /* reverse internal/extension link */ |
108 | 0 | #define DEPFLAG_IS_PART 0x0080 /* has a partition dependency */ |
109 | 0 | #define DEPFLAG_SUBOBJECT 0x0100 /* subobject of another deletable object */ |
110 | | |
111 | | |
112 | | /* expansible list of ObjectAddresses */ |
113 | | struct ObjectAddresses |
114 | | { |
115 | | ObjectAddress *refs; /* => palloc'd array */ |
116 | | ObjectAddressExtra *extras; /* => palloc'd array, or NULL if not used */ |
117 | | int numrefs; /* current number of references */ |
118 | | int maxrefs; /* current size of palloc'd array(s) */ |
119 | | }; |
120 | | |
121 | | /* typedef ObjectAddresses appears in dependency.h */ |
122 | | |
123 | | /* threaded list of ObjectAddresses, for recursion detection */ |
124 | | typedef struct ObjectAddressStack |
125 | | { |
126 | | const ObjectAddress *object; /* object being visited */ |
127 | | int flags; /* its current flag bits */ |
128 | | struct ObjectAddressStack *next; /* next outer stack level */ |
129 | | } ObjectAddressStack; |
130 | | |
131 | | /* temporary storage in findDependentObjects */ |
132 | | typedef struct |
133 | | { |
134 | | ObjectAddress obj; /* object to be deleted --- MUST BE FIRST */ |
135 | | int subflags; /* flags to pass down when recursing to obj */ |
136 | | } ObjectAddressAndFlags; |
137 | | |
138 | | /* for find_expr_references_walker */ |
139 | | typedef struct |
140 | | { |
141 | | ObjectAddresses *addrs; /* addresses being accumulated */ |
142 | | List *rtables; /* list of rangetables to resolve Vars */ |
143 | | } find_expr_references_context; |
144 | | |
145 | | |
146 | | static void findDependentObjects(const ObjectAddress *object, |
147 | | int objflags, |
148 | | int flags, |
149 | | ObjectAddressStack *stack, |
150 | | ObjectAddresses *targetObjects, |
151 | | const ObjectAddresses *pendingObjects, |
152 | | Relation *depRel); |
153 | | static void reportDependentObjects(const ObjectAddresses *targetObjects, |
154 | | DropBehavior behavior, |
155 | | int flags, |
156 | | const ObjectAddress *origObject); |
157 | | static void deleteOneObject(const ObjectAddress *object, |
158 | | Relation *depRel, int32 flags); |
159 | | static void doDeletion(const ObjectAddress *object, int flags); |
160 | | static bool find_expr_references_walker(Node *node, |
161 | | find_expr_references_context *context); |
162 | | static void process_function_rte_ref(RangeTblEntry *rte, AttrNumber attnum, |
163 | | find_expr_references_context *context); |
164 | | static void eliminate_duplicate_dependencies(ObjectAddresses *addrs); |
165 | | static int object_address_comparator(const void *a, const void *b); |
166 | | static void add_object_address(Oid classId, Oid objectId, int32 subId, |
167 | | ObjectAddresses *addrs); |
168 | | static void add_exact_object_address_extra(const ObjectAddress *object, |
169 | | const ObjectAddressExtra *extra, |
170 | | ObjectAddresses *addrs); |
171 | | static bool object_address_present_add_flags(const ObjectAddress *object, |
172 | | int flags, |
173 | | ObjectAddresses *addrs); |
174 | | static bool stack_address_present_add_flags(const ObjectAddress *object, |
175 | | int flags, |
176 | | ObjectAddressStack *stack); |
177 | | static void DeleteInitPrivs(const ObjectAddress *object); |
178 | | |
179 | | |
180 | | /* |
181 | | * Go through the objects given running the final actions on them, and execute |
182 | | * the actual deletion. |
183 | | */ |
184 | | static void |
185 | | deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel, |
186 | | int flags) |
187 | 0 | { |
188 | 0 | int i; |
189 | | |
190 | | /* |
191 | | * Keep track of objects for event triggers, if necessary. |
192 | | */ |
193 | 0 | if (trackDroppedObjectsNeeded() && !(flags & PERFORM_DELETION_INTERNAL)) |
194 | 0 | { |
195 | 0 | for (i = 0; i < targetObjects->numrefs; i++) |
196 | 0 | { |
197 | 0 | const ObjectAddress *thisobj = &targetObjects->refs[i]; |
198 | 0 | const ObjectAddressExtra *extra = &targetObjects->extras[i]; |
199 | 0 | bool original = false; |
200 | 0 | bool normal = false; |
201 | |
|
202 | 0 | if (extra->flags & DEPFLAG_ORIGINAL) |
203 | 0 | original = true; |
204 | 0 | if (extra->flags & DEPFLAG_NORMAL) |
205 | 0 | normal = true; |
206 | 0 | if (extra->flags & DEPFLAG_REVERSE) |
207 | 0 | normal = true; |
208 | |
|
209 | 0 | if (EventTriggerSupportsObject(thisobj)) |
210 | 0 | { |
211 | 0 | EventTriggerSQLDropAddObject(thisobj, original, normal); |
212 | 0 | } |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | | /* |
217 | | * Delete all the objects in the proper order, except that if told to, we |
218 | | * should skip the original object(s). |
219 | | */ |
220 | 0 | for (i = 0; i < targetObjects->numrefs; i++) |
221 | 0 | { |
222 | 0 | ObjectAddress *thisobj = targetObjects->refs + i; |
223 | 0 | ObjectAddressExtra *thisextra = targetObjects->extras + i; |
224 | |
|
225 | 0 | if ((flags & PERFORM_DELETION_SKIP_ORIGINAL) && |
226 | 0 | (thisextra->flags & DEPFLAG_ORIGINAL)) |
227 | 0 | continue; |
228 | | |
229 | 0 | deleteOneObject(thisobj, depRel, flags); |
230 | 0 | } |
231 | 0 | } |
232 | | |
233 | | /* |
234 | | * performDeletion: attempt to drop the specified object. If CASCADE |
235 | | * behavior is specified, also drop any dependent objects (recursively). |
236 | | * If RESTRICT behavior is specified, error out if there are any dependent |
237 | | * objects, except for those that should be implicitly dropped anyway |
238 | | * according to the dependency type. |
239 | | * |
240 | | * This is the outer control routine for all forms of DROP that drop objects |
241 | | * that can participate in dependencies. Note that performMultipleDeletions |
242 | | * is a variant on the same theme; if you change anything here you'll likely |
243 | | * need to fix that too. |
244 | | * |
245 | | * Bits in the flags argument can include: |
246 | | * |
247 | | * PERFORM_DELETION_INTERNAL: indicates that the drop operation is not the |
248 | | * direct result of a user-initiated action. For example, when a temporary |
249 | | * schema is cleaned out so that a new backend can use it, or when a column |
250 | | * default is dropped as an intermediate step while adding a new one, that's |
251 | | * an internal operation. On the other hand, when we drop something because |
252 | | * the user issued a DROP statement against it, that's not internal. Currently |
253 | | * this suppresses calling event triggers and making some permissions checks. |
254 | | * |
255 | | * PERFORM_DELETION_CONCURRENTLY: perform the drop concurrently. This does |
256 | | * not currently work for anything except dropping indexes; don't set it for |
257 | | * other object types or you may get strange results. |
258 | | * |
259 | | * PERFORM_DELETION_QUIETLY: reduce message level from NOTICE to DEBUG2. |
260 | | * |
261 | | * PERFORM_DELETION_SKIP_ORIGINAL: do not delete the specified object(s), |
262 | | * but only what depends on it/them. |
263 | | * |
264 | | * PERFORM_DELETION_SKIP_EXTENSIONS: do not delete extensions, even when |
265 | | * deleting objects that are part of an extension. This should generally |
266 | | * be used only when dropping temporary objects. |
267 | | * |
268 | | * PERFORM_DELETION_CONCURRENT_LOCK: perform the drop normally but with a lock |
269 | | * as if it were concurrent. This is used by REINDEX CONCURRENTLY. |
270 | | * |
271 | | */ |
272 | | void |
273 | | performDeletion(const ObjectAddress *object, |
274 | | DropBehavior behavior, int flags) |
275 | 0 | { |
276 | 0 | Relation depRel; |
277 | 0 | ObjectAddresses *targetObjects; |
278 | | |
279 | | /* |
280 | | * We save some cycles by opening pg_depend just once and passing the |
281 | | * Relation pointer down to all the recursive deletion steps. |
282 | | */ |
283 | 0 | depRel = table_open(DependRelationId, RowExclusiveLock); |
284 | | |
285 | | /* |
286 | | * Acquire deletion lock on the target object. (Ideally the caller has |
287 | | * done this already, but many places are sloppy about it.) |
288 | | */ |
289 | 0 | AcquireDeletionLock(object, 0); |
290 | | |
291 | | /* |
292 | | * Construct a list of objects to delete (ie, the given object plus |
293 | | * everything directly or indirectly dependent on it). |
294 | | */ |
295 | 0 | targetObjects = new_object_addresses(); |
296 | |
|
297 | 0 | findDependentObjects(object, |
298 | 0 | DEPFLAG_ORIGINAL, |
299 | 0 | flags, |
300 | 0 | NULL, /* empty stack */ |
301 | 0 | targetObjects, |
302 | 0 | NULL, /* no pendingObjects */ |
303 | 0 | &depRel); |
304 | | |
305 | | /* |
306 | | * Check if deletion is allowed, and report about cascaded deletes. |
307 | | */ |
308 | 0 | reportDependentObjects(targetObjects, |
309 | 0 | behavior, |
310 | 0 | flags, |
311 | 0 | object); |
312 | | |
313 | | /* do the deed */ |
314 | 0 | deleteObjectsInList(targetObjects, &depRel, flags); |
315 | | |
316 | | /* And clean up */ |
317 | 0 | free_object_addresses(targetObjects); |
318 | |
|
319 | 0 | table_close(depRel, RowExclusiveLock); |
320 | 0 | } |
321 | | |
322 | | /* |
323 | | * performMultipleDeletions: Similar to performDeletion, but act on multiple |
324 | | * objects at once. |
325 | | * |
326 | | * The main difference from issuing multiple performDeletion calls is that the |
327 | | * list of objects that would be implicitly dropped, for each object to be |
328 | | * dropped, is the union of the implicit-object list for all objects. This |
329 | | * makes each check be more relaxed. |
330 | | */ |
331 | | void |
332 | | performMultipleDeletions(const ObjectAddresses *objects, |
333 | | DropBehavior behavior, int flags) |
334 | 0 | { |
335 | 0 | Relation depRel; |
336 | 0 | ObjectAddresses *targetObjects; |
337 | 0 | int i; |
338 | | |
339 | | /* No work if no objects... */ |
340 | 0 | if (objects->numrefs <= 0) |
341 | 0 | return; |
342 | | |
343 | | /* |
344 | | * We save some cycles by opening pg_depend just once and passing the |
345 | | * Relation pointer down to all the recursive deletion steps. |
346 | | */ |
347 | 0 | depRel = table_open(DependRelationId, RowExclusiveLock); |
348 | | |
349 | | /* |
350 | | * Construct a list of objects to delete (ie, the given objects plus |
351 | | * everything directly or indirectly dependent on them). Note that |
352 | | * because we pass the whole objects list as pendingObjects context, we |
353 | | * won't get a failure from trying to delete an object that is internally |
354 | | * dependent on another one in the list; we'll just skip that object and |
355 | | * delete it when we reach its owner. |
356 | | */ |
357 | 0 | targetObjects = new_object_addresses(); |
358 | |
|
359 | 0 | for (i = 0; i < objects->numrefs; i++) |
360 | 0 | { |
361 | 0 | const ObjectAddress *thisobj = objects->refs + i; |
362 | | |
363 | | /* |
364 | | * Acquire deletion lock on each target object. (Ideally the caller |
365 | | * has done this already, but many places are sloppy about it.) |
366 | | */ |
367 | 0 | AcquireDeletionLock(thisobj, flags); |
368 | |
|
369 | 0 | findDependentObjects(thisobj, |
370 | 0 | DEPFLAG_ORIGINAL, |
371 | 0 | flags, |
372 | 0 | NULL, /* empty stack */ |
373 | 0 | targetObjects, |
374 | 0 | objects, |
375 | 0 | &depRel); |
376 | 0 | } |
377 | | |
378 | | /* |
379 | | * Check if deletion is allowed, and report about cascaded deletes. |
380 | | * |
381 | | * If there's exactly one object being deleted, report it the same way as |
382 | | * in performDeletion(), else we have to be vaguer. |
383 | | */ |
384 | 0 | reportDependentObjects(targetObjects, |
385 | 0 | behavior, |
386 | 0 | flags, |
387 | 0 | (objects->numrefs == 1 ? objects->refs : NULL)); |
388 | | |
389 | | /* do the deed */ |
390 | 0 | deleteObjectsInList(targetObjects, &depRel, flags); |
391 | | |
392 | | /* And clean up */ |
393 | 0 | free_object_addresses(targetObjects); |
394 | |
|
395 | 0 | table_close(depRel, RowExclusiveLock); |
396 | 0 | } |
397 | | |
398 | | /* |
399 | | * findDependentObjects - find all objects that depend on 'object' |
400 | | * |
401 | | * For every object that depends on the starting object, acquire a deletion |
402 | | * lock on the object, add it to targetObjects (if not already there), |
403 | | * and recursively find objects that depend on it. An object's dependencies |
404 | | * will be placed into targetObjects before the object itself; this means |
405 | | * that the finished list's order represents a safe deletion order. |
406 | | * |
407 | | * The caller must already have a deletion lock on 'object' itself, |
408 | | * but must not have added it to targetObjects. (Note: there are corner |
409 | | * cases where we won't add the object either, and will also release the |
410 | | * caller-taken lock. This is a bit ugly, but the API is set up this way |
411 | | * to allow easy rechecking of an object's liveness after we lock it. See |
412 | | * notes within the function.) |
413 | | * |
414 | | * When dropping a whole object (subId = 0), we find dependencies for |
415 | | * its sub-objects too. |
416 | | * |
417 | | * object: the object to add to targetObjects and find dependencies on |
418 | | * objflags: flags to be ORed into the object's targetObjects entry |
419 | | * flags: PERFORM_DELETION_xxx flags for the deletion operation as a whole |
420 | | * stack: list of objects being visited in current recursion; topmost item |
421 | | * is the object that we recursed from (NULL for external callers) |
422 | | * targetObjects: list of objects that are scheduled to be deleted |
423 | | * pendingObjects: list of other objects slated for destruction, but |
424 | | * not necessarily in targetObjects yet (can be NULL if none) |
425 | | * *depRel: already opened pg_depend relation |
426 | | * |
427 | | * Note: objflags describes the reason for visiting this particular object |
428 | | * at this time, and is not passed down when recursing. The flags argument |
429 | | * is passed down, since it describes what we're doing overall. |
430 | | */ |
431 | | static void |
432 | | findDependentObjects(const ObjectAddress *object, |
433 | | int objflags, |
434 | | int flags, |
435 | | ObjectAddressStack *stack, |
436 | | ObjectAddresses *targetObjects, |
437 | | const ObjectAddresses *pendingObjects, |
438 | | Relation *depRel) |
439 | 0 | { |
440 | 0 | ScanKeyData key[3]; |
441 | 0 | int nkeys; |
442 | 0 | SysScanDesc scan; |
443 | 0 | HeapTuple tup; |
444 | 0 | ObjectAddress otherObject; |
445 | 0 | ObjectAddress owningObject; |
446 | 0 | ObjectAddress partitionObject; |
447 | 0 | ObjectAddressAndFlags *dependentObjects; |
448 | 0 | int numDependentObjects; |
449 | 0 | int maxDependentObjects; |
450 | 0 | ObjectAddressStack mystack; |
451 | 0 | ObjectAddressExtra extra; |
452 | | |
453 | | /* |
454 | | * If the target object is already being visited in an outer recursion |
455 | | * level, just report the current objflags back to that level and exit. |
456 | | * This is needed to avoid infinite recursion in the face of circular |
457 | | * dependencies. |
458 | | * |
459 | | * The stack check alone would result in dependency loops being broken at |
460 | | * an arbitrary point, ie, the first member object of the loop to be |
461 | | * visited is the last one to be deleted. This is obviously unworkable. |
462 | | * However, the check for internal dependency below guarantees that we |
463 | | * will not break a loop at an internal dependency: if we enter the loop |
464 | | * at an "owned" object we will switch and start at the "owning" object |
465 | | * instead. We could probably hack something up to avoid breaking at an |
466 | | * auto dependency, too, if we had to. However there are no known cases |
467 | | * where that would be necessary. |
468 | | */ |
469 | 0 | if (stack_address_present_add_flags(object, objflags, stack)) |
470 | 0 | return; |
471 | | |
472 | | /* |
473 | | * since this function recurses, it could be driven to stack overflow, |
474 | | * because of the deep dependency tree, not only due to dependency loops. |
475 | | */ |
476 | 0 | check_stack_depth(); |
477 | | |
478 | | /* |
479 | | * It's also possible that the target object has already been completely |
480 | | * processed and put into targetObjects. If so, again we just add the |
481 | | * specified objflags to its entry and return. |
482 | | * |
483 | | * (Note: in these early-exit cases we could release the caller-taken |
484 | | * lock, since the object is presumably now locked multiple times; but it |
485 | | * seems not worth the cycles.) |
486 | | */ |
487 | 0 | if (object_address_present_add_flags(object, objflags, targetObjects)) |
488 | 0 | return; |
489 | | |
490 | | /* |
491 | | * If the target object is pinned, we can just error out immediately; it |
492 | | * won't have any objects recorded as depending on it. |
493 | | */ |
494 | 0 | if (IsPinnedObject(object->classId, object->objectId)) |
495 | 0 | ereport(ERROR, |
496 | 0 | (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), |
497 | 0 | errmsg("cannot drop %s because it is required by the database system", |
498 | 0 | getObjectDescription(object, false)))); |
499 | | |
500 | | /* |
501 | | * The target object might be internally dependent on some other object |
502 | | * (its "owner"), and/or be a member of an extension (also considered its |
503 | | * owner). If so, and if we aren't recursing from the owning object, we |
504 | | * have to transform this deletion request into a deletion request of the |
505 | | * owning object. (We'll eventually recurse back to this object, but the |
506 | | * owning object has to be visited first so it will be deleted after.) The |
507 | | * way to find out about this is to scan the pg_depend entries that show |
508 | | * what this object depends on. |
509 | | */ |
510 | 0 | ScanKeyInit(&key[0], |
511 | 0 | Anum_pg_depend_classid, |
512 | 0 | BTEqualStrategyNumber, F_OIDEQ, |
513 | 0 | ObjectIdGetDatum(object->classId)); |
514 | 0 | ScanKeyInit(&key[1], |
515 | 0 | Anum_pg_depend_objid, |
516 | 0 | BTEqualStrategyNumber, F_OIDEQ, |
517 | 0 | ObjectIdGetDatum(object->objectId)); |
518 | 0 | if (object->objectSubId != 0) |
519 | 0 | { |
520 | | /* Consider only dependencies of this sub-object */ |
521 | 0 | ScanKeyInit(&key[2], |
522 | 0 | Anum_pg_depend_objsubid, |
523 | 0 | BTEqualStrategyNumber, F_INT4EQ, |
524 | 0 | Int32GetDatum(object->objectSubId)); |
525 | 0 | nkeys = 3; |
526 | 0 | } |
527 | 0 | else |
528 | 0 | { |
529 | | /* Consider dependencies of this object and any sub-objects it has */ |
530 | 0 | nkeys = 2; |
531 | 0 | } |
532 | |
|
533 | 0 | scan = systable_beginscan(*depRel, DependDependerIndexId, true, |
534 | 0 | NULL, nkeys, key); |
535 | | |
536 | | /* initialize variables that loop may fill */ |
537 | 0 | memset(&owningObject, 0, sizeof(owningObject)); |
538 | 0 | memset(&partitionObject, 0, sizeof(partitionObject)); |
539 | |
|
540 | 0 | while (HeapTupleIsValid(tup = systable_getnext(scan))) |
541 | 0 | { |
542 | 0 | Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); |
543 | |
|
544 | 0 | otherObject.classId = foundDep->refclassid; |
545 | 0 | otherObject.objectId = foundDep->refobjid; |
546 | 0 | otherObject.objectSubId = foundDep->refobjsubid; |
547 | | |
548 | | /* |
549 | | * When scanning dependencies of a whole object, we may find rows |
550 | | * linking sub-objects of the object to the object itself. (Normally, |
551 | | * such a dependency is implicit, but we must make explicit ones in |
552 | | * some cases involving partitioning.) We must ignore such rows to |
553 | | * avoid infinite recursion. |
554 | | */ |
555 | 0 | if (otherObject.classId == object->classId && |
556 | 0 | otherObject.objectId == object->objectId && |
557 | 0 | object->objectSubId == 0) |
558 | 0 | continue; |
559 | | |
560 | 0 | switch (foundDep->deptype) |
561 | 0 | { |
562 | 0 | case DEPENDENCY_NORMAL: |
563 | 0 | case DEPENDENCY_AUTO: |
564 | 0 | case DEPENDENCY_AUTO_EXTENSION: |
565 | | /* no problem */ |
566 | 0 | break; |
567 | | |
568 | 0 | case DEPENDENCY_EXTENSION: |
569 | | |
570 | | /* |
571 | | * If told to, ignore EXTENSION dependencies altogether. This |
572 | | * flag is normally used to prevent dropping extensions during |
573 | | * temporary-object cleanup, even if a temp object was created |
574 | | * during an extension script. |
575 | | */ |
576 | 0 | if (flags & PERFORM_DELETION_SKIP_EXTENSIONS) |
577 | 0 | break; |
578 | | |
579 | | /* |
580 | | * If the other object is the extension currently being |
581 | | * created/altered, ignore this dependency and continue with |
582 | | * the deletion. This allows dropping of an extension's |
583 | | * objects within the extension's scripts, as well as corner |
584 | | * cases such as dropping a transient object created within |
585 | | * such a script. |
586 | | */ |
587 | 0 | if (creating_extension && |
588 | 0 | otherObject.classId == ExtensionRelationId && |
589 | 0 | otherObject.objectId == CurrentExtensionObject) |
590 | 0 | break; |
591 | | |
592 | | /* Otherwise, treat this like an internal dependency */ |
593 | | /* FALL THRU */ |
594 | | |
595 | 0 | case DEPENDENCY_INTERNAL: |
596 | | |
597 | | /* |
598 | | * This object is part of the internal implementation of |
599 | | * another object, or is part of the extension that is the |
600 | | * other object. We have three cases: |
601 | | * |
602 | | * 1. At the outermost recursion level, we must disallow the |
603 | | * DROP. However, if the owning object is listed in |
604 | | * pendingObjects, just release the caller's lock and return; |
605 | | * we'll eventually complete the DROP when we reach that entry |
606 | | * in the pending list. |
607 | | * |
608 | | * Note: the above statement is true only if this pg_depend |
609 | | * entry still exists by then; in principle, therefore, we |
610 | | * could miss deleting an item the user told us to delete. |
611 | | * However, no inconsistency can result: since we're at outer |
612 | | * level, there is no object depending on this one. |
613 | | */ |
614 | 0 | if (stack == NULL) |
615 | 0 | { |
616 | 0 | if (pendingObjects && |
617 | 0 | object_address_present(&otherObject, pendingObjects)) |
618 | 0 | { |
619 | 0 | systable_endscan(scan); |
620 | | /* need to release caller's lock; see notes below */ |
621 | 0 | ReleaseDeletionLock(object); |
622 | 0 | return; |
623 | 0 | } |
624 | | |
625 | | /* |
626 | | * We postpone actually issuing the error message until |
627 | | * after this loop, so that we can make the behavior |
628 | | * independent of the ordering of pg_depend entries, at |
629 | | * least if there's not more than one INTERNAL and one |
630 | | * EXTENSION dependency. (If there's more, we'll complain |
631 | | * about a random one of them.) Prefer to complain about |
632 | | * EXTENSION, since that's generally a more important |
633 | | * dependency. |
634 | | */ |
635 | 0 | if (!OidIsValid(owningObject.classId) || |
636 | 0 | foundDep->deptype == DEPENDENCY_EXTENSION) |
637 | 0 | owningObject = otherObject; |
638 | 0 | break; |
639 | 0 | } |
640 | | |
641 | | /* |
642 | | * 2. When recursing from the other end of this dependency, |
643 | | * it's okay to continue with the deletion. This holds when |
644 | | * recursing from a whole object that includes the nominal |
645 | | * other end as a component, too. Since there can be more |
646 | | * than one "owning" object, we have to allow matches that are |
647 | | * more than one level down in the stack. |
648 | | */ |
649 | 0 | if (stack_address_present_add_flags(&otherObject, 0, stack)) |
650 | 0 | break; |
651 | | |
652 | | /* |
653 | | * 3. Not all the owning objects have been visited, so |
654 | | * transform this deletion request into a delete of this |
655 | | * owning object. |
656 | | * |
657 | | * First, release caller's lock on this object and get |
658 | | * deletion lock on the owning object. (We must release |
659 | | * caller's lock to avoid deadlock against a concurrent |
660 | | * deletion of the owning object.) |
661 | | */ |
662 | 0 | ReleaseDeletionLock(object); |
663 | 0 | AcquireDeletionLock(&otherObject, 0); |
664 | | |
665 | | /* |
666 | | * The owning object might have been deleted while we waited |
667 | | * to lock it; if so, neither it nor the current object are |
668 | | * interesting anymore. We test this by checking the |
669 | | * pg_depend entry (see notes below). |
670 | | */ |
671 | 0 | if (!systable_recheck_tuple(scan, tup)) |
672 | 0 | { |
673 | 0 | systable_endscan(scan); |
674 | 0 | ReleaseDeletionLock(&otherObject); |
675 | 0 | return; |
676 | 0 | } |
677 | | |
678 | | /* |
679 | | * One way or the other, we're done with the scan; might as |
680 | | * well close it down before recursing, to reduce peak |
681 | | * resource consumption. |
682 | | */ |
683 | 0 | systable_endscan(scan); |
684 | | |
685 | | /* |
686 | | * Okay, recurse to the owning object instead of proceeding. |
687 | | * |
688 | | * We do not need to stack the current object; we want the |
689 | | * traversal order to be as if the original reference had |
690 | | * linked to the owning object instead of this one. |
691 | | * |
692 | | * The dependency type is a "reverse" dependency: we need to |
693 | | * delete the owning object if this one is to be deleted, but |
694 | | * this linkage is never a reason for an automatic deletion. |
695 | | */ |
696 | 0 | findDependentObjects(&otherObject, |
697 | 0 | DEPFLAG_REVERSE, |
698 | 0 | flags, |
699 | 0 | stack, |
700 | 0 | targetObjects, |
701 | 0 | pendingObjects, |
702 | 0 | depRel); |
703 | | |
704 | | /* |
705 | | * The current target object should have been added to |
706 | | * targetObjects while processing the owning object; but it |
707 | | * probably got only the flag bits associated with the |
708 | | * dependency we're looking at. We need to add the objflags |
709 | | * that were passed to this recursion level, too, else we may |
710 | | * get a bogus failure in reportDependentObjects (if, for |
711 | | * example, we were called due to a partition dependency). |
712 | | * |
713 | | * If somehow the current object didn't get scheduled for |
714 | | * deletion, bleat. (That would imply that somebody deleted |
715 | | * this dependency record before the recursion got to it.) |
716 | | * Another idea would be to reacquire lock on the current |
717 | | * object and resume trying to delete it, but it seems not |
718 | | * worth dealing with the race conditions inherent in that. |
719 | | */ |
720 | 0 | if (!object_address_present_add_flags(object, objflags, |
721 | 0 | targetObjects)) |
722 | 0 | elog(ERROR, "deletion of owning object %s failed to delete %s", |
723 | 0 | getObjectDescription(&otherObject, false), |
724 | 0 | getObjectDescription(object, false)); |
725 | | |
726 | | /* And we're done here. */ |
727 | 0 | return; |
728 | | |
729 | 0 | case DEPENDENCY_PARTITION_PRI: |
730 | | |
731 | | /* |
732 | | * Remember that this object has a partition-type dependency. |
733 | | * After the dependency scan, we'll complain if we didn't find |
734 | | * a reason to delete one of its partition dependencies. |
735 | | */ |
736 | 0 | objflags |= DEPFLAG_IS_PART; |
737 | | |
738 | | /* |
739 | | * Also remember the primary partition owner, for error |
740 | | * messages. If there are multiple primary owners (which |
741 | | * there should not be), we'll report a random one of them. |
742 | | */ |
743 | 0 | partitionObject = otherObject; |
744 | 0 | break; |
745 | | |
746 | 0 | case DEPENDENCY_PARTITION_SEC: |
747 | | |
748 | | /* |
749 | | * Only use secondary partition owners in error messages if we |
750 | | * find no primary owner (which probably shouldn't happen). |
751 | | */ |
752 | 0 | if (!(objflags & DEPFLAG_IS_PART)) |
753 | 0 | partitionObject = otherObject; |
754 | | |
755 | | /* |
756 | | * Remember that this object has a partition-type dependency. |
757 | | * After the dependency scan, we'll complain if we didn't find |
758 | | * a reason to delete one of its partition dependencies. |
759 | | */ |
760 | 0 | objflags |= DEPFLAG_IS_PART; |
761 | 0 | break; |
762 | | |
763 | 0 | default: |
764 | 0 | elog(ERROR, "unrecognized dependency type '%c' for %s", |
765 | 0 | foundDep->deptype, getObjectDescription(object, false)); |
766 | 0 | break; |
767 | 0 | } |
768 | 0 | } |
769 | | |
770 | 0 | systable_endscan(scan); |
771 | | |
772 | | /* |
773 | | * If we found an INTERNAL or EXTENSION dependency when we're at outer |
774 | | * level, complain about it now. If we also found a PARTITION dependency, |
775 | | * we prefer to report the PARTITION dependency. This is arbitrary but |
776 | | * seems to be more useful in practice. |
777 | | */ |
778 | 0 | if (OidIsValid(owningObject.classId)) |
779 | 0 | { |
780 | 0 | char *otherObjDesc; |
781 | |
|
782 | 0 | if (OidIsValid(partitionObject.classId)) |
783 | 0 | otherObjDesc = getObjectDescription(&partitionObject, false); |
784 | 0 | else |
785 | 0 | otherObjDesc = getObjectDescription(&owningObject, false); |
786 | |
|
787 | 0 | ereport(ERROR, |
788 | 0 | (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), |
789 | 0 | errmsg("cannot drop %s because %s requires it", |
790 | 0 | getObjectDescription(object, false), otherObjDesc), |
791 | 0 | errhint("You can drop %s instead.", otherObjDesc))); |
792 | 0 | } |
793 | | |
794 | | /* |
795 | | * Next, identify all objects that directly depend on the current object. |
796 | | * To ensure predictable deletion order, we collect them up in |
797 | | * dependentObjects and sort the list before actually recursing. (The |
798 | | * deletion order would be valid in any case, but doing this ensures |
799 | | * consistent output from DROP CASCADE commands, which is helpful for |
800 | | * regression testing.) |
801 | | */ |
802 | 0 | maxDependentObjects = 128; /* arbitrary initial allocation */ |
803 | 0 | dependentObjects = (ObjectAddressAndFlags *) |
804 | 0 | palloc(maxDependentObjects * sizeof(ObjectAddressAndFlags)); |
805 | 0 | numDependentObjects = 0; |
806 | |
|
807 | 0 | ScanKeyInit(&key[0], |
808 | 0 | Anum_pg_depend_refclassid, |
809 | 0 | BTEqualStrategyNumber, F_OIDEQ, |
810 | 0 | ObjectIdGetDatum(object->classId)); |
811 | 0 | ScanKeyInit(&key[1], |
812 | 0 | Anum_pg_depend_refobjid, |
813 | 0 | BTEqualStrategyNumber, F_OIDEQ, |
814 | 0 | ObjectIdGetDatum(object->objectId)); |
815 | 0 | if (object->objectSubId != 0) |
816 | 0 | { |
817 | 0 | ScanKeyInit(&key[2], |
818 | 0 | Anum_pg_depend_refobjsubid, |
819 | 0 | BTEqualStrategyNumber, F_INT4EQ, |
820 | 0 | Int32GetDatum(object->objectSubId)); |
821 | 0 | nkeys = 3; |
822 | 0 | } |
823 | 0 | else |
824 | 0 | nkeys = 2; |
825 | |
|
826 | 0 | scan = systable_beginscan(*depRel, DependReferenceIndexId, true, |
827 | 0 | NULL, nkeys, key); |
828 | |
|
829 | 0 | while (HeapTupleIsValid(tup = systable_getnext(scan))) |
830 | 0 | { |
831 | 0 | Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); |
832 | 0 | int subflags; |
833 | |
|
834 | 0 | otherObject.classId = foundDep->classid; |
835 | 0 | otherObject.objectId = foundDep->objid; |
836 | 0 | otherObject.objectSubId = foundDep->objsubid; |
837 | | |
838 | | /* |
839 | | * If what we found is a sub-object of the current object, just ignore |
840 | | * it. (Normally, such a dependency is implicit, but we must make |
841 | | * explicit ones in some cases involving partitioning.) |
842 | | */ |
843 | 0 | if (otherObject.classId == object->classId && |
844 | 0 | otherObject.objectId == object->objectId && |
845 | 0 | object->objectSubId == 0) |
846 | 0 | continue; |
847 | | |
848 | | /* |
849 | | * Must lock the dependent object before recursing to it. |
850 | | */ |
851 | 0 | AcquireDeletionLock(&otherObject, 0); |
852 | | |
853 | | /* |
854 | | * The dependent object might have been deleted while we waited to |
855 | | * lock it; if so, we don't need to do anything more with it. We can |
856 | | * test this cheaply and independently of the object's type by seeing |
857 | | * if the pg_depend tuple we are looking at is still live. (If the |
858 | | * object got deleted, the tuple would have been deleted too.) |
859 | | */ |
860 | 0 | if (!systable_recheck_tuple(scan, tup)) |
861 | 0 | { |
862 | | /* release the now-useless lock */ |
863 | 0 | ReleaseDeletionLock(&otherObject); |
864 | | /* and continue scanning for dependencies */ |
865 | 0 | continue; |
866 | 0 | } |
867 | | |
868 | | /* |
869 | | * We do need to delete it, so identify objflags to be passed down, |
870 | | * which depend on the dependency type. |
871 | | */ |
872 | 0 | switch (foundDep->deptype) |
873 | 0 | { |
874 | 0 | case DEPENDENCY_NORMAL: |
875 | 0 | subflags = DEPFLAG_NORMAL; |
876 | 0 | break; |
877 | 0 | case DEPENDENCY_AUTO: |
878 | 0 | case DEPENDENCY_AUTO_EXTENSION: |
879 | 0 | subflags = DEPFLAG_AUTO; |
880 | 0 | break; |
881 | 0 | case DEPENDENCY_INTERNAL: |
882 | 0 | subflags = DEPFLAG_INTERNAL; |
883 | 0 | break; |
884 | 0 | case DEPENDENCY_PARTITION_PRI: |
885 | 0 | case DEPENDENCY_PARTITION_SEC: |
886 | 0 | subflags = DEPFLAG_PARTITION; |
887 | 0 | break; |
888 | 0 | case DEPENDENCY_EXTENSION: |
889 | 0 | subflags = DEPFLAG_EXTENSION; |
890 | 0 | break; |
891 | 0 | default: |
892 | 0 | elog(ERROR, "unrecognized dependency type '%c' for %s", |
893 | 0 | foundDep->deptype, getObjectDescription(object, false)); |
894 | 0 | subflags = 0; /* keep compiler quiet */ |
895 | 0 | break; |
896 | 0 | } |
897 | | |
898 | | /* And add it to the pending-objects list */ |
899 | 0 | if (numDependentObjects >= maxDependentObjects) |
900 | 0 | { |
901 | | /* enlarge array if needed */ |
902 | 0 | maxDependentObjects *= 2; |
903 | 0 | dependentObjects = (ObjectAddressAndFlags *) |
904 | 0 | repalloc(dependentObjects, |
905 | 0 | maxDependentObjects * sizeof(ObjectAddressAndFlags)); |
906 | 0 | } |
907 | |
|
908 | 0 | dependentObjects[numDependentObjects].obj = otherObject; |
909 | 0 | dependentObjects[numDependentObjects].subflags = subflags; |
910 | 0 | numDependentObjects++; |
911 | 0 | } |
912 | | |
913 | 0 | systable_endscan(scan); |
914 | | |
915 | | /* |
916 | | * Now we can sort the dependent objects into a stable visitation order. |
917 | | * It's safe to use object_address_comparator here since the obj field is |
918 | | * first within ObjectAddressAndFlags. |
919 | | */ |
920 | 0 | if (numDependentObjects > 1) |
921 | 0 | qsort(dependentObjects, numDependentObjects, |
922 | 0 | sizeof(ObjectAddressAndFlags), |
923 | 0 | object_address_comparator); |
924 | | |
925 | | /* |
926 | | * Now recurse to the dependent objects. We must visit them first since |
927 | | * they have to be deleted before the current object. |
928 | | */ |
929 | 0 | mystack.object = object; /* set up a new stack level */ |
930 | 0 | mystack.flags = objflags; |
931 | 0 | mystack.next = stack; |
932 | |
|
933 | 0 | for (int i = 0; i < numDependentObjects; i++) |
934 | 0 | { |
935 | 0 | ObjectAddressAndFlags *depObj = dependentObjects + i; |
936 | |
|
937 | 0 | findDependentObjects(&depObj->obj, |
938 | 0 | depObj->subflags, |
939 | 0 | flags, |
940 | 0 | &mystack, |
941 | 0 | targetObjects, |
942 | 0 | pendingObjects, |
943 | 0 | depRel); |
944 | 0 | } |
945 | |
|
946 | 0 | pfree(dependentObjects); |
947 | | |
948 | | /* |
949 | | * Finally, we can add the target object to targetObjects. Be careful to |
950 | | * include any flags that were passed back down to us from inner recursion |
951 | | * levels. Record the "dependee" as being either the most important |
952 | | * partition owner if there is one, else the object we recursed from, if |
953 | | * any. (The logic in reportDependentObjects() is such that it can only |
954 | | * need one of those objects.) |
955 | | */ |
956 | 0 | extra.flags = mystack.flags; |
957 | 0 | if (extra.flags & DEPFLAG_IS_PART) |
958 | 0 | extra.dependee = partitionObject; |
959 | 0 | else if (stack) |
960 | 0 | extra.dependee = *stack->object; |
961 | 0 | else |
962 | 0 | memset(&extra.dependee, 0, sizeof(extra.dependee)); |
963 | 0 | add_exact_object_address_extra(object, &extra, targetObjects); |
964 | 0 | } |
965 | | |
966 | | /* |
967 | | * reportDependentObjects - report about dependencies, and fail if RESTRICT |
968 | | * |
969 | | * Tell the user about dependent objects that we are going to delete |
970 | | * (or would need to delete, but are prevented by RESTRICT mode); |
971 | | * then error out if there are any and it's not CASCADE mode. |
972 | | * |
973 | | * targetObjects: list of objects that are scheduled to be deleted |
974 | | * behavior: RESTRICT or CASCADE |
975 | | * flags: other flags for the deletion operation |
976 | | * origObject: base object of deletion, or NULL if not available |
977 | | * (the latter case occurs in DROP OWNED) |
978 | | */ |
979 | | static void |
980 | | reportDependentObjects(const ObjectAddresses *targetObjects, |
981 | | DropBehavior behavior, |
982 | | int flags, |
983 | | const ObjectAddress *origObject) |
984 | 0 | { |
985 | 0 | int msglevel = (flags & PERFORM_DELETION_QUIETLY) ? DEBUG2 : NOTICE; |
986 | 0 | bool ok = true; |
987 | 0 | StringInfoData clientdetail; |
988 | 0 | StringInfoData logdetail; |
989 | 0 | int numReportedClient = 0; |
990 | 0 | int numNotReportedClient = 0; |
991 | 0 | int i; |
992 | | |
993 | | /* |
994 | | * If we need to delete any partition-dependent objects, make sure that |
995 | | * we're deleting at least one of their partition dependencies, too. That |
996 | | * can be detected by checking that we reached them by a PARTITION |
997 | | * dependency at some point. |
998 | | * |
999 | | * We just report the first such object, as in most cases the only way to |
1000 | | * trigger this complaint is to explicitly try to delete one partition of |
1001 | | * a partitioned object. |
1002 | | */ |
1003 | 0 | for (i = 0; i < targetObjects->numrefs; i++) |
1004 | 0 | { |
1005 | 0 | const ObjectAddressExtra *extra = &targetObjects->extras[i]; |
1006 | |
|
1007 | 0 | if ((extra->flags & DEPFLAG_IS_PART) && |
1008 | 0 | !(extra->flags & DEPFLAG_PARTITION)) |
1009 | 0 | { |
1010 | 0 | const ObjectAddress *object = &targetObjects->refs[i]; |
1011 | 0 | char *otherObjDesc = getObjectDescription(&extra->dependee, |
1012 | 0 | false); |
1013 | |
|
1014 | 0 | ereport(ERROR, |
1015 | 0 | (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), |
1016 | 0 | errmsg("cannot drop %s because %s requires it", |
1017 | 0 | getObjectDescription(object, false), otherObjDesc), |
1018 | 0 | errhint("You can drop %s instead.", otherObjDesc))); |
1019 | 0 | } |
1020 | 0 | } |
1021 | | |
1022 | | /* |
1023 | | * If no error is to be thrown, and the msglevel is too low to be shown to |
1024 | | * either client or server log, there's no need to do any of the rest of |
1025 | | * the work. |
1026 | | */ |
1027 | 0 | if (behavior == DROP_CASCADE && |
1028 | 0 | !message_level_is_interesting(msglevel)) |
1029 | 0 | return; |
1030 | | |
1031 | | /* |
1032 | | * We limit the number of dependencies reported to the client to |
1033 | | * MAX_REPORTED_DEPS, since client software may not deal well with |
1034 | | * enormous error strings. The server log always gets a full report. |
1035 | | */ |
1036 | 0 | #define MAX_REPORTED_DEPS 100 |
1037 | | |
1038 | 0 | initStringInfo(&clientdetail); |
1039 | 0 | initStringInfo(&logdetail); |
1040 | | |
1041 | | /* |
1042 | | * We process the list back to front (ie, in dependency order not deletion |
1043 | | * order), since this makes for a more understandable display. |
1044 | | */ |
1045 | 0 | for (i = targetObjects->numrefs - 1; i >= 0; i--) |
1046 | 0 | { |
1047 | 0 | const ObjectAddress *obj = &targetObjects->refs[i]; |
1048 | 0 | const ObjectAddressExtra *extra = &targetObjects->extras[i]; |
1049 | 0 | char *objDesc; |
1050 | | |
1051 | | /* Ignore the original deletion target(s) */ |
1052 | 0 | if (extra->flags & DEPFLAG_ORIGINAL) |
1053 | 0 | continue; |
1054 | | |
1055 | | /* Also ignore sub-objects; we'll report the whole object elsewhere */ |
1056 | 0 | if (extra->flags & DEPFLAG_SUBOBJECT) |
1057 | 0 | continue; |
1058 | | |
1059 | 0 | objDesc = getObjectDescription(obj, false); |
1060 | | |
1061 | | /* An object being dropped concurrently doesn't need to be reported */ |
1062 | 0 | if (objDesc == NULL) |
1063 | 0 | continue; |
1064 | | |
1065 | | /* |
1066 | | * If, at any stage of the recursive search, we reached the object via |
1067 | | * an AUTO, INTERNAL, PARTITION, or EXTENSION dependency, then it's |
1068 | | * okay to delete it even in RESTRICT mode. |
1069 | | */ |
1070 | 0 | if (extra->flags & (DEPFLAG_AUTO | |
1071 | 0 | DEPFLAG_INTERNAL | |
1072 | 0 | DEPFLAG_PARTITION | |
1073 | 0 | DEPFLAG_EXTENSION)) |
1074 | 0 | { |
1075 | | /* |
1076 | | * auto-cascades are reported at DEBUG2, not msglevel. We don't |
1077 | | * try to combine them with the regular message because the |
1078 | | * results are too confusing when client_min_messages and |
1079 | | * log_min_messages are different. |
1080 | | */ |
1081 | 0 | ereport(DEBUG2, |
1082 | 0 | (errmsg_internal("drop auto-cascades to %s", |
1083 | 0 | objDesc))); |
1084 | 0 | } |
1085 | 0 | else if (behavior == DROP_RESTRICT) |
1086 | 0 | { |
1087 | 0 | char *otherDesc = getObjectDescription(&extra->dependee, |
1088 | 0 | false); |
1089 | |
|
1090 | 0 | if (otherDesc) |
1091 | 0 | { |
1092 | 0 | if (numReportedClient < MAX_REPORTED_DEPS) |
1093 | 0 | { |
1094 | | /* separate entries with a newline */ |
1095 | 0 | if (clientdetail.len != 0) |
1096 | 0 | appendStringInfoChar(&clientdetail, '\n'); |
1097 | 0 | appendStringInfo(&clientdetail, _("%s depends on %s"), |
1098 | 0 | objDesc, otherDesc); |
1099 | 0 | numReportedClient++; |
1100 | 0 | } |
1101 | 0 | else |
1102 | 0 | numNotReportedClient++; |
1103 | | /* separate entries with a newline */ |
1104 | 0 | if (logdetail.len != 0) |
1105 | 0 | appendStringInfoChar(&logdetail, '\n'); |
1106 | 0 | appendStringInfo(&logdetail, _("%s depends on %s"), |
1107 | 0 | objDesc, otherDesc); |
1108 | 0 | pfree(otherDesc); |
1109 | 0 | } |
1110 | 0 | else |
1111 | 0 | numNotReportedClient++; |
1112 | 0 | ok = false; |
1113 | 0 | } |
1114 | 0 | else |
1115 | 0 | { |
1116 | 0 | if (numReportedClient < MAX_REPORTED_DEPS) |
1117 | 0 | { |
1118 | | /* separate entries with a newline */ |
1119 | 0 | if (clientdetail.len != 0) |
1120 | 0 | appendStringInfoChar(&clientdetail, '\n'); |
1121 | 0 | appendStringInfo(&clientdetail, _("drop cascades to %s"), |
1122 | 0 | objDesc); |
1123 | 0 | numReportedClient++; |
1124 | 0 | } |
1125 | 0 | else |
1126 | 0 | numNotReportedClient++; |
1127 | | /* separate entries with a newline */ |
1128 | 0 | if (logdetail.len != 0) |
1129 | 0 | appendStringInfoChar(&logdetail, '\n'); |
1130 | 0 | appendStringInfo(&logdetail, _("drop cascades to %s"), |
1131 | 0 | objDesc); |
1132 | 0 | } |
1133 | | |
1134 | 0 | pfree(objDesc); |
1135 | 0 | } |
1136 | | |
1137 | 0 | if (numNotReportedClient > 0) |
1138 | 0 | appendStringInfo(&clientdetail, ngettext("\nand %d other object " |
1139 | 0 | "(see server log for list)", |
1140 | 0 | "\nand %d other objects " |
1141 | 0 | "(see server log for list)", |
1142 | 0 | numNotReportedClient), |
1143 | 0 | numNotReportedClient); |
1144 | |
|
1145 | 0 | if (!ok) |
1146 | 0 | { |
1147 | 0 | if (origObject) |
1148 | 0 | ereport(ERROR, |
1149 | 0 | (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), |
1150 | 0 | errmsg("cannot drop %s because other objects depend on it", |
1151 | 0 | getObjectDescription(origObject, false)), |
1152 | 0 | errdetail_internal("%s", clientdetail.data), |
1153 | 0 | errdetail_log("%s", logdetail.data), |
1154 | 0 | errhint("Use DROP ... CASCADE to drop the dependent objects too."))); |
1155 | 0 | else |
1156 | 0 | ereport(ERROR, |
1157 | 0 | (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), |
1158 | 0 | errmsg("cannot drop desired object(s) because other objects depend on them"), |
1159 | 0 | errdetail_internal("%s", clientdetail.data), |
1160 | 0 | errdetail_log("%s", logdetail.data), |
1161 | 0 | errhint("Use DROP ... CASCADE to drop the dependent objects too."))); |
1162 | 0 | } |
1163 | 0 | else if (numReportedClient > 1) |
1164 | 0 | { |
1165 | 0 | ereport(msglevel, |
1166 | 0 | (errmsg_plural("drop cascades to %d other object", |
1167 | 0 | "drop cascades to %d other objects", |
1168 | 0 | numReportedClient + numNotReportedClient, |
1169 | 0 | numReportedClient + numNotReportedClient), |
1170 | 0 | errdetail_internal("%s", clientdetail.data), |
1171 | 0 | errdetail_log("%s", logdetail.data))); |
1172 | 0 | } |
1173 | 0 | else if (numReportedClient == 1) |
1174 | 0 | { |
1175 | | /* we just use the single item as-is */ |
1176 | 0 | ereport(msglevel, |
1177 | 0 | (errmsg_internal("%s", clientdetail.data))); |
1178 | 0 | } |
1179 | | |
1180 | 0 | pfree(clientdetail.data); |
1181 | 0 | pfree(logdetail.data); |
1182 | 0 | } |
1183 | | |
1184 | | /* |
1185 | | * Drop an object by OID. Works for most catalogs, if no special processing |
1186 | | * is needed. |
1187 | | */ |
1188 | | static void |
1189 | | DropObjectById(const ObjectAddress *object) |
1190 | 0 | { |
1191 | 0 | int cacheId; |
1192 | 0 | Relation rel; |
1193 | 0 | HeapTuple tup; |
1194 | |
|
1195 | 0 | cacheId = get_object_catcache_oid(object->classId); |
1196 | |
|
1197 | 0 | rel = table_open(object->classId, RowExclusiveLock); |
1198 | | |
1199 | | /* |
1200 | | * Use the system cache for the oid column, if one exists. |
1201 | | */ |
1202 | 0 | if (cacheId >= 0) |
1203 | 0 | { |
1204 | 0 | tup = SearchSysCache1(cacheId, ObjectIdGetDatum(object->objectId)); |
1205 | 0 | if (!HeapTupleIsValid(tup)) |
1206 | 0 | elog(ERROR, "cache lookup failed for %s %u", |
1207 | 0 | get_object_class_descr(object->classId), object->objectId); |
1208 | | |
1209 | 0 | CatalogTupleDelete(rel, &tup->t_self); |
1210 | |
|
1211 | 0 | ReleaseSysCache(tup); |
1212 | 0 | } |
1213 | 0 | else |
1214 | 0 | { |
1215 | 0 | ScanKeyData skey[1]; |
1216 | 0 | SysScanDesc scan; |
1217 | |
|
1218 | 0 | ScanKeyInit(&skey[0], |
1219 | 0 | get_object_attnum_oid(object->classId), |
1220 | 0 | BTEqualStrategyNumber, F_OIDEQ, |
1221 | 0 | ObjectIdGetDatum(object->objectId)); |
1222 | |
|
1223 | 0 | scan = systable_beginscan(rel, get_object_oid_index(object->classId), true, |
1224 | 0 | NULL, 1, skey); |
1225 | | |
1226 | | /* we expect exactly one match */ |
1227 | 0 | tup = systable_getnext(scan); |
1228 | 0 | if (!HeapTupleIsValid(tup)) |
1229 | 0 | elog(ERROR, "could not find tuple for %s %u", |
1230 | 0 | get_object_class_descr(object->classId), object->objectId); |
1231 | | |
1232 | 0 | CatalogTupleDelete(rel, &tup->t_self); |
1233 | |
|
1234 | 0 | systable_endscan(scan); |
1235 | 0 | } |
1236 | | |
1237 | 0 | table_close(rel, RowExclusiveLock); |
1238 | 0 | } |
1239 | | |
1240 | | /* |
1241 | | * deleteOneObject: delete a single object for performDeletion. |
1242 | | * |
1243 | | * *depRel is the already-open pg_depend relation. |
1244 | | */ |
1245 | | static void |
1246 | | deleteOneObject(const ObjectAddress *object, Relation *depRel, int flags) |
1247 | 0 | { |
1248 | 0 | ScanKeyData key[3]; |
1249 | 0 | int nkeys; |
1250 | 0 | SysScanDesc scan; |
1251 | 0 | HeapTuple tup; |
1252 | | |
1253 | | /* DROP hook of the objects being removed */ |
1254 | 0 | InvokeObjectDropHookArg(object->classId, object->objectId, |
1255 | 0 | object->objectSubId, flags); |
1256 | | |
1257 | | /* |
1258 | | * Close depRel if we are doing a drop concurrently. The object deletion |
1259 | | * subroutine will commit the current transaction, so we can't keep the |
1260 | | * relation open across doDeletion(). |
1261 | | */ |
1262 | 0 | if (flags & PERFORM_DELETION_CONCURRENTLY) |
1263 | 0 | table_close(*depRel, RowExclusiveLock); |
1264 | | |
1265 | | /* |
1266 | | * Delete the object itself, in an object-type-dependent way. |
1267 | | * |
1268 | | * We used to do this after removing the outgoing dependency links, but it |
1269 | | * seems just as reasonable to do it beforehand. In the concurrent case |
1270 | | * we *must* do it in this order, because we can't make any transactional |
1271 | | * updates before calling doDeletion() --- they'd get committed right |
1272 | | * away, which is not cool if the deletion then fails. |
1273 | | */ |
1274 | 0 | doDeletion(object, flags); |
1275 | | |
1276 | | /* |
1277 | | * Reopen depRel if we closed it above |
1278 | | */ |
1279 | 0 | if (flags & PERFORM_DELETION_CONCURRENTLY) |
1280 | 0 | *depRel = table_open(DependRelationId, RowExclusiveLock); |
1281 | | |
1282 | | /* |
1283 | | * Now remove any pg_depend records that link from this object to others. |
1284 | | * (Any records linking to this object should be gone already.) |
1285 | | * |
1286 | | * When dropping a whole object (subId = 0), remove all pg_depend records |
1287 | | * for its sub-objects too. |
1288 | | */ |
1289 | 0 | ScanKeyInit(&key[0], |
1290 | 0 | Anum_pg_depend_classid, |
1291 | 0 | BTEqualStrategyNumber, F_OIDEQ, |
1292 | 0 | ObjectIdGetDatum(object->classId)); |
1293 | 0 | ScanKeyInit(&key[1], |
1294 | 0 | Anum_pg_depend_objid, |
1295 | 0 | BTEqualStrategyNumber, F_OIDEQ, |
1296 | 0 | ObjectIdGetDatum(object->objectId)); |
1297 | 0 | if (object->objectSubId != 0) |
1298 | 0 | { |
1299 | 0 | ScanKeyInit(&key[2], |
1300 | 0 | Anum_pg_depend_objsubid, |
1301 | 0 | BTEqualStrategyNumber, F_INT4EQ, |
1302 | 0 | Int32GetDatum(object->objectSubId)); |
1303 | 0 | nkeys = 3; |
1304 | 0 | } |
1305 | 0 | else |
1306 | 0 | nkeys = 2; |
1307 | |
|
1308 | 0 | scan = systable_beginscan(*depRel, DependDependerIndexId, true, |
1309 | 0 | NULL, nkeys, key); |
1310 | |
|
1311 | 0 | while (HeapTupleIsValid(tup = systable_getnext(scan))) |
1312 | 0 | { |
1313 | 0 | CatalogTupleDelete(*depRel, &tup->t_self); |
1314 | 0 | } |
1315 | |
|
1316 | 0 | systable_endscan(scan); |
1317 | | |
1318 | | /* |
1319 | | * Delete shared dependency references related to this object. Again, if |
1320 | | * subId = 0, remove records for sub-objects too. |
1321 | | */ |
1322 | 0 | deleteSharedDependencyRecordsFor(object->classId, object->objectId, |
1323 | 0 | object->objectSubId); |
1324 | | |
1325 | | |
1326 | | /* |
1327 | | * Delete any comments, security labels, or initial privileges associated |
1328 | | * with this object. (This is a convenient place to do these things, |
1329 | | * rather than having every object type know to do it.) As above, all |
1330 | | * these functions must remove records for sub-objects too if the subid is |
1331 | | * zero. |
1332 | | */ |
1333 | 0 | DeleteComments(object->objectId, object->classId, object->objectSubId); |
1334 | 0 | DeleteSecurityLabel(object); |
1335 | 0 | DeleteInitPrivs(object); |
1336 | | |
1337 | | /* |
1338 | | * CommandCounterIncrement here to ensure that preceding changes are all |
1339 | | * visible to the next deletion step. |
1340 | | */ |
1341 | 0 | CommandCounterIncrement(); |
1342 | | |
1343 | | /* |
1344 | | * And we're done! |
1345 | | */ |
1346 | 0 | } |
1347 | | |
1348 | | /* |
1349 | | * doDeletion: actually delete a single object |
1350 | | */ |
1351 | | static void |
1352 | | doDeletion(const ObjectAddress *object, int flags) |
1353 | 0 | { |
1354 | 0 | switch (object->classId) |
1355 | 0 | { |
1356 | 0 | case RelationRelationId: |
1357 | 0 | { |
1358 | 0 | char relKind = get_rel_relkind(object->objectId); |
1359 | |
|
1360 | 0 | if (relKind == RELKIND_INDEX || |
1361 | 0 | relKind == RELKIND_PARTITIONED_INDEX) |
1362 | 0 | { |
1363 | 0 | bool concurrent = ((flags & PERFORM_DELETION_CONCURRENTLY) != 0); |
1364 | 0 | bool concurrent_lock_mode = ((flags & PERFORM_DELETION_CONCURRENT_LOCK) != 0); |
1365 | |
|
1366 | 0 | Assert(object->objectSubId == 0); |
1367 | 0 | index_drop(object->objectId, concurrent, concurrent_lock_mode); |
1368 | 0 | } |
1369 | 0 | else |
1370 | 0 | { |
1371 | 0 | if (object->objectSubId != 0) |
1372 | 0 | RemoveAttributeById(object->objectId, |
1373 | 0 | object->objectSubId); |
1374 | 0 | else |
1375 | 0 | heap_drop_with_catalog(object->objectId); |
1376 | 0 | } |
1377 | | |
1378 | | /* |
1379 | | * for a sequence, in addition to dropping the heap, also |
1380 | | * delete pg_sequence tuple |
1381 | | */ |
1382 | 0 | if (relKind == RELKIND_SEQUENCE) |
1383 | 0 | DeleteSequenceTuple(object->objectId); |
1384 | 0 | break; |
1385 | 0 | } |
1386 | | |
1387 | 0 | case ProcedureRelationId: |
1388 | 0 | RemoveFunctionById(object->objectId); |
1389 | 0 | break; |
1390 | | |
1391 | 0 | case TypeRelationId: |
1392 | 0 | RemoveTypeById(object->objectId); |
1393 | 0 | break; |
1394 | | |
1395 | 0 | case ConstraintRelationId: |
1396 | 0 | RemoveConstraintById(object->objectId); |
1397 | 0 | break; |
1398 | | |
1399 | 0 | case AttrDefaultRelationId: |
1400 | 0 | RemoveAttrDefaultById(object->objectId); |
1401 | 0 | break; |
1402 | | |
1403 | 0 | case LargeObjectRelationId: |
1404 | 0 | LargeObjectDrop(object->objectId); |
1405 | 0 | break; |
1406 | | |
1407 | 0 | case OperatorRelationId: |
1408 | 0 | RemoveOperatorById(object->objectId); |
1409 | 0 | break; |
1410 | | |
1411 | 0 | case RewriteRelationId: |
1412 | 0 | RemoveRewriteRuleById(object->objectId); |
1413 | 0 | break; |
1414 | | |
1415 | 0 | case TriggerRelationId: |
1416 | 0 | RemoveTriggerById(object->objectId); |
1417 | 0 | break; |
1418 | | |
1419 | 0 | case StatisticExtRelationId: |
1420 | 0 | RemoveStatisticsById(object->objectId); |
1421 | 0 | break; |
1422 | | |
1423 | 0 | case TSConfigRelationId: |
1424 | 0 | RemoveTSConfigurationById(object->objectId); |
1425 | 0 | break; |
1426 | | |
1427 | 0 | case ExtensionRelationId: |
1428 | 0 | RemoveExtensionById(object->objectId); |
1429 | 0 | break; |
1430 | | |
1431 | 0 | case PolicyRelationId: |
1432 | 0 | RemovePolicyById(object->objectId); |
1433 | 0 | break; |
1434 | | |
1435 | 0 | case PublicationNamespaceRelationId: |
1436 | 0 | RemovePublicationSchemaById(object->objectId); |
1437 | 0 | break; |
1438 | | |
1439 | 0 | case PublicationRelRelationId: |
1440 | 0 | RemovePublicationRelById(object->objectId); |
1441 | 0 | break; |
1442 | | |
1443 | 0 | case PublicationRelationId: |
1444 | 0 | RemovePublicationById(object->objectId); |
1445 | 0 | break; |
1446 | | |
1447 | 0 | case CastRelationId: |
1448 | 0 | case CollationRelationId: |
1449 | 0 | case ConversionRelationId: |
1450 | 0 | case LanguageRelationId: |
1451 | 0 | case OperatorClassRelationId: |
1452 | 0 | case OperatorFamilyRelationId: |
1453 | 0 | case AccessMethodRelationId: |
1454 | 0 | case AccessMethodOperatorRelationId: |
1455 | 0 | case AccessMethodProcedureRelationId: |
1456 | 0 | case NamespaceRelationId: |
1457 | 0 | case TSParserRelationId: |
1458 | 0 | case TSDictionaryRelationId: |
1459 | 0 | case TSTemplateRelationId: |
1460 | 0 | case ForeignDataWrapperRelationId: |
1461 | 0 | case ForeignServerRelationId: |
1462 | 0 | case UserMappingRelationId: |
1463 | 0 | case DefaultAclRelationId: |
1464 | 0 | case EventTriggerRelationId: |
1465 | 0 | case TransformRelationId: |
1466 | 0 | case AuthMemRelationId: |
1467 | 0 | DropObjectById(object); |
1468 | 0 | break; |
1469 | | |
1470 | | /* |
1471 | | * These global object types are not supported here. |
1472 | | */ |
1473 | 0 | case AuthIdRelationId: |
1474 | 0 | case DatabaseRelationId: |
1475 | 0 | case TableSpaceRelationId: |
1476 | 0 | case SubscriptionRelationId: |
1477 | 0 | case ParameterAclRelationId: |
1478 | 0 | elog(ERROR, "global objects cannot be deleted by doDeletion"); |
1479 | 0 | break; |
1480 | | |
1481 | 0 | default: |
1482 | 0 | elog(ERROR, "unsupported object class: %u", object->classId); |
1483 | 0 | } |
1484 | 0 | } |
1485 | | |
1486 | | /* |
1487 | | * AcquireDeletionLock - acquire a suitable lock for deleting an object |
1488 | | * |
1489 | | * Accepts the same flags as performDeletion (though currently only |
1490 | | * PERFORM_DELETION_CONCURRENTLY does anything). |
1491 | | * |
1492 | | * We use LockRelation for relations, and otherwise LockSharedObject or |
1493 | | * LockDatabaseObject as appropriate for the object type. |
1494 | | */ |
1495 | | void |
1496 | | AcquireDeletionLock(const ObjectAddress *object, int flags) |
1497 | 0 | { |
1498 | 0 | if (object->classId == RelationRelationId) |
1499 | 0 | { |
1500 | | /* |
1501 | | * In DROP INDEX CONCURRENTLY, take only ShareUpdateExclusiveLock on |
1502 | | * the index for the moment. index_drop() will promote the lock once |
1503 | | * it's safe to do so. In all other cases we need full exclusive |
1504 | | * lock. |
1505 | | */ |
1506 | 0 | if (flags & PERFORM_DELETION_CONCURRENTLY) |
1507 | 0 | LockRelationOid(object->objectId, ShareUpdateExclusiveLock); |
1508 | 0 | else |
1509 | 0 | LockRelationOid(object->objectId, AccessExclusiveLock); |
1510 | 0 | } |
1511 | 0 | else if (object->classId == AuthMemRelationId) |
1512 | 0 | LockSharedObject(object->classId, object->objectId, 0, |
1513 | 0 | AccessExclusiveLock); |
1514 | 0 | else |
1515 | 0 | { |
1516 | | /* assume we should lock the whole object not a sub-object */ |
1517 | 0 | LockDatabaseObject(object->classId, object->objectId, 0, |
1518 | 0 | AccessExclusiveLock); |
1519 | 0 | } |
1520 | 0 | } |
1521 | | |
1522 | | /* |
1523 | | * ReleaseDeletionLock - release an object deletion lock |
1524 | | * |
1525 | | * Companion to AcquireDeletionLock. |
1526 | | */ |
1527 | | void |
1528 | | ReleaseDeletionLock(const ObjectAddress *object) |
1529 | 0 | { |
1530 | 0 | if (object->classId == RelationRelationId) |
1531 | 0 | UnlockRelationOid(object->objectId, AccessExclusiveLock); |
1532 | 0 | else |
1533 | | /* assume we should lock the whole object not a sub-object */ |
1534 | 0 | UnlockDatabaseObject(object->classId, object->objectId, 0, |
1535 | 0 | AccessExclusiveLock); |
1536 | 0 | } |
1537 | | |
1538 | | /* |
1539 | | * recordDependencyOnExpr - find expression dependencies |
1540 | | * |
1541 | | * This is used to find the dependencies of rules, constraint expressions, |
1542 | | * etc. |
1543 | | * |
1544 | | * Given an expression or query in node-tree form, find all the objects |
1545 | | * it refers to (tables, columns, operators, functions, etc). Record |
1546 | | * a dependency of the specified type from the given depender object |
1547 | | * to each object mentioned in the expression. |
1548 | | * |
1549 | | * rtable is the rangetable to be used to interpret Vars with varlevelsup=0. |
1550 | | * It can be NIL if no such variables are expected. |
1551 | | */ |
1552 | | void |
1553 | | recordDependencyOnExpr(const ObjectAddress *depender, |
1554 | | Node *expr, List *rtable, |
1555 | | DependencyType behavior) |
1556 | 0 | { |
1557 | 0 | find_expr_references_context context; |
1558 | |
|
1559 | 0 | context.addrs = new_object_addresses(); |
1560 | | |
1561 | | /* Set up interpretation for Vars at varlevelsup = 0 */ |
1562 | 0 | context.rtables = list_make1(rtable); |
1563 | | |
1564 | | /* Scan the expression tree for referenceable objects */ |
1565 | 0 | find_expr_references_walker(expr, &context); |
1566 | | |
1567 | | /* Remove any duplicates */ |
1568 | 0 | eliminate_duplicate_dependencies(context.addrs); |
1569 | | |
1570 | | /* And record 'em */ |
1571 | 0 | recordMultipleDependencies(depender, |
1572 | 0 | context.addrs->refs, context.addrs->numrefs, |
1573 | 0 | behavior); |
1574 | |
|
1575 | 0 | free_object_addresses(context.addrs); |
1576 | 0 | } |
1577 | | |
1578 | | /* |
1579 | | * recordDependencyOnSingleRelExpr - find expression dependencies |
1580 | | * |
1581 | | * As above, but only one relation is expected to be referenced (with |
1582 | | * varno = 1 and varlevelsup = 0). Pass the relation OID instead of a |
1583 | | * range table. An additional frammish is that dependencies on that |
1584 | | * relation's component columns will be marked with 'self_behavior', |
1585 | | * whereas 'behavior' is used for everything else; also, if 'reverse_self' |
1586 | | * is true, those dependencies are reversed so that the columns are made |
1587 | | * to depend on the table not vice versa. |
1588 | | * |
1589 | | * NOTE: the caller should ensure that a whole-table dependency on the |
1590 | | * specified relation is created separately, if one is needed. In particular, |
1591 | | * a whole-row Var "relation.*" will not cause this routine to emit any |
1592 | | * dependency item. This is appropriate behavior for subexpressions of an |
1593 | | * ordinary query, so other cases need to cope as necessary. |
1594 | | */ |
1595 | | void |
1596 | | recordDependencyOnSingleRelExpr(const ObjectAddress *depender, |
1597 | | Node *expr, Oid relId, |
1598 | | DependencyType behavior, |
1599 | | DependencyType self_behavior, |
1600 | | bool reverse_self) |
1601 | 0 | { |
1602 | 0 | find_expr_references_context context; |
1603 | 0 | RangeTblEntry rte = {0}; |
1604 | |
|
1605 | 0 | context.addrs = new_object_addresses(); |
1606 | | |
1607 | | /* We gin up a rather bogus rangetable list to handle Vars */ |
1608 | 0 | rte.type = T_RangeTblEntry; |
1609 | 0 | rte.rtekind = RTE_RELATION; |
1610 | 0 | rte.relid = relId; |
1611 | 0 | rte.relkind = RELKIND_RELATION; /* no need for exactness here */ |
1612 | 0 | rte.rellockmode = AccessShareLock; |
1613 | |
|
1614 | 0 | context.rtables = list_make1(list_make1(&rte)); |
1615 | | |
1616 | | /* Scan the expression tree for referenceable objects */ |
1617 | 0 | find_expr_references_walker(expr, &context); |
1618 | | |
1619 | | /* Remove any duplicates */ |
1620 | 0 | eliminate_duplicate_dependencies(context.addrs); |
1621 | | |
1622 | | /* Separate self-dependencies if necessary */ |
1623 | 0 | if ((behavior != self_behavior || reverse_self) && |
1624 | 0 | context.addrs->numrefs > 0) |
1625 | 0 | { |
1626 | 0 | ObjectAddresses *self_addrs; |
1627 | 0 | ObjectAddress *outobj; |
1628 | 0 | int oldref, |
1629 | 0 | outrefs; |
1630 | |
|
1631 | 0 | self_addrs = new_object_addresses(); |
1632 | |
|
1633 | 0 | outobj = context.addrs->refs; |
1634 | 0 | outrefs = 0; |
1635 | 0 | for (oldref = 0; oldref < context.addrs->numrefs; oldref++) |
1636 | 0 | { |
1637 | 0 | ObjectAddress *thisobj = context.addrs->refs + oldref; |
1638 | |
|
1639 | 0 | if (thisobj->classId == RelationRelationId && |
1640 | 0 | thisobj->objectId == relId) |
1641 | 0 | { |
1642 | | /* Move this ref into self_addrs */ |
1643 | 0 | add_exact_object_address(thisobj, self_addrs); |
1644 | 0 | } |
1645 | 0 | else |
1646 | 0 | { |
1647 | | /* Keep it in context.addrs */ |
1648 | 0 | *outobj = *thisobj; |
1649 | 0 | outobj++; |
1650 | 0 | outrefs++; |
1651 | 0 | } |
1652 | 0 | } |
1653 | 0 | context.addrs->numrefs = outrefs; |
1654 | | |
1655 | | /* Record the self-dependencies with the appropriate direction */ |
1656 | 0 | if (!reverse_self) |
1657 | 0 | recordMultipleDependencies(depender, |
1658 | 0 | self_addrs->refs, self_addrs->numrefs, |
1659 | 0 | self_behavior); |
1660 | 0 | else |
1661 | 0 | { |
1662 | | /* Can't use recordMultipleDependencies, so do it the hard way */ |
1663 | 0 | int selfref; |
1664 | |
|
1665 | 0 | for (selfref = 0; selfref < self_addrs->numrefs; selfref++) |
1666 | 0 | { |
1667 | 0 | ObjectAddress *thisobj = self_addrs->refs + selfref; |
1668 | |
|
1669 | 0 | recordDependencyOn(thisobj, depender, self_behavior); |
1670 | 0 | } |
1671 | 0 | } |
1672 | |
|
1673 | 0 | free_object_addresses(self_addrs); |
1674 | 0 | } |
1675 | | |
1676 | | /* Record the external dependencies */ |
1677 | 0 | recordMultipleDependencies(depender, |
1678 | 0 | context.addrs->refs, context.addrs->numrefs, |
1679 | 0 | behavior); |
1680 | |
|
1681 | 0 | free_object_addresses(context.addrs); |
1682 | 0 | } |
1683 | | |
1684 | | /* |
1685 | | * Recursively search an expression tree for object references. |
1686 | | * |
1687 | | * Note: in many cases we do not need to create dependencies on the datatypes |
1688 | | * involved in an expression, because we'll have an indirect dependency via |
1689 | | * some other object. For instance Var nodes depend on a column which depends |
1690 | | * on the datatype, and OpExpr nodes depend on the operator which depends on |
1691 | | * the datatype. However we do need a type dependency if there is no such |
1692 | | * indirect dependency, as for example in Const and CoerceToDomain nodes. |
1693 | | * |
1694 | | * Similarly, we don't need to create dependencies on collations except where |
1695 | | * the collation is being freshly introduced to the expression. |
1696 | | */ |
1697 | | static bool |
1698 | | find_expr_references_walker(Node *node, |
1699 | | find_expr_references_context *context) |
1700 | 0 | { |
1701 | 0 | if (node == NULL) |
1702 | 0 | return false; |
1703 | 0 | if (IsA(node, Var)) |
1704 | 0 | { |
1705 | 0 | Var *var = (Var *) node; |
1706 | 0 | List *rtable; |
1707 | 0 | RangeTblEntry *rte; |
1708 | | |
1709 | | /* Find matching rtable entry, or complain if not found */ |
1710 | 0 | if (var->varlevelsup >= list_length(context->rtables)) |
1711 | 0 | elog(ERROR, "invalid varlevelsup %d", var->varlevelsup); |
1712 | 0 | rtable = (List *) list_nth(context->rtables, var->varlevelsup); |
1713 | 0 | if (var->varno <= 0 || var->varno > list_length(rtable)) |
1714 | 0 | elog(ERROR, "invalid varno %d", var->varno); |
1715 | 0 | rte = rt_fetch(var->varno, rtable); |
1716 | | |
1717 | | /* |
1718 | | * A whole-row Var references no specific columns, so adds no new |
1719 | | * dependency. (We assume that there is a whole-table dependency |
1720 | | * arising from each underlying rangetable entry. While we could |
1721 | | * record such a dependency when finding a whole-row Var that |
1722 | | * references a relation directly, it's quite unclear how to extend |
1723 | | * that to whole-row Vars for JOINs, so it seems better to leave the |
1724 | | * responsibility with the range table. Note that this poses some |
1725 | | * risks for identifying dependencies of stand-alone expressions: |
1726 | | * whole-table references may need to be created separately.) |
1727 | | */ |
1728 | 0 | if (var->varattno == InvalidAttrNumber) |
1729 | 0 | return false; |
1730 | 0 | if (rte->rtekind == RTE_RELATION) |
1731 | 0 | { |
1732 | | /* If it's a plain relation, reference this column */ |
1733 | 0 | add_object_address(RelationRelationId, rte->relid, var->varattno, |
1734 | 0 | context->addrs); |
1735 | 0 | } |
1736 | 0 | else if (rte->rtekind == RTE_FUNCTION) |
1737 | 0 | { |
1738 | | /* Might need to add a dependency on a composite type's column */ |
1739 | | /* (done out of line, because it's a bit bulky) */ |
1740 | 0 | process_function_rte_ref(rte, var->varattno, context); |
1741 | 0 | } |
1742 | | |
1743 | | /* |
1744 | | * Vars referencing other RTE types require no additional work. In |
1745 | | * particular, a join alias Var can be ignored, because it must |
1746 | | * reference a merged USING column. The relevant join input columns |
1747 | | * will also be referenced in the join qual, and any type coercion |
1748 | | * functions involved in the alias expression will be dealt with when |
1749 | | * we scan the RTE itself. |
1750 | | */ |
1751 | 0 | return false; |
1752 | 0 | } |
1753 | 0 | else if (IsA(node, Const)) |
1754 | 0 | { |
1755 | 0 | Const *con = (Const *) node; |
1756 | 0 | Oid objoid; |
1757 | | |
1758 | | /* A constant must depend on the constant's datatype */ |
1759 | 0 | add_object_address(TypeRelationId, con->consttype, 0, |
1760 | 0 | context->addrs); |
1761 | | |
1762 | | /* |
1763 | | * We must also depend on the constant's collation: it could be |
1764 | | * different from the datatype's, if a CollateExpr was const-folded to |
1765 | | * a simple constant. However we can save work in the most common |
1766 | | * case where the collation is "default", since we know that's pinned. |
1767 | | */ |
1768 | 0 | if (OidIsValid(con->constcollid) && |
1769 | 0 | con->constcollid != DEFAULT_COLLATION_OID) |
1770 | 0 | add_object_address(CollationRelationId, con->constcollid, 0, |
1771 | 0 | context->addrs); |
1772 | | |
1773 | | /* |
1774 | | * If it's a regclass or similar literal referring to an existing |
1775 | | * object, add a reference to that object. (Currently, only the |
1776 | | * regclass and regconfig cases have any likely use, but we may as |
1777 | | * well handle all the OID-alias datatypes consistently.) |
1778 | | */ |
1779 | 0 | if (!con->constisnull) |
1780 | 0 | { |
1781 | 0 | switch (con->consttype) |
1782 | 0 | { |
1783 | 0 | case REGPROCOID: |
1784 | 0 | case REGPROCEDUREOID: |
1785 | 0 | objoid = DatumGetObjectId(con->constvalue); |
1786 | 0 | if (SearchSysCacheExists1(PROCOID, |
1787 | 0 | ObjectIdGetDatum(objoid))) |
1788 | 0 | add_object_address(ProcedureRelationId, objoid, 0, |
1789 | 0 | context->addrs); |
1790 | 0 | break; |
1791 | 0 | case REGOPEROID: |
1792 | 0 | case REGOPERATOROID: |
1793 | 0 | objoid = DatumGetObjectId(con->constvalue); |
1794 | 0 | if (SearchSysCacheExists1(OPEROID, |
1795 | 0 | ObjectIdGetDatum(objoid))) |
1796 | 0 | add_object_address(OperatorRelationId, objoid, 0, |
1797 | 0 | context->addrs); |
1798 | 0 | break; |
1799 | 0 | case REGCLASSOID: |
1800 | 0 | objoid = DatumGetObjectId(con->constvalue); |
1801 | 0 | if (SearchSysCacheExists1(RELOID, |
1802 | 0 | ObjectIdGetDatum(objoid))) |
1803 | 0 | add_object_address(RelationRelationId, objoid, 0, |
1804 | 0 | context->addrs); |
1805 | 0 | break; |
1806 | 0 | case REGTYPEOID: |
1807 | 0 | objoid = DatumGetObjectId(con->constvalue); |
1808 | 0 | if (SearchSysCacheExists1(TYPEOID, |
1809 | 0 | ObjectIdGetDatum(objoid))) |
1810 | 0 | add_object_address(TypeRelationId, objoid, 0, |
1811 | 0 | context->addrs); |
1812 | 0 | break; |
1813 | 0 | case REGCOLLATIONOID: |
1814 | 0 | objoid = DatumGetObjectId(con->constvalue); |
1815 | 0 | if (SearchSysCacheExists1(COLLOID, |
1816 | 0 | ObjectIdGetDatum(objoid))) |
1817 | 0 | add_object_address(CollationRelationId, objoid, 0, |
1818 | 0 | context->addrs); |
1819 | 0 | break; |
1820 | 0 | case REGCONFIGOID: |
1821 | 0 | objoid = DatumGetObjectId(con->constvalue); |
1822 | 0 | if (SearchSysCacheExists1(TSCONFIGOID, |
1823 | 0 | ObjectIdGetDatum(objoid))) |
1824 | 0 | add_object_address(TSConfigRelationId, objoid, 0, |
1825 | 0 | context->addrs); |
1826 | 0 | break; |
1827 | 0 | case REGDICTIONARYOID: |
1828 | 0 | objoid = DatumGetObjectId(con->constvalue); |
1829 | 0 | if (SearchSysCacheExists1(TSDICTOID, |
1830 | 0 | ObjectIdGetDatum(objoid))) |
1831 | 0 | add_object_address(TSDictionaryRelationId, objoid, 0, |
1832 | 0 | context->addrs); |
1833 | 0 | break; |
1834 | | |
1835 | 0 | case REGNAMESPACEOID: |
1836 | 0 | objoid = DatumGetObjectId(con->constvalue); |
1837 | 0 | if (SearchSysCacheExists1(NAMESPACEOID, |
1838 | 0 | ObjectIdGetDatum(objoid))) |
1839 | 0 | add_object_address(NamespaceRelationId, objoid, 0, |
1840 | 0 | context->addrs); |
1841 | 0 | break; |
1842 | | |
1843 | | /* |
1844 | | * Dependencies for regrole should be shared among all |
1845 | | * databases, so explicitly inhibit to have dependencies. |
1846 | | */ |
1847 | 0 | case REGROLEOID: |
1848 | 0 | ereport(ERROR, |
1849 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1850 | 0 | errmsg("constant of the type %s cannot be used here", |
1851 | 0 | "regrole"))); |
1852 | 0 | break; |
1853 | | |
1854 | | /* |
1855 | | * Dependencies for regdatabase should be shared among all |
1856 | | * databases, so explicitly inhibit to have dependencies. |
1857 | | */ |
1858 | 0 | case REGDATABASEOID: |
1859 | 0 | ereport(ERROR, |
1860 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1861 | 0 | errmsg("constant of the type %s cannot be used here", |
1862 | 0 | "regdatabase"))); |
1863 | 0 | break; |
1864 | 0 | } |
1865 | 0 | } |
1866 | 0 | return false; |
1867 | 0 | } |
1868 | 0 | else if (IsA(node, Param)) |
1869 | 0 | { |
1870 | 0 | Param *param = (Param *) node; |
1871 | | |
1872 | | /* A parameter must depend on the parameter's datatype */ |
1873 | 0 | add_object_address(TypeRelationId, param->paramtype, 0, |
1874 | 0 | context->addrs); |
1875 | | /* and its collation, just as for Consts */ |
1876 | 0 | if (OidIsValid(param->paramcollid) && |
1877 | 0 | param->paramcollid != DEFAULT_COLLATION_OID) |
1878 | 0 | add_object_address(CollationRelationId, param->paramcollid, 0, |
1879 | 0 | context->addrs); |
1880 | 0 | } |
1881 | 0 | else if (IsA(node, FuncExpr)) |
1882 | 0 | { |
1883 | 0 | FuncExpr *funcexpr = (FuncExpr *) node; |
1884 | |
|
1885 | 0 | add_object_address(ProcedureRelationId, funcexpr->funcid, 0, |
1886 | 0 | context->addrs); |
1887 | | /* fall through to examine arguments */ |
1888 | 0 | } |
1889 | 0 | else if (IsA(node, OpExpr)) |
1890 | 0 | { |
1891 | 0 | OpExpr *opexpr = (OpExpr *) node; |
1892 | |
|
1893 | 0 | add_object_address(OperatorRelationId, opexpr->opno, 0, |
1894 | 0 | context->addrs); |
1895 | | /* fall through to examine arguments */ |
1896 | 0 | } |
1897 | 0 | else if (IsA(node, DistinctExpr)) |
1898 | 0 | { |
1899 | 0 | DistinctExpr *distinctexpr = (DistinctExpr *) node; |
1900 | |
|
1901 | 0 | add_object_address(OperatorRelationId, distinctexpr->opno, 0, |
1902 | 0 | context->addrs); |
1903 | | /* fall through to examine arguments */ |
1904 | 0 | } |
1905 | 0 | else if (IsA(node, NullIfExpr)) |
1906 | 0 | { |
1907 | 0 | NullIfExpr *nullifexpr = (NullIfExpr *) node; |
1908 | |
|
1909 | 0 | add_object_address(OperatorRelationId, nullifexpr->opno, 0, |
1910 | 0 | context->addrs); |
1911 | | /* fall through to examine arguments */ |
1912 | 0 | } |
1913 | 0 | else if (IsA(node, ScalarArrayOpExpr)) |
1914 | 0 | { |
1915 | 0 | ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node; |
1916 | |
|
1917 | 0 | add_object_address(OperatorRelationId, opexpr->opno, 0, |
1918 | 0 | context->addrs); |
1919 | | /* fall through to examine arguments */ |
1920 | 0 | } |
1921 | 0 | else if (IsA(node, Aggref)) |
1922 | 0 | { |
1923 | 0 | Aggref *aggref = (Aggref *) node; |
1924 | |
|
1925 | 0 | add_object_address(ProcedureRelationId, aggref->aggfnoid, 0, |
1926 | 0 | context->addrs); |
1927 | | /* fall through to examine arguments */ |
1928 | 0 | } |
1929 | 0 | else if (IsA(node, WindowFunc)) |
1930 | 0 | { |
1931 | 0 | WindowFunc *wfunc = (WindowFunc *) node; |
1932 | |
|
1933 | 0 | add_object_address(ProcedureRelationId, wfunc->winfnoid, 0, |
1934 | 0 | context->addrs); |
1935 | | /* fall through to examine arguments */ |
1936 | 0 | } |
1937 | 0 | else if (IsA(node, SubscriptingRef)) |
1938 | 0 | { |
1939 | 0 | SubscriptingRef *sbsref = (SubscriptingRef *) node; |
1940 | | |
1941 | | /* |
1942 | | * The refexpr should provide adequate dependency on refcontainertype, |
1943 | | * and that type in turn depends on refelemtype. However, a custom |
1944 | | * subscripting handler might set refrestype to something different |
1945 | | * from either of those, in which case we'd better record it. |
1946 | | */ |
1947 | 0 | if (sbsref->refrestype != sbsref->refcontainertype && |
1948 | 0 | sbsref->refrestype != sbsref->refelemtype) |
1949 | 0 | add_object_address(TypeRelationId, sbsref->refrestype, 0, |
1950 | 0 | context->addrs); |
1951 | | /* fall through to examine arguments */ |
1952 | 0 | } |
1953 | 0 | else if (IsA(node, SubPlan)) |
1954 | 0 | { |
1955 | | /* Extra work needed here if we ever need this case */ |
1956 | 0 | elog(ERROR, "already-planned subqueries not supported"); |
1957 | 0 | } |
1958 | 0 | else if (IsA(node, FieldSelect)) |
1959 | 0 | { |
1960 | 0 | FieldSelect *fselect = (FieldSelect *) node; |
1961 | 0 | Oid argtype = getBaseType(exprType((Node *) fselect->arg)); |
1962 | 0 | Oid reltype = get_typ_typrelid(argtype); |
1963 | | |
1964 | | /* |
1965 | | * We need a dependency on the specific column named in FieldSelect, |
1966 | | * assuming we can identify the pg_class OID for it. (Probably we |
1967 | | * always can at the moment, but in future it might be possible for |
1968 | | * argtype to be RECORDOID.) If we can make a column dependency then |
1969 | | * we shouldn't need a dependency on the column's type; but if we |
1970 | | * can't, make a dependency on the type, as it might not appear |
1971 | | * anywhere else in the expression. |
1972 | | */ |
1973 | 0 | if (OidIsValid(reltype)) |
1974 | 0 | add_object_address(RelationRelationId, reltype, fselect->fieldnum, |
1975 | 0 | context->addrs); |
1976 | 0 | else |
1977 | 0 | add_object_address(TypeRelationId, fselect->resulttype, 0, |
1978 | 0 | context->addrs); |
1979 | | /* the collation might not be referenced anywhere else, either */ |
1980 | 0 | if (OidIsValid(fselect->resultcollid) && |
1981 | 0 | fselect->resultcollid != DEFAULT_COLLATION_OID) |
1982 | 0 | add_object_address(CollationRelationId, fselect->resultcollid, 0, |
1983 | 0 | context->addrs); |
1984 | 0 | } |
1985 | 0 | else if (IsA(node, FieldStore)) |
1986 | 0 | { |
1987 | 0 | FieldStore *fstore = (FieldStore *) node; |
1988 | 0 | Oid reltype = get_typ_typrelid(fstore->resulttype); |
1989 | | |
1990 | | /* similar considerations to FieldSelect, but multiple column(s) */ |
1991 | 0 | if (OidIsValid(reltype)) |
1992 | 0 | { |
1993 | 0 | ListCell *l; |
1994 | |
|
1995 | 0 | foreach(l, fstore->fieldnums) |
1996 | 0 | add_object_address(RelationRelationId, reltype, lfirst_int(l), |
1997 | 0 | context->addrs); |
1998 | 0 | } |
1999 | 0 | else |
2000 | 0 | add_object_address(TypeRelationId, fstore->resulttype, 0, |
2001 | 0 | context->addrs); |
2002 | 0 | } |
2003 | 0 | else if (IsA(node, RelabelType)) |
2004 | 0 | { |
2005 | 0 | RelabelType *relab = (RelabelType *) node; |
2006 | | |
2007 | | /* since there is no function dependency, need to depend on type */ |
2008 | 0 | add_object_address(TypeRelationId, relab->resulttype, 0, |
2009 | 0 | context->addrs); |
2010 | | /* the collation might not be referenced anywhere else, either */ |
2011 | 0 | if (OidIsValid(relab->resultcollid) && |
2012 | 0 | relab->resultcollid != DEFAULT_COLLATION_OID) |
2013 | 0 | add_object_address(CollationRelationId, relab->resultcollid, 0, |
2014 | 0 | context->addrs); |
2015 | 0 | } |
2016 | 0 | else if (IsA(node, CoerceViaIO)) |
2017 | 0 | { |
2018 | 0 | CoerceViaIO *iocoerce = (CoerceViaIO *) node; |
2019 | | |
2020 | | /* since there is no exposed function, need to depend on type */ |
2021 | 0 | add_object_address(TypeRelationId, iocoerce->resulttype, 0, |
2022 | 0 | context->addrs); |
2023 | | /* the collation might not be referenced anywhere else, either */ |
2024 | 0 | if (OidIsValid(iocoerce->resultcollid) && |
2025 | 0 | iocoerce->resultcollid != DEFAULT_COLLATION_OID) |
2026 | 0 | add_object_address(CollationRelationId, iocoerce->resultcollid, 0, |
2027 | 0 | context->addrs); |
2028 | 0 | } |
2029 | 0 | else if (IsA(node, ArrayCoerceExpr)) |
2030 | 0 | { |
2031 | 0 | ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; |
2032 | | |
2033 | | /* as above, depend on type */ |
2034 | 0 | add_object_address(TypeRelationId, acoerce->resulttype, 0, |
2035 | 0 | context->addrs); |
2036 | | /* the collation might not be referenced anywhere else, either */ |
2037 | 0 | if (OidIsValid(acoerce->resultcollid) && |
2038 | 0 | acoerce->resultcollid != DEFAULT_COLLATION_OID) |
2039 | 0 | add_object_address(CollationRelationId, acoerce->resultcollid, 0, |
2040 | 0 | context->addrs); |
2041 | | /* fall through to examine arguments */ |
2042 | 0 | } |
2043 | 0 | else if (IsA(node, ConvertRowtypeExpr)) |
2044 | 0 | { |
2045 | 0 | ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node; |
2046 | | |
2047 | | /* since there is no function dependency, need to depend on type */ |
2048 | 0 | add_object_address(TypeRelationId, cvt->resulttype, 0, |
2049 | 0 | context->addrs); |
2050 | 0 | } |
2051 | 0 | else if (IsA(node, CollateExpr)) |
2052 | 0 | { |
2053 | 0 | CollateExpr *coll = (CollateExpr *) node; |
2054 | |
|
2055 | 0 | add_object_address(CollationRelationId, coll->collOid, 0, |
2056 | 0 | context->addrs); |
2057 | 0 | } |
2058 | 0 | else if (IsA(node, RowExpr)) |
2059 | 0 | { |
2060 | 0 | RowExpr *rowexpr = (RowExpr *) node; |
2061 | |
|
2062 | 0 | add_object_address(TypeRelationId, rowexpr->row_typeid, 0, |
2063 | 0 | context->addrs); |
2064 | 0 | } |
2065 | 0 | else if (IsA(node, RowCompareExpr)) |
2066 | 0 | { |
2067 | 0 | RowCompareExpr *rcexpr = (RowCompareExpr *) node; |
2068 | 0 | ListCell *l; |
2069 | |
|
2070 | 0 | foreach(l, rcexpr->opnos) |
2071 | 0 | { |
2072 | 0 | add_object_address(OperatorRelationId, lfirst_oid(l), 0, |
2073 | 0 | context->addrs); |
2074 | 0 | } |
2075 | 0 | foreach(l, rcexpr->opfamilies) |
2076 | 0 | { |
2077 | 0 | add_object_address(OperatorFamilyRelationId, lfirst_oid(l), 0, |
2078 | 0 | context->addrs); |
2079 | 0 | } |
2080 | | /* fall through to examine arguments */ |
2081 | 0 | } |
2082 | 0 | else if (IsA(node, CoerceToDomain)) |
2083 | 0 | { |
2084 | 0 | CoerceToDomain *cd = (CoerceToDomain *) node; |
2085 | |
|
2086 | 0 | add_object_address(TypeRelationId, cd->resulttype, 0, |
2087 | 0 | context->addrs); |
2088 | 0 | } |
2089 | 0 | else if (IsA(node, NextValueExpr)) |
2090 | 0 | { |
2091 | 0 | NextValueExpr *nve = (NextValueExpr *) node; |
2092 | |
|
2093 | 0 | add_object_address(RelationRelationId, nve->seqid, 0, |
2094 | 0 | context->addrs); |
2095 | 0 | } |
2096 | 0 | else if (IsA(node, OnConflictExpr)) |
2097 | 0 | { |
2098 | 0 | OnConflictExpr *onconflict = (OnConflictExpr *) node; |
2099 | |
|
2100 | 0 | if (OidIsValid(onconflict->constraint)) |
2101 | 0 | add_object_address(ConstraintRelationId, onconflict->constraint, 0, |
2102 | 0 | context->addrs); |
2103 | | /* fall through to examine arguments */ |
2104 | 0 | } |
2105 | 0 | else if (IsA(node, SortGroupClause)) |
2106 | 0 | { |
2107 | 0 | SortGroupClause *sgc = (SortGroupClause *) node; |
2108 | |
|
2109 | 0 | add_object_address(OperatorRelationId, sgc->eqop, 0, |
2110 | 0 | context->addrs); |
2111 | 0 | if (OidIsValid(sgc->sortop)) |
2112 | 0 | add_object_address(OperatorRelationId, sgc->sortop, 0, |
2113 | 0 | context->addrs); |
2114 | 0 | return false; |
2115 | 0 | } |
2116 | 0 | else if (IsA(node, WindowClause)) |
2117 | 0 | { |
2118 | 0 | WindowClause *wc = (WindowClause *) node; |
2119 | |
|
2120 | 0 | if (OidIsValid(wc->startInRangeFunc)) |
2121 | 0 | add_object_address(ProcedureRelationId, wc->startInRangeFunc, 0, |
2122 | 0 | context->addrs); |
2123 | 0 | if (OidIsValid(wc->endInRangeFunc)) |
2124 | 0 | add_object_address(ProcedureRelationId, wc->endInRangeFunc, 0, |
2125 | 0 | context->addrs); |
2126 | 0 | if (OidIsValid(wc->inRangeColl) && |
2127 | 0 | wc->inRangeColl != DEFAULT_COLLATION_OID) |
2128 | 0 | add_object_address(CollationRelationId, wc->inRangeColl, 0, |
2129 | 0 | context->addrs); |
2130 | | /* fall through to examine substructure */ |
2131 | 0 | } |
2132 | 0 | else if (IsA(node, CTECycleClause)) |
2133 | 0 | { |
2134 | 0 | CTECycleClause *cc = (CTECycleClause *) node; |
2135 | |
|
2136 | 0 | if (OidIsValid(cc->cycle_mark_type)) |
2137 | 0 | add_object_address(TypeRelationId, cc->cycle_mark_type, 0, |
2138 | 0 | context->addrs); |
2139 | 0 | if (OidIsValid(cc->cycle_mark_collation)) |
2140 | 0 | add_object_address(CollationRelationId, cc->cycle_mark_collation, 0, |
2141 | 0 | context->addrs); |
2142 | 0 | if (OidIsValid(cc->cycle_mark_neop)) |
2143 | 0 | add_object_address(OperatorRelationId, cc->cycle_mark_neop, 0, |
2144 | 0 | context->addrs); |
2145 | | /* fall through to examine substructure */ |
2146 | 0 | } |
2147 | 0 | else if (IsA(node, Query)) |
2148 | 0 | { |
2149 | | /* Recurse into RTE subquery or not-yet-planned sublink subquery */ |
2150 | 0 | Query *query = (Query *) node; |
2151 | 0 | ListCell *lc; |
2152 | 0 | bool result; |
2153 | | |
2154 | | /* |
2155 | | * Add whole-relation refs for each plain relation mentioned in the |
2156 | | * subquery's rtable, and ensure we add refs for any type-coercion |
2157 | | * functions used in join alias lists. |
2158 | | * |
2159 | | * Note: query_tree_walker takes care of recursing into RTE_FUNCTION |
2160 | | * RTEs, subqueries, etc, so no need to do that here. But we must |
2161 | | * tell it not to visit join alias lists, or we'll add refs for join |
2162 | | * input columns whether or not they are actually used in our query. |
2163 | | * |
2164 | | * Note: we don't need to worry about collations mentioned in |
2165 | | * RTE_VALUES or RTE_CTE RTEs, because those must just duplicate |
2166 | | * collations referenced in other parts of the Query. We do have to |
2167 | | * worry about collations mentioned in RTE_FUNCTION, but we take care |
2168 | | * of those when we recurse to the RangeTblFunction node(s). |
2169 | | */ |
2170 | 0 | foreach(lc, query->rtable) |
2171 | 0 | { |
2172 | 0 | RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); |
2173 | |
|
2174 | 0 | switch (rte->rtekind) |
2175 | 0 | { |
2176 | 0 | case RTE_RELATION: |
2177 | 0 | add_object_address(RelationRelationId, rte->relid, 0, |
2178 | 0 | context->addrs); |
2179 | 0 | break; |
2180 | 0 | case RTE_JOIN: |
2181 | | |
2182 | | /* |
2183 | | * Examine joinaliasvars entries only for merged JOIN |
2184 | | * USING columns. Only those entries could contain |
2185 | | * type-coercion functions. Also, their join input |
2186 | | * columns must be referenced in the join quals, so this |
2187 | | * won't accidentally add refs to otherwise-unused join |
2188 | | * input columns. (We want to ref the type coercion |
2189 | | * functions even if the merged column isn't explicitly |
2190 | | * used anywhere, to protect possible expansion of the |
2191 | | * join RTE as a whole-row var, and because it seems like |
2192 | | * a bad idea to allow dropping a function that's present |
2193 | | * in our query tree, whether or not it could get called.) |
2194 | | */ |
2195 | 0 | context->rtables = lcons(query->rtable, context->rtables); |
2196 | 0 | for (int i = 0; i < rte->joinmergedcols; i++) |
2197 | 0 | { |
2198 | 0 | Node *aliasvar = list_nth(rte->joinaliasvars, i); |
2199 | |
|
2200 | 0 | if (!IsA(aliasvar, Var)) |
2201 | 0 | find_expr_references_walker(aliasvar, context); |
2202 | 0 | } |
2203 | 0 | context->rtables = list_delete_first(context->rtables); |
2204 | 0 | break; |
2205 | 0 | case RTE_NAMEDTUPLESTORE: |
2206 | | |
2207 | | /* |
2208 | | * Cataloged objects cannot depend on tuplestores, because |
2209 | | * those have no cataloged representation. For now we can |
2210 | | * call the tuplestore a "transition table" because that's |
2211 | | * the only kind exposed to SQL, but someday we might have |
2212 | | * to work harder. |
2213 | | */ |
2214 | 0 | ereport(ERROR, |
2215 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
2216 | 0 | errmsg("transition table \"%s\" cannot be referenced in a persistent object", |
2217 | 0 | rte->eref->aliasname))); |
2218 | 0 | break; |
2219 | 0 | default: |
2220 | | /* Other RTE types can be ignored here */ |
2221 | 0 | break; |
2222 | 0 | } |
2223 | 0 | } |
2224 | | |
2225 | | /* |
2226 | | * If the query is an INSERT or UPDATE, we should create a dependency |
2227 | | * on each target column, to prevent the specific target column from |
2228 | | * being dropped. Although we will visit the TargetEntry nodes again |
2229 | | * during query_tree_walker, we won't have enough context to do this |
2230 | | * conveniently, so do it here. |
2231 | | */ |
2232 | 0 | if (query->commandType == CMD_INSERT || |
2233 | 0 | query->commandType == CMD_UPDATE) |
2234 | 0 | { |
2235 | 0 | RangeTblEntry *rte; |
2236 | |
|
2237 | 0 | if (query->resultRelation <= 0 || |
2238 | 0 | query->resultRelation > list_length(query->rtable)) |
2239 | 0 | elog(ERROR, "invalid resultRelation %d", |
2240 | 0 | query->resultRelation); |
2241 | 0 | rte = rt_fetch(query->resultRelation, query->rtable); |
2242 | 0 | if (rte->rtekind == RTE_RELATION) |
2243 | 0 | { |
2244 | 0 | foreach(lc, query->targetList) |
2245 | 0 | { |
2246 | 0 | TargetEntry *tle = (TargetEntry *) lfirst(lc); |
2247 | |
|
2248 | 0 | if (tle->resjunk) |
2249 | 0 | continue; /* ignore junk tlist items */ |
2250 | 0 | add_object_address(RelationRelationId, rte->relid, tle->resno, |
2251 | 0 | context->addrs); |
2252 | 0 | } |
2253 | 0 | } |
2254 | 0 | } |
2255 | | |
2256 | | /* |
2257 | | * Add dependencies on constraints listed in query's constraintDeps |
2258 | | */ |
2259 | 0 | foreach(lc, query->constraintDeps) |
2260 | 0 | { |
2261 | 0 | add_object_address(ConstraintRelationId, lfirst_oid(lc), 0, |
2262 | 0 | context->addrs); |
2263 | 0 | } |
2264 | | |
2265 | | /* Examine substructure of query */ |
2266 | 0 | context->rtables = lcons(query->rtable, context->rtables); |
2267 | 0 | result = query_tree_walker(query, |
2268 | 0 | find_expr_references_walker, |
2269 | 0 | context, |
2270 | 0 | QTW_IGNORE_JOINALIASES | |
2271 | 0 | QTW_EXAMINE_SORTGROUP); |
2272 | 0 | context->rtables = list_delete_first(context->rtables); |
2273 | 0 | return result; |
2274 | 0 | } |
2275 | 0 | else if (IsA(node, SetOperationStmt)) |
2276 | 0 | { |
2277 | 0 | SetOperationStmt *setop = (SetOperationStmt *) node; |
2278 | | |
2279 | | /* we need to look at the groupClauses for operator references */ |
2280 | 0 | find_expr_references_walker((Node *) setop->groupClauses, context); |
2281 | | /* fall through to examine child nodes */ |
2282 | 0 | } |
2283 | 0 | else if (IsA(node, RangeTblFunction)) |
2284 | 0 | { |
2285 | 0 | RangeTblFunction *rtfunc = (RangeTblFunction *) node; |
2286 | 0 | ListCell *ct; |
2287 | | |
2288 | | /* |
2289 | | * Add refs for any datatypes and collations used in a column |
2290 | | * definition list for a RECORD function. (For other cases, it should |
2291 | | * be enough to depend on the function itself.) |
2292 | | */ |
2293 | 0 | foreach(ct, rtfunc->funccoltypes) |
2294 | 0 | { |
2295 | 0 | add_object_address(TypeRelationId, lfirst_oid(ct), 0, |
2296 | 0 | context->addrs); |
2297 | 0 | } |
2298 | 0 | foreach(ct, rtfunc->funccolcollations) |
2299 | 0 | { |
2300 | 0 | Oid collid = lfirst_oid(ct); |
2301 | |
|
2302 | 0 | if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID) |
2303 | 0 | add_object_address(CollationRelationId, collid, 0, |
2304 | 0 | context->addrs); |
2305 | 0 | } |
2306 | 0 | } |
2307 | 0 | else if (IsA(node, TableFunc)) |
2308 | 0 | { |
2309 | 0 | TableFunc *tf = (TableFunc *) node; |
2310 | 0 | ListCell *ct; |
2311 | | |
2312 | | /* |
2313 | | * Add refs for the datatypes and collations used in the TableFunc. |
2314 | | */ |
2315 | 0 | foreach(ct, tf->coltypes) |
2316 | 0 | { |
2317 | 0 | add_object_address(TypeRelationId, lfirst_oid(ct), 0, |
2318 | 0 | context->addrs); |
2319 | 0 | } |
2320 | 0 | foreach(ct, tf->colcollations) |
2321 | 0 | { |
2322 | 0 | Oid collid = lfirst_oid(ct); |
2323 | |
|
2324 | 0 | if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID) |
2325 | 0 | add_object_address(CollationRelationId, collid, 0, |
2326 | 0 | context->addrs); |
2327 | 0 | } |
2328 | 0 | } |
2329 | 0 | else if (IsA(node, TableSampleClause)) |
2330 | 0 | { |
2331 | 0 | TableSampleClause *tsc = (TableSampleClause *) node; |
2332 | |
|
2333 | 0 | add_object_address(ProcedureRelationId, tsc->tsmhandler, 0, |
2334 | 0 | context->addrs); |
2335 | | /* fall through to examine arguments */ |
2336 | 0 | } |
2337 | | |
2338 | 0 | return expression_tree_walker(node, find_expr_references_walker, |
2339 | 0 | context); |
2340 | 0 | } |
2341 | | |
2342 | | /* |
2343 | | * find_expr_references_walker subroutine: handle a Var reference |
2344 | | * to an RTE_FUNCTION RTE |
2345 | | */ |
2346 | | static void |
2347 | | process_function_rte_ref(RangeTblEntry *rte, AttrNumber attnum, |
2348 | | find_expr_references_context *context) |
2349 | 0 | { |
2350 | 0 | int atts_done = 0; |
2351 | 0 | ListCell *lc; |
2352 | | |
2353 | | /* |
2354 | | * Identify which RangeTblFunction produces this attnum, and see if it |
2355 | | * returns a composite type. If so, we'd better make a dependency on the |
2356 | | * referenced column of the composite type (or actually, of its associated |
2357 | | * relation). |
2358 | | */ |
2359 | 0 | foreach(lc, rte->functions) |
2360 | 0 | { |
2361 | 0 | RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); |
2362 | |
|
2363 | 0 | if (attnum > atts_done && |
2364 | 0 | attnum <= atts_done + rtfunc->funccolcount) |
2365 | 0 | { |
2366 | 0 | TupleDesc tupdesc; |
2367 | | |
2368 | | /* If it has a coldeflist, it certainly returns RECORD */ |
2369 | 0 | if (rtfunc->funccolnames != NIL) |
2370 | 0 | tupdesc = NULL; /* no need to work hard */ |
2371 | 0 | else |
2372 | 0 | tupdesc = get_expr_result_tupdesc(rtfunc->funcexpr, true); |
2373 | 0 | if (tupdesc && tupdesc->tdtypeid != RECORDOID) |
2374 | 0 | { |
2375 | | /* |
2376 | | * Named composite type, so individual columns could get |
2377 | | * dropped. Make a dependency on this specific column. |
2378 | | */ |
2379 | 0 | Oid reltype = get_typ_typrelid(tupdesc->tdtypeid); |
2380 | |
|
2381 | 0 | Assert(attnum - atts_done <= tupdesc->natts); |
2382 | 0 | if (OidIsValid(reltype)) /* can this fail? */ |
2383 | 0 | add_object_address(RelationRelationId, reltype, |
2384 | 0 | attnum - atts_done, |
2385 | 0 | context->addrs); |
2386 | 0 | return; |
2387 | 0 | } |
2388 | | /* Nothing to do; function's result type is handled elsewhere */ |
2389 | 0 | return; |
2390 | 0 | } |
2391 | 0 | atts_done += rtfunc->funccolcount; |
2392 | 0 | } |
2393 | | |
2394 | | /* If we get here, must be looking for the ordinality column */ |
2395 | 0 | if (rte->funcordinality && attnum == atts_done + 1) |
2396 | 0 | return; |
2397 | | |
2398 | | /* this probably can't happen ... */ |
2399 | 0 | ereport(ERROR, |
2400 | 0 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
2401 | 0 | errmsg("column %d of relation \"%s\" does not exist", |
2402 | 0 | attnum, rte->eref->aliasname))); |
2403 | 0 | } |
2404 | | |
2405 | | /* |
2406 | | * Given an array of dependency references, eliminate any duplicates. |
2407 | | */ |
2408 | | static void |
2409 | | eliminate_duplicate_dependencies(ObjectAddresses *addrs) |
2410 | 0 | { |
2411 | 0 | ObjectAddress *priorobj; |
2412 | 0 | int oldref, |
2413 | 0 | newrefs; |
2414 | | |
2415 | | /* |
2416 | | * We can't sort if the array has "extra" data, because there's no way to |
2417 | | * keep it in sync. Fortunately that combination of features is not |
2418 | | * needed. |
2419 | | */ |
2420 | 0 | Assert(!addrs->extras); |
2421 | |
|
2422 | 0 | if (addrs->numrefs <= 1) |
2423 | 0 | return; /* nothing to do */ |
2424 | | |
2425 | | /* Sort the refs so that duplicates are adjacent */ |
2426 | 0 | qsort(addrs->refs, addrs->numrefs, sizeof(ObjectAddress), |
2427 | 0 | object_address_comparator); |
2428 | | |
2429 | | /* Remove dups */ |
2430 | 0 | priorobj = addrs->refs; |
2431 | 0 | newrefs = 1; |
2432 | 0 | for (oldref = 1; oldref < addrs->numrefs; oldref++) |
2433 | 0 | { |
2434 | 0 | ObjectAddress *thisobj = addrs->refs + oldref; |
2435 | |
|
2436 | 0 | if (priorobj->classId == thisobj->classId && |
2437 | 0 | priorobj->objectId == thisobj->objectId) |
2438 | 0 | { |
2439 | 0 | if (priorobj->objectSubId == thisobj->objectSubId) |
2440 | 0 | continue; /* identical, so drop thisobj */ |
2441 | | |
2442 | | /* |
2443 | | * If we have a whole-object reference and a reference to a part |
2444 | | * of the same object, we don't need the whole-object reference |
2445 | | * (for example, we don't need to reference both table foo and |
2446 | | * column foo.bar). The whole-object reference will always appear |
2447 | | * first in the sorted list. |
2448 | | */ |
2449 | 0 | if (priorobj->objectSubId == 0) |
2450 | 0 | { |
2451 | | /* replace whole ref with partial */ |
2452 | 0 | priorobj->objectSubId = thisobj->objectSubId; |
2453 | 0 | continue; |
2454 | 0 | } |
2455 | 0 | } |
2456 | | /* Not identical, so add thisobj to output set */ |
2457 | 0 | priorobj++; |
2458 | 0 | *priorobj = *thisobj; |
2459 | 0 | newrefs++; |
2460 | 0 | } |
2461 | |
|
2462 | 0 | addrs->numrefs = newrefs; |
2463 | 0 | } |
2464 | | |
2465 | | /* |
2466 | | * qsort comparator for ObjectAddress items |
2467 | | */ |
2468 | | static int |
2469 | | object_address_comparator(const void *a, const void *b) |
2470 | 0 | { |
2471 | 0 | const ObjectAddress *obja = (const ObjectAddress *) a; |
2472 | 0 | const ObjectAddress *objb = (const ObjectAddress *) b; |
2473 | | |
2474 | | /* |
2475 | | * Primary sort key is OID descending. Most of the time, this will result |
2476 | | * in putting newer objects before older ones, which is likely to be the |
2477 | | * right order to delete in. |
2478 | | */ |
2479 | 0 | if (obja->objectId > objb->objectId) |
2480 | 0 | return -1; |
2481 | 0 | if (obja->objectId < objb->objectId) |
2482 | 0 | return 1; |
2483 | | |
2484 | | /* |
2485 | | * Next sort on catalog ID, in case identical OIDs appear in different |
2486 | | * catalogs. Sort direction is pretty arbitrary here. |
2487 | | */ |
2488 | 0 | if (obja->classId < objb->classId) |
2489 | 0 | return -1; |
2490 | 0 | if (obja->classId > objb->classId) |
2491 | 0 | return 1; |
2492 | | |
2493 | | /* |
2494 | | * Last, sort on object subId. |
2495 | | * |
2496 | | * We sort the subId as an unsigned int so that 0 (the whole object) will |
2497 | | * come first. This is essential for eliminate_duplicate_dependencies, |
2498 | | * and is also the best order for findDependentObjects. |
2499 | | */ |
2500 | 0 | if ((unsigned int) obja->objectSubId < (unsigned int) objb->objectSubId) |
2501 | 0 | return -1; |
2502 | 0 | if ((unsigned int) obja->objectSubId > (unsigned int) objb->objectSubId) |
2503 | 0 | return 1; |
2504 | 0 | return 0; |
2505 | 0 | } |
2506 | | |
2507 | | /* |
2508 | | * Routines for handling an expansible array of ObjectAddress items. |
2509 | | * |
2510 | | * new_object_addresses: create a new ObjectAddresses array. |
2511 | | */ |
2512 | | ObjectAddresses * |
2513 | | new_object_addresses(void) |
2514 | 0 | { |
2515 | 0 | ObjectAddresses *addrs; |
2516 | |
|
2517 | 0 | addrs = palloc(sizeof(ObjectAddresses)); |
2518 | |
|
2519 | 0 | addrs->numrefs = 0; |
2520 | 0 | addrs->maxrefs = 32; |
2521 | 0 | addrs->refs = (ObjectAddress *) |
2522 | 0 | palloc(addrs->maxrefs * sizeof(ObjectAddress)); |
2523 | 0 | addrs->extras = NULL; /* until/unless needed */ |
2524 | |
|
2525 | 0 | return addrs; |
2526 | 0 | } |
2527 | | |
2528 | | /* |
2529 | | * Add an entry to an ObjectAddresses array. |
2530 | | */ |
2531 | | static void |
2532 | | add_object_address(Oid classId, Oid objectId, int32 subId, |
2533 | | ObjectAddresses *addrs) |
2534 | 0 | { |
2535 | 0 | ObjectAddress *item; |
2536 | | |
2537 | | /* enlarge array if needed */ |
2538 | 0 | if (addrs->numrefs >= addrs->maxrefs) |
2539 | 0 | { |
2540 | 0 | addrs->maxrefs *= 2; |
2541 | 0 | addrs->refs = (ObjectAddress *) |
2542 | 0 | repalloc(addrs->refs, addrs->maxrefs * sizeof(ObjectAddress)); |
2543 | 0 | Assert(!addrs->extras); |
2544 | 0 | } |
2545 | | /* record this item */ |
2546 | 0 | item = addrs->refs + addrs->numrefs; |
2547 | 0 | item->classId = classId; |
2548 | 0 | item->objectId = objectId; |
2549 | 0 | item->objectSubId = subId; |
2550 | 0 | addrs->numrefs++; |
2551 | 0 | } |
2552 | | |
2553 | | /* |
2554 | | * Add an entry to an ObjectAddresses array. |
2555 | | * |
2556 | | * As above, but specify entry exactly. |
2557 | | */ |
2558 | | void |
2559 | | add_exact_object_address(const ObjectAddress *object, |
2560 | | ObjectAddresses *addrs) |
2561 | 0 | { |
2562 | 0 | ObjectAddress *item; |
2563 | | |
2564 | | /* enlarge array if needed */ |
2565 | 0 | if (addrs->numrefs >= addrs->maxrefs) |
2566 | 0 | { |
2567 | 0 | addrs->maxrefs *= 2; |
2568 | 0 | addrs->refs = (ObjectAddress *) |
2569 | 0 | repalloc(addrs->refs, addrs->maxrefs * sizeof(ObjectAddress)); |
2570 | 0 | Assert(!addrs->extras); |
2571 | 0 | } |
2572 | | /* record this item */ |
2573 | 0 | item = addrs->refs + addrs->numrefs; |
2574 | 0 | *item = *object; |
2575 | 0 | addrs->numrefs++; |
2576 | 0 | } |
2577 | | |
2578 | | /* |
2579 | | * Add an entry to an ObjectAddresses array. |
2580 | | * |
2581 | | * As above, but specify entry exactly and provide some "extra" data too. |
2582 | | */ |
2583 | | static void |
2584 | | add_exact_object_address_extra(const ObjectAddress *object, |
2585 | | const ObjectAddressExtra *extra, |
2586 | | ObjectAddresses *addrs) |
2587 | 0 | { |
2588 | 0 | ObjectAddress *item; |
2589 | 0 | ObjectAddressExtra *itemextra; |
2590 | | |
2591 | | /* allocate extra space if first time */ |
2592 | 0 | if (!addrs->extras) |
2593 | 0 | addrs->extras = (ObjectAddressExtra *) |
2594 | 0 | palloc(addrs->maxrefs * sizeof(ObjectAddressExtra)); |
2595 | | |
2596 | | /* enlarge array if needed */ |
2597 | 0 | if (addrs->numrefs >= addrs->maxrefs) |
2598 | 0 | { |
2599 | 0 | addrs->maxrefs *= 2; |
2600 | 0 | addrs->refs = (ObjectAddress *) |
2601 | 0 | repalloc(addrs->refs, addrs->maxrefs * sizeof(ObjectAddress)); |
2602 | 0 | addrs->extras = (ObjectAddressExtra *) |
2603 | 0 | repalloc(addrs->extras, addrs->maxrefs * sizeof(ObjectAddressExtra)); |
2604 | 0 | } |
2605 | | /* record this item */ |
2606 | 0 | item = addrs->refs + addrs->numrefs; |
2607 | 0 | *item = *object; |
2608 | 0 | itemextra = addrs->extras + addrs->numrefs; |
2609 | 0 | *itemextra = *extra; |
2610 | 0 | addrs->numrefs++; |
2611 | 0 | } |
2612 | | |
2613 | | /* |
2614 | | * Test whether an object is present in an ObjectAddresses array. |
2615 | | * |
2616 | | * We return "true" if object is a subobject of something in the array, too. |
2617 | | */ |
2618 | | bool |
2619 | | object_address_present(const ObjectAddress *object, |
2620 | | const ObjectAddresses *addrs) |
2621 | 0 | { |
2622 | 0 | int i; |
2623 | |
|
2624 | 0 | for (i = addrs->numrefs - 1; i >= 0; i--) |
2625 | 0 | { |
2626 | 0 | const ObjectAddress *thisobj = addrs->refs + i; |
2627 | |
|
2628 | 0 | if (object->classId == thisobj->classId && |
2629 | 0 | object->objectId == thisobj->objectId) |
2630 | 0 | { |
2631 | 0 | if (object->objectSubId == thisobj->objectSubId || |
2632 | 0 | thisobj->objectSubId == 0) |
2633 | 0 | return true; |
2634 | 0 | } |
2635 | 0 | } |
2636 | | |
2637 | 0 | return false; |
2638 | 0 | } |
2639 | | |
2640 | | /* |
2641 | | * As above, except that if the object is present then also OR the given |
2642 | | * flags into its associated extra data (which must exist). |
2643 | | */ |
2644 | | static bool |
2645 | | object_address_present_add_flags(const ObjectAddress *object, |
2646 | | int flags, |
2647 | | ObjectAddresses *addrs) |
2648 | 0 | { |
2649 | 0 | bool result = false; |
2650 | 0 | int i; |
2651 | |
|
2652 | 0 | for (i = addrs->numrefs - 1; i >= 0; i--) |
2653 | 0 | { |
2654 | 0 | ObjectAddress *thisobj = addrs->refs + i; |
2655 | |
|
2656 | 0 | if (object->classId == thisobj->classId && |
2657 | 0 | object->objectId == thisobj->objectId) |
2658 | 0 | { |
2659 | 0 | if (object->objectSubId == thisobj->objectSubId) |
2660 | 0 | { |
2661 | 0 | ObjectAddressExtra *thisextra = addrs->extras + i; |
2662 | |
|
2663 | 0 | thisextra->flags |= flags; |
2664 | 0 | result = true; |
2665 | 0 | } |
2666 | 0 | else if (thisobj->objectSubId == 0) |
2667 | 0 | { |
2668 | | /* |
2669 | | * We get here if we find a need to delete a column after |
2670 | | * having already decided to drop its whole table. Obviously |
2671 | | * we no longer need to drop the subobject, so report that we |
2672 | | * found the subobject in the array. But don't plaster its |
2673 | | * flags on the whole object. |
2674 | | */ |
2675 | 0 | result = true; |
2676 | 0 | } |
2677 | 0 | else if (object->objectSubId == 0) |
2678 | 0 | { |
2679 | | /* |
2680 | | * We get here if we find a need to delete a whole table after |
2681 | | * having already decided to drop one of its columns. We |
2682 | | * can't report that the whole object is in the array, but we |
2683 | | * should mark the subobject with the whole object's flags. |
2684 | | * |
2685 | | * It might seem attractive to physically delete the column's |
2686 | | * array entry, or at least mark it as no longer needing |
2687 | | * separate deletion. But that could lead to, e.g., dropping |
2688 | | * the column's datatype before we drop the table, which does |
2689 | | * not seem like a good idea. This is a very rare situation |
2690 | | * in practice, so we just take the hit of doing a separate |
2691 | | * DROP COLUMN action even though we know we're gonna delete |
2692 | | * the table later. |
2693 | | * |
2694 | | * What we can do, though, is mark this as a subobject so that |
2695 | | * we don't report it separately, which is confusing because |
2696 | | * it's unpredictable whether it happens or not. But do so |
2697 | | * only if flags != 0 (flags == 0 is a read-only probe). |
2698 | | * |
2699 | | * Because there could be other subobjects of this object in |
2700 | | * the array, this case means we always have to loop through |
2701 | | * the whole array; we cannot exit early on a match. |
2702 | | */ |
2703 | 0 | ObjectAddressExtra *thisextra = addrs->extras + i; |
2704 | |
|
2705 | 0 | if (flags) |
2706 | 0 | thisextra->flags |= (flags | DEPFLAG_SUBOBJECT); |
2707 | 0 | } |
2708 | 0 | } |
2709 | 0 | } |
2710 | |
|
2711 | 0 | return result; |
2712 | 0 | } |
2713 | | |
2714 | | /* |
2715 | | * Similar to above, except we search an ObjectAddressStack. |
2716 | | */ |
2717 | | static bool |
2718 | | stack_address_present_add_flags(const ObjectAddress *object, |
2719 | | int flags, |
2720 | | ObjectAddressStack *stack) |
2721 | 0 | { |
2722 | 0 | bool result = false; |
2723 | 0 | ObjectAddressStack *stackptr; |
2724 | |
|
2725 | 0 | for (stackptr = stack; stackptr; stackptr = stackptr->next) |
2726 | 0 | { |
2727 | 0 | const ObjectAddress *thisobj = stackptr->object; |
2728 | |
|
2729 | 0 | if (object->classId == thisobj->classId && |
2730 | 0 | object->objectId == thisobj->objectId) |
2731 | 0 | { |
2732 | 0 | if (object->objectSubId == thisobj->objectSubId) |
2733 | 0 | { |
2734 | 0 | stackptr->flags |= flags; |
2735 | 0 | result = true; |
2736 | 0 | } |
2737 | 0 | else if (thisobj->objectSubId == 0) |
2738 | 0 | { |
2739 | | /* |
2740 | | * We're visiting a column with whole table already on stack. |
2741 | | * As in object_address_present_add_flags(), we can skip |
2742 | | * further processing of the subobject, but we don't want to |
2743 | | * propagate flags for the subobject to the whole object. |
2744 | | */ |
2745 | 0 | result = true; |
2746 | 0 | } |
2747 | 0 | else if (object->objectSubId == 0) |
2748 | 0 | { |
2749 | | /* |
2750 | | * We're visiting a table with column already on stack. As in |
2751 | | * object_address_present_add_flags(), we should propagate |
2752 | | * flags for the whole object to each of its subobjects. |
2753 | | */ |
2754 | 0 | if (flags) |
2755 | 0 | stackptr->flags |= (flags | DEPFLAG_SUBOBJECT); |
2756 | 0 | } |
2757 | 0 | } |
2758 | 0 | } |
2759 | |
|
2760 | 0 | return result; |
2761 | 0 | } |
2762 | | |
2763 | | /* |
2764 | | * Record multiple dependencies from an ObjectAddresses array, after first |
2765 | | * removing any duplicates. |
2766 | | */ |
2767 | | void |
2768 | | record_object_address_dependencies(const ObjectAddress *depender, |
2769 | | ObjectAddresses *referenced, |
2770 | | DependencyType behavior) |
2771 | 0 | { |
2772 | 0 | eliminate_duplicate_dependencies(referenced); |
2773 | 0 | recordMultipleDependencies(depender, |
2774 | 0 | referenced->refs, referenced->numrefs, |
2775 | 0 | behavior); |
2776 | 0 | } |
2777 | | |
2778 | | /* |
2779 | | * Sort the items in an ObjectAddresses array. |
2780 | | * |
2781 | | * The major sort key is OID-descending, so that newer objects will be listed |
2782 | | * first in most cases. This is primarily useful for ensuring stable outputs |
2783 | | * from regression tests; it's not recommended if the order of the objects is |
2784 | | * determined by user input, such as the order of targets in a DROP command. |
2785 | | */ |
2786 | | void |
2787 | | sort_object_addresses(ObjectAddresses *addrs) |
2788 | 0 | { |
2789 | 0 | if (addrs->numrefs > 1) |
2790 | 0 | qsort(addrs->refs, addrs->numrefs, |
2791 | 0 | sizeof(ObjectAddress), |
2792 | 0 | object_address_comparator); |
2793 | 0 | } |
2794 | | |
2795 | | /* |
2796 | | * Clean up when done with an ObjectAddresses array. |
2797 | | */ |
2798 | | void |
2799 | | free_object_addresses(ObjectAddresses *addrs) |
2800 | 0 | { |
2801 | 0 | pfree(addrs->refs); |
2802 | 0 | if (addrs->extras) |
2803 | 0 | pfree(addrs->extras); |
2804 | 0 | pfree(addrs); |
2805 | 0 | } |
2806 | | |
2807 | | /* |
2808 | | * delete initial ACL for extension objects |
2809 | | */ |
2810 | | static void |
2811 | | DeleteInitPrivs(const ObjectAddress *object) |
2812 | 0 | { |
2813 | 0 | Relation relation; |
2814 | 0 | ScanKeyData key[3]; |
2815 | 0 | int nkeys; |
2816 | 0 | SysScanDesc scan; |
2817 | 0 | HeapTuple oldtuple; |
2818 | |
|
2819 | 0 | relation = table_open(InitPrivsRelationId, RowExclusiveLock); |
2820 | |
|
2821 | 0 | ScanKeyInit(&key[0], |
2822 | 0 | Anum_pg_init_privs_objoid, |
2823 | 0 | BTEqualStrategyNumber, F_OIDEQ, |
2824 | 0 | ObjectIdGetDatum(object->objectId)); |
2825 | 0 | ScanKeyInit(&key[1], |
2826 | 0 | Anum_pg_init_privs_classoid, |
2827 | 0 | BTEqualStrategyNumber, F_OIDEQ, |
2828 | 0 | ObjectIdGetDatum(object->classId)); |
2829 | 0 | if (object->objectSubId != 0) |
2830 | 0 | { |
2831 | 0 | ScanKeyInit(&key[2], |
2832 | 0 | Anum_pg_init_privs_objsubid, |
2833 | 0 | BTEqualStrategyNumber, F_INT4EQ, |
2834 | 0 | Int32GetDatum(object->objectSubId)); |
2835 | 0 | nkeys = 3; |
2836 | 0 | } |
2837 | 0 | else |
2838 | 0 | nkeys = 2; |
2839 | |
|
2840 | 0 | scan = systable_beginscan(relation, InitPrivsObjIndexId, true, |
2841 | 0 | NULL, nkeys, key); |
2842 | |
|
2843 | 0 | while (HeapTupleIsValid(oldtuple = systable_getnext(scan))) |
2844 | 0 | CatalogTupleDelete(relation, &oldtuple->t_self); |
2845 | |
|
2846 | 0 | systable_endscan(scan); |
2847 | |
|
2848 | 0 | table_close(relation, RowExclusiveLock); |
2849 | 0 | } |