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