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