]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_shdepend.c
Remove 576 references of include files that were not needed.
[postgresql] / src / backend / catalog / pg_shdepend.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_shdepend.c
4  *        routines to support manipulation of the pg_shdepend relation
5  *
6  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.12 2006/07/14 14:52:18 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
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"
41
42
43 typedef enum
44 {
45         LOCAL_OBJECT,
46         SHARED_OBJECT,
47         REMOTE_OBJECT
48 } objectType;
49
50 static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2,
51                            Oid **diff);
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,
66                                            int count);
67 static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel);
68
69
70 /*
71  * recordSharedDependencyOn
72  *
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).
76  *
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.
80  *
81  * Dependencies on pinned objects are not recorded.
82  */
83 void
84 recordSharedDependencyOn(ObjectAddress *depender,
85                                                  ObjectAddress *referenced,
86                                                  SharedDependencyType deptype)
87 {
88         Relation        sdepRel;
89
90         /*
91          * Objects in pg_shdepend can't have SubIds.
92          */
93         Assert(depender->objectSubId == 0);
94         Assert(referenced->objectSubId == 0);
95
96         /*
97          * During bootstrap, do nothing since pg_shdepend may not exist yet.
98          * initdb will fill in appropriate pg_shdepend entries after bootstrap.
99          */
100         if (IsBootstrapProcessingMode())
101                 return;
102
103         sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
104
105         /* If the referenced object is pinned, do nothing. */
106         if (!isSharedObjectPinned(referenced->classId, referenced->objectId,
107                                                           sdepRel))
108         {
109                 shdepAddDependency(sdepRel, depender->classId, depender->objectId,
110                                                    referenced->classId, referenced->objectId,
111                                                    deptype);
112         }
113
114         heap_close(sdepRel, RowExclusiveLock);
115 }
116
117 /*
118  * recordDependencyOnOwner
119  *
120  * A convenient wrapper of recordSharedDependencyOn -- register the specified
121  * user as owner of the given object.
122  *
123  * Note: it's the caller's responsibility to ensure that there isn't an owner
124  * entry for the object already.
125  */
126 void
127 recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
128 {
129         ObjectAddress myself,
130                                 referenced;
131
132         myself.classId = classId;
133         myself.objectId = objectId;
134         myself.objectSubId = 0;
135
136         referenced.classId = AuthIdRelationId;
137         referenced.objectId = owner;
138         referenced.objectSubId = 0;
139
140         recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER);
141 }
142
143 /*
144  * shdepChangeDep
145  *
146  * Update shared dependency records to account for an updated referenced
147  * object.      This is an internal workhorse for operations such as changing
148  * an object's owner.
149  *
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.
153  *
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).
157  *
158  * sdepRel must be the pg_shdepend relation, already opened and suitably
159  * locked.
160  */
161 static void
162 shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
163                            Oid refclassid, Oid refobjid,
164                            SharedDependencyType deptype)
165 {
166         Oid                     dbid = classIdGetDbId(classid);
167         HeapTuple       oldtup = NULL;
168         HeapTuple       scantup;
169         ScanKeyData key[3];
170         SysScanDesc scan;
171
172         /*
173          * Make sure the new referenced object doesn't go away while we record the
174          * dependency.
175          */
176         shdepLockAndCheckObject(refclassid, refobjid);
177
178         /*
179          * Look for a previous entry
180          */
181         ScanKeyInit(&key[0],
182                                 Anum_pg_shdepend_dbid,
183                                 BTEqualStrategyNumber, F_OIDEQ,
184                                 ObjectIdGetDatum(dbid));
185         ScanKeyInit(&key[1],
186                                 Anum_pg_shdepend_classid,
187                                 BTEqualStrategyNumber, F_OIDEQ,
188                                 ObjectIdGetDatum(classid));
189         ScanKeyInit(&key[2],
190                                 Anum_pg_shdepend_objid,
191                                 BTEqualStrategyNumber, F_OIDEQ,
192                                 ObjectIdGetDatum(objid));
193
194         scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
195                                                           SnapshotNow, 3, key);
196
197         while ((scantup = systable_getnext(scan)) != NULL)
198         {
199                 /* Ignore if not of the target dependency type */
200                 if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype)
201                         continue;
202                 /* Caller screwed up if multiple matches */
203                 if (oldtup)
204                         elog(ERROR,
205                                  "multiple pg_shdepend entries for object %u/%u deptype %c",
206                                  classid, objid, deptype);
207                 oldtup = heap_copytuple(scantup);
208         }
209
210         systable_endscan(scan);
211
212         if (isSharedObjectPinned(refclassid, refobjid, sdepRel))
213         {
214                 /* No new entry needed, so just delete existing entry if any */
215                 if (oldtup)
216                         simple_heap_delete(sdepRel, &oldtup->t_self);
217         }
218         else if (oldtup)
219         {
220                 /* Need to update existing entry */
221                 Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup);
222
223                 /* Since oldtup is a copy, we can just modify it in-memory */
224                 shForm->refclassid = refclassid;
225                 shForm->refobjid = refobjid;
226
227                 simple_heap_update(sdepRel, &oldtup->t_self, oldtup);
228
229                 /* keep indexes current */
230                 CatalogUpdateIndexes(sdepRel, oldtup);
231         }
232         else
233         {
234                 /* Need to insert new entry */
235                 Datum           values[Natts_pg_shdepend];
236                 bool            nulls[Natts_pg_shdepend];
237
238                 memset(nulls, 0, sizeof(nulls));
239
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);
243
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);
247
248                 /*
249                  * we are reusing oldtup just to avoid declaring a new variable, but
250                  * it's certainly a new tuple
251                  */
252                 oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls);
253                 simple_heap_insert(sdepRel, oldtup);
254
255                 /* keep indexes current */
256                 CatalogUpdateIndexes(sdepRel, oldtup);
257         }
258
259         if (oldtup)
260                 heap_freetuple(oldtup);
261 }
262
263 /*
264  * changeDependencyOnOwner
265  *
266  * Update the shared dependencies to account for the new owner.
267  */
268 void
269 changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
270 {
271         Relation        sdepRel;
272
273         sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
274
275         /* Adjust the SHARED_DEPENDENCY_OWNER entry */
276         shdepChangeDep(sdepRel, classId, objectId,
277                                    AuthIdRelationId, newOwnerId,
278                                    SHARED_DEPENDENCY_OWNER);
279
280         /*----------
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.
284          *
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
292          * things this way.
293          *
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.
297          *----------
298          */
299         shdepDropDependency(sdepRel, classId, objectId,
300                                                 AuthIdRelationId, newOwnerId,
301                                                 SHARED_DEPENDENCY_ACL);
302
303         heap_close(sdepRel, RowExclusiveLock);
304 }
305
306 /*
307  * getOidListDiff
308  *              Helper for updateAclDependencies.
309  *
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.
313  *
314  * NOTE: Both input arrays are pfreed.
315  */
316 static int
317 getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff)
318 {
319         Oid                *result;
320         int                     i,
321                                 j,
322                                 k = 0;
323
324         AssertArg(nlist1 >= nlist2 && nlist2 >= 0);
325
326         result = palloc(sizeof(Oid) * (nlist1 - nlist2));
327         *diff = result;
328
329         for (i = 0, j = 0; i < nlist1 && j < nlist2;)
330         {
331                 if (list1[i] == list2[j])
332                 {
333                         i++;
334                         j++;
335                 }
336                 else if (list1[i] < list2[j])
337                 {
338                         result[k++] = list1[i];
339                         i++;
340                 }
341                 else
342                 {
343                         /* can't happen */
344                         elog(WARNING, "invalid element %u in shorter list", list2[j]);
345                         j++;
346                 }
347         }
348
349         for (; i < nlist1; i++)
350                 result[k++] = list1[i];
351
352         /* We should have copied the exact number of elements */
353         AssertState(k == (nlist1 - nlist2));
354
355         if (list1)
356                 pfree(list1);
357         if (list2)
358                 pfree(list2);
359
360         return k;
361 }
362
363 /*
364  * updateAclDependencies
365  *              Update the pg_shdepend info for an object's ACL during GRANT/REVOKE.
366  *
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
372  *
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.
376  *
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.
382  *
383  * NOTE: Both input arrays must be sorted and de-duped.  They are pfreed
384  * before return.
385  */
386 void
387 updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant,
388                                           int noldmembers, Oid *oldmembers,
389                                           int nnewmembers, Oid *newmembers)
390 {
391         Relation        sdepRel;
392         Oid                *diff;
393         int                     ndiff,
394                                 i;
395
396         /*
397          * Calculate the differences between the old and new lists.
398          */
399         if (isGrant)
400                 ndiff = getOidListDiff(newmembers, nnewmembers,
401                                                            oldmembers, noldmembers, &diff);
402         else
403                 ndiff = getOidListDiff(oldmembers, noldmembers,
404                                                            newmembers, nnewmembers, &diff);
405
406         if (ndiff > 0)
407         {
408                 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
409
410                 /* Add or drop the respective dependency */
411                 for (i = 0; i < ndiff; i++)
412                 {
413                         Oid                     roleid = diff[i];
414
415                         /*
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.)
419                          */
420                         if (roleid == ownerId)
421                                 continue;
422
423                         /* Skip pinned roles */
424                         if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
425                                 continue;
426
427                         if (isGrant)
428                                 shdepAddDependency(sdepRel, classId, objectId,
429                                                                    AuthIdRelationId, roleid,
430                                                                    SHARED_DEPENDENCY_ACL);
431                         else
432                                 shdepDropDependency(sdepRel, classId, objectId,
433                                                                         AuthIdRelationId, roleid,
434                                                                         SHARED_DEPENDENCY_ACL);
435                 }
436
437                 heap_close(sdepRel, RowExclusiveLock);
438         }
439
440         pfree(diff);
441 }
442
443 /*
444  * A struct to keep track of dependencies found in other databases.
445  */
446 typedef struct
447 {
448         Oid                     dbOid;
449         int                     count;
450 } remoteDep;
451
452 /*
453  * checkSharedDependencies
454  *
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.
458  *
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.
464  *
465  * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early.
466  */
467 char *
468 checkSharedDependencies(Oid classId, Oid objectId)
469 {
470         Relation        sdepRel;
471         ScanKeyData key[2];
472         SysScanDesc scan;
473         HeapTuple       tup;
474         int                     totalDeps = 0;
475         int                     numLocalDeps = 0;
476         int                     numSharedDeps = 0;
477         List       *remDeps = NIL;
478         ListCell   *cell;
479         ObjectAddress object;
480         StringInfoData descs;
481
482         /*
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.
485          */
486 #define MAX_REPORTED_DEPS 100
487
488         initStringInfo(&descs);
489
490         sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
491
492         ScanKeyInit(&key[0],
493                                 Anum_pg_shdepend_refclassid,
494                                 BTEqualStrategyNumber, F_OIDEQ,
495                                 ObjectIdGetDatum(classId));
496         ScanKeyInit(&key[1],
497                                 Anum_pg_shdepend_refobjid,
498                                 BTEqualStrategyNumber, F_OIDEQ,
499                                 ObjectIdGetDatum(objectId));
500
501         scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
502                                                           SnapshotNow, 2, key);
503
504         while (HeapTupleIsValid(tup = systable_getnext(scan)))
505         {
506                 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
507
508                 /* This case can be dispatched quickly */
509                 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
510                 {
511                         object.classId = classId;
512                         object.objectId = objectId;
513                         object.objectSubId = 0;
514                         ereport(ERROR,
515                                         (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
516                                          errmsg("cannot drop %s because it is required by the database system",
517                                                         getObjectDescription(&object))));
518                 }
519
520                 object.classId = sdepForm->classid;
521                 object.objectId = sdepForm->objid;
522                 object.objectSubId = 0;
523
524                 /*
525                  * If it's a dependency local to this database or it's a shared
526                  * object, describe it.
527                  *
528                  * If it's a remote dependency, keep track of it so we can report the
529                  * number of them later.
530                  */
531                 if (sdepForm->dbid == MyDatabaseId)
532                 {
533                         numLocalDeps++;
534                         if (++totalDeps <= MAX_REPORTED_DEPS)
535                                 storeObjectDescription(&descs, LOCAL_OBJECT, &object,
536                                                                            sdepForm->deptype, 0);
537                 }
538                 else if (sdepForm->dbid == InvalidOid)
539                 {
540                         numSharedDeps++;
541                         if (++totalDeps <= MAX_REPORTED_DEPS)
542                                 storeObjectDescription(&descs, SHARED_OBJECT, &object,
543                                                                            sdepForm->deptype, 0);
544                 }
545                 else
546                 {
547                         /* It's not local nor shared, so it must be remote. */
548                         remoteDep  *dep;
549                         bool            stored = false;
550
551                         /*
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,
555                          * I suppose.
556                          */
557                         foreach(cell, remDeps)
558                         {
559                                 dep = lfirst(cell);
560                                 if (dep->dbOid == sdepForm->dbid)
561                                 {
562                                         dep->count++;
563                                         stored = true;
564                                         break;
565                                 }
566                         }
567                         if (!stored)
568                         {
569                                 dep = (remoteDep *) palloc(sizeof(remoteDep));
570                                 dep->dbOid = sdepForm->dbid;
571                                 dep->count = 1;
572                                 remDeps = lappend(remDeps, dep);
573                                 totalDeps++;
574                         }
575                 }
576         }
577
578         systable_endscan(scan);
579
580         heap_close(sdepRel, AccessShareLock);
581
582         if (totalDeps > MAX_REPORTED_DEPS)
583         {
584                 /*
585                  * Report seems unreasonably long, so reduce it to per-database info
586                  *
587                  * Note: we don't ever suppress per-database totals, which should be
588                  * OK as long as there aren't too many databases ...
589                  */
590                 descs.len = 0;                  /* reset to empty */
591                 descs.data[0] = '\0';
592
593                 if (numLocalDeps > 0)
594                 {
595                         appendStringInfo(&descs, _("%d objects in this database"),
596                                                          numLocalDeps);
597                         if (numSharedDeps > 0)
598                                 appendStringInfoChar(&descs, '\n');
599                 }
600                 if (numSharedDeps > 0)
601                         appendStringInfo(&descs, _("%d shared objects"),
602                                                          numSharedDeps);
603         }
604
605         foreach(cell, remDeps)
606         {
607                 remoteDep  *dep = lfirst(cell);
608
609                 object.classId = DatabaseRelationId;
610                 object.objectId = dep->dbOid;
611                 object.objectSubId = 0;
612
613                 storeObjectDescription(&descs, REMOTE_OBJECT, &object,
614                                                            SHARED_DEPENDENCY_INVALID, dep->count);
615         }
616
617         list_free_deep(remDeps);
618
619         if (descs.len == 0)
620         {
621                 pfree(descs.data);
622                 return NULL;
623         }
624
625         return descs.data;
626 }
627
628 /*
629  * copyTemplateDependencies
630  *
631  * Routine to create the initial shared dependencies of a new database.
632  * We simply copy the dependencies from the template database.
633  */
634 void
635 copyTemplateDependencies(Oid templateDbId, Oid newDbId)
636 {
637         Relation        sdepRel;
638         TupleDesc       sdepDesc;
639         ScanKeyData key[1];
640         SysScanDesc scan;
641         HeapTuple       tup;
642         CatalogIndexState indstate;
643         Datum           values[Natts_pg_shdepend];
644         bool            nulls[Natts_pg_shdepend];
645         bool            replace[Natts_pg_shdepend];
646
647         sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
648         sdepDesc = RelationGetDescr(sdepRel);
649
650         indstate = CatalogOpenIndexes(sdepRel);
651
652         /* Scan all entries with dbid = templateDbId */
653         ScanKeyInit(&key[0],
654                                 Anum_pg_shdepend_dbid,
655                                 BTEqualStrategyNumber, F_OIDEQ,
656                                 ObjectIdGetDatum(templateDbId));
657
658         scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
659                                                           SnapshotNow, 1, key);
660
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));
665
666         replace[Anum_pg_shdepend_dbid - 1] = true;
667         values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId);
668
669         /*
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
674          * what we want.
675          */
676         while (HeapTupleIsValid(tup = systable_getnext(scan)))
677         {
678                 HeapTuple       newtup;
679
680                 newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace);
681                 simple_heap_insert(sdepRel, newtup);
682
683                 /* Keep indexes current */
684                 CatalogIndexInsert(indstate, newtup);
685
686                 heap_freetuple(newtup);
687         }
688
689         systable_endscan(scan);
690
691         CatalogCloseIndexes(indstate);
692         heap_close(sdepRel, RowExclusiveLock);
693 }
694
695 /*
696  * dropDatabaseDependencies
697  *
698  * Delete pg_shdepend entries corresponding to a database that's being
699  * dropped.
700  */
701 void
702 dropDatabaseDependencies(Oid databaseId)
703 {
704         Relation        sdepRel;
705         ScanKeyData key[1];
706         SysScanDesc scan;
707         HeapTuple       tup;
708
709         sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
710
711         /*
712          * First, delete all the entries that have the database Oid in the dbid
713          * field.
714          */
715         ScanKeyInit(&key[0],
716                                 Anum_pg_shdepend_dbid,
717                                 BTEqualStrategyNumber, F_OIDEQ,
718                                 ObjectIdGetDatum(databaseId));
719         /* We leave the other index fields unspecified */
720
721         scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
722                                                           SnapshotNow, 1, key);
723
724         while (HeapTupleIsValid(tup = systable_getnext(scan)))
725         {
726                 simple_heap_delete(sdepRel, &tup->t_self);
727         }
728
729         systable_endscan(scan);
730
731         /* Now delete all entries corresponding to the database itself */
732         shdepDropDependency(sdepRel, DatabaseRelationId, databaseId,
733                                                 InvalidOid, InvalidOid,
734                                                 SHARED_DEPENDENCY_INVALID);
735
736         heap_close(sdepRel, RowExclusiveLock);
737 }
738
739 /*
740  * deleteSharedDependencyRecordsFor
741  *
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).
745  */
746 void
747 deleteSharedDependencyRecordsFor(Oid classId, Oid objectId)
748 {
749         Relation        sdepRel;
750
751         sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
752
753         shdepDropDependency(sdepRel, classId, objectId,
754                                                 InvalidOid, InvalidOid,
755                                                 SHARED_DEPENDENCY_INVALID);
756
757         heap_close(sdepRel, RowExclusiveLock);
758 }
759
760 /*
761  * shdepAddDependency
762  *              Internal workhorse for inserting into pg_shdepend
763  *
764  * sdepRel must be the pg_shdepend relation, already opened and suitably
765  * locked.
766  */
767 static void
768 shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
769                                    Oid refclassId, Oid refobjId,
770                                    SharedDependencyType deptype)
771 {
772         HeapTuple       tup;
773         Datum           values[Natts_pg_shdepend];
774         bool            nulls[Natts_pg_shdepend];
775
776         /*
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.
780          */
781         shdepLockAndCheckObject(refclassId, refobjId);
782
783         memset(nulls, false, sizeof(nulls));
784
785         /*
786          * Form the new tuple and record the dependency.
787          */
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);
791
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);
795
796         tup = heap_form_tuple(sdepRel->rd_att, values, nulls);
797
798         simple_heap_insert(sdepRel, tup);
799
800         /* keep indexes current */
801         CatalogUpdateIndexes(sdepRel, tup);
802
803         /* clean up */
804         heap_freetuple(tup);
805 }
806
807 /*
808  * shdepDropDependency
809  *              Internal workhorse for deleting entries from pg_shdepend.
810  *
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
816  *
817  * sdepRel must be the pg_shdepend relation, already opened and suitably
818  * locked.
819  */
820 static void
821 shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
822                                         Oid refclassId, Oid refobjId,
823                                         SharedDependencyType deptype)
824 {
825         ScanKeyData key[3];
826         SysScanDesc scan;
827         HeapTuple       tup;
828
829         /* Scan for entries matching the dependent object */
830         ScanKeyInit(&key[0],
831                                 Anum_pg_shdepend_dbid,
832                                 BTEqualStrategyNumber, F_OIDEQ,
833                                 ObjectIdGetDatum(classIdGetDbId(classId)));
834         ScanKeyInit(&key[1],
835                                 Anum_pg_shdepend_classid,
836                                 BTEqualStrategyNumber, F_OIDEQ,
837                                 ObjectIdGetDatum(classId));
838         ScanKeyInit(&key[2],
839                                 Anum_pg_shdepend_objid,
840                                 BTEqualStrategyNumber, F_OIDEQ,
841                                 ObjectIdGetDatum(objectId));
842
843         scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
844                                                           SnapshotNow, 3, key);
845
846         while (HeapTupleIsValid(tup = systable_getnext(scan)))
847         {
848                 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
849
850                 /* Filter entries according to additional parameters */
851                 if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId)
852                         continue;
853                 if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId)
854                         continue;
855                 if (deptype != SHARED_DEPENDENCY_INVALID &&
856                         shdepForm->deptype != deptype)
857                         continue;
858
859                 /* OK, delete it */
860                 simple_heap_delete(sdepRel, &tup->t_self);
861         }
862
863         systable_endscan(scan);
864 }
865
866 /*
867  * classIdGetDbId
868  *
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.
872  *
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
875  * bottleneck.
876  */
877 static Oid
878 classIdGetDbId(Oid classId)
879 {
880         Oid                     dbId;
881         HeapTuple       tup;
882
883         tup = SearchSysCache(RELOID,
884                                                  ObjectIdGetDatum(classId),
885                                                  0, 0, 0);
886         if (!HeapTupleIsValid(tup))
887                 elog(ERROR, "cache lookup failed for relation %u", classId);
888
889         if (((Form_pg_class) GETSTRUCT(tup))->relisshared)
890                 dbId = InvalidOid;
891         else
892                 dbId = MyDatabaseId;
893
894         ReleaseSysCache(tup);
895
896         return dbId;
897 }
898
899 /*
900  * shdepLockAndCheckObject
901  *
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
905  * does not return!
906  */
907 static void
908 shdepLockAndCheckObject(Oid classId, Oid objectId)
909 {
910         /* AccessShareLock should be OK, since we are not modifying the object */
911         LockSharedObject(classId, objectId, 0, AccessShareLock);
912
913         switch (classId)
914         {
915                 case AuthIdRelationId:
916                         if (!SearchSysCacheExists(AUTHOID,
917                                                                           ObjectIdGetDatum(objectId),
918                                                                           0, 0, 0))
919                                 ereport(ERROR,
920                                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
921                                                  errmsg("role %u was concurrently dropped",
922                                                                 objectId)));
923                         break;
924
925                         /*
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
929                          * these lines:
930                          */
931 #ifdef NOT_USED
932                 case TableSpaceRelationId:
933                         {
934                                 /* For lack of a syscache on pg_tablespace, do this: */
935                                 char       *tablespace = get_tablespace_name(objectId);
936
937                                 if (tablespace == NULL)
938                                         ereport(ERROR,
939                                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
940                                                          errmsg("tablespace %u was concurrently dropped",
941                                                                         objectId)));
942                                 pfree(tablespace);
943                                 break;
944                         }
945 #endif
946
947                 default:
948                         elog(ERROR, "unrecognized shared classId: %u", classId);
949         }
950 }
951
952
953 /*
954  * storeObjectDescription
955  *              Append the description of a dependent object to "descs"
956  *
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.
961  *
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.
966  */
967 static void
968 storeObjectDescription(StringInfo descs, objectType type,
969                                            ObjectAddress *object,
970                                            SharedDependencyType deptype,
971                                            int count)
972 {
973         char       *objdesc = getObjectDescription(object);
974
975         /* separate entries with a newline */
976         if (descs->len != 0)
977                 appendStringInfoChar(descs, '\n');
978
979         switch (type)
980         {
981                 case LOCAL_OBJECT:
982                 case SHARED_OBJECT:
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);
987                         else
988                                 elog(ERROR, "unrecognized dependency type: %d",
989                                          (int) deptype);
990                         break;
991
992                 case REMOTE_OBJECT:
993                         /* translator: %s will always be "database %s" */
994                         appendStringInfo(descs, _("%d objects in %s"), count, objdesc);
995                         break;
996
997                 default:
998                         elog(ERROR, "unrecognized object type: %d", type);
999         }
1000
1001         pfree(objdesc);
1002 }
1003
1004
1005 /*
1006  * isSharedObjectPinned
1007  *              Return whether a given shared object has a SHARED_DEPENDENCY_PIN entry.
1008  *
1009  * sdepRel must be the pg_shdepend relation, already opened and suitably
1010  * locked.
1011  */
1012 static bool
1013 isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
1014 {
1015         bool            result = false;
1016         ScanKeyData key[2];
1017         SysScanDesc scan;
1018         HeapTuple       tup;
1019
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));
1028
1029         scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1030                                                           SnapshotNow, 2, key);
1031
1032         /*
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
1036          * need to loop.
1037          */
1038         tup = systable_getnext(scan);
1039         if (HeapTupleIsValid(tup))
1040         {
1041                 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
1042
1043                 if (shdepForm->deptype == SHARED_DEPENDENCY_PIN)
1044                         result = true;
1045         }
1046
1047         systable_endscan(scan);
1048
1049         return result;
1050 }
1051
1052 /*
1053  * shdepDropOwned
1054  *
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.)
1058  */
1059 void
1060 shdepDropOwned(List *roleids, DropBehavior behavior)
1061 {
1062         Relation        sdepRel;
1063         ListCell   *cell;
1064
1065         sdepRel = heap_open(SharedDependRelationId, AccessExclusiveLock);
1066
1067         /*
1068          * For each role, find the dependent objects and drop them using the
1069          * regular (non-shared) dependency management.
1070          */
1071         foreach(cell, roleids)
1072         {
1073                 Oid                     roleid = lfirst_oid(cell);
1074                 ScanKeyData key[2];
1075                 SysScanDesc scan;
1076                 HeapTuple       tuple;
1077
1078                 /* Doesn't work for pinned objects */
1079                 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1080                 {
1081                         ObjectAddress obj;
1082
1083                         obj.classId = AuthIdRelationId;
1084                         obj.objectId = roleid;
1085                         obj.objectSubId = 0;
1086
1087                         ereport(ERROR,
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))));
1092                 }
1093
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));
1102
1103                 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1104                                                                   SnapshotNow, 2, key);
1105
1106                 while ((tuple = systable_getnext(scan)) != NULL)
1107                 {
1108                         Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1109
1110                         /* We only operate on objects on the current database */
1111                         if (sdepForm->dbid != MyDatabaseId)
1112                                 continue;
1113
1114                         switch (sdepForm->deptype)
1115                         {
1116                                         ObjectAddress obj;
1117                                         GrantObjectType objtype;
1118                                         InternalGrant istmt;
1119
1120                                         /* Shouldn't happen */
1121                                 case SHARED_DEPENDENCY_PIN:
1122                                 case SHARED_DEPENDENCY_INVALID:
1123                                         elog(ERROR, "unexpected dependency type");
1124                                         break;
1125                                 case SHARED_DEPENDENCY_ACL:
1126                                         switch (sdepForm->classid)
1127                                         {
1128                                                 case RelationRelationId:
1129                                                 {
1130                                                         /* is it a sequence or non-sequence? */
1131                                                         Form_pg_class pg_class_tuple;
1132                                                         HeapTuple       tuple;
1133
1134                                                         tuple = SearchSysCache(RELOID,
1135                                                                 ObjectIdGetDatum(sdepForm->objid),
1136                                                                 0, 0, 0);
1137                                                         if (!HeapTupleIsValid(tuple))
1138                                                                 elog(ERROR, "cache lookup failed for relation %u",
1139                                                                                         sdepForm->objid);
1140                                                         pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
1141                                                         if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
1142                                                                 istmt.objtype = ACL_OBJECT_SEQUENCE;
1143                                                         else
1144                                                                 istmt.objtype = ACL_OBJECT_RELATION;
1145                                                         ReleaseSysCache(tuple);
1146                                                         break;
1147                                                 }
1148                                                 case DatabaseRelationId:
1149                                                         istmt.objtype = ACL_OBJECT_DATABASE;
1150                                                         break;
1151                                                 case ProcedureRelationId:
1152                                                         istmt.objtype = ACL_OBJECT_FUNCTION;
1153                                                         break;
1154                                                 case LanguageRelationId:
1155                                                         istmt.objtype = ACL_OBJECT_LANGUAGE;
1156                                                         break;
1157                                                 case NamespaceRelationId:
1158                                                         istmt.objtype = ACL_OBJECT_NAMESPACE;
1159                                                         break;
1160                                                 case TableSpaceRelationId:
1161                                                         istmt.objtype = ACL_OBJECT_TABLESPACE;
1162                                                         break;
1163                                                 default:
1164                                                         elog(ERROR, "unexpected object type %d",
1165                                                                  sdepForm->classid);
1166                                                         /* keep compiler quiet */
1167                                                         objtype = (GrantObjectType) 0;
1168                                                         break;
1169                                         }
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;
1177
1178                                         ExecGrantStmt_oids(&istmt);
1179                                         break;
1180                                 case SHARED_DEPENDENCY_OWNER:
1181
1182                                         /*
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.
1186                                          */
1187                                         if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid))
1188                                                 continue;
1189
1190                                         /* Drop the object */
1191                                         obj.classId = sdepForm->classid;
1192                                         obj.objectId = sdepForm->objid;
1193                                         obj.objectSubId = 0;
1194                                         performDeletion(&obj, behavior);
1195                                         break;
1196                         }
1197                 }
1198
1199                 systable_endscan(scan);
1200         }
1201
1202         heap_close(sdepRel, AccessExclusiveLock);
1203 }
1204
1205 /*
1206  * shdepReassignOwned
1207  *
1208  * Change the owner of objects owned by any of the roles in roleids to
1209  * newrole.  Grants are not touched.
1210  */
1211 void
1212 shdepReassignOwned(List *roleids, Oid newrole)
1213 {
1214         Relation        sdepRel;
1215         ListCell   *cell;
1216
1217         sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
1218
1219         foreach(cell, roleids)
1220         {
1221                 SysScanDesc scan;
1222                 ScanKeyData key[2];
1223                 HeapTuple       tuple;
1224                 Oid                     roleid = lfirst_oid(cell);
1225
1226                 /* Refuse to work on pinned roles */
1227                 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1228                 {
1229                         ObjectAddress obj;
1230
1231                         obj.classId = AuthIdRelationId;
1232                         obj.objectId = roleid;
1233                         obj.objectSubId = 0;
1234
1235                         ereport(ERROR,
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))));
1240
1241                         /*
1242                          * There's no need to tell the whole truth, which is that we
1243                          * didn't track these dependencies at all ...
1244                          */
1245                 }
1246
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));
1255
1256                 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1257                                                                   SnapshotNow, 2, key);
1258
1259                 while ((tuple = systable_getnext(scan)) != NULL)
1260                 {
1261                         Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1262
1263                         /* We only operate on objects on the current database */
1264                         if (sdepForm->dbid != MyDatabaseId)
1265                                 continue;
1266
1267                         /* Unexpected because we checked for pins above */
1268                         if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
1269                                 elog(ERROR, "unexpected shared pin");
1270
1271                         /* We leave non-owner dependencies alone */
1272                         if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
1273                                 continue;
1274
1275                         /*
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.
1279                          */
1280                         if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid))
1281                                 continue;
1282
1283                         /* Issue the appropiate ALTER OWNER call */
1284                         switch (sdepForm->classid)
1285                         {
1286                                 case ConversionRelationId:
1287                                         AlterConversionOwner_oid(sdepForm->objid, newrole);
1288                                         break;
1289
1290                                 case TypeRelationId:
1291                                         AlterTypeOwnerInternal(sdepForm->objid, newrole);
1292                                         break;
1293
1294                                 case OperatorRelationId:
1295                                         AlterOperatorOwner_oid(sdepForm->objid, newrole);
1296                                         break;
1297
1298                                 case NamespaceRelationId:
1299                                         AlterSchemaOwner_oid(sdepForm->objid, newrole);
1300                                         break;
1301
1302                                 case RelationRelationId:
1303                                         ATExecChangeOwner(sdepForm->objid, newrole, false);
1304                                         break;
1305
1306                                 case ProcedureRelationId:
1307                                         AlterFunctionOwner_oid(sdepForm->objid, newrole);
1308                                         break;
1309
1310                                 default:
1311                                         elog(ERROR, "unexpected classid %d", sdepForm->classid);
1312                                         break;
1313                         }
1314                         /* Make sure the next iteration will see my changes */
1315                         CommandCounterIncrement();
1316                 }
1317
1318                 systable_endscan(scan);
1319         }
1320
1321         heap_close(sdepRel, AccessShareLock);
1322 }