1 /*-------------------------------------------------------------------------
2 *
3 * pg_shdepend.c
4 * routines to support manipulation of the pg_shdepend relation
5 *
6 * Portions Copyright (c) 1996-2018, 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/heapam.h"
19 #include "access/htup_details.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_collation.h"
26 #include "catalog/pg_conversion.h"
27 #include "catalog/pg_database.h"
28 #include "catalog/pg_default_acl.h"
29 #include "catalog/pg_event_trigger.h"
30 #include "catalog/pg_extension.h"
QueryInterfaceIUnknown31 #include "catalog/pg_foreign_data_wrapper.h"
32 #include "catalog/pg_foreign_server.h"
33 #include "catalog/pg_language.h"
34 #include "catalog/pg_largeobject.h"
35 #include "catalog/pg_largeobject_metadata.h"
36 #include "catalog/pg_namespace.h"
37 #include "catalog/pg_operator.h"
38 #include "catalog/pg_opclass.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/collationcmds.h"
52 #include "commands/conversioncmds.h"
53 #include "commands/defrem.h"
54 #include "commands/event_trigger.h"
55 #include "commands/extension.h"
56 #include "commands/policy.h"
57 #include "commands/proclang.h"
58 #include "commands/publicationcmds.h"
59 #include "commands/schemacmds.h"
60 #include "commands/subscriptioncmds.h"
61 #include "commands/tablecmds.h"
62 #include "commands/tablespace.h"
63 #include "commands/typecmds.h"
64 #include "storage/lmgr.h"
65 #include "miscadmin.h"
66 #include "utils/acl.h"
67 #include "utils/fmgroids.h"
68 #include "utils/syscache.h"
69 #include "utils/tqual.h"
70
71
72 typedef enum
73 {
74 LOCAL_OBJECT,
75 SHARED_OBJECT,
76 REMOTE_OBJECT
77 } SharedDependencyObjectType;
78
79 static void getOidListDiff(Oid *list1, int *nlist1, Oid *list2, int *nlist2);
80 static Oid classIdGetDbId(Oid classId);
81 static void shdepChangeDep(Relation sdepRel,
82 Oid classid, Oid objid, int32 objsubid,
83 Oid refclassid, Oid refobjid,
84 SharedDependencyType deptype);
85 static void shdepAddDependency(Relation sdepRel,
86 Oid classId, Oid objectId, int32 objsubId,
87 Oid refclassId, Oid refobjId,
88 SharedDependencyType deptype);
89 static void shdepDropDependency(Relation sdepRel,
90 Oid classId, Oid objectId, int32 objsubId,
91 bool drop_subobjects,
92 Oid refclassId, Oid refobjId,
93 SharedDependencyType deptype);
94 static void storeObjectDescription(StringInfo descs,
95 SharedDependencyObjectType type,
96 ObjectAddress *object,
97 SharedDependencyType deptype,
98 int count);
99 static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel);
100
101
102 /*
103 * recordSharedDependencyOn
104 *
105 * Record a dependency between 2 objects via their respective ObjectAddresses.
106 * The first argument is the dependent object, the second the one it
107 * references (which must be a shared object).
108 *
109 * This locks the referenced object and makes sure it still exists.
110 * Then it creates an entry in pg_shdepend. The lock is kept until
111 * the end of the transaction.
112 *
113 * Dependencies on pinned objects are not recorded.
114 */
115 void
116 recordSharedDependencyOn(ObjectAddress *depender,
117 ObjectAddress *referenced,
118 SharedDependencyType deptype)
119 {
120 Relation sdepRel;
121
122 /*
123 * Objects in pg_shdepend can't have SubIds.
124 */
125 Assert(depender->objectSubId == 0);
126 Assert(referenced->objectSubId == 0);
127
128 /*
129 * During bootstrap, do nothing since pg_shdepend may not exist yet.
130 * initdb will fill in appropriate pg_shdepend entries after bootstrap.
131 */
132 if (IsBootstrapProcessingMode())
133 return;
134
135 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
136
137 /* If the referenced object is pinned, do nothing. */
138 if (!isSharedObjectPinned(referenced->classId, referenced->objectId,
139 sdepRel))
140 {
141 shdepAddDependency(sdepRel, depender->classId, depender->objectId,
142 depender->objectSubId,
143 referenced->classId, referenced->objectId,
144 deptype);
145 }
146
147 heap_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 recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
161 {
162 ObjectAddress myself,
163 referenced;
164
165 myself.classId = classId;
166 myself.objectId = objectId;
167 myself.objectSubId = 0;
168
169 referenced.classId = AuthIdRelationId;
170 referenced.objectId = owner;
171 referenced.objectSubId = 0;
172
173 recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER);
174 }
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 shdepChangeDep(Relation sdepRel,
199 Oid classid, Oid objid, int32 objsubid,
200 Oid refclassid, Oid refobjid,
201 SharedDependencyType deptype)
202 {
203 Oid dbid = classIdGetDbId(classid);
204 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 shdepLockAndCheckObject(refclassid, refobjid);
214
215 /*
216 * Look for a previous entry
217 */
218 ScanKeyInit(&key[0],
219 Anum_pg_shdepend_dbid,
220 BTEqualStrategyNumber, F_OIDEQ,
221 ObjectIdGetDatum(dbid));
222 ScanKeyInit(&key[1],
223 Anum_pg_shdepend_classid,
224 BTEqualStrategyNumber, F_OIDEQ,
225 ObjectIdGetDatum(classid));
226 ScanKeyInit(&key[2],
227 Anum_pg_shdepend_objid,
228 BTEqualStrategyNumber, F_OIDEQ,
229 ObjectIdGetDatum(objid));
230 ScanKeyInit(&key[3],
231 Anum_pg_shdepend_objsubid,
232 BTEqualStrategyNumber, F_INT4EQ,
233 Int32GetDatum(objsubid));
234
235 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
236 NULL, 4, key);
237
238 while ((scantup = systable_getnext(scan)) != NULL)
239 {
240 /* Ignore if not of the target dependency type */
241 if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype)
242 continue;
243 /* Caller screwed up if multiple matches */
244 if (oldtup)
245 elog(ERROR,
246 "multiple pg_shdepend entries for object %u/%u/%d deptype %c",
247 classid, objid, objsubid, deptype);
248 oldtup = heap_copytuple(scantup);
249 }
250
251 systable_endscan(scan);
252
253 if (isSharedObjectPinned(refclassid, refobjid, sdepRel))
254 {
255 /* No new entry needed, so just delete existing entry if any */
256 if (oldtup)
257 CatalogTupleDelete(sdepRel, &oldtup->t_self);
258 }
259 else if (oldtup)
260 {
261 /* Need to update existing entry */
262 Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup);
263
264 /* Since oldtup is a copy, we can just modify it in-memory */
265 shForm->refclassid = refclassid;
266 shForm->refobjid = refobjid;
267
268 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 memset(nulls, false, sizeof(nulls));
277
278 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid);
279 values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid);
280 values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid);
281 values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubid);
282
283 values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
284 values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
285 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 oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls);
292 CatalogTupleInsert(sdepRel, oldtup);
293 }
294
295 if (oldtup)
296 heap_freetuple(oldtup);
297 }
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 changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
309 {
310 Relation sdepRel;
311
312 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
313
314 /* Adjust the SHARED_DEPENDENCY_OWNER entry */
315 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 shdepDropDependency(sdepRel, classId, objectId, 0, true,
340 AuthIdRelationId, newOwnerId,
341 SHARED_DEPENDENCY_ACL);
342
343 heap_close(sdepRel, RowExclusiveLock);
344 }
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 recordDependencyOnTablespace(Oid classId, Oid objectId, Oid tablespace)
357 {
358 ObjectAddress myself,
359 referenced;
360
361 ObjectAddressSet(myself, classId, objectId);
362 ObjectAddressSet(referenced, TableSpaceRelationId, tablespace);
363
364 recordSharedDependencyOn(&myself, &referenced,
365 SHARED_DEPENDENCY_TABLESPACE);
366 }
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 changeDependencyOnTablespace(Oid classId, Oid objectId, Oid newTablespaceId)
378 {
379 Relation sdepRel;
380
381 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
382
383 if (newTablespaceId != DEFAULTTABLESPACE_OID &&
384 newTablespaceId != InvalidOid)
385 shdepChangeDep(sdepRel,
386 classId, objectId, 0,
387 TableSpaceRelationId, newTablespaceId,
388 SHARED_DEPENDENCY_TABLESPACE);
389 else
390 shdepDropDependency(sdepRel,
391 classId, objectId, 0, true,
392 InvalidOid, InvalidOid,
393 SHARED_DEPENDENCY_INVALID);
394
395 heap_close(sdepRel, RowExclusiveLock);
396 }
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 getOidListDiff(Oid *list1, int *nlist1, Oid *list2, int *nlist2)
408 {
409 int in1,
410 in2,
411 out1,
412 out2;
413
414 in1 = in2 = out1 = out2 = 0;
415 while (in1 < *nlist1 && in2 < *nlist2)
416 {
417 if (list1[in1] == list2[in2])
418 {
419 /* skip over duplicates */
420 in1++;
421 in2++;
422 }
423 else if (list1[in1] < list2[in2])
424 {
425 /* list1[in1] is not in list2 */
426 list1[out1++] = list1[in1++];
427 }
428 else
429 {
430 /* list2[in2] is not in list1 */
431 list2[out2++] = list2[in2++];
432 }
433 }
434
435 /* any remaining list1 entries are not in list2 */
436 while (in1 < *nlist1)
437 {
438 list1[out1++] = list1[in1++];
439 }
440
441 /* any remaining list2 entries are not in list1 */
442 while (in2 < *nlist2)
443 {
444 list2[out2++] = list2[in2++];
445 }
446
447 *nlist1 = out1;
448 *nlist2 = out2;
449 }
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 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 getOidListDiff(oldmembers, &noldmembers, newmembers, &nnewmembers);
492
493 if (noldmembers > 0 || nnewmembers > 0)
494 {
495 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
496
497 /* Add new dependencies that weren't already present */
498 for (i = 0; i < nnewmembers; i++)
499 {
500 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 if (roleid == ownerId)
508 continue;
509
510 /* Skip pinned roles; they don't need dependency entries */
511 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
512 continue;
513
514 shdepAddDependency(sdepRel, classId, objectId, objsubId,
515 AuthIdRelationId, roleid,
516 SHARED_DEPENDENCY_ACL);
517 }
518
519 /* Drop no-longer-used old dependencies */
520 for (i = 0; i < noldmembers; i++)
521 {
522 Oid roleid = oldmembers[i];
523
524 /* Skip the owner, same as above */
525 if (roleid == ownerId)
526 continue;
527
528 /* Skip pinned roles */
529 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
530 continue;
531
532 shdepDropDependency(sdepRel, classId, objectId, objsubId,
533 false, /* exact match on objsubId */
534 AuthIdRelationId, roleid,
535 SHARED_DEPENDENCY_ACL);
536 }
537
538 heap_close(sdepRel, RowExclusiveLock);
539 }
540
541 if (oldmembers)
542 pfree(oldmembers);
543 if (newmembers)
544 pfree(newmembers);
545 }
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 * checkSharedDependencies
558 *
559 * Check whether there are shared dependency entries for a given shared
560 * object; return true if so.
561 *
562 * In addition, return a string containing a newline-separated list of object
563 * descriptions that depend on the shared object, or NULL if none is found.
564 * We actually return two such strings; the "detail" result is suitable for
565 * returning to the client as an errdetail() string, and is limited in size.
566 * The "detail_log" string is potentially much longer, and should be emitted
567 * to the server log only.
568 *
569 * We can find three different kinds of dependencies: dependencies on objects
570 * of the current database; dependencies on shared objects; and dependencies
571 * on objects local to other databases. We can (and do) provide descriptions
572 * of the two former kinds of objects, but we can't do that for "remote"
573 * objects, so we just provide a count of them.
574 *
575 * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early.
576 */
577 bool
578 checkSharedDependencies(Oid classId, Oid objectId,
579 char **detail_msg, char **detail_log_msg)
580 {
581 Relation sdepRel;
582 ScanKeyData key[2];
583 SysScanDesc scan;
584 HeapTuple tup;
585 int numReportedDeps = 0;
586 int numNotReportedDeps = 0;
587 int numNotReportedDbs = 0;
588 List *remDeps = NIL;
589 ListCell *cell;
590 ObjectAddress object;
591 StringInfoData descs;
592 StringInfoData alldescs;
593
594 /*
595 * We limit the number of dependencies reported to the client to
596 * MAX_REPORTED_DEPS, since client software may not deal well with
597 * enormous error strings. The server log always gets a full report.
598 */
599 #define MAX_REPORTED_DEPS 100
600
601 initStringInfo(&descs);
602 initStringInfo(&alldescs);
603
604 sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
605
606 ScanKeyInit(&key[0],
607 Anum_pg_shdepend_refclassid,
608 BTEqualStrategyNumber, F_OIDEQ,
609 ObjectIdGetDatum(classId));
610 ScanKeyInit(&key[1],
611 Anum_pg_shdepend_refobjid,
612 BTEqualStrategyNumber, F_OIDEQ,
613 ObjectIdGetDatum(objectId));
614
615 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
616 NULL, 2, key);
617
618 while (HeapTupleIsValid(tup = systable_getnext(scan)))
619 {
620 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
621
622 /* This case can be dispatched quickly */
623 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
624 {
625 object.classId = classId;
626 object.objectId = objectId;
627 object.objectSubId = 0;
628 ereport(ERROR,
629 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
630 errmsg("cannot drop %s because it is required by the database system",
631 getObjectDescription(&object))));
632 }
633
634 object.classId = sdepForm->classid;
635 object.objectId = sdepForm->objid;
636 object.objectSubId = sdepForm->objsubid;
637
638 /*
639 * If it's a dependency local to this database or it's a shared
640 * object, describe it.
641 *
642 * If it's a remote dependency, keep track of it so we can report the
643 * number of them later.
644 */
645 if (sdepForm->dbid == MyDatabaseId)
646 {
647 if (numReportedDeps < MAX_REPORTED_DEPS)
648 {
649 numReportedDeps++;
650 storeObjectDescription(&descs, LOCAL_OBJECT, &object,
651 sdepForm->deptype, 0);
652 }
653 else
654 numNotReportedDeps++;
655 storeObjectDescription(&alldescs, LOCAL_OBJECT, &object,
656 sdepForm->deptype, 0);
657 }
658 else if (sdepForm->dbid == InvalidOid)
659 {
660 if (numReportedDeps < MAX_REPORTED_DEPS)
661 {
662 numReportedDeps++;
663 storeObjectDescription(&descs, SHARED_OBJECT, &object,
664 sdepForm->deptype, 0);
665 }
666 else
667 numNotReportedDeps++;
668 storeObjectDescription(&alldescs, SHARED_OBJECT, &object,
669 sdepForm->deptype, 0);
670 }
671 else
672 {
673 /* It's not local nor shared, so it must be remote. */
674 remoteDep *dep;
675 bool stored = false;
676
677 /*
678 * XXX this info is kept on a simple List. Maybe it's not good
679 * for performance, but using a hash table seems needlessly
680 * complex. The expected number of databases is not high anyway,
681 * I suppose.
682 */
683 foreach(cell, remDeps)
684 {
685 dep = lfirst(cell);
686 if (dep->dbOid == sdepForm->dbid)
687 {
688 dep->count++;
689 stored = true;
690 break;
691 }
692 }
693 if (!stored)
694 {
695 dep = (remoteDep *) palloc(sizeof(remoteDep));
696 dep->dbOid = sdepForm->dbid;
697 dep->count = 1;
698 remDeps = lappend(remDeps, dep);
699 }
700 }
701 }
702
703 systable_endscan(scan);
704
705 heap_close(sdepRel, AccessShareLock);
706
707 /*
708 * Summarize dependencies in remote databases.
709 */
710 foreach(cell, remDeps)
711 {
712 remoteDep *dep = lfirst(cell);
713
714 object.classId = DatabaseRelationId;
715 object.objectId = dep->dbOid;
716 object.objectSubId = 0;
717
718 if (numReportedDeps < MAX_REPORTED_DEPS)
719 {
720 numReportedDeps++;
721 storeObjectDescription(&descs, REMOTE_OBJECT, &object,
722 SHARED_DEPENDENCY_INVALID, dep->count);
723 }
724 else
725 numNotReportedDbs++;
726 storeObjectDescription(&alldescs, REMOTE_OBJECT, &object,
727 SHARED_DEPENDENCY_INVALID, dep->count);
728 }
729
730 list_free_deep(remDeps);
731
732 if (descs.len == 0)
733 {
734 pfree(descs.data);
735 pfree(alldescs.data);
736 *detail_msg = *detail_log_msg = NULL;
737 return false;
738 }
739
740 if (numNotReportedDeps > 0)
741 appendStringInfo(&descs, ngettext("\nand %d other object "
742 "(see server log for list)",
743 "\nand %d other objects "
744 "(see server log for list)",
745 numNotReportedDeps),
746 numNotReportedDeps);
747 if (numNotReportedDbs > 0)
748 appendStringInfo(&descs, ngettext("\nand objects in %d other database "
749 "(see server log for list)",
750 "\nand objects in %d other databases "
751 "(see server log for list)",
752 numNotReportedDbs),
753 numNotReportedDbs);
754
755 *detail_msg = descs.data;
756 *detail_log_msg = alldescs.data;
757 return true;
758 }
759
760 /*
761 * copyTemplateDependencies
762 *
763 * Routine to create the initial shared dependencies of a new database.
764 * We simply copy the dependencies from the template database.
765 */
766 void
767 copyTemplateDependencies(Oid templateDbId, Oid newDbId)
768 {
769 Relation sdepRel;
770 TupleDesc sdepDesc;
771 ScanKeyData key[1];
772 SysScanDesc scan;
773 HeapTuple tup;
774 CatalogIndexState indstate;
775 Datum values[Natts_pg_shdepend];
776 bool nulls[Natts_pg_shdepend];
777 bool replace[Natts_pg_shdepend];
778
779 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
780 sdepDesc = RelationGetDescr(sdepRel);
781
782 indstate = CatalogOpenIndexes(sdepRel);
783
784 /* Scan all entries with dbid = templateDbId */
785 ScanKeyInit(&key[0],
786 Anum_pg_shdepend_dbid,
787 BTEqualStrategyNumber, F_OIDEQ,
788 ObjectIdGetDatum(templateDbId));
789
790 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
791 NULL, 1, key);
792
793 /* Set up to copy the tuples except for inserting newDbId */
794 memset(values, 0, sizeof(values));
795 memset(nulls, false, sizeof(nulls));
796 memset(replace, false, sizeof(replace));
797
798 replace[Anum_pg_shdepend_dbid - 1] = true;
799 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId);
800
801 /*
802 * Copy the entries of the original database, changing the database Id to
803 * that of the new database. Note that because we are not copying rows
804 * with dbId == 0 (ie, rows describing dependent shared objects) we won't
805 * copy the ownership dependency of the template database itself; this is
806 * what we want.
807 */
808 while (HeapTupleIsValid(tup = systable_getnext(scan)))
809 {
810 HeapTuple newtup;
811
812 newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace);
813 CatalogTupleInsertWithInfo(sdepRel, newtup, indstate);
814
815 heap_freetuple(newtup);
816 }
817
818 systable_endscan(scan);
819
820 CatalogCloseIndexes(indstate);
821 heap_close(sdepRel, RowExclusiveLock);
822 }
823
824 /*
825 * dropDatabaseDependencies
826 *
827 * Delete pg_shdepend entries corresponding to a database that's being
828 * dropped.
829 */
830 void
831 dropDatabaseDependencies(Oid databaseId)
832 {
833 Relation sdepRel;
834 ScanKeyData key[1];
835 SysScanDesc scan;
836 HeapTuple tup;
837
838 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
839
840 /*
841 * First, delete all the entries that have the database Oid in the dbid
842 * field.
843 */
844 ScanKeyInit(&key[0],
845 Anum_pg_shdepend_dbid,
846 BTEqualStrategyNumber, F_OIDEQ,
847 ObjectIdGetDatum(databaseId));
848 /* We leave the other index fields unspecified */
849
850 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
851 NULL, 1, key);
852
853 while (HeapTupleIsValid(tup = systable_getnext(scan)))
854 {
855 CatalogTupleDelete(sdepRel, &tup->t_self);
856 }
857
858 systable_endscan(scan);
859
860 /* Now delete all entries corresponding to the database itself */
861 shdepDropDependency(sdepRel, DatabaseRelationId, databaseId, 0, true,
862 InvalidOid, InvalidOid,
863 SHARED_DEPENDENCY_INVALID);
864
865 heap_close(sdepRel, RowExclusiveLock);
866 }
867
868 /*
869 * deleteSharedDependencyRecordsFor
870 *
871 * Delete all pg_shdepend entries corresponding to an object that's being
872 * dropped or modified. The object is assumed to be either a shared object
873 * or local to the current database (the classId tells us which).
874 *
875 * If objectSubId is zero, we are deleting a whole object, so get rid of
876 * pg_shdepend entries for subobjects as well.
877 */
878 void
879 deleteSharedDependencyRecordsFor(Oid classId, Oid objectId, int32 objectSubId)
880 {
881 Relation sdepRel;
882
883 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
884
885 shdepDropDependency(sdepRel, classId, objectId, objectSubId,
886 (objectSubId == 0),
887 InvalidOid, InvalidOid,
888 SHARED_DEPENDENCY_INVALID);
889
890 heap_close(sdepRel, RowExclusiveLock);
891 }
892
893 /*
894 * shdepAddDependency
895 * Internal workhorse for inserting into pg_shdepend
896 *
897 * sdepRel must be the pg_shdepend relation, already opened and suitably
898 * locked.
899 */
900 static void
901 shdepAddDependency(Relation sdepRel,
902 Oid classId, Oid objectId, int32 objsubId,
903 Oid refclassId, Oid refobjId,
904 SharedDependencyType deptype)
905 {
906 HeapTuple tup;
907 Datum values[Natts_pg_shdepend];
908 bool nulls[Natts_pg_shdepend];
909
910 /*
911 * Make sure the object doesn't go away while we record the dependency on
912 * it. DROP routines should lock the object exclusively before they check
913 * shared dependencies.
914 */
915 shdepLockAndCheckObject(refclassId, refobjId);
916
917 memset(nulls, false, sizeof(nulls));
918
919 /*
920 * Form the new tuple and record the dependency.
921 */
922 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId));
923 values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId);
924 values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId);
925 values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubId);
926
927 values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
928 values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
929 values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
930
931 tup = heap_form_tuple(sdepRel->rd_att, values, nulls);
932
933 CatalogTupleInsert(sdepRel, tup);
934
935 /* clean up */
936 heap_freetuple(tup);
937 }
938
939 /*
940 * shdepDropDependency
941 * Internal workhorse for deleting entries from pg_shdepend.
942 *
943 * We drop entries having the following properties:
944 * dependent object is the one identified by classId/objectId/objsubId
945 * if refclassId isn't InvalidOid, it must match the entry's refclassid
946 * if refobjId isn't InvalidOid, it must match the entry's refobjid
947 * if deptype isn't SHARED_DEPENDENCY_INVALID, it must match entry's deptype
948 *
949 * If drop_subobjects is true, we ignore objsubId and consider all entries
950 * matching classId/objectId.
951 *
952 * sdepRel must be the pg_shdepend relation, already opened and suitably
953 * locked.
954 */
955 static void
956 shdepDropDependency(Relation sdepRel,
957 Oid classId, Oid objectId, int32 objsubId,
958 bool drop_subobjects,
959 Oid refclassId, Oid refobjId,
960 SharedDependencyType deptype)
961 {
962 ScanKeyData key[4];
963 int nkeys;
964 SysScanDesc scan;
965 HeapTuple tup;
966
967 /* Scan for entries matching the dependent object */
968 ScanKeyInit(&key[0],
969 Anum_pg_shdepend_dbid,
970 BTEqualStrategyNumber, F_OIDEQ,
971 ObjectIdGetDatum(classIdGetDbId(classId)));
972 ScanKeyInit(&key[1],
973 Anum_pg_shdepend_classid,
974 BTEqualStrategyNumber, F_OIDEQ,
975 ObjectIdGetDatum(classId));
976 ScanKeyInit(&key[2],
977 Anum_pg_shdepend_objid,
978 BTEqualStrategyNumber, F_OIDEQ,
979 ObjectIdGetDatum(objectId));
980 if (drop_subobjects)
981 nkeys = 3;
982 else
983 {
984 ScanKeyInit(&key[3],
985 Anum_pg_shdepend_objsubid,
986 BTEqualStrategyNumber, F_INT4EQ,
987 Int32GetDatum(objsubId));
988 nkeys = 4;
989 }
990
991 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
992 NULL, nkeys, key);
993
994 while (HeapTupleIsValid(tup = systable_getnext(scan)))
995 {
996 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
997
998 /* Filter entries according to additional parameters */
999 if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId)
1000 continue;
1001 if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId)
1002 continue;
1003 if (deptype != SHARED_DEPENDENCY_INVALID &&
1004 shdepForm->deptype != deptype)
1005 continue;
1006
1007 /* OK, delete it */
1008 CatalogTupleDelete(sdepRel, &tup->t_self);
1009 }
1010
1011 systable_endscan(scan);
1012 }
1013
1014 /*
1015 * classIdGetDbId
1016 *
1017 * Get the database Id that should be used in pg_shdepend, given the OID
1018 * of the catalog containing the object. For shared objects, it's 0
1019 * (InvalidOid); for all other objects, it's the current database Id.
1020 */
1021 static Oid
1022 classIdGetDbId(Oid classId)
1023 {
1024 Oid dbId;
1025
1026 if (IsSharedRelation(classId))
1027 dbId = InvalidOid;
1028 else
1029 dbId = MyDatabaseId;
1030
1031 return dbId;
1032 }
1033
1034 /*
1035 * shdepLockAndCheckObject
1036 *
1037 * Lock the object that we are about to record a dependency on.
1038 * After it's locked, verify that it hasn't been dropped while we
1039 * weren't looking. If the object has been dropped, this function
1040 * does not return!
1041 */
1042 void
1043 shdepLockAndCheckObject(Oid classId, Oid objectId)
1044 {
1045 /* AccessShareLock should be OK, since we are not modifying the object */
1046 LockSharedObject(classId, objectId, 0, AccessShareLock);
1047
1048 switch (classId)
1049 {
1050 case AuthIdRelationId:
1051 if (!SearchSysCacheExists1(AUTHOID, ObjectIdGetDatum(objectId)))
1052 ereport(ERROR,
1053 (errcode(ERRCODE_UNDEFINED_OBJECT),
1054 errmsg("role %u was concurrently dropped",
1055 objectId)));
1056 break;
1057
1058 case TableSpaceRelationId:
1059 {
1060 /* For lack of a syscache on pg_tablespace, do this: */
1061 char *tablespace = get_tablespace_name(objectId);
1062
1063 if (tablespace == NULL)
1064 ereport(ERROR,
1065 (errcode(ERRCODE_UNDEFINED_OBJECT),
1066 errmsg("tablespace %u was concurrently dropped",
1067 objectId)));
1068 pfree(tablespace);
1069 break;
1070 }
1071
1072 case DatabaseRelationId:
1073 {
1074 /* For lack of a syscache on pg_database, do this: */
1075 char *database = get_database_name(objectId);
1076
1077 if (database == NULL)
1078 ereport(ERROR,
1079 (errcode(ERRCODE_UNDEFINED_OBJECT),
1080 errmsg("database %u was concurrently dropped",
1081 objectId)));
1082 pfree(database);
1083 break;
1084 }
1085
1086
1087 default:
1088 elog(ERROR, "unrecognized shared classId: %u", classId);
1089 }
1090 }
1091
1092
1093 /*
1094 * storeObjectDescription
1095 * Append the description of a dependent object to "descs"
1096 *
1097 * While searching for dependencies of a shared object, we stash the
1098 * descriptions of dependent objects we find in a single string, which we
1099 * later pass to ereport() in the DETAIL field when somebody attempts to
1100 * drop a referenced shared object.
1101 *
1102 * When type is LOCAL_OBJECT or SHARED_OBJECT, we expect object to be the
1103 * dependent object, deptype is the dependency type, and count is not used.
1104 * When type is REMOTE_OBJECT, we expect object to be the database object,
1105 * and count to be nonzero; deptype is not used in this case.
1106 */
1107 static void
1108 storeObjectDescription(StringInfo descs,
1109 SharedDependencyObjectType type,
1110 ObjectAddress *object,
1111 SharedDependencyType deptype,
1112 int count)
1113 {
1114 char *objdesc = getObjectDescription(object);
1115
1116 /*
1117 * An object being dropped concurrently doesn't need to be reported.
1118 */
1119 if (objdesc == NULL)
1120 return;
1121
1122 /* separate entries with a newline */
1123 if (descs->len != 0)
1124 appendStringInfoChar(descs, '\n');
1125
1126 switch (type)
1127 {
1128 case LOCAL_OBJECT:
1129 case SHARED_OBJECT:
1130 if (deptype == SHARED_DEPENDENCY_OWNER)
1131 appendStringInfo(descs, _("owner of %s"), objdesc);
1132 else if (deptype == SHARED_DEPENDENCY_ACL)
1133 appendStringInfo(descs, _("privileges for %s"), objdesc);
1134 else if (deptype == SHARED_DEPENDENCY_POLICY)
1135 appendStringInfo(descs, _("target of %s"), objdesc);
1136 else if (deptype == SHARED_DEPENDENCY_TABLESPACE)
1137 appendStringInfo(descs, _("tablespace for %s"), objdesc);
1138 else
1139 elog(ERROR, "unrecognized dependency type: %d",
1140 (int) deptype);
1141 break;
1142
1143 case REMOTE_OBJECT:
1144 /* translator: %s will always be "database %s" */
1145 appendStringInfo(descs, ngettext("%d object in %s",
1146 "%d objects in %s",
1147 count),
1148 count, objdesc);
1149 break;
1150
1151 default:
1152 elog(ERROR, "unrecognized object type: %d", type);
1153 }
1154
1155 pfree(objdesc);
1156 }
1157
1158
1159 /*
1160 * isSharedObjectPinned
1161 * Return whether a given shared object has a SHARED_DEPENDENCY_PIN entry.
1162 *
1163 * sdepRel must be the pg_shdepend relation, already opened and suitably
1164 * locked.
1165 */
1166 static bool
1167 isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
1168 {
1169 bool result = false;
1170 ScanKeyData key[2];
1171 SysScanDesc scan;
1172 HeapTuple tup;
1173
1174 ScanKeyInit(&key[0],
1175 Anum_pg_shdepend_refclassid,
1176 BTEqualStrategyNumber, F_OIDEQ,
1177 ObjectIdGetDatum(classId));
1178 ScanKeyInit(&key[1],
1179 Anum_pg_shdepend_refobjid,
1180 BTEqualStrategyNumber, F_OIDEQ,
1181 ObjectIdGetDatum(objectId));
1182
1183 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1184 NULL, 2, key);
1185
1186 /*
1187 * Since we won't generate additional pg_shdepend entries for pinned
1188 * objects, there can be at most one entry referencing a pinned object.
1189 * Hence, it's sufficient to look at the first returned tuple; we don't
1190 * need to loop.
1191 */
1192 tup = systable_getnext(scan);
1193 if (HeapTupleIsValid(tup))
1194 {
1195 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
1196
1197 if (shdepForm->deptype == SHARED_DEPENDENCY_PIN)
1198 result = true;
1199 }
1200
1201 systable_endscan(scan);
1202
1203 return result;
1204 }
1205
1206 /*
1207 * shdepDropOwned
1208 *
1209 * Drop the objects owned by any one of the given RoleIds. If a role has
1210 * access to an object, the grant will be removed as well (but the object
1211 * will not, of course).
1212 *
1213 * We can revoke grants immediately while doing the scan, but drops are
1214 * saved up and done all at once with performMultipleDeletions. This
1215 * is necessary so that we don't get failures from trying to delete
1216 * interdependent objects in the wrong order.
1217 */
1218 void
1219 shdepDropOwned(List *roleids, DropBehavior behavior)
1220 {
1221 Relation sdepRel;
1222 ListCell *cell;
1223 ObjectAddresses *deleteobjs;
1224
1225 deleteobjs = new_object_addresses();
1226
1227 /*
1228 * We don't need this strong a lock here, but we'll call routines that
1229 * acquire RowExclusiveLock. Better get that right now to avoid potential
1230 * deadlock failures.
1231 */
1232 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
1233
1234 /*
1235 * For each role, find the dependent objects and drop them using the
1236 * regular (non-shared) dependency management.
1237 */
1238 foreach(cell, roleids)
1239 {
1240 Oid roleid = lfirst_oid(cell);
1241 ScanKeyData key[2];
1242 SysScanDesc scan;
1243 HeapTuple tuple;
1244
1245 /* Doesn't work for pinned objects */
1246 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1247 {
1248 ObjectAddress obj;
1249
1250 obj.classId = AuthIdRelationId;
1251 obj.objectId = roleid;
1252 obj.objectSubId = 0;
1253
1254 ereport(ERROR,
1255 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1256 errmsg("cannot drop objects owned by %s because they are "
1257 "required by the database system",
1258 getObjectDescription(&obj))));
1259 }
1260
1261 ScanKeyInit(&key[0],
1262 Anum_pg_shdepend_refclassid,
1263 BTEqualStrategyNumber, F_OIDEQ,
1264 ObjectIdGetDatum(AuthIdRelationId));
1265 ScanKeyInit(&key[1],
1266 Anum_pg_shdepend_refobjid,
1267 BTEqualStrategyNumber, F_OIDEQ,
1268 ObjectIdGetDatum(roleid));
1269
1270 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1271 NULL, 2, key);
1272
1273 while ((tuple = systable_getnext(scan)) != NULL)
1274 {
1275 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1276 ObjectAddress obj;
1277
1278 /*
1279 * We only operate on shared objects and objects in the current
1280 * database
1281 */
1282 if (sdepForm->dbid != MyDatabaseId &&
1283 sdepForm->dbid != InvalidOid)
1284 continue;
1285
1286 switch (sdepForm->deptype)
1287 {
1288 /* Shouldn't happen */
1289 case SHARED_DEPENDENCY_PIN:
1290 case SHARED_DEPENDENCY_INVALID:
1291 elog(ERROR, "unexpected dependency type");
1292 break;
1293 case SHARED_DEPENDENCY_ACL:
1294 RemoveRoleFromObjectACL(roleid,
1295 sdepForm->classid,
1296 sdepForm->objid);
1297 break;
1298 case SHARED_DEPENDENCY_POLICY:
1299 /*
1300 * Try to remove role from policy; if unable to, remove
1301 * policy.
1302 */
1303 if (!RemoveRoleFromObjectPolicy(roleid,
1304 sdepForm->classid,
1305 sdepForm->objid))
1306 {
1307 obj.classId = sdepForm->classid;
1308 obj.objectId = sdepForm->objid;
1309 obj.objectSubId = sdepForm->objsubid;
1310 /*
1311 * Acquire lock on object, then verify this dependency
1312 * is still relevant. If not, the object might have
1313 * been dropped or the policy modified. Ignore the
1314 * object in that case.
1315 */
1316 AcquireDeletionLock(&obj, 0);
1317 if (!systable_recheck_tuple(scan, tuple))
1318 {
1319 ReleaseDeletionLock(&obj);
1320 break;
1321 }
1322 add_exact_object_address(&obj, deleteobjs);
1323 }
1324 break;
1325 case SHARED_DEPENDENCY_OWNER:
1326 /* If a local object, save it for deletion below */
1327 if (sdepForm->dbid == MyDatabaseId)
1328 {
1329 obj.classId = sdepForm->classid;
1330 obj.objectId = sdepForm->objid;
1331 obj.objectSubId = sdepForm->objsubid;
1332 /* as above */
1333 AcquireDeletionLock(&obj, 0);
1334 if (!systable_recheck_tuple(scan, tuple))
1335 {
1336 ReleaseDeletionLock(&obj);
1337 break;
1338 }
1339 add_exact_object_address(&obj, deleteobjs);
1340 }
1341 break;
1342 }
1343 }
1344
1345 systable_endscan(scan);
1346 }
1347
1348 /* the dependency mechanism does the actual work */
1349 performMultipleDeletions(deleteobjs, behavior, 0);
1350
1351 heap_close(sdepRel, RowExclusiveLock);
1352
1353 free_object_addresses(deleteobjs);
1354 }
1355
1356 /*
1357 * shdepReassignOwned
1358 *
1359 * Change the owner of objects owned by any of the roles in roleids to
1360 * newrole. Grants are not touched.
1361 */
1362 void
1363 shdepReassignOwned(List *roleids, Oid newrole)
1364 {
1365 Relation sdepRel;
1366 ListCell *cell;
1367
1368 /*
1369 * We don't need this strong a lock here, but we'll call routines that
1370 * acquire RowExclusiveLock. Better get that right now to avoid potential
1371 * deadlock problems.
1372 */
1373 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
1374
1375 foreach(cell, roleids)
1376 {
1377 SysScanDesc scan;
1378 ScanKeyData key[2];
1379 HeapTuple tuple;
1380 Oid roleid = lfirst_oid(cell);
1381
1382 /* Refuse to work on pinned roles */
1383 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1384 {
1385 ObjectAddress obj;
1386
1387 obj.classId = AuthIdRelationId;
1388 obj.objectId = roleid;
1389 obj.objectSubId = 0;
1390
1391 ereport(ERROR,
1392 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1393 errmsg("cannot reassign ownership of objects owned by %s because they are required by the database system",
1394 getObjectDescription(&obj))));
1395
1396 /*
1397 * There's no need to tell the whole truth, which is that we
1398 * didn't track these dependencies at all ...
1399 */
1400 }
1401
1402 ScanKeyInit(&key[0],
1403 Anum_pg_shdepend_refclassid,
1404 BTEqualStrategyNumber, F_OIDEQ,
1405 ObjectIdGetDatum(AuthIdRelationId));
1406 ScanKeyInit(&key[1],
1407 Anum_pg_shdepend_refobjid,
1408 BTEqualStrategyNumber, F_OIDEQ,
1409 ObjectIdGetDatum(roleid));
1410
1411 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1412 NULL, 2, key);
1413
1414 while ((tuple = systable_getnext(scan)) != NULL)
1415 {
1416 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1417
1418 /*
1419 * We only operate on shared objects and objects in the current
1420 * database
1421 */
1422 if (sdepForm->dbid != MyDatabaseId &&
1423 sdepForm->dbid != InvalidOid)
1424 continue;
1425
1426 /* Unexpected because we checked for pins above */
1427 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
1428 elog(ERROR, "unexpected shared pin");
1429
1430 /* We leave non-owner dependencies alone */
1431 if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
1432 continue;
1433
1434 /* Issue the appropriate ALTER OWNER call */
1435 switch (sdepForm->classid)
1436 {
1437 case TypeRelationId:
1438 AlterTypeOwner_oid(sdepForm->objid, newrole, true);
1439 break;
1440
1441 case NamespaceRelationId:
1442 AlterSchemaOwner_oid(sdepForm->objid, newrole);
1443 break;
1444
1445 case RelationRelationId:
1446
1447 /*
1448 * Pass recursing = true so that we don't fail on indexes,
1449 * owned sequences, etc when we happen to visit them
1450 * before their parent table.
1451 */
1452 ATExecChangeOwner(sdepForm->objid, newrole, true, AccessExclusiveLock);
1453 break;
1454
1455 case DefaultAclRelationId:
1456
1457 /*
1458 * Ignore default ACLs; they should be handled by DROP
1459 * OWNED, not REASSIGN OWNED.
1460 */
1461 break;
1462
1463 case UserMappingRelationId:
1464 /* ditto */
1465 break;
1466
1467 case ForeignServerRelationId:
1468 AlterForeignServerOwner_oid(sdepForm->objid, newrole);
1469 break;
1470
1471 case ForeignDataWrapperRelationId:
1472 AlterForeignDataWrapperOwner_oid(sdepForm->objid, newrole);
1473 break;
1474
1475 case EventTriggerRelationId:
1476 AlterEventTriggerOwner_oid(sdepForm->objid, newrole);
1477 break;
1478
1479 case PublicationRelationId:
1480 AlterPublicationOwner_oid(sdepForm->objid, newrole);
1481 break;
1482
1483 case SubscriptionRelationId:
1484 AlterSubscriptionOwner_oid(sdepForm->objid, newrole);
1485 break;
1486
1487 /* Generic alter owner cases */
1488 case CollationRelationId:
1489 case ConversionRelationId:
1490 case OperatorRelationId:
1491 case ProcedureRelationId:
1492 case LanguageRelationId:
1493 case LargeObjectRelationId:
1494 case OperatorFamilyRelationId:
1495 case OperatorClassRelationId:
1496 case ExtensionRelationId:
1497 case StatisticExtRelationId:
1498 case TableSpaceRelationId:
1499 case DatabaseRelationId:
1500 case TSConfigRelationId:
1501 case TSDictionaryRelationId:
1502 {
1503 Oid classId = sdepForm->classid;
1504 Relation catalog;
1505
1506 if (classId == LargeObjectRelationId)
1507 classId = LargeObjectMetadataRelationId;
1508
1509 catalog = heap_open(classId, RowExclusiveLock);
1510
1511 AlterObjectOwner_internal(catalog, sdepForm->objid,
1512 newrole);
1513
1514 heap_close(catalog, NoLock);
1515 }
1516 break;
1517
1518 default:
1519 elog(ERROR, "unexpected classid %u", sdepForm->classid);
1520 break;
1521 }
1522 /* Make sure the next iteration will see my changes */
1523 CommandCounterIncrement();
1524 }
1525
1526 systable_endscan(scan);
1527 }
1528
1529 heap_close(sdepRel, RowExclusiveLock);
1530 }
1531