1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_shdepend relation
6 * Portions Copyright (c) 1996-2005, 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.5 2005/11/22 18:17:08 momjian Exp $
13 *-------------------------------------------------------------------------
17 #include "access/genam.h"
18 #include "access/heapam.h"
19 #include "utils/acl.h"
20 #include "catalog/dependency.h"
21 #include "catalog/indexing.h"
22 #include "catalog/pg_authid.h"
23 #include "catalog/pg_conversion.h"
24 #include "catalog/pg_database.h"
25 #include "catalog/pg_language.h"
26 #include "catalog/pg_namespace.h"
27 #include "catalog/pg_operator.h"
28 #include "catalog/pg_proc.h"
29 #include "catalog/pg_shdepend.h"
30 #include "catalog/pg_tablespace.h"
31 #include "catalog/pg_type.h"
32 #include "commands/conversioncmds.h"
33 #include "commands/defrem.h"
34 #include "commands/schemacmds.h"
35 #include "commands/tablecmds.h"
36 #include "commands/typecmds.h"
37 #include "lib/stringinfo.h"
38 #include "miscadmin.h"
39 #include "utils/fmgroids.h"
40 #include "utils/inval.h"
41 #include "utils/syscache.h"
51 static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2,
53 static Oid classIdGetDbId(Oid classId);
54 static void shdepLockAndCheckObject(Oid classId, Oid objectId);
55 static void shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
56 Oid refclassid, Oid refobjid,
57 SharedDependencyType deptype);
58 static void shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
59 Oid refclassId, Oid refobjId,
60 SharedDependencyType deptype);
61 static void shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
62 Oid refclassId, Oid refobjId,
63 SharedDependencyType deptype);
64 static void storeObjectDescription(StringInfo descs, objectType type,
65 ObjectAddress *object,
66 SharedDependencyType deptype,
68 static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel);
72 * recordSharedDependencyOn
74 * Record a dependency between 2 objects via their respective ObjectAddresses.
75 * The first argument is the dependent object, the second the one it
76 * references (which must be a shared object).
78 * This locks the referenced object and makes sure it still exists.
79 * Then it creates an entry in pg_shdepend. The lock is kept until
80 * the end of the transaction.
82 * Dependencies on pinned objects are not recorded.
85 recordSharedDependencyOn(ObjectAddress *depender,
86 ObjectAddress *referenced,
87 SharedDependencyType deptype)
92 * Objects in pg_shdepend can't have SubIds.
94 Assert(depender->objectSubId == 0);
95 Assert(referenced->objectSubId == 0);
98 * During bootstrap, do nothing since pg_shdepend may not exist yet.
99 * initdb will fill in appropriate pg_shdepend entries after bootstrap.
101 if (IsBootstrapProcessingMode())
104 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
106 /* If the referenced object is pinned, do nothing. */
107 if (!isSharedObjectPinned(referenced->classId, referenced->objectId,
110 shdepAddDependency(sdepRel, depender->classId, depender->objectId,
111 referenced->classId, referenced->objectId,
115 heap_close(sdepRel, RowExclusiveLock);
119 * recordDependencyOnOwner
121 * A convenient wrapper of recordSharedDependencyOn -- register the specified
122 * user as owner of the given object.
124 * Note: it's the caller's responsibility to ensure that there isn't an owner
125 * entry for the object already.
128 recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
130 ObjectAddress myself,
133 myself.classId = classId;
134 myself.objectId = objectId;
135 myself.objectSubId = 0;
137 referenced.classId = AuthIdRelationId;
138 referenced.objectId = owner;
139 referenced.objectSubId = 0;
141 recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER);
147 * Update shared dependency records to account for an updated referenced
148 * object. This is an internal workhorse for operations such as changing
151 * There must be no more than one existing entry for the given dependent
152 * object and dependency type! So in practice this can only be used for
153 * updating SHARED_DEPENDENCY_OWNER entries, which should have that property.
155 * If there is no previous entry, we assume it was referencing a PINned
156 * object, so we create a new entry. If the new referenced object is
157 * PINned, we don't create an entry (and drop the old one, if any).
159 * sdepRel must be the pg_shdepend relation, already opened and suitably
163 shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
164 Oid refclassid, Oid refobjid,
165 SharedDependencyType deptype)
167 Oid dbid = classIdGetDbId(classid);
168 HeapTuple oldtup = NULL;
174 * Make sure the new referenced object doesn't go away while we record the
177 shdepLockAndCheckObject(refclassid, refobjid);
180 * Look for a previous entry
183 Anum_pg_shdepend_dbid,
184 BTEqualStrategyNumber, F_OIDEQ,
185 ObjectIdGetDatum(dbid));
187 Anum_pg_shdepend_classid,
188 BTEqualStrategyNumber, F_OIDEQ,
189 ObjectIdGetDatum(classid));
191 Anum_pg_shdepend_objid,
192 BTEqualStrategyNumber, F_OIDEQ,
193 ObjectIdGetDatum(objid));
195 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
196 SnapshotNow, 3, key);
198 while ((scantup = systable_getnext(scan)) != NULL)
200 /* Ignore if not of the target dependency type */
201 if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype)
203 /* Caller screwed up if multiple matches */
206 "multiple pg_shdepend entries for object %u/%u deptype %c",
207 classid, objid, deptype);
208 oldtup = heap_copytuple(scantup);
211 systable_endscan(scan);
213 if (isSharedObjectPinned(refclassid, refobjid, sdepRel))
215 /* No new entry needed, so just delete existing entry if any */
217 simple_heap_delete(sdepRel, &oldtup->t_self);
221 /* Need to update existing entry */
222 Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup);
224 /* Since oldtup is a copy, we can just modify it in-memory */
225 shForm->refclassid = refclassid;
226 shForm->refobjid = refobjid;
228 simple_heap_update(sdepRel, &oldtup->t_self, oldtup);
230 /* keep indexes current */
231 CatalogUpdateIndexes(sdepRel, oldtup);
235 /* Need to insert new entry */
236 Datum values[Natts_pg_shdepend];
237 bool nulls[Natts_pg_shdepend];
239 memset(nulls, 0, sizeof(nulls));
241 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid);
242 values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid);
243 values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid);
245 values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
246 values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
247 values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
250 * we are reusing oldtup just to avoid declaring a new variable, but
251 * it's certainly a new tuple
253 oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls);
254 simple_heap_insert(sdepRel, oldtup);
256 /* keep indexes current */
257 CatalogUpdateIndexes(sdepRel, oldtup);
261 heap_freetuple(oldtup);
265 * changeDependencyOnOwner
267 * Update the shared dependencies to account for the new owner.
270 changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
274 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
276 /* Adjust the SHARED_DEPENDENCY_OWNER entry */
277 shdepChangeDep(sdepRel, classId, objectId,
278 AuthIdRelationId, newOwnerId,
279 SHARED_DEPENDENCY_OWNER);
282 * There should never be a SHARED_DEPENDENCY_ACL entry for the owner,
283 * so get rid of it if there is one. This can happen if the new owner
284 * was previously granted some rights to the object.
286 * This step is analogous to aclnewowner's removal of duplicate entries
287 * in the ACL. We have to do it to handle this scenario:
288 * A grants some rights on an object to B
289 * ALTER OWNER changes the object's owner to B
290 * ALTER OWNER changes the object's owner to C
291 * The third step would remove all mention of B from the object's ACL,
292 * but we'd still have a SHARED_DEPENDENCY_ACL for B if we did not do
295 * The rule against having a SHARED_DEPENDENCY_ACL entry for the owner
296 * allows us to fix things up in just this one place, without having
297 * to make the various ALTER OWNER routines each know about it.
300 shdepDropDependency(sdepRel, classId, objectId,
301 AuthIdRelationId, newOwnerId,
302 SHARED_DEPENDENCY_ACL);
304 heap_close(sdepRel, RowExclusiveLock);
309 * Helper for updateAclDependencies.
311 * Takes two Oid arrays and returns elements from the first not found in the
312 * second. We assume both arrays are sorted and de-duped, and that the
313 * second array does not contain any values not found in the first.
315 * NOTE: Both input arrays are pfreed.
318 getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff)
325 AssertArg(nlist1 >= nlist2 && nlist2 >= 0);
327 result = palloc(sizeof(Oid) * (nlist1 - nlist2));
330 for (i = 0, j = 0; i < nlist1 && j < nlist2;)
332 if (list1[i] == list2[j])
337 else if (list1[i] < list2[j])
339 result[k++] = list1[i];
345 elog(WARNING, "invalid element %u in shorter list", list2[j]);
350 for (; i < nlist1; i++)
351 result[k++] = list1[i];
353 /* We should have copied the exact number of elements */
354 AssertState(k == (nlist1 - nlist2));
365 * updateAclDependencies
366 * Update the pg_shdepend info for an object's ACL during GRANT/REVOKE.
368 * classId, objectId: identify the object whose ACL this is
369 * ownerId: role owning the object
370 * isGrant: are we adding or removing ACL entries?
371 * noldmembers, oldmembers: array of roleids appearing in old ACL
372 * nnewmembers, newmembers: array of roleids appearing in new ACL
374 * We calculate the difference between the new and old lists of roles,
375 * and then insert (if it's a grant) or delete (if it's a revoke) from
376 * pg_shdepend as appropiate.
378 * Note that we can't insert blindly at grant, because we would end up with
379 * duplicate registered dependencies. We could check for existence of the
380 * tuple before inserting, but that seems to be more expensive than what we are
381 * doing now. On the other hand, we can't just delete the tuples blindly at
382 * revoke, because the user may still have other privileges.
384 * NOTE: Both input arrays must be sorted and de-duped. They are pfreed
388 updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant,
389 int noldmembers, Oid *oldmembers,
390 int nnewmembers, Oid *newmembers)
398 * Calculate the differences between the old and new lists.
401 ndiff = getOidListDiff(newmembers, nnewmembers,
402 oldmembers, noldmembers, &diff);
404 ndiff = getOidListDiff(oldmembers, noldmembers,
405 newmembers, nnewmembers, &diff);
409 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
411 /* Add or drop the respective dependency */
412 for (i = 0; i < ndiff; i++)
414 Oid roleid = diff[i];
417 * Skip the owner: he has an OWNER shdep entry instead. (This is
418 * not just a space optimization; it makes ALTER OWNER easier. See
419 * notes in changeDependencyOnOwner.)
421 if (roleid == ownerId)
424 /* Skip pinned roles */
425 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
429 shdepAddDependency(sdepRel, classId, objectId,
430 AuthIdRelationId, roleid,
431 SHARED_DEPENDENCY_ACL);
433 shdepDropDependency(sdepRel, classId, objectId,
434 AuthIdRelationId, roleid,
435 SHARED_DEPENDENCY_ACL);
438 heap_close(sdepRel, RowExclusiveLock);
445 * A struct to keep track of dependencies found in other databases.
454 * checkSharedDependencies
456 * Check whether there are shared dependency entries for a given shared
457 * object. Returns a string containing a newline-separated list of object
458 * descriptions that depend on the shared object, or NULL if none is found.
460 * We can find three different kinds of dependencies: dependencies on objects
461 * of the current database; dependencies on shared objects; and dependencies
462 * on objects local to other databases. We can (and do) provide descriptions
463 * of the two former kinds of objects, but we can't do that for "remote"
464 * objects, so we just provide a count of them.
466 * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early.
469 checkSharedDependencies(Oid classId, Oid objectId)
476 int numLocalDeps = 0;
477 int numSharedDeps = 0;
480 ObjectAddress object;
481 StringInfoData descs;
484 * We try to limit the number of reported dependencies to something sane,
485 * both for the user's sake and to avoid blowing out memory.
487 #define MAX_REPORTED_DEPS 100
489 initStringInfo(&descs);
491 sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
494 Anum_pg_shdepend_refclassid,
495 BTEqualStrategyNumber, F_OIDEQ,
496 ObjectIdGetDatum(classId));
498 Anum_pg_shdepend_refobjid,
499 BTEqualStrategyNumber, F_OIDEQ,
500 ObjectIdGetDatum(objectId));
502 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
503 SnapshotNow, 2, key);
505 while (HeapTupleIsValid(tup = systable_getnext(scan)))
507 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
509 /* This case can be dispatched quickly */
510 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
512 object.classId = classId;
513 object.objectId = objectId;
514 object.objectSubId = 0;
516 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
517 errmsg("cannot drop %s because it is required by the database system",
518 getObjectDescription(&object))));
521 object.classId = sdepForm->classid;
522 object.objectId = sdepForm->objid;
523 object.objectSubId = 0;
526 * If it's a dependency local to this database or it's a shared
527 * object, describe it.
529 * If it's a remote dependency, keep track of it so we can report the
530 * number of them later.
532 if (sdepForm->dbid == MyDatabaseId)
535 if (++totalDeps <= MAX_REPORTED_DEPS)
536 storeObjectDescription(&descs, LOCAL_OBJECT, &object,
537 sdepForm->deptype, 0);
539 else if (sdepForm->dbid == InvalidOid)
542 if (++totalDeps <= MAX_REPORTED_DEPS)
543 storeObjectDescription(&descs, SHARED_OBJECT, &object,
544 sdepForm->deptype, 0);
548 /* It's not local nor shared, so it must be remote. */
553 * XXX this info is kept on a simple List. Maybe it's not good
554 * for performance, but using a hash table seems needlessly
555 * complex. The expected number of databases is not high anyway,
558 foreach(cell, remDeps)
561 if (dep->dbOid == sdepForm->dbid)
570 dep = (remoteDep *) palloc(sizeof(remoteDep));
571 dep->dbOid = sdepForm->dbid;
573 remDeps = lappend(remDeps, dep);
579 systable_endscan(scan);
581 heap_close(sdepRel, AccessShareLock);
583 if (totalDeps > MAX_REPORTED_DEPS)
586 * Report seems unreasonably long, so reduce it to per-database info
588 * Note: we don't ever suppress per-database totals, which should be
589 * OK as long as there aren't too many databases ...
591 descs.len = 0; /* reset to empty */
592 descs.data[0] = '\0';
594 if (numLocalDeps > 0)
596 appendStringInfo(&descs, _("%d objects in this database"),
598 if (numSharedDeps > 0)
599 appendStringInfoChar(&descs, '\n');
601 if (numSharedDeps > 0)
602 appendStringInfo(&descs, _("%d shared objects"),
606 foreach(cell, remDeps)
608 remoteDep *dep = lfirst(cell);
610 object.classId = DatabaseRelationId;
611 object.objectId = dep->dbOid;
612 object.objectSubId = 0;
614 storeObjectDescription(&descs, REMOTE_OBJECT, &object,
615 SHARED_DEPENDENCY_INVALID, dep->count);
618 list_free_deep(remDeps);
630 * copyTemplateDependencies
632 * Routine to create the initial shared dependencies of a new database.
633 * We simply copy the dependencies from the template database.
636 copyTemplateDependencies(Oid templateDbId, Oid newDbId)
643 CatalogIndexState indstate;
644 Datum values[Natts_pg_shdepend];
645 bool nulls[Natts_pg_shdepend];
646 bool replace[Natts_pg_shdepend];
648 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
649 sdepDesc = RelationGetDescr(sdepRel);
651 indstate = CatalogOpenIndexes(sdepRel);
653 /* Scan all entries with dbid = templateDbId */
655 Anum_pg_shdepend_dbid,
656 BTEqualStrategyNumber, F_OIDEQ,
657 ObjectIdGetDatum(templateDbId));
659 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
660 SnapshotNow, 1, key);
662 /* Set up to copy the tuples except for inserting newDbId */
663 memset(values, 0, sizeof(values));
664 memset(nulls, false, sizeof(nulls));
665 memset(replace, false, sizeof(replace));
667 replace[Anum_pg_shdepend_dbid - 1] = true;
668 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId);
671 * Copy the entries of the original database, changing the database Id to
672 * that of the new database. Note that because we are not copying rows
673 * with dbId == 0 (ie, rows describing dependent shared objects) we won't
674 * copy the ownership dependency of the template database itself; this is
677 while (HeapTupleIsValid(tup = systable_getnext(scan)))
681 newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace);
682 simple_heap_insert(sdepRel, newtup);
684 /* Keep indexes current */
685 CatalogIndexInsert(indstate, newtup);
687 heap_freetuple(newtup);
690 systable_endscan(scan);
692 CatalogCloseIndexes(indstate);
693 heap_close(sdepRel, RowExclusiveLock);
697 * dropDatabaseDependencies
699 * Delete pg_shdepend entries corresponding to a database that's being
703 dropDatabaseDependencies(Oid databaseId)
710 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
713 * First, delete all the entries that have the database Oid in the dbid
717 Anum_pg_shdepend_dbid,
718 BTEqualStrategyNumber, F_OIDEQ,
719 ObjectIdGetDatum(databaseId));
720 /* We leave the other index fields unspecified */
722 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
723 SnapshotNow, 1, key);
725 while (HeapTupleIsValid(tup = systable_getnext(scan)))
727 simple_heap_delete(sdepRel, &tup->t_self);
730 systable_endscan(scan);
732 /* Now delete all entries corresponding to the database itself */
733 shdepDropDependency(sdepRel, DatabaseRelationId, databaseId,
734 InvalidOid, InvalidOid,
735 SHARED_DEPENDENCY_INVALID);
737 heap_close(sdepRel, RowExclusiveLock);
741 * deleteSharedDependencyRecordsFor
743 * Delete all pg_shdepend entries corresponding to an object that's being
744 * dropped or modified. The object is assumed to be either a shared object
745 * or local to the current database (the classId tells us which).
748 deleteSharedDependencyRecordsFor(Oid classId, Oid objectId)
752 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
754 shdepDropDependency(sdepRel, classId, objectId,
755 InvalidOid, InvalidOid,
756 SHARED_DEPENDENCY_INVALID);
758 heap_close(sdepRel, RowExclusiveLock);
763 * Internal workhorse for inserting into pg_shdepend
765 * sdepRel must be the pg_shdepend relation, already opened and suitably
769 shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
770 Oid refclassId, Oid refobjId,
771 SharedDependencyType deptype)
774 Datum values[Natts_pg_shdepend];
775 bool nulls[Natts_pg_shdepend];
778 * Make sure the object doesn't go away while we record the dependency on
779 * it. DROP routines should lock the object exclusively before they check
780 * shared dependencies.
782 shdepLockAndCheckObject(refclassId, refobjId);
784 memset(nulls, false, sizeof(nulls));
787 * Form the new tuple and record the dependency.
789 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId));
790 values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId);
791 values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId);
793 values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
794 values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
795 values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
797 tup = heap_form_tuple(sdepRel->rd_att, values, nulls);
799 simple_heap_insert(sdepRel, tup);
801 /* keep indexes current */
802 CatalogUpdateIndexes(sdepRel, tup);
809 * shdepDropDependency
810 * Internal workhorse for deleting entries from pg_shdepend.
812 * We drop entries having the following properties:
813 * dependent object is the one identified by classId/objectId
814 * if refclassId isn't InvalidOid, it must match the entry's refclassid
815 * if refobjId isn't InvalidOid, it must match the entry's refobjid
816 * if deptype isn't SHARED_DEPENDENCY_INVALID, it must match entry's deptype
818 * sdepRel must be the pg_shdepend relation, already opened and suitably
822 shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
823 Oid refclassId, Oid refobjId,
824 SharedDependencyType deptype)
830 /* Scan for entries matching the dependent object */
832 Anum_pg_shdepend_dbid,
833 BTEqualStrategyNumber, F_OIDEQ,
834 ObjectIdGetDatum(classIdGetDbId(classId)));
836 Anum_pg_shdepend_classid,
837 BTEqualStrategyNumber, F_OIDEQ,
838 ObjectIdGetDatum(classId));
840 Anum_pg_shdepend_objid,
841 BTEqualStrategyNumber, F_OIDEQ,
842 ObjectIdGetDatum(objectId));
844 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
845 SnapshotNow, 3, key);
847 while (HeapTupleIsValid(tup = systable_getnext(scan)))
849 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
851 /* Filter entries according to additional parameters */
852 if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId)
854 if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId)
856 if (deptype != SHARED_DEPENDENCY_INVALID &&
857 shdepForm->deptype != deptype)
861 simple_heap_delete(sdepRel, &tup->t_self);
864 systable_endscan(scan);
870 * Get the database Id that should be used in pg_shdepend, given the OID
871 * of the catalog containing the object. For shared objects, it's 0
872 * (InvalidOid); for all other objects, it's the current database Id.
874 * XXX it's awfully tempting to hard-wire this instead of doing a syscache
875 * lookup ... but resist the temptation, unless you can prove it's a
879 classIdGetDbId(Oid classId)
884 tup = SearchSysCache(RELOID,
885 ObjectIdGetDatum(classId),
887 if (!HeapTupleIsValid(tup))
888 elog(ERROR, "cache lookup failed for relation %u", classId);
890 if (((Form_pg_class) GETSTRUCT(tup))->relisshared)
895 ReleaseSysCache(tup);
901 * shdepLockAndCheckObject
903 * Lock the object that we are about to record a dependency on.
904 * After it's locked, verify that it hasn't been dropped while we
905 * weren't looking. If the object has been dropped, this function
909 shdepLockAndCheckObject(Oid classId, Oid objectId)
911 /* AccessShareLock should be OK, since we are not modifying the object */
912 LockSharedObject(classId, objectId, 0, AccessShareLock);
915 * We have to recognize sinval updates here, else our local syscache may
916 * still contain the object even if it was just dropped.
918 AcceptInvalidationMessages();
922 case AuthIdRelationId:
923 if (!SearchSysCacheExists(AUTHOID,
924 ObjectIdGetDatum(objectId),
927 (errcode(ERRCODE_UNDEFINED_OBJECT),
928 errmsg("role %u was concurrently dropped",
933 * Currently, this routine need not support any other shared
934 * object types besides roles. If we wanted to record explicit
935 * dependencies on databases or tablespaces, we'd need code along
939 case TableSpaceRelationId:
941 /* For lack of a syscache on pg_tablespace, do this: */
942 char *tablespace = get_tablespace_name(objectId);
944 if (tablespace == NULL)
946 (errcode(ERRCODE_UNDEFINED_OBJECT),
947 errmsg("tablespace %u was concurrently dropped",
955 elog(ERROR, "unrecognized shared classId: %u", classId);
961 * storeObjectDescription
962 * Append the description of a dependent object to "descs"
964 * While searching for dependencies of a shared object, we stash the
965 * descriptions of dependent objects we find in a single string, which we
966 * later pass to ereport() in the DETAIL field when somebody attempts to
967 * drop a referenced shared object.
969 * When type is LOCAL_OBJECT or SHARED_OBJECT, we expect object to be the
970 * dependent object, deptype is the dependency type, and count is not used.
971 * When type is REMOTE_OBJECT, we expect object to be the database object,
972 * and count to be nonzero; deptype is not used in this case.
975 storeObjectDescription(StringInfo descs, objectType type,
976 ObjectAddress *object,
977 SharedDependencyType deptype,
980 char *objdesc = getObjectDescription(object);
982 /* separate entries with a newline */
984 appendStringInfoChar(descs, '\n');
990 if (deptype == SHARED_DEPENDENCY_OWNER)
991 appendStringInfo(descs, _("owner of %s"), objdesc);
992 else if (deptype == SHARED_DEPENDENCY_ACL)
993 appendStringInfo(descs, _("access to %s"), objdesc);
995 elog(ERROR, "unrecognized dependency type: %d",
1000 /* translator: %s will always be "database %s" */
1001 appendStringInfo(descs, _("%d objects in %s"), count, objdesc);
1005 elog(ERROR, "unrecognized object type: %d", type);
1013 * isSharedObjectPinned
1014 * Return whether a given shared object has a SHARED_DEPENDENCY_PIN entry.
1016 * sdepRel must be the pg_shdepend relation, already opened and suitably
1020 isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
1022 bool result = false;
1027 ScanKeyInit(&key[0],
1028 Anum_pg_shdepend_refclassid,
1029 BTEqualStrategyNumber, F_OIDEQ,
1030 ObjectIdGetDatum(classId));
1031 ScanKeyInit(&key[1],
1032 Anum_pg_shdepend_refobjid,
1033 BTEqualStrategyNumber, F_OIDEQ,
1034 ObjectIdGetDatum(objectId));
1036 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1037 SnapshotNow, 2, key);
1040 * Since we won't generate additional pg_shdepend entries for pinned
1041 * objects, there can be at most one entry referencing a pinned object.
1042 * Hence, it's sufficient to look at the first returned tuple; we don't
1045 tup = systable_getnext(scan);
1046 if (HeapTupleIsValid(tup))
1048 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
1050 if (shdepForm->deptype == SHARED_DEPENDENCY_PIN)
1054 systable_endscan(scan);
1062 * Drop the objects owned by any one of the given RoleIds. If a role has
1063 * access to an object, the grant will be removed as well (but the object
1064 * will not, of course.)
1067 shdepDropOwned(List *roleids, DropBehavior behavior)
1072 sdepRel = heap_open(SharedDependRelationId, AccessExclusiveLock);
1075 * For each role, find the dependent objects and drop them using the
1076 * regular (non-shared) dependency management.
1078 foreach(cell, roleids)
1080 Oid roleid = lfirst_oid(cell);
1085 /* Doesn't work for pinned objects */
1086 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1090 obj.classId = AuthIdRelationId;
1091 obj.objectId = roleid;
1092 obj.objectSubId = 0;
1095 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1096 errmsg("cannot drop objects owned by %s because they are "
1097 "required by the database system",
1098 getObjectDescription(&obj))));
1101 ScanKeyInit(&key[0],
1102 Anum_pg_shdepend_refclassid,
1103 BTEqualStrategyNumber, F_OIDEQ,
1104 ObjectIdGetDatum(AuthIdRelationId));
1105 ScanKeyInit(&key[1],
1106 Anum_pg_shdepend_refobjid,
1107 BTEqualStrategyNumber, F_OIDEQ,
1108 ObjectIdGetDatum(roleid));
1110 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1111 SnapshotNow, 2, key);
1113 while ((tuple = systable_getnext(scan)) != NULL)
1115 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1117 /* We only operate on objects on the current database */
1118 if (sdepForm->dbid != MyDatabaseId)
1121 switch (sdepForm->deptype)
1124 GrantObjectType objtype;
1126 /* Shouldn't happen */
1127 case SHARED_DEPENDENCY_PIN:
1128 case SHARED_DEPENDENCY_INVALID:
1129 elog(ERROR, "unexpected dependency type");
1131 case SHARED_DEPENDENCY_ACL:
1132 switch (sdepForm->classid)
1134 case RelationRelationId:
1135 objtype = ACL_OBJECT_RELATION;
1137 case DatabaseRelationId:
1138 objtype = ACL_OBJECT_DATABASE;
1140 case ProcedureRelationId:
1141 objtype = ACL_OBJECT_FUNCTION;
1143 case LanguageRelationId:
1144 objtype = ACL_OBJECT_LANGUAGE;
1146 case NamespaceRelationId:
1147 objtype = ACL_OBJECT_NAMESPACE;
1149 case TableSpaceRelationId:
1150 objtype = ACL_OBJECT_TABLESPACE;
1153 elog(ERROR, "unexpected object type %d",
1155 /* keep compiler quiet */
1156 objtype = (GrantObjectType) 0;
1160 ExecGrantStmt_oids(false, objtype,
1161 list_make1_oid(sdepForm->objid), true,
1162 ACL_NO_RIGHTS, list_make1_oid(roleid),
1163 false, DROP_CASCADE);
1165 case SHARED_DEPENDENCY_OWNER:
1168 * If there's a regular (non-shared) dependency on this
1169 * object marked with DEPENDENCY_INTERNAL, skip this
1170 * object. We will drop the referencer object instead.
1172 if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid))
1175 /* Drop the object */
1176 obj.classId = sdepForm->classid;
1177 obj.objectId = sdepForm->objid;
1178 obj.objectSubId = 0;
1179 performDeletion(&obj, behavior);
1184 systable_endscan(scan);
1187 heap_close(sdepRel, AccessExclusiveLock);
1191 * shdepReassignOwned
1193 * Change the owner of objects owned by any of the roles in roleids to
1194 * newrole. Grants are not touched.
1197 shdepReassignOwned(List *roleids, Oid newrole)
1202 sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
1204 foreach(cell, roleids)
1209 Oid roleid = lfirst_oid(cell);
1211 /* Refuse to work on pinned roles */
1212 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1216 obj.classId = AuthIdRelationId;
1217 obj.objectId = roleid;
1218 obj.objectSubId = 0;
1221 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1222 errmsg("cannot drop objects owned by %s because they are "
1223 "required by the database system",
1224 getObjectDescription(&obj))));
1227 * There's no need to tell the whole truth, which is that we
1228 * didn't track these dependencies at all ...
1232 ScanKeyInit(&key[0],
1233 Anum_pg_shdepend_refclassid,
1234 BTEqualStrategyNumber, F_OIDEQ,
1235 ObjectIdGetDatum(AuthIdRelationId));
1236 ScanKeyInit(&key[1],
1237 Anum_pg_shdepend_refobjid,
1238 BTEqualStrategyNumber, F_OIDEQ,
1239 ObjectIdGetDatum(roleid));
1241 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1242 SnapshotNow, 2, key);
1244 while ((tuple = systable_getnext(scan)) != NULL)
1246 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1248 /* We only operate on objects on the current database */
1249 if (sdepForm->dbid != MyDatabaseId)
1252 /* Unexpected because we checked for pins above */
1253 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
1254 elog(ERROR, "unexpected shared pin");
1256 /* We leave non-owner dependencies alone */
1257 if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
1261 * If there's a regular (non-shared) dependency on this object
1262 * marked with DEPENDENCY_INTERNAL, skip this object. We will
1263 * alter the referencer object instead.
1265 if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid))
1268 /* Issue the appropiate ALTER OWNER call */
1269 switch (sdepForm->classid)
1271 case ConversionRelationId:
1272 AlterConversionOwner_oid(sdepForm->objid, newrole);
1275 case TypeRelationId:
1276 AlterTypeOwnerInternal(sdepForm->objid, newrole);
1279 case OperatorRelationId:
1280 AlterOperatorOwner_oid(sdepForm->objid, newrole);
1283 case NamespaceRelationId:
1284 AlterSchemaOwner_oid(sdepForm->objid, newrole);
1287 case RelationRelationId:
1288 ATExecChangeOwner(sdepForm->objid, newrole, false);
1291 case ProcedureRelationId:
1292 AlterFunctionOwner_oid(sdepForm->objid, newrole);
1296 elog(ERROR, "unexpected classid %d", sdepForm->classid);
1299 /* Make sure the next iteration will see my changes */
1300 CommandCounterIncrement();
1303 systable_endscan(scan);
1306 heap_close(sdepRel, AccessShareLock);