1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_shdepend relation
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.32 2009/03/26 22:26:06 petere Exp $
13 *-------------------------------------------------------------------------
17 #include "access/genam.h"
18 #include "access/heapam.h"
19 #include "access/xact.h"
20 #include "catalog/catalog.h"
21 #include "catalog/dependency.h"
22 #include "catalog/indexing.h"
23 #include "catalog/pg_authid.h"
24 #include "catalog/pg_conversion.h"
25 #include "catalog/pg_database.h"
26 #include "catalog/pg_language.h"
27 #include "catalog/pg_namespace.h"
28 #include "catalog/pg_operator.h"
29 #include "catalog/pg_proc.h"
30 #include "catalog/pg_shdepend.h"
31 #include "catalog/pg_tablespace.h"
32 #include "catalog/pg_type.h"
33 #include "commands/conversioncmds.h"
34 #include "commands/defrem.h"
35 #include "commands/proclang.h"
36 #include "commands/schemacmds.h"
37 #include "commands/tablecmds.h"
38 #include "commands/typecmds.h"
39 #include "storage/lmgr.h"
40 #include "miscadmin.h"
41 #include "utils/acl.h"
42 #include "utils/fmgroids.h"
43 #include "utils/syscache.h"
44 #include "utils/tqual.h"
54 static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2,
56 static Oid classIdGetDbId(Oid classId);
57 static void shdepLockAndCheckObject(Oid classId, Oid objectId);
58 static void shdepChangeDep(Relation sdepRel,
59 Oid classid, Oid objid, int32 objsubid,
60 Oid refclassid, Oid refobjid,
61 SharedDependencyType deptype);
62 static void shdepAddDependency(Relation sdepRel,
63 Oid classId, Oid objectId, int32 objsubId,
64 Oid refclassId, Oid refobjId,
65 SharedDependencyType deptype);
66 static void shdepDropDependency(Relation sdepRel,
67 Oid classId, Oid objectId, int32 objsubId,
69 Oid refclassId, Oid refobjId,
70 SharedDependencyType deptype);
71 static void storeObjectDescription(StringInfo descs, objectType type,
72 ObjectAddress *object,
73 SharedDependencyType deptype,
75 static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel);
79 * recordSharedDependencyOn
81 * Record a dependency between 2 objects via their respective ObjectAddresses.
82 * The first argument is the dependent object, the second the one it
83 * references (which must be a shared object).
85 * This locks the referenced object and makes sure it still exists.
86 * Then it creates an entry in pg_shdepend. The lock is kept until
87 * the end of the transaction.
89 * Dependencies on pinned objects are not recorded.
92 recordSharedDependencyOn(ObjectAddress *depender,
93 ObjectAddress *referenced,
94 SharedDependencyType deptype)
99 * Objects in pg_shdepend can't have SubIds.
101 Assert(depender->objectSubId == 0);
102 Assert(referenced->objectSubId == 0);
105 * During bootstrap, do nothing since pg_shdepend may not exist yet.
106 * initdb will fill in appropriate pg_shdepend entries after bootstrap.
108 if (IsBootstrapProcessingMode())
111 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
113 /* If the referenced object is pinned, do nothing. */
114 if (!isSharedObjectPinned(referenced->classId, referenced->objectId,
117 shdepAddDependency(sdepRel, depender->classId, depender->objectId,
118 depender->objectSubId,
119 referenced->classId, referenced->objectId,
123 heap_close(sdepRel, RowExclusiveLock);
127 * recordDependencyOnOwner
129 * A convenient wrapper of recordSharedDependencyOn -- register the specified
130 * user as owner of the given object.
132 * Note: it's the caller's responsibility to ensure that there isn't an owner
133 * entry for the object already.
136 recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
138 ObjectAddress myself,
141 myself.classId = classId;
142 myself.objectId = objectId;
143 myself.objectSubId = 0;
145 referenced.classId = AuthIdRelationId;
146 referenced.objectId = owner;
147 referenced.objectSubId = 0;
149 recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER);
155 * Update shared dependency records to account for an updated referenced
156 * object. This is an internal workhorse for operations such as changing
159 * There must be no more than one existing entry for the given dependent
160 * object and dependency type! So in practice this can only be used for
161 * updating SHARED_DEPENDENCY_OWNER entries, which should have that property.
163 * If there is no previous entry, we assume it was referencing a PINned
164 * object, so we create a new entry. If the new referenced object is
165 * PINned, we don't create an entry (and drop the old one, if any).
167 * sdepRel must be the pg_shdepend relation, already opened and suitably
171 shdepChangeDep(Relation sdepRel,
172 Oid classid, Oid objid, int32 objsubid,
173 Oid refclassid, Oid refobjid,
174 SharedDependencyType deptype)
176 Oid dbid = classIdGetDbId(classid);
177 HeapTuple oldtup = NULL;
183 * Make sure the new referenced object doesn't go away while we record the
186 shdepLockAndCheckObject(refclassid, refobjid);
189 * Look for a previous entry
192 Anum_pg_shdepend_dbid,
193 BTEqualStrategyNumber, F_OIDEQ,
194 ObjectIdGetDatum(dbid));
196 Anum_pg_shdepend_classid,
197 BTEqualStrategyNumber, F_OIDEQ,
198 ObjectIdGetDatum(classid));
200 Anum_pg_shdepend_objid,
201 BTEqualStrategyNumber, F_OIDEQ,
202 ObjectIdGetDatum(objid));
204 Anum_pg_shdepend_objsubid,
205 BTEqualStrategyNumber, F_INT4EQ,
206 Int32GetDatum(objsubid));
208 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
209 SnapshotNow, 4, key);
211 while ((scantup = systable_getnext(scan)) != NULL)
213 /* Ignore if not of the target dependency type */
214 if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype)
216 /* Caller screwed up if multiple matches */
219 "multiple pg_shdepend entries for object %u/%u/%d deptype %c",
220 classid, objid, objsubid, deptype);
221 oldtup = heap_copytuple(scantup);
224 systable_endscan(scan);
226 if (isSharedObjectPinned(refclassid, refobjid, sdepRel))
228 /* No new entry needed, so just delete existing entry if any */
230 simple_heap_delete(sdepRel, &oldtup->t_self);
234 /* Need to update existing entry */
235 Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup);
237 /* Since oldtup is a copy, we can just modify it in-memory */
238 shForm->refclassid = refclassid;
239 shForm->refobjid = refobjid;
241 simple_heap_update(sdepRel, &oldtup->t_self, oldtup);
243 /* keep indexes current */
244 CatalogUpdateIndexes(sdepRel, oldtup);
248 /* Need to insert new entry */
249 Datum values[Natts_pg_shdepend];
250 bool nulls[Natts_pg_shdepend];
252 memset(nulls, false, sizeof(nulls));
254 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid);
255 values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid);
256 values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid);
257 values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubid);
259 values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
260 values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
261 values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
264 * we are reusing oldtup just to avoid declaring a new variable, but
265 * it's certainly a new tuple
267 oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls);
268 simple_heap_insert(sdepRel, oldtup);
270 /* keep indexes current */
271 CatalogUpdateIndexes(sdepRel, oldtup);
275 heap_freetuple(oldtup);
279 * changeDependencyOnOwner
281 * Update the shared dependencies to account for the new owner.
283 * Note: we don't need an objsubid argument because only whole objects
287 changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
291 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
293 /* Adjust the SHARED_DEPENDENCY_OWNER entry */
294 shdepChangeDep(sdepRel,
295 classId, objectId, 0,
296 AuthIdRelationId, newOwnerId,
297 SHARED_DEPENDENCY_OWNER);
300 * There should never be a SHARED_DEPENDENCY_ACL entry for the owner,
301 * so get rid of it if there is one. This can happen if the new owner
302 * was previously granted some rights to the object.
304 * This step is analogous to aclnewowner's removal of duplicate entries
305 * in the ACL. We have to do it to handle this scenario:
306 * A grants some rights on an object to B
307 * ALTER OWNER changes the object's owner to B
308 * ALTER OWNER changes the object's owner to C
309 * The third step would remove all mention of B from the object's ACL,
310 * but we'd still have a SHARED_DEPENDENCY_ACL for B if we did not do
313 * The rule against having a SHARED_DEPENDENCY_ACL entry for the owner
314 * allows us to fix things up in just this one place, without having
315 * to make the various ALTER OWNER routines each know about it.
318 shdepDropDependency(sdepRel, classId, objectId, 0, true,
319 AuthIdRelationId, newOwnerId,
320 SHARED_DEPENDENCY_ACL);
322 heap_close(sdepRel, RowExclusiveLock);
327 * Helper for updateAclDependencies.
329 * Takes two Oid arrays and returns elements from the first not found in the
330 * second. We assume both arrays are sorted and de-duped, and that the
331 * second array does not contain any values not found in the first.
333 * NOTE: Both input arrays are pfreed.
336 getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff)
343 AssertArg(nlist1 >= nlist2 && nlist2 >= 0);
345 result = palloc(sizeof(Oid) * (nlist1 - nlist2));
348 for (i = 0, j = 0; i < nlist1 && j < nlist2;)
350 if (list1[i] == list2[j])
355 else if (list1[i] < list2[j])
357 result[k++] = list1[i];
363 elog(WARNING, "invalid element %u in shorter list", list2[j]);
368 for (; i < nlist1; i++)
369 result[k++] = list1[i];
371 /* We should have copied the exact number of elements */
372 AssertState(k == (nlist1 - nlist2));
383 * updateAclDependencies
384 * Update the pg_shdepend info for an object's ACL during GRANT/REVOKE.
386 * classId, objectId, objsubId: identify the object whose ACL this is
387 * ownerId: role owning the object
388 * isGrant: are we adding or removing ACL entries?
389 * noldmembers, oldmembers: array of roleids appearing in old ACL
390 * nnewmembers, newmembers: array of roleids appearing in new ACL
392 * We calculate the difference between the new and old lists of roles,
393 * and then insert (if it's a grant) or delete (if it's a revoke) from
394 * pg_shdepend as appropiate.
396 * Note that we can't insert blindly at grant, because we would end up with
397 * duplicate registered dependencies. We could check for existence of the
398 * tuple before inserting, but that seems to be more expensive than what we are
399 * doing now. On the other hand, we can't just delete the tuples blindly at
400 * revoke, because the user may still have other privileges.
402 * NOTE: Both input arrays must be sorted and de-duped. They are pfreed
406 updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
407 Oid ownerId, bool isGrant,
408 int noldmembers, Oid *oldmembers,
409 int nnewmembers, Oid *newmembers)
417 * Calculate the differences between the old and new lists.
420 ndiff = getOidListDiff(newmembers, nnewmembers,
421 oldmembers, noldmembers, &diff);
423 ndiff = getOidListDiff(oldmembers, noldmembers,
424 newmembers, nnewmembers, &diff);
428 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
430 /* Add or drop the respective dependency */
431 for (i = 0; i < ndiff; i++)
433 Oid roleid = diff[i];
436 * Skip the owner: he has an OWNER shdep entry instead. (This is
437 * not just a space optimization; it makes ALTER OWNER easier. See
438 * notes in changeDependencyOnOwner.)
440 if (roleid == ownerId)
443 /* Skip pinned roles */
444 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
448 shdepAddDependency(sdepRel, classId, objectId, objsubId,
449 AuthIdRelationId, roleid,
450 SHARED_DEPENDENCY_ACL);
452 shdepDropDependency(sdepRel, classId, objectId, objsubId,
453 false, /* exact match on objsubId */
454 AuthIdRelationId, roleid,
455 SHARED_DEPENDENCY_ACL);
458 heap_close(sdepRel, RowExclusiveLock);
465 * A struct to keep track of dependencies found in other databases.
474 * checkSharedDependencies
476 * Check whether there are shared dependency entries for a given shared
477 * object; return true if so.
479 * In addition, return a string containing a newline-separated list of object
480 * descriptions that depend on the shared object, or NULL if none is found.
481 * We actually return two such strings; the "detail" result is suitable for
482 * returning to the client as an errdetail() string, and is limited in size.
483 * The "detail_log" string is potentially much longer, and should be emitted
484 * to the server log only.
486 * We can find three different kinds of dependencies: dependencies on objects
487 * of the current database; dependencies on shared objects; and dependencies
488 * on objects local to other databases. We can (and do) provide descriptions
489 * of the two former kinds of objects, but we can't do that for "remote"
490 * objects, so we just provide a count of them.
492 * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early.
495 checkSharedDependencies(Oid classId, Oid objectId,
496 char **detail_msg, char **detail_log_msg)
502 int numReportedDeps = 0;
503 int numNotReportedDeps = 0;
504 int numNotReportedDbs = 0;
507 ObjectAddress object;
508 StringInfoData descs;
509 StringInfoData alldescs;
512 * We limit the number of dependencies reported to the client to
513 * MAX_REPORTED_DEPS, since client software may not deal well with
514 * enormous error strings. The server log always gets a full report.
516 #define MAX_REPORTED_DEPS 100
518 initStringInfo(&descs);
519 initStringInfo(&alldescs);
521 sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
524 Anum_pg_shdepend_refclassid,
525 BTEqualStrategyNumber, F_OIDEQ,
526 ObjectIdGetDatum(classId));
528 Anum_pg_shdepend_refobjid,
529 BTEqualStrategyNumber, F_OIDEQ,
530 ObjectIdGetDatum(objectId));
532 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
533 SnapshotNow, 2, key);
535 while (HeapTupleIsValid(tup = systable_getnext(scan)))
537 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
539 /* This case can be dispatched quickly */
540 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
542 object.classId = classId;
543 object.objectId = objectId;
544 object.objectSubId = 0;
546 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
547 errmsg("cannot drop %s because it is required by the database system",
548 getObjectDescription(&object))));
551 object.classId = sdepForm->classid;
552 object.objectId = sdepForm->objid;
553 object.objectSubId = sdepForm->objsubid;
556 * If it's a dependency local to this database or it's a shared
557 * object, describe it.
559 * If it's a remote dependency, keep track of it so we can report the
560 * number of them later.
562 if (sdepForm->dbid == MyDatabaseId)
564 if (numReportedDeps < MAX_REPORTED_DEPS)
567 storeObjectDescription(&descs, LOCAL_OBJECT, &object,
568 sdepForm->deptype, 0);
571 numNotReportedDeps++;
572 storeObjectDescription(&alldescs, LOCAL_OBJECT, &object,
573 sdepForm->deptype, 0);
575 else if (sdepForm->dbid == InvalidOid)
577 if (numReportedDeps < MAX_REPORTED_DEPS)
580 storeObjectDescription(&descs, SHARED_OBJECT, &object,
581 sdepForm->deptype, 0);
584 numNotReportedDeps++;
585 storeObjectDescription(&alldescs, SHARED_OBJECT, &object,
586 sdepForm->deptype, 0);
590 /* It's not local nor shared, so it must be remote. */
595 * XXX this info is kept on a simple List. Maybe it's not good
596 * for performance, but using a hash table seems needlessly
597 * complex. The expected number of databases is not high anyway,
600 foreach(cell, remDeps)
603 if (dep->dbOid == sdepForm->dbid)
612 dep = (remoteDep *) palloc(sizeof(remoteDep));
613 dep->dbOid = sdepForm->dbid;
615 remDeps = lappend(remDeps, dep);
620 systable_endscan(scan);
622 heap_close(sdepRel, AccessShareLock);
625 * Summarize dependencies in remote databases.
627 foreach(cell, remDeps)
629 remoteDep *dep = lfirst(cell);
631 object.classId = DatabaseRelationId;
632 object.objectId = dep->dbOid;
633 object.objectSubId = 0;
635 if (numReportedDeps < MAX_REPORTED_DEPS)
638 storeObjectDescription(&descs, REMOTE_OBJECT, &object,
639 SHARED_DEPENDENCY_INVALID, dep->count);
643 storeObjectDescription(&alldescs, REMOTE_OBJECT, &object,
644 SHARED_DEPENDENCY_INVALID, dep->count);
647 list_free_deep(remDeps);
652 pfree(alldescs.data);
653 *detail_msg = *detail_log_msg = NULL;
657 if (numNotReportedDeps > 0)
658 appendStringInfo(&descs, ngettext("\nand %d other object "
659 "(see server log for list)",
660 "\nand %d other objects "
661 "(see server log for list)",
664 if (numNotReportedDbs > 0)
665 appendStringInfo(&descs, ngettext("\nand objects in %d other database "
666 "(see server log for list)",
667 "\nand objects in %d other databases "
668 "(see server log for list)",
672 *detail_msg = descs.data;
673 *detail_log_msg = alldescs.data;
678 * copyTemplateDependencies
680 * Routine to create the initial shared dependencies of a new database.
681 * We simply copy the dependencies from the template database.
684 copyTemplateDependencies(Oid templateDbId, Oid newDbId)
691 CatalogIndexState indstate;
692 Datum values[Natts_pg_shdepend];
693 bool nulls[Natts_pg_shdepend];
694 bool replace[Natts_pg_shdepend];
696 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
697 sdepDesc = RelationGetDescr(sdepRel);
699 indstate = CatalogOpenIndexes(sdepRel);
701 /* Scan all entries with dbid = templateDbId */
703 Anum_pg_shdepend_dbid,
704 BTEqualStrategyNumber, F_OIDEQ,
705 ObjectIdGetDatum(templateDbId));
707 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
708 SnapshotNow, 1, key);
710 /* Set up to copy the tuples except for inserting newDbId */
711 memset(values, 0, sizeof(values));
712 memset(nulls, false, sizeof(nulls));
713 memset(replace, false, sizeof(replace));
715 replace[Anum_pg_shdepend_dbid - 1] = true;
716 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId);
719 * Copy the entries of the original database, changing the database Id to
720 * that of the new database. Note that because we are not copying rows
721 * with dbId == 0 (ie, rows describing dependent shared objects) we won't
722 * copy the ownership dependency of the template database itself; this is
725 while (HeapTupleIsValid(tup = systable_getnext(scan)))
729 newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace);
730 simple_heap_insert(sdepRel, newtup);
732 /* Keep indexes current */
733 CatalogIndexInsert(indstate, newtup);
735 heap_freetuple(newtup);
738 systable_endscan(scan);
740 CatalogCloseIndexes(indstate);
741 heap_close(sdepRel, RowExclusiveLock);
745 * dropDatabaseDependencies
747 * Delete pg_shdepend entries corresponding to a database that's being
751 dropDatabaseDependencies(Oid databaseId)
758 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
761 * First, delete all the entries that have the database Oid in the dbid
765 Anum_pg_shdepend_dbid,
766 BTEqualStrategyNumber, F_OIDEQ,
767 ObjectIdGetDatum(databaseId));
768 /* We leave the other index fields unspecified */
770 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
771 SnapshotNow, 1, key);
773 while (HeapTupleIsValid(tup = systable_getnext(scan)))
775 simple_heap_delete(sdepRel, &tup->t_self);
778 systable_endscan(scan);
780 /* Now delete all entries corresponding to the database itself */
781 shdepDropDependency(sdepRel, DatabaseRelationId, databaseId, 0, true,
782 InvalidOid, InvalidOid,
783 SHARED_DEPENDENCY_INVALID);
785 heap_close(sdepRel, RowExclusiveLock);
789 * deleteSharedDependencyRecordsFor
791 * Delete all pg_shdepend entries corresponding to an object that's being
792 * dropped or modified. The object is assumed to be either a shared object
793 * or local to the current database (the classId tells us which).
795 * If objectSubId is zero, we are deleting a whole object, so get rid of
796 * pg_shdepend entries for subobjects as well.
799 deleteSharedDependencyRecordsFor(Oid classId, Oid objectId, int32 objectSubId)
803 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
805 shdepDropDependency(sdepRel, classId, objectId, objectSubId,
807 InvalidOid, InvalidOid,
808 SHARED_DEPENDENCY_INVALID);
810 heap_close(sdepRel, RowExclusiveLock);
815 * Internal workhorse for inserting into pg_shdepend
817 * sdepRel must be the pg_shdepend relation, already opened and suitably
821 shdepAddDependency(Relation sdepRel,
822 Oid classId, Oid objectId, int32 objsubId,
823 Oid refclassId, Oid refobjId,
824 SharedDependencyType deptype)
827 Datum values[Natts_pg_shdepend];
828 bool nulls[Natts_pg_shdepend];
831 * Make sure the object doesn't go away while we record the dependency on
832 * it. DROP routines should lock the object exclusively before they check
833 * shared dependencies.
835 shdepLockAndCheckObject(refclassId, refobjId);
837 memset(nulls, false, sizeof(nulls));
840 * Form the new tuple and record the dependency.
842 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId));
843 values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId);
844 values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId);
845 values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubId);
847 values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
848 values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
849 values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
851 tup = heap_form_tuple(sdepRel->rd_att, values, nulls);
853 simple_heap_insert(sdepRel, tup);
855 /* keep indexes current */
856 CatalogUpdateIndexes(sdepRel, tup);
863 * shdepDropDependency
864 * Internal workhorse for deleting entries from pg_shdepend.
866 * We drop entries having the following properties:
867 * dependent object is the one identified by classId/objectId/objsubId
868 * if refclassId isn't InvalidOid, it must match the entry's refclassid
869 * if refobjId isn't InvalidOid, it must match the entry's refobjid
870 * if deptype isn't SHARED_DEPENDENCY_INVALID, it must match entry's deptype
872 * If drop_subobjects is true, we ignore objsubId and consider all entries
873 * matching classId/objectId.
875 * sdepRel must be the pg_shdepend relation, already opened and suitably
879 shdepDropDependency(Relation sdepRel,
880 Oid classId, Oid objectId, int32 objsubId,
881 bool drop_subobjects,
882 Oid refclassId, Oid refobjId,
883 SharedDependencyType deptype)
890 /* Scan for entries matching the dependent object */
892 Anum_pg_shdepend_dbid,
893 BTEqualStrategyNumber, F_OIDEQ,
894 ObjectIdGetDatum(classIdGetDbId(classId)));
896 Anum_pg_shdepend_classid,
897 BTEqualStrategyNumber, F_OIDEQ,
898 ObjectIdGetDatum(classId));
900 Anum_pg_shdepend_objid,
901 BTEqualStrategyNumber, F_OIDEQ,
902 ObjectIdGetDatum(objectId));
908 Anum_pg_shdepend_objsubid,
909 BTEqualStrategyNumber, F_INT4EQ,
910 Int32GetDatum(objsubId));
914 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
915 SnapshotNow, nkeys, key);
917 while (HeapTupleIsValid(tup = systable_getnext(scan)))
919 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
921 /* Filter entries according to additional parameters */
922 if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId)
924 if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId)
926 if (deptype != SHARED_DEPENDENCY_INVALID &&
927 shdepForm->deptype != deptype)
931 simple_heap_delete(sdepRel, &tup->t_self);
934 systable_endscan(scan);
940 * Get the database Id that should be used in pg_shdepend, given the OID
941 * of the catalog containing the object. For shared objects, it's 0
942 * (InvalidOid); for all other objects, it's the current database Id.
945 classIdGetDbId(Oid classId)
949 if (IsSharedRelation(classId))
958 * shdepLockAndCheckObject
960 * Lock the object that we are about to record a dependency on.
961 * After it's locked, verify that it hasn't been dropped while we
962 * weren't looking. If the object has been dropped, this function
966 shdepLockAndCheckObject(Oid classId, Oid objectId)
968 /* AccessShareLock should be OK, since we are not modifying the object */
969 LockSharedObject(classId, objectId, 0, AccessShareLock);
973 case AuthIdRelationId:
974 if (!SearchSysCacheExists(AUTHOID,
975 ObjectIdGetDatum(objectId),
978 (errcode(ERRCODE_UNDEFINED_OBJECT),
979 errmsg("role %u was concurrently dropped",
984 * Currently, this routine need not support any other shared
985 * object types besides roles. If we wanted to record explicit
986 * dependencies on databases or tablespaces, we'd need code along
990 case TableSpaceRelationId:
992 /* For lack of a syscache on pg_tablespace, do this: */
993 char *tablespace = get_tablespace_name(objectId);
995 if (tablespace == NULL)
997 (errcode(ERRCODE_UNDEFINED_OBJECT),
998 errmsg("tablespace %u was concurrently dropped",
1006 elog(ERROR, "unrecognized shared classId: %u", classId);
1012 * storeObjectDescription
1013 * Append the description of a dependent object to "descs"
1015 * While searching for dependencies of a shared object, we stash the
1016 * descriptions of dependent objects we find in a single string, which we
1017 * later pass to ereport() in the DETAIL field when somebody attempts to
1018 * drop a referenced shared object.
1020 * When type is LOCAL_OBJECT or SHARED_OBJECT, we expect object to be the
1021 * dependent object, deptype is the dependency type, and count is not used.
1022 * When type is REMOTE_OBJECT, we expect object to be the database object,
1023 * and count to be nonzero; deptype is not used in this case.
1026 storeObjectDescription(StringInfo descs, objectType type,
1027 ObjectAddress *object,
1028 SharedDependencyType deptype,
1031 char *objdesc = getObjectDescription(object);
1033 /* separate entries with a newline */
1034 if (descs->len != 0)
1035 appendStringInfoChar(descs, '\n');
1041 if (deptype == SHARED_DEPENDENCY_OWNER)
1042 appendStringInfo(descs, _("owner of %s"), objdesc);
1043 else if (deptype == SHARED_DEPENDENCY_ACL)
1044 appendStringInfo(descs, _("access to %s"), objdesc);
1046 elog(ERROR, "unrecognized dependency type: %d",
1051 /* translator: %s will always be "database %s" */
1052 appendStringInfo(descs, ngettext("%d object in %s", "%d objects in %s", count), count, objdesc);
1056 elog(ERROR, "unrecognized object type: %d", type);
1064 * isSharedObjectPinned
1065 * Return whether a given shared object has a SHARED_DEPENDENCY_PIN entry.
1067 * sdepRel must be the pg_shdepend relation, already opened and suitably
1071 isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
1073 bool result = false;
1078 ScanKeyInit(&key[0],
1079 Anum_pg_shdepend_refclassid,
1080 BTEqualStrategyNumber, F_OIDEQ,
1081 ObjectIdGetDatum(classId));
1082 ScanKeyInit(&key[1],
1083 Anum_pg_shdepend_refobjid,
1084 BTEqualStrategyNumber, F_OIDEQ,
1085 ObjectIdGetDatum(objectId));
1087 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1088 SnapshotNow, 2, key);
1091 * Since we won't generate additional pg_shdepend entries for pinned
1092 * objects, there can be at most one entry referencing a pinned object.
1093 * Hence, it's sufficient to look at the first returned tuple; we don't
1096 tup = systable_getnext(scan);
1097 if (HeapTupleIsValid(tup))
1099 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
1101 if (shdepForm->deptype == SHARED_DEPENDENCY_PIN)
1105 systable_endscan(scan);
1113 * Drop the objects owned by any one of the given RoleIds. If a role has
1114 * access to an object, the grant will be removed as well (but the object
1115 * will not, of course).
1117 * We can revoke grants immediately while doing the scan, but drops are
1118 * saved up and done all at once with performMultipleDeletions. This
1119 * is necessary so that we don't get failures from trying to delete
1120 * interdependent objects in the wrong order.
1123 shdepDropOwned(List *roleids, DropBehavior behavior)
1127 ObjectAddresses *deleteobjs;
1129 deleteobjs = new_object_addresses();
1132 * We don't need this strong a lock here, but we'll call routines that
1133 * acquire RowExclusiveLock. Better get that right now to avoid potential
1134 * deadlock failures.
1136 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
1139 * For each role, find the dependent objects and drop them using the
1140 * regular (non-shared) dependency management.
1142 foreach(cell, roleids)
1144 Oid roleid = lfirst_oid(cell);
1149 /* Doesn't work for pinned objects */
1150 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1154 obj.classId = AuthIdRelationId;
1155 obj.objectId = roleid;
1156 obj.objectSubId = 0;
1159 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1160 errmsg("cannot drop objects owned by %s because they are "
1161 "required by the database system",
1162 getObjectDescription(&obj))));
1165 ScanKeyInit(&key[0],
1166 Anum_pg_shdepend_refclassid,
1167 BTEqualStrategyNumber, F_OIDEQ,
1168 ObjectIdGetDatum(AuthIdRelationId));
1169 ScanKeyInit(&key[1],
1170 Anum_pg_shdepend_refobjid,
1171 BTEqualStrategyNumber, F_OIDEQ,
1172 ObjectIdGetDatum(roleid));
1174 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1175 SnapshotNow, 2, key);
1177 while ((tuple = systable_getnext(scan)) != NULL)
1179 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1180 InternalGrant istmt;
1183 /* We only operate on objects in the current database */
1184 if (sdepForm->dbid != MyDatabaseId)
1187 switch (sdepForm->deptype)
1189 /* Shouldn't happen */
1190 case SHARED_DEPENDENCY_PIN:
1191 case SHARED_DEPENDENCY_INVALID:
1192 elog(ERROR, "unexpected dependency type");
1194 case SHARED_DEPENDENCY_ACL:
1195 switch (sdepForm->classid)
1197 case RelationRelationId:
1198 /* it's OK to use RELATION for a sequence */
1199 istmt.objtype = ACL_OBJECT_RELATION;
1201 case DatabaseRelationId:
1202 istmt.objtype = ACL_OBJECT_DATABASE;
1204 case ProcedureRelationId:
1205 istmt.objtype = ACL_OBJECT_FUNCTION;
1207 case LanguageRelationId:
1208 istmt.objtype = ACL_OBJECT_LANGUAGE;
1210 case NamespaceRelationId:
1211 istmt.objtype = ACL_OBJECT_NAMESPACE;
1213 case TableSpaceRelationId:
1214 istmt.objtype = ACL_OBJECT_TABLESPACE;
1217 elog(ERROR, "unexpected object type %d",
1221 istmt.is_grant = false;
1222 istmt.objects = list_make1_oid(sdepForm->objid);
1223 istmt.all_privs = true;
1224 istmt.privileges = ACL_NO_RIGHTS;
1225 istmt.col_privs = NIL;
1226 istmt.grantees = list_make1_oid(roleid);
1227 istmt.grant_option = false;
1228 istmt.behavior = DROP_CASCADE;
1230 ExecGrantStmt_oids(&istmt);
1232 case SHARED_DEPENDENCY_OWNER:
1233 /* Save it for deletion below */
1234 obj.classId = sdepForm->classid;
1235 obj.objectId = sdepForm->objid;
1236 obj.objectSubId = sdepForm->objsubid;
1237 add_exact_object_address(&obj, deleteobjs);
1242 systable_endscan(scan);
1245 /* the dependency mechanism does the actual work */
1246 performMultipleDeletions(deleteobjs, behavior);
1248 heap_close(sdepRel, RowExclusiveLock);
1250 free_object_addresses(deleteobjs);
1254 * shdepReassignOwned
1256 * Change the owner of objects owned by any of the roles in roleids to
1257 * newrole. Grants are not touched.
1260 shdepReassignOwned(List *roleids, Oid newrole)
1266 * We don't need this strong a lock here, but we'll call routines that
1267 * acquire RowExclusiveLock. Better get that right now to avoid potential
1268 * deadlock problems.
1270 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
1272 foreach(cell, roleids)
1277 Oid roleid = lfirst_oid(cell);
1279 /* Refuse to work on pinned roles */
1280 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1284 obj.classId = AuthIdRelationId;
1285 obj.objectId = roleid;
1286 obj.objectSubId = 0;
1289 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1290 errmsg("cannot drop objects owned by %s because they are "
1291 "required by the database system",
1292 getObjectDescription(&obj))));
1295 * There's no need to tell the whole truth, which is that we
1296 * didn't track these dependencies at all ...
1300 ScanKeyInit(&key[0],
1301 Anum_pg_shdepend_refclassid,
1302 BTEqualStrategyNumber, F_OIDEQ,
1303 ObjectIdGetDatum(AuthIdRelationId));
1304 ScanKeyInit(&key[1],
1305 Anum_pg_shdepend_refobjid,
1306 BTEqualStrategyNumber, F_OIDEQ,
1307 ObjectIdGetDatum(roleid));
1309 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1310 SnapshotNow, 2, key);
1312 while ((tuple = systable_getnext(scan)) != NULL)
1314 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1316 /* We only operate on objects in the current database */
1317 if (sdepForm->dbid != MyDatabaseId)
1320 /* Unexpected because we checked for pins above */
1321 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
1322 elog(ERROR, "unexpected shared pin");
1324 /* We leave non-owner dependencies alone */
1325 if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
1328 /* Issue the appropriate ALTER OWNER call */
1329 switch (sdepForm->classid)
1331 case ConversionRelationId:
1332 AlterConversionOwner_oid(sdepForm->objid, newrole);
1335 case TypeRelationId:
1336 AlterTypeOwnerInternal(sdepForm->objid, newrole, true);
1339 case OperatorRelationId:
1340 AlterOperatorOwner_oid(sdepForm->objid, newrole);
1343 case NamespaceRelationId:
1344 AlterSchemaOwner_oid(sdepForm->objid, newrole);
1347 case RelationRelationId:
1350 * Pass recursing = true so that we don't fail on indexes,
1351 * owned sequences, etc when we happen to visit them
1352 * before their parent table.
1354 ATExecChangeOwner(sdepForm->objid, newrole, true);
1357 case ProcedureRelationId:
1358 AlterFunctionOwner_oid(sdepForm->objid, newrole);
1361 case LanguageRelationId:
1362 AlterLanguageOwner_oid(sdepForm->objid, newrole);
1366 elog(ERROR, "unexpected classid %d", sdepForm->classid);
1369 /* Make sure the next iteration will see my changes */
1370 CommandCounterIncrement();
1373 systable_endscan(scan);
1376 heap_close(sdepRel, RowExclusiveLock);