Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_shdepend.c
4 : * routines to support manipulation of the pg_shdepend relation
5 : *
6 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/catalog/pg_shdepend.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/indexing.h"
24 : #include "catalog/pg_authid.h"
25 : #include "catalog/pg_auth_members.h"
26 : #include "catalog/pg_collation.h"
27 : #include "catalog/pg_conversion.h"
28 : #include "catalog/pg_database.h"
29 : #include "catalog/pg_default_acl.h"
30 : #include "catalog/pg_event_trigger.h"
31 : #include "catalog/pg_extension.h"
32 : #include "catalog/pg_foreign_data_wrapper.h"
33 : #include "catalog/pg_foreign_server.h"
34 : #include "catalog/pg_language.h"
35 : #include "catalog/pg_largeobject.h"
36 : #include "catalog/pg_largeobject_metadata.h"
37 : #include "catalog/pg_namespace.h"
38 : #include "catalog/pg_opclass.h"
39 : #include "catalog/pg_operator.h"
40 : #include "catalog/pg_opfamily.h"
41 : #include "catalog/pg_proc.h"
42 : #include "catalog/pg_shdepend.h"
43 : #include "catalog/pg_statistic_ext.h"
44 : #include "catalog/pg_subscription.h"
45 : #include "catalog/pg_tablespace.h"
46 : #include "catalog/pg_ts_config.h"
47 : #include "catalog/pg_ts_dict.h"
48 : #include "catalog/pg_type.h"
49 : #include "catalog/pg_user_mapping.h"
50 : #include "commands/alter.h"
51 : #include "commands/collationcmds.h"
52 : #include "commands/conversioncmds.h"
53 : #include "commands/dbcommands.h"
54 : #include "commands/defrem.h"
55 : #include "commands/event_trigger.h"
56 : #include "commands/extension.h"
57 : #include "commands/policy.h"
58 : #include "commands/proclang.h"
59 : #include "commands/publicationcmds.h"
60 : #include "commands/schemacmds.h"
61 : #include "commands/subscriptioncmds.h"
62 : #include "commands/tablecmds.h"
63 : #include "commands/tablespace.h"
64 : #include "commands/typecmds.h"
65 : #include "miscadmin.h"
66 : #include "storage/lmgr.h"
67 : #include "utils/acl.h"
68 : #include "utils/fmgroids.h"
69 : #include "utils/memutils.h"
70 : #include "utils/syscache.h"
71 :
72 : typedef enum
73 : {
74 : LOCAL_OBJECT,
75 : SHARED_OBJECT,
76 : REMOTE_OBJECT
77 : } SharedDependencyObjectType;
78 :
79 : typedef struct
80 : {
81 : ObjectAddress object;
82 : char deptype;
83 : SharedDependencyObjectType objtype;
84 : } ShDependObjectInfo;
85 :
86 : static void getOidListDiff(Oid *list1, int *nlist1, Oid *list2, int *nlist2);
87 : static Oid classIdGetDbId(Oid classId);
88 : static void shdepChangeDep(Relation sdepRel,
89 : Oid classid, Oid objid, int32 objsubid,
90 : Oid refclassid, Oid refobjid,
91 : SharedDependencyType deptype);
92 : static void shdepAddDependency(Relation sdepRel,
93 : Oid classId, Oid objectId, int32 objsubId,
94 : Oid refclassId, Oid refobjId,
95 : SharedDependencyType deptype);
96 : static void shdepDropDependency(Relation sdepRel,
97 : Oid classId, Oid objectId, int32 objsubId,
98 : bool drop_subobjects,
99 : Oid refclassId, Oid refobjId,
100 : SharedDependencyType deptype);
101 : static void storeObjectDescription(StringInfo descs,
102 : SharedDependencyObjectType type,
103 : ObjectAddress *object,
104 : SharedDependencyType deptype,
105 : int count);
106 :
107 :
108 : /*
109 : * recordSharedDependencyOn
110 : *
111 : * Record a dependency between 2 objects via their respective ObjectAddresses.
112 : * The first argument is the dependent object, the second the one it
113 : * references (which must be a shared object).
114 : *
115 : * This locks the referenced object and makes sure it still exists.
116 : * Then it creates an entry in pg_shdepend. The lock is kept until
117 : * the end of the transaction.
118 : *
119 : * Dependencies on pinned objects are not recorded.
120 : */
121 : void
122 722338 : recordSharedDependencyOn(ObjectAddress *depender,
123 : ObjectAddress *referenced,
124 : SharedDependencyType deptype)
125 : {
126 : Relation sdepRel;
127 :
128 : /*
129 : * Objects in pg_shdepend can't have SubIds.
130 : */
131 : Assert(depender->objectSubId == 0);
132 : Assert(referenced->objectSubId == 0);
133 :
134 : /*
135 : * During bootstrap, do nothing since pg_shdepend may not exist yet.
136 : * initdb will fill in appropriate pg_shdepend entries after bootstrap.
137 : */
138 722338 : if (IsBootstrapProcessingMode())
139 0 : return;
140 :
141 722338 : sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
142 :
143 : /* If the referenced object is pinned, do nothing. */
144 722338 : if (!IsPinnedObject(referenced->classId, referenced->objectId))
145 : {
146 4096 : shdepAddDependency(sdepRel, depender->classId, depender->objectId,
147 : depender->objectSubId,
148 : referenced->classId, referenced->objectId,
149 : deptype);
150 : }
151 :
152 722338 : table_close(sdepRel, RowExclusiveLock);
153 : }
154 :
155 : /*
156 : * recordDependencyOnOwner
157 : *
158 : * A convenient wrapper of recordSharedDependencyOn -- register the specified
159 : * user as owner of the given object.
160 : *
161 : * Note: it's the caller's responsibility to ensure that there isn't an owner
162 : * entry for the object already.
163 : */
164 : void
165 722036 : recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
166 : {
167 : ObjectAddress myself,
168 : referenced;
169 :
170 722036 : myself.classId = classId;
171 722036 : myself.objectId = objectId;
172 722036 : myself.objectSubId = 0;
173 :
174 722036 : referenced.classId = AuthIdRelationId;
175 722036 : referenced.objectId = owner;
176 722036 : referenced.objectSubId = 0;
177 :
178 722036 : recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER);
179 722036 : }
180 :
181 : /*
182 : * shdepChangeDep
183 : *
184 : * Update shared dependency records to account for an updated referenced
185 : * object. This is an internal workhorse for operations such as changing
186 : * an object's owner.
187 : *
188 : * There must be no more than one existing entry for the given dependent
189 : * object and dependency type! So in practice this can only be used for
190 : * updating SHARED_DEPENDENCY_OWNER and SHARED_DEPENDENCY_TABLESPACE
191 : * entries, which should have that property.
192 : *
193 : * If there is no previous entry, we assume it was referencing a PINned
194 : * object, so we create a new entry. If the new referenced object is
195 : * PINned, we don't create an entry (and drop the old one, if any).
196 : * (For tablespaces, we don't record dependencies in certain cases, so
197 : * there are other possible reasons for entries to be missing.)
198 : *
199 : * sdepRel must be the pg_shdepend relation, already opened and suitably
200 : * locked.
201 : */
202 : static void
203 652 : shdepChangeDep(Relation sdepRel,
204 : Oid classid, Oid objid, int32 objsubid,
205 : Oid refclassid, Oid refobjid,
206 : SharedDependencyType deptype)
207 : {
208 652 : Oid dbid = classIdGetDbId(classid);
209 652 : HeapTuple oldtup = NULL;
210 : HeapTuple scantup;
211 : ScanKeyData key[4];
212 : SysScanDesc scan;
213 :
214 : /*
215 : * Make sure the new referenced object doesn't go away while we record the
216 : * dependency.
217 : */
218 652 : shdepLockAndCheckObject(refclassid, refobjid);
219 :
220 : /*
221 : * Look for a previous entry
222 : */
223 652 : ScanKeyInit(&key[0],
224 : Anum_pg_shdepend_dbid,
225 : BTEqualStrategyNumber, F_OIDEQ,
226 : ObjectIdGetDatum(dbid));
227 652 : ScanKeyInit(&key[1],
228 : Anum_pg_shdepend_classid,
229 : BTEqualStrategyNumber, F_OIDEQ,
230 : ObjectIdGetDatum(classid));
231 652 : ScanKeyInit(&key[2],
232 : Anum_pg_shdepend_objid,
233 : BTEqualStrategyNumber, F_OIDEQ,
234 : ObjectIdGetDatum(objid));
235 652 : ScanKeyInit(&key[3],
236 : Anum_pg_shdepend_objsubid,
237 : BTEqualStrategyNumber, F_INT4EQ,
238 : Int32GetDatum(objsubid));
239 :
240 652 : scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
241 : NULL, 4, key);
242 :
243 994 : while ((scantup = systable_getnext(scan)) != NULL)
244 : {
245 : /* Ignore if not of the target dependency type */
246 342 : if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype)
247 20 : continue;
248 : /* Caller screwed up if multiple matches */
249 322 : if (oldtup)
250 0 : elog(ERROR,
251 : "multiple pg_shdepend entries for object %u/%u/%d deptype %c",
252 : classid, objid, objsubid, deptype);
253 322 : oldtup = heap_copytuple(scantup);
254 : }
255 :
256 652 : systable_endscan(scan);
257 :
258 652 : if (IsPinnedObject(refclassid, refobjid))
259 : {
260 : /* No new entry needed, so just delete existing entry if any */
261 28 : if (oldtup)
262 20 : CatalogTupleDelete(sdepRel, &oldtup->t_self);
263 : }
264 624 : else if (oldtup)
265 : {
266 : /* Need to update existing entry */
267 302 : Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup);
268 :
269 : /* Since oldtup is a copy, we can just modify it in-memory */
270 302 : shForm->refclassid = refclassid;
271 302 : shForm->refobjid = refobjid;
272 :
273 302 : CatalogTupleUpdate(sdepRel, &oldtup->t_self, oldtup);
274 : }
275 : else
276 : {
277 : /* Need to insert new entry */
278 : Datum values[Natts_pg_shdepend];
279 : bool nulls[Natts_pg_shdepend];
280 :
281 322 : memset(nulls, false, sizeof(nulls));
282 :
283 322 : values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid);
284 322 : values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid);
285 322 : values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid);
286 322 : values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubid);
287 :
288 322 : values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
289 322 : values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
290 322 : values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
291 :
292 : /*
293 : * we are reusing oldtup just to avoid declaring a new variable, but
294 : * it's certainly a new tuple
295 : */
296 322 : oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls);
297 322 : CatalogTupleInsert(sdepRel, oldtup);
298 : }
299 :
300 652 : if (oldtup)
301 644 : heap_freetuple(oldtup);
302 652 : }
303 :
304 : /*
305 : * changeDependencyOnOwner
306 : *
307 : * Update the shared dependencies to account for the new owner.
308 : *
309 : * Note: we don't need an objsubid argument because only whole objects
310 : * have owners.
311 : */
312 : void
313 640 : changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
314 : {
315 : Relation sdepRel;
316 :
317 640 : sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
318 :
319 : /* Adjust the SHARED_DEPENDENCY_OWNER entry */
320 640 : shdepChangeDep(sdepRel,
321 : classId, objectId, 0,
322 : AuthIdRelationId, newOwnerId,
323 : SHARED_DEPENDENCY_OWNER);
324 :
325 : /*----------
326 : * There should never be a SHARED_DEPENDENCY_ACL entry for the owner,
327 : * so get rid of it if there is one. This can happen if the new owner
328 : * was previously granted some rights to the object.
329 : *
330 : * This step is analogous to aclnewowner's removal of duplicate entries
331 : * in the ACL. We have to do it to handle this scenario:
332 : * A grants some rights on an object to B
333 : * ALTER OWNER changes the object's owner to B
334 : * ALTER OWNER changes the object's owner to C
335 : * The third step would remove all mention of B from the object's ACL,
336 : * but we'd still have a SHARED_DEPENDENCY_ACL for B if we did not do
337 : * things this way.
338 : *
339 : * The rule against having a SHARED_DEPENDENCY_ACL entry for the owner
340 : * allows us to fix things up in just this one place, without having
341 : * to make the various ALTER OWNER routines each know about it.
342 : *----------
343 : */
344 640 : shdepDropDependency(sdepRel, classId, objectId, 0, true,
345 : AuthIdRelationId, newOwnerId,
346 : SHARED_DEPENDENCY_ACL);
347 :
348 640 : table_close(sdepRel, RowExclusiveLock);
349 640 : }
350 :
351 : /*
352 : * recordDependencyOnTablespace
353 : *
354 : * A convenient wrapper of recordSharedDependencyOn -- register the specified
355 : * tablespace as default for the given object.
356 : *
357 : * Note: it's the caller's responsibility to ensure that there isn't a
358 : * tablespace entry for the object already.
359 : */
360 : void
361 106 : recordDependencyOnTablespace(Oid classId, Oid objectId, Oid tablespace)
362 : {
363 : ObjectAddress myself,
364 : referenced;
365 :
366 106 : ObjectAddressSet(myself, classId, objectId);
367 106 : ObjectAddressSet(referenced, TableSpaceRelationId, tablespace);
368 :
369 106 : recordSharedDependencyOn(&myself, &referenced,
370 : SHARED_DEPENDENCY_TABLESPACE);
371 106 : }
372 :
373 : /*
374 : * changeDependencyOnTablespace
375 : *
376 : * Update the shared dependencies to account for the new tablespace.
377 : *
378 : * Note: we don't need an objsubid argument because only whole objects
379 : * have tablespaces.
380 : */
381 : void
382 30 : changeDependencyOnTablespace(Oid classId, Oid objectId, Oid newTablespaceId)
383 : {
384 : Relation sdepRel;
385 :
386 30 : sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
387 :
388 30 : if (newTablespaceId != DEFAULTTABLESPACE_OID &&
389 : newTablespaceId != InvalidOid)
390 12 : shdepChangeDep(sdepRel,
391 : classId, objectId, 0,
392 : TableSpaceRelationId, newTablespaceId,
393 : SHARED_DEPENDENCY_TABLESPACE);
394 : else
395 18 : shdepDropDependency(sdepRel,
396 : classId, objectId, 0, true,
397 : InvalidOid, InvalidOid,
398 : SHARED_DEPENDENCY_INVALID);
399 :
400 30 : table_close(sdepRel, RowExclusiveLock);
401 30 : }
402 :
403 : /*
404 : * getOidListDiff
405 : * Helper for updateAclDependencies.
406 : *
407 : * Takes two Oid arrays and removes elements that are common to both arrays,
408 : * leaving just those that are in one input but not the other.
409 : * We assume both arrays have been sorted and de-duped.
410 : */
411 : static void
412 109918 : getOidListDiff(Oid *list1, int *nlist1, Oid *list2, int *nlist2)
413 : {
414 : int in1,
415 : in2,
416 : out1,
417 : out2;
418 :
419 109918 : in1 = in2 = out1 = out2 = 0;
420 120362 : while (in1 < *nlist1 && in2 < *nlist2)
421 : {
422 10444 : if (list1[in1] == list2[in2])
423 : {
424 : /* skip over duplicates */
425 10302 : in1++;
426 10302 : in2++;
427 : }
428 142 : else if (list1[in1] < list2[in2])
429 : {
430 : /* list1[in1] is not in list2 */
431 102 : list1[out1++] = list1[in1++];
432 : }
433 : else
434 : {
435 : /* list2[in2] is not in list1 */
436 40 : list2[out2++] = list2[in2++];
437 : }
438 : }
439 :
440 : /* any remaining list1 entries are not in list2 */
441 110540 : while (in1 < *nlist1)
442 : {
443 622 : list1[out1++] = list1[in1++];
444 : }
445 :
446 : /* any remaining list2 entries are not in list1 */
447 219286 : while (in2 < *nlist2)
448 : {
449 109368 : list2[out2++] = list2[in2++];
450 : }
451 :
452 109918 : *nlist1 = out1;
453 109918 : *nlist2 = out2;
454 109918 : }
455 :
456 : /*
457 : * updateAclDependencies
458 : * Update the pg_shdepend info for an object's ACL during GRANT/REVOKE.
459 : *
460 : * classId, objectId, objsubId: identify the object whose ACL this is
461 : * ownerId: role owning the object
462 : * noldmembers, oldmembers: array of roleids appearing in old ACL
463 : * nnewmembers, newmembers: array of roleids appearing in new ACL
464 : *
465 : * We calculate the differences between the new and old lists of roles,
466 : * and then insert or delete from pg_shdepend as appropriate.
467 : *
468 : * Note that we can't just insert all referenced roles blindly during GRANT,
469 : * because we would end up with duplicate registered dependencies. We could
470 : * check for existence of the tuples before inserting, but that seems to be
471 : * more expensive than what we are doing here. Likewise we can't just delete
472 : * blindly during REVOKE, because the user may still have other privileges.
473 : * It is also possible that REVOKE actually adds dependencies, due to
474 : * instantiation of a formerly implicit default ACL (although at present,
475 : * all such dependencies should be for the owning role, which we ignore here).
476 : *
477 : * NOTE: Both input arrays must be sorted and de-duped. (Typically they
478 : * are extracted from an ACL array by aclmembers(), which takes care of
479 : * both requirements.) The arrays are pfreed before return.
480 : */
481 : void
482 109918 : updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
483 : Oid ownerId,
484 : int noldmembers, Oid *oldmembers,
485 : int nnewmembers, Oid *newmembers)
486 : {
487 : Relation sdepRel;
488 : int i;
489 :
490 : /*
491 : * Remove entries that are common to both lists; those represent existing
492 : * dependencies we don't need to change.
493 : *
494 : * OK to overwrite the inputs since we'll pfree them anyway.
495 : */
496 109918 : getOidListDiff(oldmembers, &noldmembers, newmembers, &nnewmembers);
497 :
498 109918 : if (noldmembers > 0 || nnewmembers > 0)
499 : {
500 108762 : sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
501 :
502 : /* Add new dependencies that weren't already present */
503 218170 : for (i = 0; i < nnewmembers; i++)
504 : {
505 109408 : Oid roleid = newmembers[i];
506 :
507 : /*
508 : * Skip the owner: he has an OWNER shdep entry instead. (This is
509 : * not just a space optimization; it makes ALTER OWNER easier. See
510 : * notes in changeDependencyOnOwner.)
511 : */
512 109408 : if (roleid == ownerId)
513 98016 : continue;
514 :
515 : /* Skip pinned roles; they don't need dependency entries */
516 11392 : if (IsPinnedObject(AuthIdRelationId, roleid))
517 9498 : continue;
518 :
519 1894 : shdepAddDependency(sdepRel, classId, objectId, objsubId,
520 : AuthIdRelationId, roleid,
521 : SHARED_DEPENDENCY_ACL);
522 : }
523 :
524 : /* Drop no-longer-used old dependencies */
525 109486 : for (i = 0; i < noldmembers; i++)
526 : {
527 724 : Oid roleid = oldmembers[i];
528 :
529 : /* Skip the owner, same as above */
530 724 : if (roleid == ownerId)
531 98 : continue;
532 :
533 : /* Skip pinned roles */
534 626 : if (IsPinnedObject(AuthIdRelationId, roleid))
535 10 : continue;
536 :
537 616 : shdepDropDependency(sdepRel, classId, objectId, objsubId,
538 : false, /* exact match on objsubId */
539 : AuthIdRelationId, roleid,
540 : SHARED_DEPENDENCY_ACL);
541 : }
542 :
543 108762 : table_close(sdepRel, RowExclusiveLock);
544 : }
545 :
546 109918 : if (oldmembers)
547 9506 : pfree(oldmembers);
548 109918 : if (newmembers)
549 109758 : pfree(newmembers);
550 109918 : }
551 :
552 : /*
553 : * A struct to keep track of dependencies found in other databases.
554 : */
555 : typedef struct
556 : {
557 : Oid dbOid;
558 : int count;
559 : } remoteDep;
560 :
561 : /*
562 : * qsort comparator for ShDependObjectInfo items
563 : */
564 : static int
565 312 : shared_dependency_comparator(const void *a, const void *b)
566 : {
567 312 : const ShDependObjectInfo *obja = (const ShDependObjectInfo *) a;
568 312 : const ShDependObjectInfo *objb = (const ShDependObjectInfo *) b;
569 :
570 : /*
571 : * Primary sort key is OID ascending.
572 : */
573 312 : if (obja->object.objectId < objb->object.objectId)
574 196 : return -1;
575 116 : if (obja->object.objectId > objb->object.objectId)
576 116 : return 1;
577 :
578 : /*
579 : * Next sort on catalog ID, in case identical OIDs appear in different
580 : * catalogs. Sort direction is pretty arbitrary here.
581 : */
582 0 : if (obja->object.classId < objb->object.classId)
583 0 : return -1;
584 0 : if (obja->object.classId > objb->object.classId)
585 0 : return 1;
586 :
587 : /*
588 : * Sort on object subId.
589 : *
590 : * We sort the subId as an unsigned int so that 0 (the whole object) will
591 : * come first.
592 : */
593 0 : if ((unsigned int) obja->object.objectSubId < (unsigned int) objb->object.objectSubId)
594 0 : return -1;
595 0 : if ((unsigned int) obja->object.objectSubId > (unsigned int) objb->object.objectSubId)
596 0 : return 1;
597 :
598 : /*
599 : * Last, sort on deptype, in case the same object has multiple dependency
600 : * types. (Note that there's no need to consider objtype, as that's
601 : * determined by the catalog OID.)
602 : */
603 0 : if (obja->deptype < objb->deptype)
604 0 : return -1;
605 0 : if (obja->deptype > objb->deptype)
606 0 : return 1;
607 :
608 0 : return 0;
609 : }
610 :
611 : /*
612 : * checkSharedDependencies
613 : *
614 : * Check whether there are shared dependency entries for a given shared
615 : * object; return true if so.
616 : *
617 : * In addition, return a string containing a newline-separated list of object
618 : * descriptions that depend on the shared object, or NULL if none is found.
619 : * We actually return two such strings; the "detail" result is suitable for
620 : * returning to the client as an errdetail() string, and is limited in size.
621 : * The "detail_log" string is potentially much longer, and should be emitted
622 : * to the server log only.
623 : *
624 : * We can find three different kinds of dependencies: dependencies on objects
625 : * of the current database; dependencies on shared objects; and dependencies
626 : * on objects local to other databases. We can (and do) provide descriptions
627 : * of the two former kinds of objects, but we can't do that for "remote"
628 : * objects, so we just provide a count of them.
629 : */
630 : bool
631 1424 : checkSharedDependencies(Oid classId, Oid objectId,
632 : char **detail_msg, char **detail_log_msg)
633 : {
634 : Relation sdepRel;
635 : ScanKeyData key[2];
636 : SysScanDesc scan;
637 : HeapTuple tup;
638 1424 : int numReportedDeps = 0;
639 1424 : int numNotReportedDeps = 0;
640 1424 : int numNotReportedDbs = 0;
641 1424 : List *remDeps = NIL;
642 : ListCell *cell;
643 : ObjectAddress object;
644 : ShDependObjectInfo *objects;
645 : int numobjects;
646 : int allocedobjects;
647 : StringInfoData descs;
648 : StringInfoData alldescs;
649 :
650 : /* This case can be dispatched quickly */
651 1424 : if (IsPinnedObject(classId, objectId))
652 : {
653 0 : object.classId = classId;
654 0 : object.objectId = objectId;
655 0 : object.objectSubId = 0;
656 0 : ereport(ERROR,
657 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
658 : errmsg("cannot drop %s because it is required by the database system",
659 : getObjectDescription(&object, false))));
660 : }
661 :
662 : /*
663 : * We limit the number of dependencies reported to the client to
664 : * MAX_REPORTED_DEPS, since client software may not deal well with
665 : * enormous error strings. The server log always gets a full report.
666 : *
667 : * For stability of regression test results, we sort local and shared
668 : * objects by OID before reporting them. We don't worry about the order
669 : * in which other databases are reported, though.
670 : */
671 : #define MAX_REPORTED_DEPS 100
672 :
673 1424 : allocedobjects = 128; /* arbitrary initial array size */
674 : objects = (ShDependObjectInfo *)
675 1424 : palloc(allocedobjects * sizeof(ShDependObjectInfo));
676 1424 : numobjects = 0;
677 1424 : initStringInfo(&descs);
678 1424 : initStringInfo(&alldescs);
679 :
680 1424 : sdepRel = table_open(SharedDependRelationId, AccessShareLock);
681 :
682 1424 : ScanKeyInit(&key[0],
683 : Anum_pg_shdepend_refclassid,
684 : BTEqualStrategyNumber, F_OIDEQ,
685 : ObjectIdGetDatum(classId));
686 1424 : ScanKeyInit(&key[1],
687 : Anum_pg_shdepend_refobjid,
688 : BTEqualStrategyNumber, F_OIDEQ,
689 : ObjectIdGetDatum(objectId));
690 :
691 1424 : scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
692 : NULL, 2, key);
693 :
694 1718 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
695 : {
696 294 : Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
697 :
698 294 : object.classId = sdepForm->classid;
699 294 : object.objectId = sdepForm->objid;
700 294 : object.objectSubId = sdepForm->objsubid;
701 :
702 : /*
703 : * If it's a dependency local to this database or it's a shared
704 : * object, add it to the objects array.
705 : *
706 : * If it's a remote dependency, keep track of it so we can report the
707 : * number of them later.
708 : */
709 294 : if (sdepForm->dbid == MyDatabaseId ||
710 90 : sdepForm->dbid == InvalidOid)
711 : {
712 294 : if (numobjects >= allocedobjects)
713 : {
714 0 : allocedobjects *= 2;
715 : objects = (ShDependObjectInfo *)
716 0 : repalloc(objects,
717 : allocedobjects * sizeof(ShDependObjectInfo));
718 : }
719 294 : objects[numobjects].object = object;
720 294 : objects[numobjects].deptype = sdepForm->deptype;
721 294 : objects[numobjects].objtype = (sdepForm->dbid == MyDatabaseId) ?
722 294 : LOCAL_OBJECT : SHARED_OBJECT;
723 294 : numobjects++;
724 : }
725 : else
726 : {
727 : /* It's not local nor shared, so it must be remote. */
728 : remoteDep *dep;
729 0 : bool stored = false;
730 :
731 : /*
732 : * XXX this info is kept on a simple List. Maybe it's not good
733 : * for performance, but using a hash table seems needlessly
734 : * complex. The expected number of databases is not high anyway,
735 : * I suppose.
736 : */
737 0 : foreach(cell, remDeps)
738 : {
739 0 : dep = lfirst(cell);
740 0 : if (dep->dbOid == sdepForm->dbid)
741 : {
742 0 : dep->count++;
743 0 : stored = true;
744 0 : break;
745 : }
746 : }
747 0 : if (!stored)
748 : {
749 0 : dep = (remoteDep *) palloc(sizeof(remoteDep));
750 0 : dep->dbOid = sdepForm->dbid;
751 0 : dep->count = 1;
752 0 : remDeps = lappend(remDeps, dep);
753 : }
754 : }
755 : }
756 :
757 1424 : systable_endscan(scan);
758 :
759 1424 : table_close(sdepRel, AccessShareLock);
760 :
761 : /*
762 : * Sort and report local and shared objects.
763 : */
764 1424 : if (numobjects > 1)
765 58 : qsort(objects, numobjects,
766 : sizeof(ShDependObjectInfo), shared_dependency_comparator);
767 :
768 1718 : for (int i = 0; i < numobjects; i++)
769 : {
770 294 : if (numReportedDeps < MAX_REPORTED_DEPS)
771 : {
772 294 : numReportedDeps++;
773 294 : storeObjectDescription(&descs,
774 294 : objects[i].objtype,
775 294 : &objects[i].object,
776 294 : objects[i].deptype,
777 : 0);
778 : }
779 : else
780 0 : numNotReportedDeps++;
781 294 : storeObjectDescription(&alldescs,
782 294 : objects[i].objtype,
783 294 : &objects[i].object,
784 294 : objects[i].deptype,
785 : 0);
786 : }
787 :
788 : /*
789 : * Summarize dependencies in remote databases.
790 : */
791 1424 : foreach(cell, remDeps)
792 : {
793 0 : remoteDep *dep = lfirst(cell);
794 :
795 0 : object.classId = DatabaseRelationId;
796 0 : object.objectId = dep->dbOid;
797 0 : object.objectSubId = 0;
798 :
799 0 : if (numReportedDeps < MAX_REPORTED_DEPS)
800 : {
801 0 : numReportedDeps++;
802 0 : storeObjectDescription(&descs, REMOTE_OBJECT, &object,
803 : SHARED_DEPENDENCY_INVALID, dep->count);
804 : }
805 : else
806 0 : numNotReportedDbs++;
807 0 : storeObjectDescription(&alldescs, REMOTE_OBJECT, &object,
808 : SHARED_DEPENDENCY_INVALID, dep->count);
809 : }
810 :
811 1424 : pfree(objects);
812 1424 : list_free_deep(remDeps);
813 :
814 1424 : if (descs.len == 0)
815 : {
816 1294 : pfree(descs.data);
817 1294 : pfree(alldescs.data);
818 1294 : *detail_msg = *detail_log_msg = NULL;
819 1294 : return false;
820 : }
821 :
822 130 : if (numNotReportedDeps > 0)
823 0 : appendStringInfo(&descs, ngettext("\nand %d other object "
824 : "(see server log for list)",
825 : "\nand %d other objects "
826 : "(see server log for list)",
827 : numNotReportedDeps),
828 : numNotReportedDeps);
829 130 : if (numNotReportedDbs > 0)
830 0 : appendStringInfo(&descs, ngettext("\nand objects in %d other database "
831 : "(see server log for list)",
832 : "\nand objects in %d other databases "
833 : "(see server log for list)",
834 : numNotReportedDbs),
835 : numNotReportedDbs);
836 :
837 130 : *detail_msg = descs.data;
838 130 : *detail_log_msg = alldescs.data;
839 130 : return true;
840 : }
841 :
842 :
843 : /*
844 : * copyTemplateDependencies
845 : *
846 : * Routine to create the initial shared dependencies of a new database.
847 : * We simply copy the dependencies from the template database.
848 : */
849 : void
850 1586 : copyTemplateDependencies(Oid templateDbId, Oid newDbId)
851 : {
852 : Relation sdepRel;
853 : TupleDesc sdepDesc;
854 : ScanKeyData key[1];
855 : SysScanDesc scan;
856 : HeapTuple tup;
857 : CatalogIndexState indstate;
858 : TupleTableSlot **slot;
859 : int max_slots,
860 : slot_init_count,
861 : slot_stored_count;
862 :
863 1586 : sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
864 1586 : sdepDesc = RelationGetDescr(sdepRel);
865 :
866 : /*
867 : * Allocate the slots to use, but delay costly initialization until we
868 : * know that they will be used.
869 : */
870 1586 : max_slots = MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_shdepend);
871 1586 : slot = palloc(sizeof(TupleTableSlot *) * max_slots);
872 :
873 1586 : indstate = CatalogOpenIndexes(sdepRel);
874 :
875 : /* Scan all entries with dbid = templateDbId */
876 1586 : ScanKeyInit(&key[0],
877 : Anum_pg_shdepend_dbid,
878 : BTEqualStrategyNumber, F_OIDEQ,
879 : ObjectIdGetDatum(templateDbId));
880 :
881 1586 : scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
882 : NULL, 1, key);
883 :
884 : /* number of slots currently storing tuples */
885 1586 : slot_stored_count = 0;
886 : /* number of slots currently initialized */
887 1586 : slot_init_count = 0;
888 :
889 : /*
890 : * Copy the entries of the original database, changing the database Id to
891 : * that of the new database. Note that because we are not copying rows
892 : * with dbId == 0 (ie, rows describing dependent shared objects) we won't
893 : * copy the ownership dependency of the template database itself; this is
894 : * what we want.
895 : */
896 1602 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
897 : {
898 : Form_pg_shdepend shdep;
899 :
900 16 : if (slot_init_count < max_slots)
901 : {
902 16 : slot[slot_stored_count] = MakeSingleTupleTableSlot(sdepDesc, &TTSOpsHeapTuple);
903 16 : slot_init_count++;
904 : }
905 :
906 16 : ExecClearTuple(slot[slot_stored_count]);
907 :
908 16 : memset(slot[slot_stored_count]->tts_isnull, false,
909 16 : slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
910 :
911 16 : shdep = (Form_pg_shdepend) GETSTRUCT(tup);
912 :
913 16 : slot[slot_stored_count]->tts_values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId);
914 16 : slot[slot_stored_count]->tts_values[Anum_pg_shdepend_classid - 1] = shdep->classid;
915 16 : slot[slot_stored_count]->tts_values[Anum_pg_shdepend_objid - 1] = shdep->objid;
916 16 : slot[slot_stored_count]->tts_values[Anum_pg_shdepend_objsubid - 1] = shdep->objsubid;
917 16 : slot[slot_stored_count]->tts_values[Anum_pg_shdepend_refclassid - 1] = shdep->refclassid;
918 16 : slot[slot_stored_count]->tts_values[Anum_pg_shdepend_refobjid - 1] = shdep->refobjid;
919 16 : slot[slot_stored_count]->tts_values[Anum_pg_shdepend_deptype - 1] = shdep->deptype;
920 :
921 16 : ExecStoreVirtualTuple(slot[slot_stored_count]);
922 16 : slot_stored_count++;
923 :
924 : /* If slots are full, insert a batch of tuples */
925 16 : if (slot_stored_count == max_slots)
926 : {
927 0 : CatalogTuplesMultiInsertWithInfo(sdepRel, slot, slot_stored_count, indstate);
928 0 : slot_stored_count = 0;
929 : }
930 : }
931 :
932 : /* Insert any tuples left in the buffer */
933 1586 : if (slot_stored_count > 0)
934 8 : CatalogTuplesMultiInsertWithInfo(sdepRel, slot, slot_stored_count, indstate);
935 :
936 1586 : systable_endscan(scan);
937 :
938 1586 : CatalogCloseIndexes(indstate);
939 1586 : table_close(sdepRel, RowExclusiveLock);
940 :
941 : /* Drop only the number of slots used */
942 1602 : for (int i = 0; i < slot_init_count; i++)
943 16 : ExecDropSingleTupleTableSlot(slot[i]);
944 1586 : pfree(slot);
945 1586 : }
946 :
947 : /*
948 : * dropDatabaseDependencies
949 : *
950 : * Delete pg_shdepend entries corresponding to a database that's being
951 : * dropped.
952 : */
953 : void
954 40 : dropDatabaseDependencies(Oid databaseId)
955 : {
956 : Relation sdepRel;
957 : ScanKeyData key[1];
958 : SysScanDesc scan;
959 : HeapTuple tup;
960 :
961 40 : sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
962 :
963 : /*
964 : * First, delete all the entries that have the database Oid in the dbid
965 : * field.
966 : */
967 40 : ScanKeyInit(&key[0],
968 : Anum_pg_shdepend_dbid,
969 : BTEqualStrategyNumber, F_OIDEQ,
970 : ObjectIdGetDatum(databaseId));
971 : /* We leave the other index fields unspecified */
972 :
973 40 : scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
974 : NULL, 1, key);
975 :
976 40 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
977 : {
978 0 : CatalogTupleDelete(sdepRel, &tup->t_self);
979 : }
980 :
981 40 : systable_endscan(scan);
982 :
983 : /* Now delete all entries corresponding to the database itself */
984 40 : shdepDropDependency(sdepRel, DatabaseRelationId, databaseId, 0, true,
985 : InvalidOid, InvalidOid,
986 : SHARED_DEPENDENCY_INVALID);
987 :
988 40 : table_close(sdepRel, RowExclusiveLock);
989 40 : }
990 :
991 : /*
992 : * deleteSharedDependencyRecordsFor
993 : *
994 : * Delete all pg_shdepend entries corresponding to an object that's being
995 : * dropped or modified. The object is assumed to be either a shared object
996 : * or local to the current database (the classId tells us which).
997 : *
998 : * If objectSubId is zero, we are deleting a whole object, so get rid of
999 : * pg_shdepend entries for subobjects as well.
1000 : */
1001 : void
1002 213178 : deleteSharedDependencyRecordsFor(Oid classId, Oid objectId, int32 objectSubId)
1003 : {
1004 : Relation sdepRel;
1005 :
1006 213178 : sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
1007 :
1008 213178 : shdepDropDependency(sdepRel, classId, objectId, objectSubId,
1009 : (objectSubId == 0),
1010 : InvalidOid, InvalidOid,
1011 : SHARED_DEPENDENCY_INVALID);
1012 :
1013 213178 : table_close(sdepRel, RowExclusiveLock);
1014 213178 : }
1015 :
1016 : /*
1017 : * shdepAddDependency
1018 : * Internal workhorse for inserting into pg_shdepend
1019 : *
1020 : * sdepRel must be the pg_shdepend relation, already opened and suitably
1021 : * locked.
1022 : */
1023 : static void
1024 5990 : shdepAddDependency(Relation sdepRel,
1025 : Oid classId, Oid objectId, int32 objsubId,
1026 : Oid refclassId, Oid refobjId,
1027 : SharedDependencyType deptype)
1028 : {
1029 : HeapTuple tup;
1030 : Datum values[Natts_pg_shdepend];
1031 : bool nulls[Natts_pg_shdepend];
1032 :
1033 : /*
1034 : * Make sure the object doesn't go away while we record the dependency on
1035 : * it. DROP routines should lock the object exclusively before they check
1036 : * shared dependencies.
1037 : */
1038 5990 : shdepLockAndCheckObject(refclassId, refobjId);
1039 :
1040 5990 : memset(nulls, false, sizeof(nulls));
1041 :
1042 : /*
1043 : * Form the new tuple and record the dependency.
1044 : */
1045 5990 : values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId));
1046 5990 : values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId);
1047 5990 : values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId);
1048 5990 : values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubId);
1049 :
1050 5990 : values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
1051 5990 : values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
1052 5990 : values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
1053 :
1054 5990 : tup = heap_form_tuple(sdepRel->rd_att, values, nulls);
1055 :
1056 5990 : CatalogTupleInsert(sdepRel, tup);
1057 :
1058 : /* clean up */
1059 5990 : heap_freetuple(tup);
1060 5990 : }
1061 :
1062 : /*
1063 : * shdepDropDependency
1064 : * Internal workhorse for deleting entries from pg_shdepend.
1065 : *
1066 : * We drop entries having the following properties:
1067 : * dependent object is the one identified by classId/objectId/objsubId
1068 : * if refclassId isn't InvalidOid, it must match the entry's refclassid
1069 : * if refobjId isn't InvalidOid, it must match the entry's refobjid
1070 : * if deptype isn't SHARED_DEPENDENCY_INVALID, it must match entry's deptype
1071 : *
1072 : * If drop_subobjects is true, we ignore objsubId and consider all entries
1073 : * matching classId/objectId.
1074 : *
1075 : * sdepRel must be the pg_shdepend relation, already opened and suitably
1076 : * locked.
1077 : */
1078 : static void
1079 214492 : shdepDropDependency(Relation sdepRel,
1080 : Oid classId, Oid objectId, int32 objsubId,
1081 : bool drop_subobjects,
1082 : Oid refclassId, Oid refobjId,
1083 : SharedDependencyType deptype)
1084 : {
1085 : ScanKeyData key[4];
1086 : int nkeys;
1087 : SysScanDesc scan;
1088 : HeapTuple tup;
1089 :
1090 : /* Scan for entries matching the dependent object */
1091 214492 : ScanKeyInit(&key[0],
1092 : Anum_pg_shdepend_dbid,
1093 : BTEqualStrategyNumber, F_OIDEQ,
1094 : ObjectIdGetDatum(classIdGetDbId(classId)));
1095 214492 : ScanKeyInit(&key[1],
1096 : Anum_pg_shdepend_classid,
1097 : BTEqualStrategyNumber, F_OIDEQ,
1098 : ObjectIdGetDatum(classId));
1099 214492 : ScanKeyInit(&key[2],
1100 : Anum_pg_shdepend_objid,
1101 : BTEqualStrategyNumber, F_OIDEQ,
1102 : ObjectIdGetDatum(objectId));
1103 214492 : if (drop_subobjects)
1104 211934 : nkeys = 3;
1105 : else
1106 : {
1107 2558 : ScanKeyInit(&key[3],
1108 : Anum_pg_shdepend_objsubid,
1109 : BTEqualStrategyNumber, F_INT4EQ,
1110 : Int32GetDatum(objsubId));
1111 2558 : nkeys = 4;
1112 : }
1113 :
1114 214492 : scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
1115 : NULL, nkeys, key);
1116 :
1117 221032 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1118 : {
1119 6540 : Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
1120 :
1121 : /* Filter entries according to additional parameters */
1122 6540 : if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId)
1123 0 : continue;
1124 6540 : if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId)
1125 732 : continue;
1126 5808 : if (deptype != SHARED_DEPENDENCY_INVALID &&
1127 624 : shdepForm->deptype != deptype)
1128 0 : continue;
1129 :
1130 : /* OK, delete it */
1131 5808 : CatalogTupleDelete(sdepRel, &tup->t_self);
1132 : }
1133 :
1134 214492 : systable_endscan(scan);
1135 214492 : }
1136 :
1137 : /*
1138 : * classIdGetDbId
1139 : *
1140 : * Get the database Id that should be used in pg_shdepend, given the OID
1141 : * of the catalog containing the object. For shared objects, it's 0
1142 : * (InvalidOid); for all other objects, it's the current database Id.
1143 : */
1144 : static Oid
1145 221134 : classIdGetDbId(Oid classId)
1146 : {
1147 : Oid dbId;
1148 :
1149 221134 : if (IsSharedRelation(classId))
1150 1324 : dbId = InvalidOid;
1151 : else
1152 219810 : dbId = MyDatabaseId;
1153 :
1154 221134 : return dbId;
1155 : }
1156 :
1157 : /*
1158 : * shdepLockAndCheckObject
1159 : *
1160 : * Lock the object that we are about to record a dependency on.
1161 : * After it's locked, verify that it hasn't been dropped while we
1162 : * weren't looking. If the object has been dropped, this function
1163 : * does not return!
1164 : */
1165 : void
1166 7736 : shdepLockAndCheckObject(Oid classId, Oid objectId)
1167 : {
1168 : /* AccessShareLock should be OK, since we are not modifying the object */
1169 7736 : LockSharedObject(classId, objectId, 0, AccessShareLock);
1170 :
1171 7736 : switch (classId)
1172 : {
1173 6586 : case AuthIdRelationId:
1174 6586 : if (!SearchSysCacheExists1(AUTHOID, ObjectIdGetDatum(objectId)))
1175 0 : ereport(ERROR,
1176 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1177 : errmsg("role %u was concurrently dropped",
1178 : objectId)));
1179 6586 : break;
1180 :
1181 118 : case TableSpaceRelationId:
1182 : {
1183 : /* For lack of a syscache on pg_tablespace, do this: */
1184 118 : char *tablespace = get_tablespace_name(objectId);
1185 :
1186 118 : if (tablespace == NULL)
1187 0 : ereport(ERROR,
1188 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1189 : errmsg("tablespace %u was concurrently dropped",
1190 : objectId)));
1191 118 : pfree(tablespace);
1192 118 : break;
1193 : }
1194 :
1195 1032 : case DatabaseRelationId:
1196 : {
1197 : /* For lack of a syscache on pg_database, do this: */
1198 1032 : char *database = get_database_name(objectId);
1199 :
1200 1032 : if (database == NULL)
1201 0 : ereport(ERROR,
1202 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1203 : errmsg("database %u was concurrently dropped",
1204 : objectId)));
1205 1032 : pfree(database);
1206 1032 : break;
1207 : }
1208 :
1209 :
1210 0 : default:
1211 0 : elog(ERROR, "unrecognized shared classId: %u", classId);
1212 : }
1213 7736 : }
1214 :
1215 :
1216 : /*
1217 : * storeObjectDescription
1218 : * Append the description of a dependent object to "descs"
1219 : *
1220 : * While searching for dependencies of a shared object, we stash the
1221 : * descriptions of dependent objects we find in a single string, which we
1222 : * later pass to ereport() in the DETAIL field when somebody attempts to
1223 : * drop a referenced shared object.
1224 : *
1225 : * When type is LOCAL_OBJECT or SHARED_OBJECT, we expect object to be the
1226 : * dependent object, deptype is the dependency type, and count is not used.
1227 : * When type is REMOTE_OBJECT, we expect object to be the database object,
1228 : * and count to be nonzero; deptype is not used in this case.
1229 : */
1230 : static void
1231 588 : storeObjectDescription(StringInfo descs,
1232 : SharedDependencyObjectType type,
1233 : ObjectAddress *object,
1234 : SharedDependencyType deptype,
1235 : int count)
1236 : {
1237 588 : char *objdesc = getObjectDescription(object, false);
1238 :
1239 : /*
1240 : * An object being dropped concurrently doesn't need to be reported.
1241 : */
1242 588 : if (objdesc == NULL)
1243 0 : return;
1244 :
1245 : /* separate entries with a newline */
1246 588 : if (descs->len != 0)
1247 328 : appendStringInfoChar(descs, '\n');
1248 :
1249 588 : switch (type)
1250 : {
1251 588 : case LOCAL_OBJECT:
1252 : case SHARED_OBJECT:
1253 588 : if (deptype == SHARED_DEPENDENCY_OWNER)
1254 252 : appendStringInfo(descs, _("owner of %s"), objdesc);
1255 336 : else if (deptype == SHARED_DEPENDENCY_ACL)
1256 300 : appendStringInfo(descs, _("privileges for %s"), objdesc);
1257 36 : else if (deptype == SHARED_DEPENDENCY_POLICY)
1258 24 : appendStringInfo(descs, _("target of %s"), objdesc);
1259 12 : else if (deptype == SHARED_DEPENDENCY_TABLESPACE)
1260 12 : appendStringInfo(descs, _("tablespace for %s"), objdesc);
1261 : else
1262 0 : elog(ERROR, "unrecognized dependency type: %d",
1263 : (int) deptype);
1264 588 : break;
1265 :
1266 0 : case REMOTE_OBJECT:
1267 : /* translator: %s will always be "database %s" */
1268 0 : appendStringInfo(descs, ngettext("%d object in %s",
1269 : "%d objects in %s",
1270 : count),
1271 : count, objdesc);
1272 0 : break;
1273 :
1274 0 : default:
1275 0 : elog(ERROR, "unrecognized object type: %d", type);
1276 : }
1277 :
1278 588 : pfree(objdesc);
1279 : }
1280 :
1281 :
1282 : /*
1283 : * shdepDropOwned
1284 : *
1285 : * Drop the objects owned by any one of the given RoleIds. If a role has
1286 : * access to an object, the grant will be removed as well (but the object
1287 : * will not, of course).
1288 : *
1289 : * We can revoke grants immediately while doing the scan, but drops are
1290 : * saved up and done all at once with performMultipleDeletions. This
1291 : * is necessary so that we don't get failures from trying to delete
1292 : * interdependent objects in the wrong order.
1293 : */
1294 : void
1295 130 : shdepDropOwned(List *roleids, DropBehavior behavior)
1296 : {
1297 : Relation sdepRel;
1298 : ListCell *cell;
1299 : ObjectAddresses *deleteobjs;
1300 :
1301 130 : deleteobjs = new_object_addresses();
1302 :
1303 : /*
1304 : * We don't need this strong a lock here, but we'll call routines that
1305 : * acquire RowExclusiveLock. Better get that right now to avoid potential
1306 : * deadlock failures.
1307 : */
1308 130 : sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
1309 :
1310 : /*
1311 : * For each role, find the dependent objects and drop them using the
1312 : * regular (non-shared) dependency management.
1313 : */
1314 284 : foreach(cell, roleids)
1315 : {
1316 154 : Oid roleid = lfirst_oid(cell);
1317 : ScanKeyData key[2];
1318 : SysScanDesc scan;
1319 : HeapTuple tuple;
1320 :
1321 : /* Doesn't work for pinned objects */
1322 154 : if (IsPinnedObject(AuthIdRelationId, roleid))
1323 : {
1324 : ObjectAddress obj;
1325 :
1326 0 : obj.classId = AuthIdRelationId;
1327 0 : obj.objectId = roleid;
1328 0 : obj.objectSubId = 0;
1329 :
1330 0 : ereport(ERROR,
1331 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1332 : errmsg("cannot drop objects owned by %s because they are "
1333 : "required by the database system",
1334 : getObjectDescription(&obj, false))));
1335 : }
1336 :
1337 154 : ScanKeyInit(&key[0],
1338 : Anum_pg_shdepend_refclassid,
1339 : BTEqualStrategyNumber, F_OIDEQ,
1340 : ObjectIdGetDatum(AuthIdRelationId));
1341 154 : ScanKeyInit(&key[1],
1342 : Anum_pg_shdepend_refobjid,
1343 : BTEqualStrategyNumber, F_OIDEQ,
1344 : ObjectIdGetDatum(roleid));
1345 :
1346 154 : scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1347 : NULL, 2, key);
1348 :
1349 810 : while ((tuple = systable_getnext(scan)) != NULL)
1350 : {
1351 656 : Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1352 : ObjectAddress obj;
1353 :
1354 : /*
1355 : * We only operate on shared objects and objects in the current
1356 : * database
1357 : */
1358 656 : if (sdepForm->dbid != MyDatabaseId &&
1359 58 : sdepForm->dbid != InvalidOid)
1360 16 : continue;
1361 :
1362 640 : switch (sdepForm->deptype)
1363 : {
1364 : /* Shouldn't happen */
1365 0 : case SHARED_DEPENDENCY_INVALID:
1366 0 : elog(ERROR, "unexpected dependency type");
1367 : break;
1368 44 : case SHARED_DEPENDENCY_POLICY:
1369 :
1370 : /*
1371 : * Try to remove role from policy; if unable to, remove
1372 : * policy.
1373 : */
1374 44 : if (!RemoveRoleFromObjectPolicy(roleid,
1375 : sdepForm->classid,
1376 : sdepForm->objid))
1377 : {
1378 20 : obj.classId = sdepForm->classid;
1379 20 : obj.objectId = sdepForm->objid;
1380 20 : obj.objectSubId = sdepForm->objsubid;
1381 :
1382 : /*
1383 : * Acquire lock on object, then verify this dependency
1384 : * is still relevant. If not, the object might have
1385 : * been dropped or the policy modified. Ignore the
1386 : * object in that case.
1387 : */
1388 20 : AcquireDeletionLock(&obj, 0);
1389 20 : if (!systable_recheck_tuple(scan, tuple))
1390 : {
1391 0 : ReleaseDeletionLock(&obj);
1392 0 : break;
1393 : }
1394 20 : add_exact_object_address(&obj, deleteobjs);
1395 : }
1396 44 : break;
1397 218 : case SHARED_DEPENDENCY_ACL:
1398 :
1399 : /*
1400 : * Dependencies on role grants are recorded using
1401 : * SHARED_DEPENDENCY_ACL, but unlike a regular ACL list
1402 : * which stores all permissions for a particular object in
1403 : * a single ACL array, there's a separate catalog row for
1404 : * each grant - so removing the grant just means removing
1405 : * the entire row.
1406 : */
1407 218 : if (sdepForm->classid != AuthMemRelationId)
1408 : {
1409 212 : RemoveRoleFromObjectACL(roleid,
1410 : sdepForm->classid,
1411 : sdepForm->objid);
1412 212 : break;
1413 : }
1414 : /* FALLTHROUGH */
1415 :
1416 : case SHARED_DEPENDENCY_OWNER:
1417 :
1418 : /*
1419 : * Save it for deletion below, if it's a local object or a
1420 : * role grant. Other shared objects, such as databases,
1421 : * should not be removed here.
1422 : */
1423 384 : if (sdepForm->dbid == MyDatabaseId ||
1424 8 : sdepForm->classid == AuthMemRelationId)
1425 : {
1426 382 : obj.classId = sdepForm->classid;
1427 382 : obj.objectId = sdepForm->objid;
1428 382 : obj.objectSubId = sdepForm->objsubid;
1429 : /* as above */
1430 382 : AcquireDeletionLock(&obj, 0);
1431 382 : if (!systable_recheck_tuple(scan, tuple))
1432 : {
1433 0 : ReleaseDeletionLock(&obj);
1434 0 : break;
1435 : }
1436 382 : add_exact_object_address(&obj, deleteobjs);
1437 : }
1438 384 : break;
1439 : }
1440 640 : }
1441 :
1442 154 : systable_endscan(scan);
1443 : }
1444 :
1445 : /*
1446 : * For stability of deletion-report ordering, sort the objects into
1447 : * approximate reverse creation order before deletion. (This might also
1448 : * make the deletion go a bit faster, since there's less chance of having
1449 : * to rearrange the objects due to dependencies.)
1450 : */
1451 130 : sort_object_addresses(deleteobjs);
1452 :
1453 : /* the dependency mechanism does the actual work */
1454 130 : performMultipleDeletions(deleteobjs, behavior, 0);
1455 :
1456 124 : table_close(sdepRel, RowExclusiveLock);
1457 :
1458 124 : free_object_addresses(deleteobjs);
1459 124 : }
1460 :
1461 : /*
1462 : * shdepReassignOwned
1463 : *
1464 : * Change the owner of objects owned by any of the roles in roleids to
1465 : * newrole. Grants are not touched.
1466 : */
1467 : void
1468 20 : shdepReassignOwned(List *roleids, Oid newrole)
1469 : {
1470 : Relation sdepRel;
1471 : ListCell *cell;
1472 :
1473 : /*
1474 : * We don't need this strong a lock here, but we'll call routines that
1475 : * acquire RowExclusiveLock. Better get that right now to avoid potential
1476 : * deadlock problems.
1477 : */
1478 20 : sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
1479 :
1480 40 : foreach(cell, roleids)
1481 : {
1482 : SysScanDesc scan;
1483 : ScanKeyData key[2];
1484 : HeapTuple tuple;
1485 20 : Oid roleid = lfirst_oid(cell);
1486 :
1487 : /* Refuse to work on pinned roles */
1488 20 : if (IsPinnedObject(AuthIdRelationId, roleid))
1489 : {
1490 : ObjectAddress obj;
1491 :
1492 0 : obj.classId = AuthIdRelationId;
1493 0 : obj.objectId = roleid;
1494 0 : obj.objectSubId = 0;
1495 :
1496 0 : ereport(ERROR,
1497 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1498 : errmsg("cannot reassign ownership of objects owned by %s because they are required by the database system",
1499 : getObjectDescription(&obj, false))));
1500 :
1501 : /*
1502 : * There's no need to tell the whole truth, which is that we
1503 : * didn't track these dependencies at all ...
1504 : */
1505 : }
1506 :
1507 20 : ScanKeyInit(&key[0],
1508 : Anum_pg_shdepend_refclassid,
1509 : BTEqualStrategyNumber, F_OIDEQ,
1510 : ObjectIdGetDatum(AuthIdRelationId));
1511 20 : ScanKeyInit(&key[1],
1512 : Anum_pg_shdepend_refobjid,
1513 : BTEqualStrategyNumber, F_OIDEQ,
1514 : ObjectIdGetDatum(roleid));
1515 :
1516 20 : scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1517 : NULL, 2, key);
1518 :
1519 152 : while ((tuple = systable_getnext(scan)) != NULL)
1520 : {
1521 132 : Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1522 : MemoryContext cxt,
1523 : oldcxt;
1524 :
1525 : /*
1526 : * We only operate on shared objects and objects in the current
1527 : * database
1528 : */
1529 132 : if (sdepForm->dbid != MyDatabaseId &&
1530 24 : sdepForm->dbid != InvalidOid)
1531 0 : continue;
1532 :
1533 : /* We leave non-owner dependencies alone */
1534 132 : if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
1535 42 : continue;
1536 :
1537 : /*
1538 : * The various ALTER OWNER routines tend to leak memory in
1539 : * CurrentMemoryContext. That's not a problem when they're only
1540 : * called once per command; but in this usage where we might be
1541 : * touching many objects, it can amount to a serious memory leak.
1542 : * Fix that by running each call in a short-lived context.
1543 : */
1544 90 : cxt = AllocSetContextCreate(CurrentMemoryContext,
1545 : "shdepReassignOwned",
1546 : ALLOCSET_DEFAULT_SIZES);
1547 90 : oldcxt = MemoryContextSwitchTo(cxt);
1548 :
1549 : /* Issue the appropriate ALTER OWNER call */
1550 90 : switch (sdepForm->classid)
1551 : {
1552 24 : case TypeRelationId:
1553 24 : AlterTypeOwner_oid(sdepForm->objid, newrole, true);
1554 24 : break;
1555 :
1556 6 : case NamespaceRelationId:
1557 6 : AlterSchemaOwner_oid(sdepForm->objid, newrole);
1558 6 : break;
1559 :
1560 24 : case RelationRelationId:
1561 :
1562 : /*
1563 : * Pass recursing = true so that we don't fail on indexes,
1564 : * owned sequences, etc when we happen to visit them
1565 : * before their parent table.
1566 : */
1567 24 : ATExecChangeOwner(sdepForm->objid, newrole, true, AccessExclusiveLock);
1568 24 : break;
1569 :
1570 6 : case DefaultAclRelationId:
1571 :
1572 : /*
1573 : * Ignore default ACLs; they should be handled by DROP
1574 : * OWNED, not REASSIGN OWNED.
1575 : */
1576 6 : break;
1577 :
1578 12 : case UserMappingRelationId:
1579 : /* ditto */
1580 12 : break;
1581 :
1582 12 : case ForeignServerRelationId:
1583 12 : AlterForeignServerOwner_oid(sdepForm->objid, newrole);
1584 12 : break;
1585 :
1586 0 : case ForeignDataWrapperRelationId:
1587 0 : AlterForeignDataWrapperOwner_oid(sdepForm->objid, newrole);
1588 0 : break;
1589 :
1590 0 : case EventTriggerRelationId:
1591 0 : AlterEventTriggerOwner_oid(sdepForm->objid, newrole);
1592 0 : break;
1593 :
1594 0 : case PublicationRelationId:
1595 0 : AlterPublicationOwner_oid(sdepForm->objid, newrole);
1596 0 : break;
1597 :
1598 0 : case SubscriptionRelationId:
1599 0 : AlterSubscriptionOwner_oid(sdepForm->objid, newrole);
1600 0 : break;
1601 :
1602 : /* Generic alter owner cases */
1603 6 : case CollationRelationId:
1604 : case ConversionRelationId:
1605 : case OperatorRelationId:
1606 : case ProcedureRelationId:
1607 : case LanguageRelationId:
1608 : case LargeObjectRelationId:
1609 : case OperatorFamilyRelationId:
1610 : case OperatorClassRelationId:
1611 : case ExtensionRelationId:
1612 : case StatisticExtRelationId:
1613 : case TableSpaceRelationId:
1614 : case DatabaseRelationId:
1615 : case TSConfigRelationId:
1616 : case TSDictionaryRelationId:
1617 : {
1618 6 : Oid classId = sdepForm->classid;
1619 : Relation catalog;
1620 :
1621 6 : if (classId == LargeObjectRelationId)
1622 0 : classId = LargeObjectMetadataRelationId;
1623 :
1624 6 : catalog = table_open(classId, RowExclusiveLock);
1625 :
1626 6 : AlterObjectOwner_internal(catalog, sdepForm->objid,
1627 : newrole);
1628 :
1629 6 : table_close(catalog, NoLock);
1630 : }
1631 6 : break;
1632 :
1633 0 : default:
1634 0 : elog(ERROR, "unexpected classid %u", sdepForm->classid);
1635 : break;
1636 : }
1637 :
1638 : /* Clean up */
1639 90 : MemoryContextSwitchTo(oldcxt);
1640 90 : MemoryContextDelete(cxt);
1641 :
1642 : /* Make sure the next iteration will see my changes */
1643 90 : CommandCounterIncrement();
1644 : }
1645 :
1646 20 : systable_endscan(scan);
1647 : }
1648 :
1649 20 : table_close(sdepRel, RowExclusiveLock);
1650 20 : }
|