1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_shdepend relation
6 * Portions Copyright (c) 1996-2006, 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.12 2006/07/14 14:52:18 momjian Exp $
13 *-------------------------------------------------------------------------
17 #include "access/genam.h"
18 #include "access/heapam.h"
19 #include "access/xact.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 "miscadmin.h"
38 #include "utils/acl.h"
39 #include "utils/fmgroids.h"
40 #include "utils/syscache.h"
50 static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2,
52 static Oid classIdGetDbId(Oid classId);
53 static void shdepLockAndCheckObject(Oid classId, Oid objectId);
54 static void shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
55 Oid refclassid, Oid refobjid,
56 SharedDependencyType deptype);
57 static void shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
58 Oid refclassId, Oid refobjId,
59 SharedDependencyType deptype);
60 static void shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
61 Oid refclassId, Oid refobjId,
62 SharedDependencyType deptype);
63 static void storeObjectDescription(StringInfo descs, objectType type,
64 ObjectAddress *object,
65 SharedDependencyType deptype,
67 static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel);
71 * recordSharedDependencyOn
73 * Record a dependency between 2 objects via their respective ObjectAddresses.
74 * The first argument is the dependent object, the second the one it
75 * references (which must be a shared object).
77 * This locks the referenced object and makes sure it still exists.
78 * Then it creates an entry in pg_shdepend. The lock is kept until
79 * the end of the transaction.
81 * Dependencies on pinned objects are not recorded.
84 recordSharedDependencyOn(ObjectAddress *depender,
85 ObjectAddress *referenced,
86 SharedDependencyType deptype)
91 * Objects in pg_shdepend can't have SubIds.
93 Assert(depender->objectSubId == 0);
94 Assert(referenced->objectSubId == 0);
97 * During bootstrap, do nothing since pg_shdepend may not exist yet.
98 * initdb will fill in appropriate pg_shdepend entries after bootstrap.
100 if (IsBootstrapProcessingMode())
103 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
105 /* If the referenced object is pinned, do nothing. */
106 if (!isSharedObjectPinned(referenced->classId, referenced->objectId,
109 shdepAddDependency(sdepRel, depender->classId, depender->objectId,
110 referenced->classId, referenced->objectId,
114 heap_close(sdepRel, RowExclusiveLock);
118 * recordDependencyOnOwner
120 * A convenient wrapper of recordSharedDependencyOn -- register the specified
121 * user as owner of the given object.
123 * Note: it's the caller's responsibility to ensure that there isn't an owner
124 * entry for the object already.
127 recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
129 ObjectAddress myself,
132 myself.classId = classId;
133 myself.objectId = objectId;
134 myself.objectSubId = 0;
136 referenced.classId = AuthIdRelationId;
137 referenced.objectId = owner;
138 referenced.objectSubId = 0;
140 recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER);
146 * Update shared dependency records to account for an updated referenced
147 * object. This is an internal workhorse for operations such as changing
150 * There must be no more than one existing entry for the given dependent
151 * object and dependency type! So in practice this can only be used for
152 * updating SHARED_DEPENDENCY_OWNER entries, which should have that property.
154 * If there is no previous entry, we assume it was referencing a PINned
155 * object, so we create a new entry. If the new referenced object is
156 * PINned, we don't create an entry (and drop the old one, if any).
158 * sdepRel must be the pg_shdepend relation, already opened and suitably
162 shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
163 Oid refclassid, Oid refobjid,
164 SharedDependencyType deptype)
166 Oid dbid = classIdGetDbId(classid);
167 HeapTuple oldtup = NULL;
173 * Make sure the new referenced object doesn't go away while we record the
176 shdepLockAndCheckObject(refclassid, refobjid);
179 * Look for a previous entry
182 Anum_pg_shdepend_dbid,
183 BTEqualStrategyNumber, F_OIDEQ,
184 ObjectIdGetDatum(dbid));
186 Anum_pg_shdepend_classid,
187 BTEqualStrategyNumber, F_OIDEQ,
188 ObjectIdGetDatum(classid));
190 Anum_pg_shdepend_objid,
191 BTEqualStrategyNumber, F_OIDEQ,
192 ObjectIdGetDatum(objid));
194 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
195 SnapshotNow, 3, key);
197 while ((scantup = systable_getnext(scan)) != NULL)
199 /* Ignore if not of the target dependency type */
200 if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype)
202 /* Caller screwed up if multiple matches */
205 "multiple pg_shdepend entries for object %u/%u deptype %c",
206 classid, objid, deptype);
207 oldtup = heap_copytuple(scantup);
210 systable_endscan(scan);
212 if (isSharedObjectPinned(refclassid, refobjid, sdepRel))
214 /* No new entry needed, so just delete existing entry if any */
216 simple_heap_delete(sdepRel, &oldtup->t_self);
220 /* Need to update existing entry */
221 Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup);
223 /* Since oldtup is a copy, we can just modify it in-memory */
224 shForm->refclassid = refclassid;
225 shForm->refobjid = refobjid;
227 simple_heap_update(sdepRel, &oldtup->t_self, oldtup);
229 /* keep indexes current */
230 CatalogUpdateIndexes(sdepRel, oldtup);
234 /* Need to insert new entry */
235 Datum values[Natts_pg_shdepend];
236 bool nulls[Natts_pg_shdepend];
238 memset(nulls, 0, sizeof(nulls));
240 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid);
241 values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid);
242 values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid);
244 values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
245 values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
246 values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
249 * we are reusing oldtup just to avoid declaring a new variable, but
250 * it's certainly a new tuple
252 oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls);
253 simple_heap_insert(sdepRel, oldtup);
255 /* keep indexes current */
256 CatalogUpdateIndexes(sdepRel, oldtup);
260 heap_freetuple(oldtup);
264 * changeDependencyOnOwner
266 * Update the shared dependencies to account for the new owner.
269 changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
273 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
275 /* Adjust the SHARED_DEPENDENCY_OWNER entry */
276 shdepChangeDep(sdepRel, classId, objectId,
277 AuthIdRelationId, newOwnerId,
278 SHARED_DEPENDENCY_OWNER);
281 * There should never be a SHARED_DEPENDENCY_ACL entry for the owner,
282 * so get rid of it if there is one. This can happen if the new owner
283 * was previously granted some rights to the object.
285 * This step is analogous to aclnewowner's removal of duplicate entries
286 * in the ACL. We have to do it to handle this scenario:
287 * A grants some rights on an object to B
288 * ALTER OWNER changes the object's owner to B
289 * ALTER OWNER changes the object's owner to C
290 * The third step would remove all mention of B from the object's ACL,
291 * but we'd still have a SHARED_DEPENDENCY_ACL for B if we did not do
294 * The rule against having a SHARED_DEPENDENCY_ACL entry for the owner
295 * allows us to fix things up in just this one place, without having
296 * to make the various ALTER OWNER routines each know about it.
299 shdepDropDependency(sdepRel, classId, objectId,
300 AuthIdRelationId, newOwnerId,
301 SHARED_DEPENDENCY_ACL);
303 heap_close(sdepRel, RowExclusiveLock);
308 * Helper for updateAclDependencies.
310 * Takes two Oid arrays and returns elements from the first not found in the
311 * second. We assume both arrays are sorted and de-duped, and that the
312 * second array does not contain any values not found in the first.
314 * NOTE: Both input arrays are pfreed.
317 getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff)
324 AssertArg(nlist1 >= nlist2 && nlist2 >= 0);
326 result = palloc(sizeof(Oid) * (nlist1 - nlist2));
329 for (i = 0, j = 0; i < nlist1 && j < nlist2;)
331 if (list1[i] == list2[j])
336 else if (list1[i] < list2[j])
338 result[k++] = list1[i];
344 elog(WARNING, "invalid element %u in shorter list", list2[j]);
349 for (; i < nlist1; i++)
350 result[k++] = list1[i];
352 /* We should have copied the exact number of elements */
353 AssertState(k == (nlist1 - nlist2));
364 * updateAclDependencies
365 * Update the pg_shdepend info for an object's ACL during GRANT/REVOKE.
367 * classId, objectId: identify the object whose ACL this is
368 * ownerId: role owning the object
369 * isGrant: are we adding or removing ACL entries?
370 * noldmembers, oldmembers: array of roleids appearing in old ACL
371 * nnewmembers, newmembers: array of roleids appearing in new ACL
373 * We calculate the difference between the new and old lists of roles,
374 * and then insert (if it's a grant) or delete (if it's a revoke) from
375 * pg_shdepend as appropiate.
377 * Note that we can't insert blindly at grant, because we would end up with
378 * duplicate registered dependencies. We could check for existence of the
379 * tuple before inserting, but that seems to be more expensive than what we are
380 * doing now. On the other hand, we can't just delete the tuples blindly at
381 * revoke, because the user may still have other privileges.
383 * NOTE: Both input arrays must be sorted and de-duped. They are pfreed
387 updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant,
388 int noldmembers, Oid *oldmembers,
389 int nnewmembers, Oid *newmembers)
397 * Calculate the differences between the old and new lists.
400 ndiff = getOidListDiff(newmembers, nnewmembers,
401 oldmembers, noldmembers, &diff);
403 ndiff = getOidListDiff(oldmembers, noldmembers,
404 newmembers, nnewmembers, &diff);
408 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
410 /* Add or drop the respective dependency */
411 for (i = 0; i < ndiff; i++)
413 Oid roleid = diff[i];
416 * Skip the owner: he has an OWNER shdep entry instead. (This is
417 * not just a space optimization; it makes ALTER OWNER easier. See
418 * notes in changeDependencyOnOwner.)
420 if (roleid == ownerId)
423 /* Skip pinned roles */
424 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
428 shdepAddDependency(sdepRel, classId, objectId,
429 AuthIdRelationId, roleid,
430 SHARED_DEPENDENCY_ACL);
432 shdepDropDependency(sdepRel, classId, objectId,
433 AuthIdRelationId, roleid,
434 SHARED_DEPENDENCY_ACL);
437 heap_close(sdepRel, RowExclusiveLock);
444 * A struct to keep track of dependencies found in other databases.
453 * checkSharedDependencies
455 * Check whether there are shared dependency entries for a given shared
456 * object. Returns a string containing a newline-separated list of object
457 * descriptions that depend on the shared object, or NULL if none is found.
459 * We can find three different kinds of dependencies: dependencies on objects
460 * of the current database; dependencies on shared objects; and dependencies
461 * on objects local to other databases. We can (and do) provide descriptions
462 * of the two former kinds of objects, but we can't do that for "remote"
463 * objects, so we just provide a count of them.
465 * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early.
468 checkSharedDependencies(Oid classId, Oid objectId)
475 int numLocalDeps = 0;
476 int numSharedDeps = 0;
479 ObjectAddress object;
480 StringInfoData descs;
483 * We try to limit the number of reported dependencies to something sane,
484 * both for the user's sake and to avoid blowing out memory.
486 #define MAX_REPORTED_DEPS 100
488 initStringInfo(&descs);
490 sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
493 Anum_pg_shdepend_refclassid,
494 BTEqualStrategyNumber, F_OIDEQ,
495 ObjectIdGetDatum(classId));
497 Anum_pg_shdepend_refobjid,
498 BTEqualStrategyNumber, F_OIDEQ,
499 ObjectIdGetDatum(objectId));
501 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
502 SnapshotNow, 2, key);
504 while (HeapTupleIsValid(tup = systable_getnext(scan)))
506 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
508 /* This case can be dispatched quickly */
509 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
511 object.classId = classId;
512 object.objectId = objectId;
513 object.objectSubId = 0;
515 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
516 errmsg("cannot drop %s because it is required by the database system",
517 getObjectDescription(&object))));
520 object.classId = sdepForm->classid;
521 object.objectId = sdepForm->objid;
522 object.objectSubId = 0;
525 * If it's a dependency local to this database or it's a shared
526 * object, describe it.
528 * If it's a remote dependency, keep track of it so we can report the
529 * number of them later.
531 if (sdepForm->dbid == MyDatabaseId)
534 if (++totalDeps <= MAX_REPORTED_DEPS)
535 storeObjectDescription(&descs, LOCAL_OBJECT, &object,
536 sdepForm->deptype, 0);
538 else if (sdepForm->dbid == InvalidOid)
541 if (++totalDeps <= MAX_REPORTED_DEPS)
542 storeObjectDescription(&descs, SHARED_OBJECT, &object,
543 sdepForm->deptype, 0);
547 /* It's not local nor shared, so it must be remote. */
552 * XXX this info is kept on a simple List. Maybe it's not good
553 * for performance, but using a hash table seems needlessly
554 * complex. The expected number of databases is not high anyway,
557 foreach(cell, remDeps)
560 if (dep->dbOid == sdepForm->dbid)
569 dep = (remoteDep *) palloc(sizeof(remoteDep));
570 dep->dbOid = sdepForm->dbid;
572 remDeps = lappend(remDeps, dep);
578 systable_endscan(scan);
580 heap_close(sdepRel, AccessShareLock);
582 if (totalDeps > MAX_REPORTED_DEPS)
585 * Report seems unreasonably long, so reduce it to per-database info
587 * Note: we don't ever suppress per-database totals, which should be
588 * OK as long as there aren't too many databases ...
590 descs.len = 0; /* reset to empty */
591 descs.data[0] = '\0';
593 if (numLocalDeps > 0)
595 appendStringInfo(&descs, _("%d objects in this database"),
597 if (numSharedDeps > 0)
598 appendStringInfoChar(&descs, '\n');
600 if (numSharedDeps > 0)
601 appendStringInfo(&descs, _("%d shared objects"),
605 foreach(cell, remDeps)
607 remoteDep *dep = lfirst(cell);
609 object.classId = DatabaseRelationId;
610 object.objectId = dep->dbOid;
611 object.objectSubId = 0;
613 storeObjectDescription(&descs, REMOTE_OBJECT, &object,
614 SHARED_DEPENDENCY_INVALID, dep->count);
617 list_free_deep(remDeps);
629 * copyTemplateDependencies
631 * Routine to create the initial shared dependencies of a new database.
632 * We simply copy the dependencies from the template database.
635 copyTemplateDependencies(Oid templateDbId, Oid newDbId)
642 CatalogIndexState indstate;
643 Datum values[Natts_pg_shdepend];
644 bool nulls[Natts_pg_shdepend];
645 bool replace[Natts_pg_shdepend];
647 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
648 sdepDesc = RelationGetDescr(sdepRel);
650 indstate = CatalogOpenIndexes(sdepRel);
652 /* Scan all entries with dbid = templateDbId */
654 Anum_pg_shdepend_dbid,
655 BTEqualStrategyNumber, F_OIDEQ,
656 ObjectIdGetDatum(templateDbId));
658 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
659 SnapshotNow, 1, key);
661 /* Set up to copy the tuples except for inserting newDbId */
662 memset(values, 0, sizeof(values));
663 memset(nulls, false, sizeof(nulls));
664 memset(replace, false, sizeof(replace));
666 replace[Anum_pg_shdepend_dbid - 1] = true;
667 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId);
670 * Copy the entries of the original database, changing the database Id to
671 * that of the new database. Note that because we are not copying rows
672 * with dbId == 0 (ie, rows describing dependent shared objects) we won't
673 * copy the ownership dependency of the template database itself; this is
676 while (HeapTupleIsValid(tup = systable_getnext(scan)))
680 newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace);
681 simple_heap_insert(sdepRel, newtup);
683 /* Keep indexes current */
684 CatalogIndexInsert(indstate, newtup);
686 heap_freetuple(newtup);
689 systable_endscan(scan);
691 CatalogCloseIndexes(indstate);
692 heap_close(sdepRel, RowExclusiveLock);
696 * dropDatabaseDependencies
698 * Delete pg_shdepend entries corresponding to a database that's being
702 dropDatabaseDependencies(Oid databaseId)
709 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
712 * First, delete all the entries that have the database Oid in the dbid
716 Anum_pg_shdepend_dbid,
717 BTEqualStrategyNumber, F_OIDEQ,
718 ObjectIdGetDatum(databaseId));
719 /* We leave the other index fields unspecified */
721 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
722 SnapshotNow, 1, key);
724 while (HeapTupleIsValid(tup = systable_getnext(scan)))
726 simple_heap_delete(sdepRel, &tup->t_self);
729 systable_endscan(scan);
731 /* Now delete all entries corresponding to the database itself */
732 shdepDropDependency(sdepRel, DatabaseRelationId, databaseId,
733 InvalidOid, InvalidOid,
734 SHARED_DEPENDENCY_INVALID);
736 heap_close(sdepRel, RowExclusiveLock);
740 * deleteSharedDependencyRecordsFor
742 * Delete all pg_shdepend entries corresponding to an object that's being
743 * dropped or modified. The object is assumed to be either a shared object
744 * or local to the current database (the classId tells us which).
747 deleteSharedDependencyRecordsFor(Oid classId, Oid objectId)
751 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
753 shdepDropDependency(sdepRel, classId, objectId,
754 InvalidOid, InvalidOid,
755 SHARED_DEPENDENCY_INVALID);
757 heap_close(sdepRel, RowExclusiveLock);
762 * Internal workhorse for inserting into pg_shdepend
764 * sdepRel must be the pg_shdepend relation, already opened and suitably
768 shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
769 Oid refclassId, Oid refobjId,
770 SharedDependencyType deptype)
773 Datum values[Natts_pg_shdepend];
774 bool nulls[Natts_pg_shdepend];
777 * Make sure the object doesn't go away while we record the dependency on
778 * it. DROP routines should lock the object exclusively before they check
779 * shared dependencies.
781 shdepLockAndCheckObject(refclassId, refobjId);
783 memset(nulls, false, sizeof(nulls));
786 * Form the new tuple and record the dependency.
788 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId));
789 values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId);
790 values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId);
792 values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
793 values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
794 values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
796 tup = heap_form_tuple(sdepRel->rd_att, values, nulls);
798 simple_heap_insert(sdepRel, tup);
800 /* keep indexes current */
801 CatalogUpdateIndexes(sdepRel, tup);
808 * shdepDropDependency
809 * Internal workhorse for deleting entries from pg_shdepend.
811 * We drop entries having the following properties:
812 * dependent object is the one identified by classId/objectId
813 * if refclassId isn't InvalidOid, it must match the entry's refclassid
814 * if refobjId isn't InvalidOid, it must match the entry's refobjid
815 * if deptype isn't SHARED_DEPENDENCY_INVALID, it must match entry's deptype
817 * sdepRel must be the pg_shdepend relation, already opened and suitably
821 shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
822 Oid refclassId, Oid refobjId,
823 SharedDependencyType deptype)
829 /* Scan for entries matching the dependent object */
831 Anum_pg_shdepend_dbid,
832 BTEqualStrategyNumber, F_OIDEQ,
833 ObjectIdGetDatum(classIdGetDbId(classId)));
835 Anum_pg_shdepend_classid,
836 BTEqualStrategyNumber, F_OIDEQ,
837 ObjectIdGetDatum(classId));
839 Anum_pg_shdepend_objid,
840 BTEqualStrategyNumber, F_OIDEQ,
841 ObjectIdGetDatum(objectId));
843 scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
844 SnapshotNow, 3, key);
846 while (HeapTupleIsValid(tup = systable_getnext(scan)))
848 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
850 /* Filter entries according to additional parameters */
851 if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId)
853 if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId)
855 if (deptype != SHARED_DEPENDENCY_INVALID &&
856 shdepForm->deptype != deptype)
860 simple_heap_delete(sdepRel, &tup->t_self);
863 systable_endscan(scan);
869 * Get the database Id that should be used in pg_shdepend, given the OID
870 * of the catalog containing the object. For shared objects, it's 0
871 * (InvalidOid); for all other objects, it's the current database Id.
873 * XXX it's awfully tempting to hard-wire this instead of doing a syscache
874 * lookup ... but resist the temptation, unless you can prove it's a
878 classIdGetDbId(Oid classId)
883 tup = SearchSysCache(RELOID,
884 ObjectIdGetDatum(classId),
886 if (!HeapTupleIsValid(tup))
887 elog(ERROR, "cache lookup failed for relation %u", classId);
889 if (((Form_pg_class) GETSTRUCT(tup))->relisshared)
894 ReleaseSysCache(tup);
900 * shdepLockAndCheckObject
902 * Lock the object that we are about to record a dependency on.
903 * After it's locked, verify that it hasn't been dropped while we
904 * weren't looking. If the object has been dropped, this function
908 shdepLockAndCheckObject(Oid classId, Oid objectId)
910 /* AccessShareLock should be OK, since we are not modifying the object */
911 LockSharedObject(classId, objectId, 0, AccessShareLock);
915 case AuthIdRelationId:
916 if (!SearchSysCacheExists(AUTHOID,
917 ObjectIdGetDatum(objectId),
920 (errcode(ERRCODE_UNDEFINED_OBJECT),
921 errmsg("role %u was concurrently dropped",
926 * Currently, this routine need not support any other shared
927 * object types besides roles. If we wanted to record explicit
928 * dependencies on databases or tablespaces, we'd need code along
932 case TableSpaceRelationId:
934 /* For lack of a syscache on pg_tablespace, do this: */
935 char *tablespace = get_tablespace_name(objectId);
937 if (tablespace == NULL)
939 (errcode(ERRCODE_UNDEFINED_OBJECT),
940 errmsg("tablespace %u was concurrently dropped",
948 elog(ERROR, "unrecognized shared classId: %u", classId);
954 * storeObjectDescription
955 * Append the description of a dependent object to "descs"
957 * While searching for dependencies of a shared object, we stash the
958 * descriptions of dependent objects we find in a single string, which we
959 * later pass to ereport() in the DETAIL field when somebody attempts to
960 * drop a referenced shared object.
962 * When type is LOCAL_OBJECT or SHARED_OBJECT, we expect object to be the
963 * dependent object, deptype is the dependency type, and count is not used.
964 * When type is REMOTE_OBJECT, we expect object to be the database object,
965 * and count to be nonzero; deptype is not used in this case.
968 storeObjectDescription(StringInfo descs, objectType type,
969 ObjectAddress *object,
970 SharedDependencyType deptype,
973 char *objdesc = getObjectDescription(object);
975 /* separate entries with a newline */
977 appendStringInfoChar(descs, '\n');
983 if (deptype == SHARED_DEPENDENCY_OWNER)
984 appendStringInfo(descs, _("owner of %s"), objdesc);
985 else if (deptype == SHARED_DEPENDENCY_ACL)
986 appendStringInfo(descs, _("access to %s"), objdesc);
988 elog(ERROR, "unrecognized dependency type: %d",
993 /* translator: %s will always be "database %s" */
994 appendStringInfo(descs, _("%d objects in %s"), count, objdesc);
998 elog(ERROR, "unrecognized object type: %d", type);
1006 * isSharedObjectPinned
1007 * Return whether a given shared object has a SHARED_DEPENDENCY_PIN entry.
1009 * sdepRel must be the pg_shdepend relation, already opened and suitably
1013 isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
1015 bool result = false;
1020 ScanKeyInit(&key[0],
1021 Anum_pg_shdepend_refclassid,
1022 BTEqualStrategyNumber, F_OIDEQ,
1023 ObjectIdGetDatum(classId));
1024 ScanKeyInit(&key[1],
1025 Anum_pg_shdepend_refobjid,
1026 BTEqualStrategyNumber, F_OIDEQ,
1027 ObjectIdGetDatum(objectId));
1029 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1030 SnapshotNow, 2, key);
1033 * Since we won't generate additional pg_shdepend entries for pinned
1034 * objects, there can be at most one entry referencing a pinned object.
1035 * Hence, it's sufficient to look at the first returned tuple; we don't
1038 tup = systable_getnext(scan);
1039 if (HeapTupleIsValid(tup))
1041 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
1043 if (shdepForm->deptype == SHARED_DEPENDENCY_PIN)
1047 systable_endscan(scan);
1055 * Drop the objects owned by any one of the given RoleIds. If a role has
1056 * access to an object, the grant will be removed as well (but the object
1057 * will not, of course.)
1060 shdepDropOwned(List *roleids, DropBehavior behavior)
1065 sdepRel = heap_open(SharedDependRelationId, AccessExclusiveLock);
1068 * For each role, find the dependent objects and drop them using the
1069 * regular (non-shared) dependency management.
1071 foreach(cell, roleids)
1073 Oid roleid = lfirst_oid(cell);
1078 /* Doesn't work for pinned objects */
1079 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1083 obj.classId = AuthIdRelationId;
1084 obj.objectId = roleid;
1085 obj.objectSubId = 0;
1088 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1089 errmsg("cannot drop objects owned by %s because they are "
1090 "required by the database system",
1091 getObjectDescription(&obj))));
1094 ScanKeyInit(&key[0],
1095 Anum_pg_shdepend_refclassid,
1096 BTEqualStrategyNumber, F_OIDEQ,
1097 ObjectIdGetDatum(AuthIdRelationId));
1098 ScanKeyInit(&key[1],
1099 Anum_pg_shdepend_refobjid,
1100 BTEqualStrategyNumber, F_OIDEQ,
1101 ObjectIdGetDatum(roleid));
1103 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1104 SnapshotNow, 2, key);
1106 while ((tuple = systable_getnext(scan)) != NULL)
1108 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1110 /* We only operate on objects on the current database */
1111 if (sdepForm->dbid != MyDatabaseId)
1114 switch (sdepForm->deptype)
1117 GrantObjectType objtype;
1118 InternalGrant istmt;
1120 /* Shouldn't happen */
1121 case SHARED_DEPENDENCY_PIN:
1122 case SHARED_DEPENDENCY_INVALID:
1123 elog(ERROR, "unexpected dependency type");
1125 case SHARED_DEPENDENCY_ACL:
1126 switch (sdepForm->classid)
1128 case RelationRelationId:
1130 /* is it a sequence or non-sequence? */
1131 Form_pg_class pg_class_tuple;
1134 tuple = SearchSysCache(RELOID,
1135 ObjectIdGetDatum(sdepForm->objid),
1137 if (!HeapTupleIsValid(tuple))
1138 elog(ERROR, "cache lookup failed for relation %u",
1140 pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
1141 if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
1142 istmt.objtype = ACL_OBJECT_SEQUENCE;
1144 istmt.objtype = ACL_OBJECT_RELATION;
1145 ReleaseSysCache(tuple);
1148 case DatabaseRelationId:
1149 istmt.objtype = ACL_OBJECT_DATABASE;
1151 case ProcedureRelationId:
1152 istmt.objtype = ACL_OBJECT_FUNCTION;
1154 case LanguageRelationId:
1155 istmt.objtype = ACL_OBJECT_LANGUAGE;
1157 case NamespaceRelationId:
1158 istmt.objtype = ACL_OBJECT_NAMESPACE;
1160 case TableSpaceRelationId:
1161 istmt.objtype = ACL_OBJECT_TABLESPACE;
1164 elog(ERROR, "unexpected object type %d",
1166 /* keep compiler quiet */
1167 objtype = (GrantObjectType) 0;
1170 istmt.is_grant = false;
1171 istmt.objects = list_make1_oid(sdepForm->objid);
1172 istmt.all_privs = true;
1173 istmt.privileges = ACL_NO_RIGHTS;
1174 istmt.grantees = list_make1_oid(roleid);
1175 istmt.grant_option = false;
1176 istmt.behavior = DROP_CASCADE;
1178 ExecGrantStmt_oids(&istmt);
1180 case SHARED_DEPENDENCY_OWNER:
1183 * If there's a regular (non-shared) dependency on this
1184 * object marked with DEPENDENCY_INTERNAL, skip this
1185 * object. We will drop the referencer object instead.
1187 if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid))
1190 /* Drop the object */
1191 obj.classId = sdepForm->classid;
1192 obj.objectId = sdepForm->objid;
1193 obj.objectSubId = 0;
1194 performDeletion(&obj, behavior);
1199 systable_endscan(scan);
1202 heap_close(sdepRel, AccessExclusiveLock);
1206 * shdepReassignOwned
1208 * Change the owner of objects owned by any of the roles in roleids to
1209 * newrole. Grants are not touched.
1212 shdepReassignOwned(List *roleids, Oid newrole)
1217 sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
1219 foreach(cell, roleids)
1224 Oid roleid = lfirst_oid(cell);
1226 /* Refuse to work on pinned roles */
1227 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1231 obj.classId = AuthIdRelationId;
1232 obj.objectId = roleid;
1233 obj.objectSubId = 0;
1236 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1237 errmsg("cannot drop objects owned by %s because they are "
1238 "required by the database system",
1239 getObjectDescription(&obj))));
1242 * There's no need to tell the whole truth, which is that we
1243 * didn't track these dependencies at all ...
1247 ScanKeyInit(&key[0],
1248 Anum_pg_shdepend_refclassid,
1249 BTEqualStrategyNumber, F_OIDEQ,
1250 ObjectIdGetDatum(AuthIdRelationId));
1251 ScanKeyInit(&key[1],
1252 Anum_pg_shdepend_refobjid,
1253 BTEqualStrategyNumber, F_OIDEQ,
1254 ObjectIdGetDatum(roleid));
1256 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1257 SnapshotNow, 2, key);
1259 while ((tuple = systable_getnext(scan)) != NULL)
1261 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1263 /* We only operate on objects on the current database */
1264 if (sdepForm->dbid != MyDatabaseId)
1267 /* Unexpected because we checked for pins above */
1268 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
1269 elog(ERROR, "unexpected shared pin");
1271 /* We leave non-owner dependencies alone */
1272 if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
1276 * If there's a regular (non-shared) dependency on this object
1277 * marked with DEPENDENCY_INTERNAL, skip this object. We will
1278 * alter the referencer object instead.
1280 if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid))
1283 /* Issue the appropiate ALTER OWNER call */
1284 switch (sdepForm->classid)
1286 case ConversionRelationId:
1287 AlterConversionOwner_oid(sdepForm->objid, newrole);
1290 case TypeRelationId:
1291 AlterTypeOwnerInternal(sdepForm->objid, newrole);
1294 case OperatorRelationId:
1295 AlterOperatorOwner_oid(sdepForm->objid, newrole);
1298 case NamespaceRelationId:
1299 AlterSchemaOwner_oid(sdepForm->objid, newrole);
1302 case RelationRelationId:
1303 ATExecChangeOwner(sdepForm->objid, newrole, false);
1306 case ProcedureRelationId:
1307 AlterFunctionOwner_oid(sdepForm->objid, newrole);
1311 elog(ERROR, "unexpected classid %d", sdepForm->classid);
1314 /* Make sure the next iteration will see my changes */
1315 CommandCounterIncrement();
1318 systable_endscan(scan);
1321 heap_close(sdepRel, AccessShareLock);