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