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