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
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);