1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_shdepend relation
6 * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/catalog/pg_shdepend.c
13 *-------------------------------------------------------------------------
17 #include "access/genam.h"
18 #include "access/heapam.h"
19 #include "access/htup_details.h"
20 #include "access/xact.h"
21 #include "catalog/catalog.h"
22 #include "catalog/dependency.h"
23 #include "catalog/indexing.h"
24 #include "catalog/pg_authid.h"
25 #include "catalog/pg_collation.h"
26 #include "catalog/pg_conversion.h"
27 #include "catalog/pg_database.h"
28 #include "catalog/pg_default_acl.h"
29 #include "catalog/pg_event_trigger.h"
30 #include "catalog/pg_extension.h"
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_tablespace.h"
43 #include "catalog/pg_ts_config.h"
44 #include "catalog/pg_ts_dict.h"
45 #include "catalog/pg_type.h"
46 #include "commands/alter.h"
47 #include "commands/dbcommands.h"
48 #include "commands/collationcmds.h"
49 #include "commands/conversioncmds.h"
50 #include "commands/defrem.h"
51 #include "commands/event_trigger.h"
52 #include "commands/extension.h"
53 #include "commands/proclang.h"
54 #include "commands/schemacmds.h"
55 #include "commands/tablecmds.h"
56 #include "commands/typecmds.h"
57 #include "storage/lmgr.h"
58 #include "miscadmin.h"
59 #include "utils/acl.h"
60 #include "utils/fmgroids.h"
61 #include "utils/syscache.h"
62 #include "utils/tqual.h"
70 } SharedDependencyObjectType;
72 static void getOidListDiff(Oid *list1, int *nlist1, Oid *list2, int *nlist2);
73 static Oid classIdGetDbId(Oid classId);
74 static void shdepChangeDep(Relation sdepRel,
75 Oid classid, Oid objid, int32 objsubid,
76 Oid refclassid, Oid refobjid,
77 SharedDependencyType deptype);
78 static void shdepAddDependency(Relation sdepRel,
79 Oid classId, Oid objectId, int32 objsubId,
80 Oid refclassId, Oid refobjId,
81 SharedDependencyType deptype);
82 static void shdepDropDependency(Relation sdepRel,
83 Oid classId, Oid objectId, int32 objsubId,
85 Oid refclassId, Oid refobjId,
86 SharedDependencyType deptype);
87 static void storeObjectDescription(StringInfo descs,
88 SharedDependencyObjectType type,
89 ObjectAddress *object,
90 SharedDependencyType deptype,
92 static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel);
96 * recordSharedDependencyOn
98 * Record a dependency between 2 objects via their respective ObjectAddresses.
99 * The first argument is the dependent object, the second the one it
100 * references (which must be a shared object).
102 * This locks the referenced object and makes sure it still exists.
103 * Then it creates an entry in pg_shdepend. The lock is kept until
104 * the end of the transaction.
106 * Dependencies on pinned objects are not recorded.
109 recordSharedDependencyOn(ObjectAddress *depender,
110 ObjectAddress *referenced,
111 SharedDependencyType deptype)
116 * Objects in pg_shdepend can't have SubIds.
118 Assert(depender->objectSubId == 0);
119 Assert(referenced->objectSubId == 0);
122 * During bootstrap, do nothing since pg_shdepend may not exist yet.
123 * initdb will fill in appropriate pg_shdepend entries after bootstrap.
125 if (IsBootstrapProcessingMode())
128 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
130 /* If the referenced object is pinned, do nothing. */
131 if (!isSharedObjectPinned(referenced->classId, referenced->objectId,
134 shdepAddDependency(sdepRel, depender->classId, depender->objectId,
135 depender->objectSubId,
136 referenced->classId, referenced->objectId,
140 heap_close(sdepRel, RowExclusiveLock);
144 * recordDependencyOnOwner
146 * A convenient wrapper of recordSharedDependencyOn -- register the specified
147 * user as owner of the given object.
149 * Note: it's the caller's responsibility to ensure that there isn't an owner
150 * entry for the object already.
153 recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
155 ObjectAddress myself,
158 myself.classId = classId;
159 myself.objectId = objectId;
160 myself.objectSubId = 0;
162 referenced.classId = AuthIdRelationId;
163 referenced.objectId = owner;
164 referenced.objectSubId = 0;
166 recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER);
172 * Update shared dependency records to account for an updated referenced
173 * object. This is an internal workhorse for operations such as changing
176 * There must be no more than one existing entry for the given dependent
177 * object and dependency type! So in practice this can only be used for
178 * updating SHARED_DEPENDENCY_OWNER entries, which should have that property.
180 * If there is no previous entry, we assume it was referencing a PINned
181 * object, so we create a new entry. If the new referenced object is
182 * PINned, we don't create an entry (and drop the old one, if any).
184 * sdepRel must be the pg_shdepend relation, already opened and suitably
188 shdepChangeDep(Relation sdepRel,
189 Oid classid, Oid objid, int32 objsubid,
190 Oid refclassid, Oid refobjid,
191 SharedDependencyType deptype)
193 Oid dbid = classIdGetDbId(classid);
194 HeapTuple oldtup = NULL;
200 * Make sure the new referenced object doesn't go away while we record the
203 shdepLockAndCheckObject(refclassid, refobjid);
206 * Look for a previous entry
209 Anum_pg_shdepend_dbid,
210 BTEqualStrategyNumber, F_OIDEQ,
211 ObjectIdGetDatum(dbid));
213 Anum_pg_shdepend_classid,
214 BTEqualStrategyNumber, F_OIDEQ,
215 ObjectIdGetDatum(classid));
217 Anum_pg_shdepend_objid,
218 BTEqualStrategyNumber, F_OIDEQ,
219 ObjectIdGetDatum(objid));
221 Anum_pg_shdepend_objsubid,
222 BTEqualStrategyNumber, F_INT4EQ,
223 Int32GetDatum(objsubid));
225 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
228 while ((scantup = systable_getnext(scan)) != NULL)
230 /* Ignore if not of the target dependency type */
231 if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype)
233 /* Caller screwed up if multiple matches */
236 "multiple pg_shdepend entries for object %u/%u/%d deptype %c",
237 classid, objid, objsubid, deptype);
238 oldtup = heap_copytuple(scantup);
241 systable_endscan(scan);
243 if (isSharedObjectPinned(refclassid, refobjid, sdepRel))
245 /* No new entry needed, so just delete existing entry if any */
247 simple_heap_delete(sdepRel, &oldtup->t_self);
251 /* Need to update existing entry */
252 Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup);
254 /* Since oldtup is a copy, we can just modify it in-memory */
255 shForm->refclassid = refclassid;
256 shForm->refobjid = refobjid;
258 simple_heap_update(sdepRel, &oldtup->t_self, oldtup);
260 /* keep indexes current */
261 CatalogUpdateIndexes(sdepRel, oldtup);
265 /* Need to insert new entry */
266 Datum values[Natts_pg_shdepend];
267 bool nulls[Natts_pg_shdepend];
269 memset(nulls, false, sizeof(nulls));
271 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid);
272 values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid);
273 values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid);
274 values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubid);
276 values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
277 values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
278 values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
281 * we are reusing oldtup just to avoid declaring a new variable, but
282 * it's certainly a new tuple
284 oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls);
285 simple_heap_insert(sdepRel, oldtup);
287 /* keep indexes current */
288 CatalogUpdateIndexes(sdepRel, oldtup);
292 heap_freetuple(oldtup);
296 * changeDependencyOnOwner
298 * Update the shared dependencies to account for the new owner.
300 * Note: we don't need an objsubid argument because only whole objects
304 changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
308 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
310 /* Adjust the SHARED_DEPENDENCY_OWNER entry */
311 shdepChangeDep(sdepRel,
312 classId, objectId, 0,
313 AuthIdRelationId, newOwnerId,
314 SHARED_DEPENDENCY_OWNER);
317 * There should never be a SHARED_DEPENDENCY_ACL entry for the owner,
318 * so get rid of it if there is one. This can happen if the new owner
319 * was previously granted some rights to the object.
321 * This step is analogous to aclnewowner's removal of duplicate entries
322 * in the ACL. We have to do it to handle this scenario:
323 * A grants some rights on an object to B
324 * ALTER OWNER changes the object's owner to B
325 * ALTER OWNER changes the object's owner to C
326 * The third step would remove all mention of B from the object's ACL,
327 * but we'd still have a SHARED_DEPENDENCY_ACL for B if we did not do
330 * The rule against having a SHARED_DEPENDENCY_ACL entry for the owner
331 * allows us to fix things up in just this one place, without having
332 * to make the various ALTER OWNER routines each know about it.
335 shdepDropDependency(sdepRel, classId, objectId, 0, true,
336 AuthIdRelationId, newOwnerId,
337 SHARED_DEPENDENCY_ACL);
339 heap_close(sdepRel, RowExclusiveLock);
344 * Helper for updateAclDependencies.
346 * Takes two Oid arrays and removes elements that are common to both arrays,
347 * leaving just those that are in one input but not the other.
348 * We assume both arrays have been sorted and de-duped.
351 getOidListDiff(Oid *list1, int *nlist1, Oid *list2, int *nlist2)
358 in1 = in2 = out1 = out2 = 0;
359 while (in1 < *nlist1 && in2 < *nlist2)
361 if (list1[in1] == list2[in2])
363 /* skip over duplicates */
367 else if (list1[in1] < list2[in2])
369 /* list1[in1] is not in list2 */
370 list1[out1++] = list1[in1++];
374 /* list2[in2] is not in list1 */
375 list2[out2++] = list2[in2++];
379 /* any remaining list1 entries are not in list2 */
380 while (in1 < *nlist1)
382 list1[out1++] = list1[in1++];
385 /* any remaining list2 entries are not in list1 */
386 while (in2 < *nlist2)
388 list2[out2++] = list2[in2++];
396 * updateAclDependencies
397 * Update the pg_shdepend info for an object's ACL during GRANT/REVOKE.
399 * classId, objectId, objsubId: identify the object whose ACL this is
400 * ownerId: role owning the object
401 * noldmembers, oldmembers: array of roleids appearing in old ACL
402 * nnewmembers, newmembers: array of roleids appearing in new ACL
404 * We calculate the differences between the new and old lists of roles,
405 * and then insert or delete from pg_shdepend as appropriate.
407 * Note that we can't just insert all referenced roles blindly during GRANT,
408 * because we would end up with duplicate registered dependencies. We could
409 * check for existence of the tuples before inserting, but that seems to be
410 * more expensive than what we are doing here. Likewise we can't just delete
411 * blindly during REVOKE, because the user may still have other privileges.
412 * It is also possible that REVOKE actually adds dependencies, due to
413 * instantiation of a formerly implicit default ACL (although at present,
414 * all such dependencies should be for the owning role, which we ignore here).
416 * NOTE: Both input arrays must be sorted and de-duped. (Typically they
417 * are extracted from an ACL array by aclmembers(), which takes care of
418 * both requirements.) The arrays are pfreed before return.
421 updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
423 int noldmembers, Oid *oldmembers,
424 int nnewmembers, Oid *newmembers)
430 * Remove entries that are common to both lists; those represent existing
431 * dependencies we don't need to change.
433 * OK to overwrite the inputs since we'll pfree them anyway.
435 getOidListDiff(oldmembers, &noldmembers, newmembers, &nnewmembers);
437 if (noldmembers > 0 || nnewmembers > 0)
439 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
441 /* Add new dependencies that weren't already present */
442 for (i = 0; i < nnewmembers; i++)
444 Oid roleid = newmembers[i];
447 * Skip the owner: he has an OWNER shdep entry instead. (This is
448 * not just a space optimization; it makes ALTER OWNER easier. See
449 * notes in changeDependencyOnOwner.)
451 if (roleid == ownerId)
454 /* Skip pinned roles; they don't need dependency entries */
455 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
458 shdepAddDependency(sdepRel, classId, objectId, objsubId,
459 AuthIdRelationId, roleid,
460 SHARED_DEPENDENCY_ACL);
463 /* Drop no-longer-used old dependencies */
464 for (i = 0; i < noldmembers; i++)
466 Oid roleid = oldmembers[i];
468 /* Skip the owner, same as above */
469 if (roleid == ownerId)
472 /* Skip pinned roles */
473 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
476 shdepDropDependency(sdepRel, classId, objectId, objsubId,
477 false, /* exact match on objsubId */
478 AuthIdRelationId, roleid,
479 SHARED_DEPENDENCY_ACL);
482 heap_close(sdepRel, RowExclusiveLock);
492 * A struct to keep track of dependencies found in other databases.
501 * checkSharedDependencies
503 * Check whether there are shared dependency entries for a given shared
504 * object; return true if so.
506 * In addition, return a string containing a newline-separated list of object
507 * descriptions that depend on the shared object, or NULL if none is found.
508 * We actually return two such strings; the "detail" result is suitable for
509 * returning to the client as an errdetail() string, and is limited in size.
510 * The "detail_log" string is potentially much longer, and should be emitted
511 * to the server log only.
513 * We can find three different kinds of dependencies: dependencies on objects
514 * of the current database; dependencies on shared objects; and dependencies
515 * on objects local to other databases. We can (and do) provide descriptions
516 * of the two former kinds of objects, but we can't do that for "remote"
517 * objects, so we just provide a count of them.
519 * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early.
522 checkSharedDependencies(Oid classId, Oid objectId,
523 char **detail_msg, char **detail_log_msg)
529 int numReportedDeps = 0;
530 int numNotReportedDeps = 0;
531 int numNotReportedDbs = 0;
534 ObjectAddress object;
535 StringInfoData descs;
536 StringInfoData alldescs;
539 * We limit the number of dependencies reported to the client to
540 * MAX_REPORTED_DEPS, since client software may not deal well with
541 * enormous error strings. The server log always gets a full report.
543 #define MAX_REPORTED_DEPS 100
545 initStringInfo(&descs);
546 initStringInfo(&alldescs);
548 sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
551 Anum_pg_shdepend_refclassid,
552 BTEqualStrategyNumber, F_OIDEQ,
553 ObjectIdGetDatum(classId));
555 Anum_pg_shdepend_refobjid,
556 BTEqualStrategyNumber, F_OIDEQ,
557 ObjectIdGetDatum(objectId));
559 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
562 while (HeapTupleIsValid(tup = systable_getnext(scan)))
564 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
566 /* This case can be dispatched quickly */
567 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
569 object.classId = classId;
570 object.objectId = objectId;
571 object.objectSubId = 0;
573 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
574 errmsg("cannot drop %s because it is required by the database system",
575 getObjectDescription(&object))));
578 object.classId = sdepForm->classid;
579 object.objectId = sdepForm->objid;
580 object.objectSubId = sdepForm->objsubid;
583 * If it's a dependency local to this database or it's a shared
584 * object, describe it.
586 * If it's a remote dependency, keep track of it so we can report the
587 * number of them later.
589 if (sdepForm->dbid == MyDatabaseId)
591 if (numReportedDeps < MAX_REPORTED_DEPS)
594 storeObjectDescription(&descs, LOCAL_OBJECT, &object,
595 sdepForm->deptype, 0);
598 numNotReportedDeps++;
599 storeObjectDescription(&alldescs, LOCAL_OBJECT, &object,
600 sdepForm->deptype, 0);
602 else if (sdepForm->dbid == InvalidOid)
604 if (numReportedDeps < MAX_REPORTED_DEPS)
607 storeObjectDescription(&descs, SHARED_OBJECT, &object,
608 sdepForm->deptype, 0);
611 numNotReportedDeps++;
612 storeObjectDescription(&alldescs, SHARED_OBJECT, &object,
613 sdepForm->deptype, 0);
617 /* It's not local nor shared, so it must be remote. */
622 * XXX this info is kept on a simple List. Maybe it's not good
623 * for performance, but using a hash table seems needlessly
624 * complex. The expected number of databases is not high anyway,
627 foreach(cell, remDeps)
630 if (dep->dbOid == sdepForm->dbid)
639 dep = (remoteDep *) palloc(sizeof(remoteDep));
640 dep->dbOid = sdepForm->dbid;
642 remDeps = lappend(remDeps, dep);
647 systable_endscan(scan);
649 heap_close(sdepRel, AccessShareLock);
652 * Summarize dependencies in remote databases.
654 foreach(cell, remDeps)
656 remoteDep *dep = lfirst(cell);
658 object.classId = DatabaseRelationId;
659 object.objectId = dep->dbOid;
660 object.objectSubId = 0;
662 if (numReportedDeps < MAX_REPORTED_DEPS)
665 storeObjectDescription(&descs, REMOTE_OBJECT, &object,
666 SHARED_DEPENDENCY_INVALID, dep->count);
670 storeObjectDescription(&alldescs, REMOTE_OBJECT, &object,
671 SHARED_DEPENDENCY_INVALID, dep->count);
674 list_free_deep(remDeps);
679 pfree(alldescs.data);
680 *detail_msg = *detail_log_msg = NULL;
684 if (numNotReportedDeps > 0)
685 appendStringInfo(&descs, ngettext("\nand %d other object "
686 "(see server log for list)",
687 "\nand %d other objects "
688 "(see server log for list)",
691 if (numNotReportedDbs > 0)
692 appendStringInfo(&descs, ngettext("\nand objects in %d other database "
693 "(see server log for list)",
694 "\nand objects in %d other databases "
695 "(see server log for list)",
699 *detail_msg = descs.data;
700 *detail_log_msg = alldescs.data;
705 * copyTemplateDependencies
707 * Routine to create the initial shared dependencies of a new database.
708 * We simply copy the dependencies from the template database.
711 copyTemplateDependencies(Oid templateDbId, Oid newDbId)
718 CatalogIndexState indstate;
719 Datum values[Natts_pg_shdepend];
720 bool nulls[Natts_pg_shdepend];
721 bool replace[Natts_pg_shdepend];
723 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
724 sdepDesc = RelationGetDescr(sdepRel);
726 indstate = CatalogOpenIndexes(sdepRel);
728 /* Scan all entries with dbid = templateDbId */
730 Anum_pg_shdepend_dbid,
731 BTEqualStrategyNumber, F_OIDEQ,
732 ObjectIdGetDatum(templateDbId));
734 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
737 /* Set up to copy the tuples except for inserting newDbId */
738 memset(values, 0, sizeof(values));
739 memset(nulls, false, sizeof(nulls));
740 memset(replace, false, sizeof(replace));
742 replace[Anum_pg_shdepend_dbid - 1] = true;
743 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId);
746 * Copy the entries of the original database, changing the database Id to
747 * that of the new database. Note that because we are not copying rows
748 * with dbId == 0 (ie, rows describing dependent shared objects) we won't
749 * copy the ownership dependency of the template database itself; this is
752 while (HeapTupleIsValid(tup = systable_getnext(scan)))
756 newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace);
757 simple_heap_insert(sdepRel, newtup);
759 /* Keep indexes current */
760 CatalogIndexInsert(indstate, newtup);
762 heap_freetuple(newtup);
765 systable_endscan(scan);
767 CatalogCloseIndexes(indstate);
768 heap_close(sdepRel, RowExclusiveLock);
772 * dropDatabaseDependencies
774 * Delete pg_shdepend entries corresponding to a database that's being
778 dropDatabaseDependencies(Oid databaseId)
785 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
788 * First, delete all the entries that have the database Oid in the dbid
792 Anum_pg_shdepend_dbid,
793 BTEqualStrategyNumber, F_OIDEQ,
794 ObjectIdGetDatum(databaseId));
795 /* We leave the other index fields unspecified */
797 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
800 while (HeapTupleIsValid(tup = systable_getnext(scan)))
802 simple_heap_delete(sdepRel, &tup->t_self);
805 systable_endscan(scan);
807 /* Now delete all entries corresponding to the database itself */
808 shdepDropDependency(sdepRel, DatabaseRelationId, databaseId, 0, true,
809 InvalidOid, InvalidOid,
810 SHARED_DEPENDENCY_INVALID);
812 heap_close(sdepRel, RowExclusiveLock);
816 * deleteSharedDependencyRecordsFor
818 * Delete all pg_shdepend entries corresponding to an object that's being
819 * dropped or modified. The object is assumed to be either a shared object
820 * or local to the current database (the classId tells us which).
822 * If objectSubId is zero, we are deleting a whole object, so get rid of
823 * pg_shdepend entries for subobjects as well.
826 deleteSharedDependencyRecordsFor(Oid classId, Oid objectId, int32 objectSubId)
830 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
832 shdepDropDependency(sdepRel, classId, objectId, objectSubId,
834 InvalidOid, InvalidOid,
835 SHARED_DEPENDENCY_INVALID);
837 heap_close(sdepRel, RowExclusiveLock);
842 * Internal workhorse for inserting into pg_shdepend
844 * sdepRel must be the pg_shdepend relation, already opened and suitably
848 shdepAddDependency(Relation sdepRel,
849 Oid classId, Oid objectId, int32 objsubId,
850 Oid refclassId, Oid refobjId,
851 SharedDependencyType deptype)
854 Datum values[Natts_pg_shdepend];
855 bool nulls[Natts_pg_shdepend];
858 * Make sure the object doesn't go away while we record the dependency on
859 * it. DROP routines should lock the object exclusively before they check
860 * shared dependencies.
862 shdepLockAndCheckObject(refclassId, refobjId);
864 memset(nulls, false, sizeof(nulls));
867 * Form the new tuple and record the dependency.
869 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId));
870 values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId);
871 values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId);
872 values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubId);
874 values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
875 values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
876 values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
878 tup = heap_form_tuple(sdepRel->rd_att, values, nulls);
880 simple_heap_insert(sdepRel, tup);
882 /* keep indexes current */
883 CatalogUpdateIndexes(sdepRel, tup);
890 * shdepDropDependency
891 * Internal workhorse for deleting entries from pg_shdepend.
893 * We drop entries having the following properties:
894 * dependent object is the one identified by classId/objectId/objsubId
895 * if refclassId isn't InvalidOid, it must match the entry's refclassid
896 * if refobjId isn't InvalidOid, it must match the entry's refobjid
897 * if deptype isn't SHARED_DEPENDENCY_INVALID, it must match entry's deptype
899 * If drop_subobjects is true, we ignore objsubId and consider all entries
900 * matching classId/objectId.
902 * sdepRel must be the pg_shdepend relation, already opened and suitably
906 shdepDropDependency(Relation sdepRel,
907 Oid classId, Oid objectId, int32 objsubId,
908 bool drop_subobjects,
909 Oid refclassId, Oid refobjId,
910 SharedDependencyType deptype)
917 /* Scan for entries matching the dependent object */
919 Anum_pg_shdepend_dbid,
920 BTEqualStrategyNumber, F_OIDEQ,
921 ObjectIdGetDatum(classIdGetDbId(classId)));
923 Anum_pg_shdepend_classid,
924 BTEqualStrategyNumber, F_OIDEQ,
925 ObjectIdGetDatum(classId));
927 Anum_pg_shdepend_objid,
928 BTEqualStrategyNumber, F_OIDEQ,
929 ObjectIdGetDatum(objectId));
935 Anum_pg_shdepend_objsubid,
936 BTEqualStrategyNumber, F_INT4EQ,
937 Int32GetDatum(objsubId));
941 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
944 while (HeapTupleIsValid(tup = systable_getnext(scan)))
946 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
948 /* Filter entries according to additional parameters */
949 if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId)
951 if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId)
953 if (deptype != SHARED_DEPENDENCY_INVALID &&
954 shdepForm->deptype != deptype)
958 simple_heap_delete(sdepRel, &tup->t_self);
961 systable_endscan(scan);
967 * Get the database Id that should be used in pg_shdepend, given the OID
968 * of the catalog containing the object. For shared objects, it's 0
969 * (InvalidOid); for all other objects, it's the current database Id.
972 classIdGetDbId(Oid classId)
976 if (IsSharedRelation(classId))
985 * shdepLockAndCheckObject
987 * Lock the object that we are about to record a dependency on.
988 * After it's locked, verify that it hasn't been dropped while we
989 * weren't looking. If the object has been dropped, this function
993 shdepLockAndCheckObject(Oid classId, Oid objectId)
995 /* AccessShareLock should be OK, since we are not modifying the object */
996 LockSharedObject(classId, objectId, 0, AccessShareLock);
1000 case AuthIdRelationId:
1001 if (!SearchSysCacheExists1(AUTHOID, ObjectIdGetDatum(objectId)))
1003 (errcode(ERRCODE_UNDEFINED_OBJECT),
1004 errmsg("role %u was concurrently dropped",
1009 * Currently, this routine need not support any other shared
1010 * object types besides roles. If we wanted to record explicit
1011 * dependencies on databases or tablespaces, we'd need code along
1015 case TableSpaceRelationId:
1017 /* For lack of a syscache on pg_tablespace, do this: */
1018 char *tablespace = get_tablespace_name(objectId);
1020 if (tablespace == NULL)
1022 (errcode(ERRCODE_UNDEFINED_OBJECT),
1023 errmsg("tablespace %u was concurrently dropped",
1030 case DatabaseRelationId:
1032 /* For lack of a syscache on pg_database, do this: */
1033 char *database = get_database_name(objectId);
1035 if (database == NULL)
1037 (errcode(ERRCODE_UNDEFINED_OBJECT),
1038 errmsg("database %u was concurrently dropped",
1046 elog(ERROR, "unrecognized shared classId: %u", classId);
1052 * storeObjectDescription
1053 * Append the description of a dependent object to "descs"
1055 * While searching for dependencies of a shared object, we stash the
1056 * descriptions of dependent objects we find in a single string, which we
1057 * later pass to ereport() in the DETAIL field when somebody attempts to
1058 * drop a referenced shared object.
1060 * When type is LOCAL_OBJECT or SHARED_OBJECT, we expect object to be the
1061 * dependent object, deptype is the dependency type, and count is not used.
1062 * When type is REMOTE_OBJECT, we expect object to be the database object,
1063 * and count to be nonzero; deptype is not used in this case.
1066 storeObjectDescription(StringInfo descs,
1067 SharedDependencyObjectType type,
1068 ObjectAddress *object,
1069 SharedDependencyType deptype,
1072 char *objdesc = getObjectDescription(object);
1074 /* separate entries with a newline */
1075 if (descs->len != 0)
1076 appendStringInfoChar(descs, '\n');
1082 if (deptype == SHARED_DEPENDENCY_OWNER)
1083 appendStringInfo(descs, _("owner of %s"), objdesc);
1084 else if (deptype == SHARED_DEPENDENCY_ACL)
1085 appendStringInfo(descs, _("privileges for %s"), objdesc);
1086 else if (deptype == SHARED_DEPENDENCY_POLICY)
1087 appendStringInfo(descs, _("target of %s"), objdesc);
1089 elog(ERROR, "unrecognized dependency type: %d",
1094 /* translator: %s will always be "database %s" */
1095 appendStringInfo(descs, ngettext("%d object in %s",
1102 elog(ERROR, "unrecognized object type: %d", type);
1110 * isSharedObjectPinned
1111 * Return whether a given shared object has a SHARED_DEPENDENCY_PIN entry.
1113 * sdepRel must be the pg_shdepend relation, already opened and suitably
1117 isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
1119 bool result = false;
1124 ScanKeyInit(&key[0],
1125 Anum_pg_shdepend_refclassid,
1126 BTEqualStrategyNumber, F_OIDEQ,
1127 ObjectIdGetDatum(classId));
1128 ScanKeyInit(&key[1],
1129 Anum_pg_shdepend_refobjid,
1130 BTEqualStrategyNumber, F_OIDEQ,
1131 ObjectIdGetDatum(objectId));
1133 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1137 * Since we won't generate additional pg_shdepend entries for pinned
1138 * objects, there can be at most one entry referencing a pinned object.
1139 * Hence, it's sufficient to look at the first returned tuple; we don't
1142 tup = systable_getnext(scan);
1143 if (HeapTupleIsValid(tup))
1145 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
1147 if (shdepForm->deptype == SHARED_DEPENDENCY_PIN)
1151 systable_endscan(scan);
1159 * Drop the objects owned by any one of the given RoleIds. If a role has
1160 * access to an object, the grant will be removed as well (but the object
1161 * will not, of course).
1163 * We can revoke grants immediately while doing the scan, but drops are
1164 * saved up and done all at once with performMultipleDeletions. This
1165 * is necessary so that we don't get failures from trying to delete
1166 * interdependent objects in the wrong order.
1169 shdepDropOwned(List *roleids, DropBehavior behavior)
1173 ObjectAddresses *deleteobjs;
1175 deleteobjs = new_object_addresses();
1178 * We don't need this strong a lock here, but we'll call routines that
1179 * acquire RowExclusiveLock. Better get that right now to avoid potential
1180 * deadlock failures.
1182 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
1185 * For each role, find the dependent objects and drop them using the
1186 * regular (non-shared) dependency management.
1188 foreach(cell, roleids)
1190 Oid roleid = lfirst_oid(cell);
1195 /* Doesn't work for pinned objects */
1196 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1200 obj.classId = AuthIdRelationId;
1201 obj.objectId = roleid;
1202 obj.objectSubId = 0;
1205 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1206 errmsg("cannot drop objects owned by %s because they are "
1207 "required by the database system",
1208 getObjectDescription(&obj))));
1211 ScanKeyInit(&key[0],
1212 Anum_pg_shdepend_refclassid,
1213 BTEqualStrategyNumber, F_OIDEQ,
1214 ObjectIdGetDatum(AuthIdRelationId));
1215 ScanKeyInit(&key[1],
1216 Anum_pg_shdepend_refobjid,
1217 BTEqualStrategyNumber, F_OIDEQ,
1218 ObjectIdGetDatum(roleid));
1220 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1223 while ((tuple = systable_getnext(scan)) != NULL)
1225 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1229 * We only operate on shared objects and objects in the current
1232 if (sdepForm->dbid != MyDatabaseId &&
1233 sdepForm->dbid != InvalidOid)
1236 switch (sdepForm->deptype)
1238 /* Shouldn't happen */
1239 case SHARED_DEPENDENCY_PIN:
1240 case SHARED_DEPENDENCY_INVALID:
1241 elog(ERROR, "unexpected dependency type");
1243 case SHARED_DEPENDENCY_ACL:
1244 RemoveRoleFromObjectACL(roleid,
1248 case SHARED_DEPENDENCY_OWNER:
1249 /* If a local object, save it for deletion below */
1250 if (sdepForm->dbid == MyDatabaseId)
1252 obj.classId = sdepForm->classid;
1253 obj.objectId = sdepForm->objid;
1254 obj.objectSubId = sdepForm->objsubid;
1255 add_exact_object_address(&obj, deleteobjs);
1261 systable_endscan(scan);
1264 /* the dependency mechanism does the actual work */
1265 performMultipleDeletions(deleteobjs, behavior, 0);
1267 heap_close(sdepRel, RowExclusiveLock);
1269 free_object_addresses(deleteobjs);
1273 * shdepReassignOwned
1275 * Change the owner of objects owned by any of the roles in roleids to
1276 * newrole. Grants are not touched.
1279 shdepReassignOwned(List *roleids, Oid newrole)
1285 * We don't need this strong a lock here, but we'll call routines that
1286 * acquire RowExclusiveLock. Better get that right now to avoid potential
1287 * deadlock problems.
1289 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
1291 foreach(cell, roleids)
1296 Oid roleid = lfirst_oid(cell);
1298 /* Refuse to work on pinned roles */
1299 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1303 obj.classId = AuthIdRelationId;
1304 obj.objectId = roleid;
1305 obj.objectSubId = 0;
1308 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1309 errmsg("cannot reassign ownership of objects owned by %s because they are required by the database system",
1310 getObjectDescription(&obj))));
1313 * There's no need to tell the whole truth, which is that we
1314 * didn't track these dependencies at all ...
1318 ScanKeyInit(&key[0],
1319 Anum_pg_shdepend_refclassid,
1320 BTEqualStrategyNumber, F_OIDEQ,
1321 ObjectIdGetDatum(AuthIdRelationId));
1322 ScanKeyInit(&key[1],
1323 Anum_pg_shdepend_refobjid,
1324 BTEqualStrategyNumber, F_OIDEQ,
1325 ObjectIdGetDatum(roleid));
1327 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1330 while ((tuple = systable_getnext(scan)) != NULL)
1332 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1335 * We only operate on shared objects and objects in the current
1338 if (sdepForm->dbid != MyDatabaseId &&
1339 sdepForm->dbid != InvalidOid)
1342 /* Unexpected because we checked for pins above */
1343 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
1344 elog(ERROR, "unexpected shared pin");
1346 /* We leave non-owner dependencies alone */
1347 if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
1350 /* Issue the appropriate ALTER OWNER call */
1351 switch (sdepForm->classid)
1353 case TypeRelationId:
1354 AlterTypeOwnerInternal(sdepForm->objid, newrole, true);
1357 case NamespaceRelationId:
1358 AlterSchemaOwner_oid(sdepForm->objid, newrole);
1361 case RelationRelationId:
1364 * Pass recursing = true so that we don't fail on indexes,
1365 * owned sequences, etc when we happen to visit them
1366 * before their parent table.
1368 ATExecChangeOwner(sdepForm->objid, newrole, true, AccessExclusiveLock);
1371 case DefaultAclRelationId:
1374 * Ignore default ACLs; they should be handled by DROP
1375 * OWNED, not REASSIGN OWNED.
1379 case ForeignServerRelationId:
1380 AlterForeignServerOwner_oid(sdepForm->objid, newrole);
1383 case ForeignDataWrapperRelationId:
1384 AlterForeignDataWrapperOwner_oid(sdepForm->objid, newrole);
1387 case EventTriggerRelationId:
1388 AlterEventTriggerOwner_oid(sdepForm->objid, newrole);
1391 /* Generic alter owner cases */
1392 case CollationRelationId:
1393 case ConversionRelationId:
1394 case OperatorRelationId:
1395 case ProcedureRelationId:
1396 case LanguageRelationId:
1397 case LargeObjectRelationId:
1398 case OperatorFamilyRelationId:
1399 case OperatorClassRelationId:
1400 case ExtensionRelationId:
1401 case TableSpaceRelationId:
1402 case DatabaseRelationId:
1403 case TSConfigRelationId:
1404 case TSDictionaryRelationId:
1406 Oid classId = sdepForm->classid;
1409 if (classId == LargeObjectRelationId)
1410 classId = LargeObjectMetadataRelationId;
1412 catalog = heap_open(classId, RowExclusiveLock);
1414 AlterObjectOwner_internal(catalog, sdepForm->objid,
1417 heap_close(catalog, NoLock);
1422 elog(ERROR, "unexpected classid %u", sdepForm->classid);
1425 /* Make sure the next iteration will see my changes */
1426 CommandCounterIncrement();
1429 systable_endscan(scan);
1432 heap_close(sdepRel, RowExclusiveLock);