]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_shdepend.c
Re-run pgindent, fixing a problem where comment lines after a blank
[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-2005, 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.5 2005/11/22 18:17:08 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/genam.h"
18 #include "access/heapam.h"
19 #include "utils/acl.h"
20 #include "catalog/dependency.h"
21 #include "catalog/indexing.h"
22 #include "catalog/pg_authid.h"
23 #include "catalog/pg_conversion.h"
24 #include "catalog/pg_database.h"
25 #include "catalog/pg_language.h"
26 #include "catalog/pg_namespace.h"
27 #include "catalog/pg_operator.h"
28 #include "catalog/pg_proc.h"
29 #include "catalog/pg_shdepend.h"
30 #include "catalog/pg_tablespace.h"
31 #include "catalog/pg_type.h"
32 #include "commands/conversioncmds.h"
33 #include "commands/defrem.h"
34 #include "commands/schemacmds.h"
35 #include "commands/tablecmds.h"
36 #include "commands/typecmds.h"
37 #include "lib/stringinfo.h"
38 #include "miscadmin.h"
39 #include "utils/fmgroids.h"
40 #include "utils/inval.h"
41 #include "utils/syscache.h"
42
43
44 typedef enum
45 {
46         LOCAL_OBJECT,
47         SHARED_OBJECT,
48         REMOTE_OBJECT
49 } objectType;
50
51 static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2,
52                            Oid **diff);
53 static Oid      classIdGetDbId(Oid classId);
54 static void shdepLockAndCheckObject(Oid classId, Oid objectId);
55 static void shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
56                            Oid refclassid, Oid refobjid,
57                            SharedDependencyType deptype);
58 static void shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
59                                    Oid refclassId, Oid refobjId,
60                                    SharedDependencyType deptype);
61 static void shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
62                                         Oid refclassId, Oid refobjId,
63                                         SharedDependencyType deptype);
64 static void storeObjectDescription(StringInfo descs, objectType type,
65                                            ObjectAddress *object,
66                                            SharedDependencyType deptype,
67                                            int count);
68 static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel);
69
70
71 /*
72  * recordSharedDependencyOn
73  *
74  * Record a dependency between 2 objects via their respective ObjectAddresses.
75  * The first argument is the dependent object, the second the one it
76  * references (which must be a shared object).
77  *
78  * This locks the referenced object and makes sure it still exists.
79  * Then it creates an entry in pg_shdepend.  The lock is kept until
80  * the end of the transaction.
81  *
82  * Dependencies on pinned objects are not recorded.
83  */
84 void
85 recordSharedDependencyOn(ObjectAddress *depender,
86                                                  ObjectAddress *referenced,
87                                                  SharedDependencyType deptype)
88 {
89         Relation        sdepRel;
90
91         /*
92          * Objects in pg_shdepend can't have SubIds.
93          */
94         Assert(depender->objectSubId == 0);
95         Assert(referenced->objectSubId == 0);
96
97         /*
98          * During bootstrap, do nothing since pg_shdepend may not exist yet.
99          * initdb will fill in appropriate pg_shdepend entries after bootstrap.
100          */
101         if (IsBootstrapProcessingMode())
102                 return;
103
104         sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
105
106         /* If the referenced object is pinned, do nothing. */
107         if (!isSharedObjectPinned(referenced->classId, referenced->objectId,
108                                                           sdepRel))
109         {
110                 shdepAddDependency(sdepRel, depender->classId, depender->objectId,
111                                                    referenced->classId, referenced->objectId,
112                                                    deptype);
113         }
114
115         heap_close(sdepRel, RowExclusiveLock);
116 }
117
118 /*
119  * recordDependencyOnOwner
120  *
121  * A convenient wrapper of recordSharedDependencyOn -- register the specified
122  * user as owner of the given object.
123  *
124  * Note: it's the caller's responsibility to ensure that there isn't an owner
125  * entry for the object already.
126  */
127 void
128 recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
129 {
130         ObjectAddress myself,
131                                 referenced;
132
133         myself.classId = classId;
134         myself.objectId = objectId;
135         myself.objectSubId = 0;
136
137         referenced.classId = AuthIdRelationId;
138         referenced.objectId = owner;
139         referenced.objectSubId = 0;
140
141         recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER);
142 }
143
144 /*
145  * shdepChangeDep
146  *
147  * Update shared dependency records to account for an updated referenced
148  * object.      This is an internal workhorse for operations such as changing
149  * an object's owner.
150  *
151  * There must be no more than one existing entry for the given dependent
152  * object and dependency type!  So in practice this can only be used for
153  * updating SHARED_DEPENDENCY_OWNER entries, which should have that property.
154  *
155  * If there is no previous entry, we assume it was referencing a PINned
156  * object, so we create a new entry.  If the new referenced object is
157  * PINned, we don't create an entry (and drop the old one, if any).
158  *
159  * sdepRel must be the pg_shdepend relation, already opened and suitably
160  * locked.
161  */
162 static void
163 shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
164                            Oid refclassid, Oid refobjid,
165                            SharedDependencyType deptype)
166 {
167         Oid                     dbid = classIdGetDbId(classid);
168         HeapTuple       oldtup = NULL;
169         HeapTuple       scantup;
170         ScanKeyData key[3];
171         SysScanDesc scan;
172
173         /*
174          * Make sure the new referenced object doesn't go away while we record the
175          * dependency.
176          */
177         shdepLockAndCheckObject(refclassid, refobjid);
178
179         /*
180          * Look for a previous entry
181          */
182         ScanKeyInit(&key[0],
183                                 Anum_pg_shdepend_dbid,
184                                 BTEqualStrategyNumber, F_OIDEQ,
185                                 ObjectIdGetDatum(dbid));
186         ScanKeyInit(&key[1],
187                                 Anum_pg_shdepend_classid,
188                                 BTEqualStrategyNumber, F_OIDEQ,
189                                 ObjectIdGetDatum(classid));
190         ScanKeyInit(&key[2],
191                                 Anum_pg_shdepend_objid,
192                                 BTEqualStrategyNumber, F_OIDEQ,
193                                 ObjectIdGetDatum(objid));
194
195         scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
196                                                           SnapshotNow, 3, key);
197
198         while ((scantup = systable_getnext(scan)) != NULL)
199         {
200                 /* Ignore if not of the target dependency type */
201                 if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype)
202                         continue;
203                 /* Caller screwed up if multiple matches */
204                 if (oldtup)
205                         elog(ERROR,
206                                  "multiple pg_shdepend entries for object %u/%u deptype %c",
207                                  classid, objid, deptype);
208                 oldtup = heap_copytuple(scantup);
209         }
210
211         systable_endscan(scan);
212
213         if (isSharedObjectPinned(refclassid, refobjid, sdepRel))
214         {
215                 /* No new entry needed, so just delete existing entry if any */
216                 if (oldtup)
217                         simple_heap_delete(sdepRel, &oldtup->t_self);
218         }
219         else if (oldtup)
220         {
221                 /* Need to update existing entry */
222                 Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup);
223
224                 /* Since oldtup is a copy, we can just modify it in-memory */
225                 shForm->refclassid = refclassid;
226                 shForm->refobjid = refobjid;
227
228                 simple_heap_update(sdepRel, &oldtup->t_self, oldtup);
229
230                 /* keep indexes current */
231                 CatalogUpdateIndexes(sdepRel, oldtup);
232         }
233         else
234         {
235                 /* Need to insert new entry */
236                 Datum           values[Natts_pg_shdepend];
237                 bool            nulls[Natts_pg_shdepend];
238
239                 memset(nulls, 0, sizeof(nulls));
240
241                 values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid);
242                 values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid);
243                 values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid);
244
245                 values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
246                 values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
247                 values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
248
249                 /*
250                  * we are reusing oldtup just to avoid declaring a new variable, but
251                  * it's certainly a new tuple
252                  */
253                 oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls);
254                 simple_heap_insert(sdepRel, oldtup);
255
256                 /* keep indexes current */
257                 CatalogUpdateIndexes(sdepRel, oldtup);
258         }
259
260         if (oldtup)
261                 heap_freetuple(oldtup);
262 }
263
264 /*
265  * changeDependencyOnOwner
266  *
267  * Update the shared dependencies to account for the new owner.
268  */
269 void
270 changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
271 {
272         Relation        sdepRel;
273
274         sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
275
276         /* Adjust the SHARED_DEPENDENCY_OWNER entry */
277         shdepChangeDep(sdepRel, classId, objectId,
278                                    AuthIdRelationId, newOwnerId,
279                                    SHARED_DEPENDENCY_OWNER);
280
281         /*----------
282          * There should never be a SHARED_DEPENDENCY_ACL entry for the owner,
283          * so get rid of it if there is one.  This can happen if the new owner
284          * was previously granted some rights to the object.
285          *
286          * This step is analogous to aclnewowner's removal of duplicate entries
287          * in the ACL.  We have to do it to handle this scenario:
288          *              A grants some rights on an object to B
289          *              ALTER OWNER changes the object's owner to B
290          *              ALTER OWNER changes the object's owner to C
291          * The third step would remove all mention of B from the object's ACL,
292          * but we'd still have a SHARED_DEPENDENCY_ACL for B if we did not do
293          * things this way.
294          *
295          * The rule against having a SHARED_DEPENDENCY_ACL entry for the owner
296          * allows us to fix things up in just this one place, without having
297          * to make the various ALTER OWNER routines each know about it.
298          *----------
299          */
300         shdepDropDependency(sdepRel, classId, objectId,
301                                                 AuthIdRelationId, newOwnerId,
302                                                 SHARED_DEPENDENCY_ACL);
303
304         heap_close(sdepRel, RowExclusiveLock);
305 }
306
307 /*
308  * getOidListDiff
309  *              Helper for updateAclDependencies.
310  *
311  * Takes two Oid arrays and returns elements from the first not found in the
312  * second.      We assume both arrays are sorted and de-duped, and that the
313  * second array does not contain any values not found in the first.
314  *
315  * NOTE: Both input arrays are pfreed.
316  */
317 static int
318 getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff)
319 {
320         Oid                *result;
321         int                     i,
322                                 j,
323                                 k = 0;
324
325         AssertArg(nlist1 >= nlist2 && nlist2 >= 0);
326
327         result = palloc(sizeof(Oid) * (nlist1 - nlist2));
328         *diff = result;
329
330         for (i = 0, j = 0; i < nlist1 && j < nlist2;)
331         {
332                 if (list1[i] == list2[j])
333                 {
334                         i++;
335                         j++;
336                 }
337                 else if (list1[i] < list2[j])
338                 {
339                         result[k++] = list1[i];
340                         i++;
341                 }
342                 else
343                 {
344                         /* can't happen */
345                         elog(WARNING, "invalid element %u in shorter list", list2[j]);
346                         j++;
347                 }
348         }
349
350         for (; i < nlist1; i++)
351                 result[k++] = list1[i];
352
353         /* We should have copied the exact number of elements */
354         AssertState(k == (nlist1 - nlist2));
355
356         if (list1)
357                 pfree(list1);
358         if (list2)
359                 pfree(list2);
360
361         return k;
362 }
363
364 /*
365  * updateAclDependencies
366  *              Update the pg_shdepend info for an object's ACL during GRANT/REVOKE.
367  *
368  * classId, objectId: identify the object whose ACL this is
369  * ownerId: role owning the object
370  * isGrant: are we adding or removing ACL entries?
371  * noldmembers, oldmembers: array of roleids appearing in old ACL
372  * nnewmembers, newmembers: array of roleids appearing in new ACL
373  *
374  * We calculate the difference between the new and old lists of roles,
375  * and then insert (if it's a grant) or delete (if it's a revoke) from
376  * pg_shdepend as appropiate.
377  *
378  * Note that we can't insert blindly at grant, because we would end up with
379  * duplicate registered dependencies.  We could check for existence of the
380  * tuple before inserting, but that seems to be more expensive than what we are
381  * doing now.  On the other hand, we can't just delete the tuples blindly at
382  * revoke, because the user may still have other privileges.
383  *
384  * NOTE: Both input arrays must be sorted and de-duped.  They are pfreed
385  * before return.
386  */
387 void
388 updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant,
389                                           int noldmembers, Oid *oldmembers,
390                                           int nnewmembers, Oid *newmembers)
391 {
392         Relation        sdepRel;
393         Oid                *diff;
394         int                     ndiff,
395                                 i;
396
397         /*
398          * Calculate the differences between the old and new lists.
399          */
400         if (isGrant)
401                 ndiff = getOidListDiff(newmembers, nnewmembers,
402                                                            oldmembers, noldmembers, &diff);
403         else
404                 ndiff = getOidListDiff(oldmembers, noldmembers,
405                                                            newmembers, nnewmembers, &diff);
406
407         if (ndiff > 0)
408         {
409                 sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
410
411                 /* Add or drop the respective dependency */
412                 for (i = 0; i < ndiff; i++)
413                 {
414                         Oid                     roleid = diff[i];
415
416                         /*
417                          * Skip the owner: he has an OWNER shdep entry instead. (This is
418                          * not just a space optimization; it makes ALTER OWNER easier. See
419                          * notes in changeDependencyOnOwner.)
420                          */
421                         if (roleid == ownerId)
422                                 continue;
423
424                         /* Skip pinned roles */
425                         if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
426                                 continue;
427
428                         if (isGrant)
429                                 shdepAddDependency(sdepRel, classId, objectId,
430                                                                    AuthIdRelationId, roleid,
431                                                                    SHARED_DEPENDENCY_ACL);
432                         else
433                                 shdepDropDependency(sdepRel, classId, objectId,
434                                                                         AuthIdRelationId, roleid,
435                                                                         SHARED_DEPENDENCY_ACL);
436                 }
437
438                 heap_close(sdepRel, RowExclusiveLock);
439         }
440
441         pfree(diff);
442 }
443
444 /*
445  * A struct to keep track of dependencies found in other databases.
446  */
447 typedef struct
448 {
449         Oid                     dbOid;
450         int                     count;
451 } remoteDep;
452
453 /*
454  * checkSharedDependencies
455  *
456  * Check whether there are shared dependency entries for a given shared
457  * object.      Returns a string containing a newline-separated list of object
458  * descriptions that depend on the shared object, or NULL if none is found.
459  *
460  * We can find three different kinds of dependencies: dependencies on objects
461  * of the current database; dependencies on shared objects; and dependencies
462  * on objects local to other databases.  We can (and do) provide descriptions
463  * of the two former kinds of objects, but we can't do that for "remote"
464  * objects, so we just provide a count of them.
465  *
466  * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early.
467  */
468 char *
469 checkSharedDependencies(Oid classId, Oid objectId)
470 {
471         Relation        sdepRel;
472         ScanKeyData key[2];
473         SysScanDesc scan;
474         HeapTuple       tup;
475         int                     totalDeps = 0;
476         int                     numLocalDeps = 0;
477         int                     numSharedDeps = 0;
478         List       *remDeps = NIL;
479         ListCell   *cell;
480         ObjectAddress object;
481         StringInfoData descs;
482
483         /*
484          * We try to limit the number of reported dependencies to something sane,
485          * both for the user's sake and to avoid blowing out memory.
486          */
487 #define MAX_REPORTED_DEPS 100
488
489         initStringInfo(&descs);
490
491         sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
492
493         ScanKeyInit(&key[0],
494                                 Anum_pg_shdepend_refclassid,
495                                 BTEqualStrategyNumber, F_OIDEQ,
496                                 ObjectIdGetDatum(classId));
497         ScanKeyInit(&key[1],
498                                 Anum_pg_shdepend_refobjid,
499                                 BTEqualStrategyNumber, F_OIDEQ,
500                                 ObjectIdGetDatum(objectId));
501
502         scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
503                                                           SnapshotNow, 2, key);
504
505         while (HeapTupleIsValid(tup = systable_getnext(scan)))
506         {
507                 Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
508
509                 /* This case can be dispatched quickly */
510                 if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
511                 {
512                         object.classId = classId;
513                         object.objectId = objectId;
514                         object.objectSubId = 0;
515                         ereport(ERROR,
516                                         (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
517                                          errmsg("cannot drop %s because it is required by the database system",
518                                                         getObjectDescription(&object))));
519                 }
520
521                 object.classId = sdepForm->classid;
522                 object.objectId = sdepForm->objid;
523                 object.objectSubId = 0;
524
525                 /*
526                  * If it's a dependency local to this database or it's a shared
527                  * object, describe it.
528                  *
529                  * If it's a remote dependency, keep track of it so we can report the
530                  * number of them later.
531                  */
532                 if (sdepForm->dbid == MyDatabaseId)
533                 {
534                         numLocalDeps++;
535                         if (++totalDeps <= MAX_REPORTED_DEPS)
536                                 storeObjectDescription(&descs, LOCAL_OBJECT, &object,
537                                                                            sdepForm->deptype, 0);
538                 }
539                 else if (sdepForm->dbid == InvalidOid)
540                 {
541                         numSharedDeps++;
542                         if (++totalDeps <= MAX_REPORTED_DEPS)
543                                 storeObjectDescription(&descs, SHARED_OBJECT, &object,
544                                                                            sdepForm->deptype, 0);
545                 }
546                 else
547                 {
548                         /* It's not local nor shared, so it must be remote. */
549                         remoteDep  *dep;
550                         bool            stored = false;
551
552                         /*
553                          * XXX this info is kept on a simple List.      Maybe it's not good
554                          * for performance, but using a hash table seems needlessly
555                          * complex.  The expected number of databases is not high anyway,
556                          * I suppose.
557                          */
558                         foreach(cell, remDeps)
559                         {
560                                 dep = lfirst(cell);
561                                 if (dep->dbOid == sdepForm->dbid)
562                                 {
563                                         dep->count++;
564                                         stored = true;
565                                         break;
566                                 }
567                         }
568                         if (!stored)
569                         {
570                                 dep = (remoteDep *) palloc(sizeof(remoteDep));
571                                 dep->dbOid = sdepForm->dbid;
572                                 dep->count = 1;
573                                 remDeps = lappend(remDeps, dep);
574                                 totalDeps++;
575                         }
576                 }
577         }
578
579         systable_endscan(scan);
580
581         heap_close(sdepRel, AccessShareLock);
582
583         if (totalDeps > MAX_REPORTED_DEPS)
584         {
585                 /*
586                  * Report seems unreasonably long, so reduce it to per-database info
587                  *
588                  * Note: we don't ever suppress per-database totals, which should be
589                  * OK as long as there aren't too many databases ...
590                  */
591                 descs.len = 0;                  /* reset to empty */
592                 descs.data[0] = '\0';
593
594                 if (numLocalDeps > 0)
595                 {
596                         appendStringInfo(&descs, _("%d objects in this database"),
597                                                          numLocalDeps);
598                         if (numSharedDeps > 0)
599                                 appendStringInfoChar(&descs, '\n');
600                 }
601                 if (numSharedDeps > 0)
602                         appendStringInfo(&descs, _("%d shared objects"),
603                                                          numSharedDeps);
604         }
605
606         foreach(cell, remDeps)
607         {
608                 remoteDep  *dep = lfirst(cell);
609
610                 object.classId = DatabaseRelationId;
611                 object.objectId = dep->dbOid;
612                 object.objectSubId = 0;
613
614                 storeObjectDescription(&descs, REMOTE_OBJECT, &object,
615                                                            SHARED_DEPENDENCY_INVALID, dep->count);
616         }
617
618         list_free_deep(remDeps);
619
620         if (descs.len == 0)
621         {
622                 pfree(descs.data);
623                 return NULL;
624         }
625
626         return descs.data;
627 }
628
629 /*
630  * copyTemplateDependencies
631  *
632  * Routine to create the initial shared dependencies of a new database.
633  * We simply copy the dependencies from the template database.
634  */
635 void
636 copyTemplateDependencies(Oid templateDbId, Oid newDbId)
637 {
638         Relation        sdepRel;
639         TupleDesc       sdepDesc;
640         ScanKeyData key[1];
641         SysScanDesc scan;
642         HeapTuple       tup;
643         CatalogIndexState indstate;
644         Datum           values[Natts_pg_shdepend];
645         bool            nulls[Natts_pg_shdepend];
646         bool            replace[Natts_pg_shdepend];
647
648         sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
649         sdepDesc = RelationGetDescr(sdepRel);
650
651         indstate = CatalogOpenIndexes(sdepRel);
652
653         /* Scan all entries with dbid = templateDbId */
654         ScanKeyInit(&key[0],
655                                 Anum_pg_shdepend_dbid,
656                                 BTEqualStrategyNumber, F_OIDEQ,
657                                 ObjectIdGetDatum(templateDbId));
658
659         scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
660                                                           SnapshotNow, 1, key);
661
662         /* Set up to copy the tuples except for inserting newDbId */
663         memset(values, 0, sizeof(values));
664         memset(nulls, false, sizeof(nulls));
665         memset(replace, false, sizeof(replace));
666
667         replace[Anum_pg_shdepend_dbid - 1] = true;
668         values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId);
669
670         /*
671          * Copy the entries of the original database, changing the database Id to
672          * that of the new database.  Note that because we are not copying rows
673          * with dbId == 0 (ie, rows describing dependent shared objects) we won't
674          * copy the ownership dependency of the template database itself; this is
675          * what we want.
676          */
677         while (HeapTupleIsValid(tup = systable_getnext(scan)))
678         {
679                 HeapTuple       newtup;
680
681                 newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace);
682                 simple_heap_insert(sdepRel, newtup);
683
684                 /* Keep indexes current */
685                 CatalogIndexInsert(indstate, newtup);
686
687                 heap_freetuple(newtup);
688         }
689
690         systable_endscan(scan);
691
692         CatalogCloseIndexes(indstate);
693         heap_close(sdepRel, RowExclusiveLock);
694 }
695
696 /*
697  * dropDatabaseDependencies
698  *
699  * Delete pg_shdepend entries corresponding to a database that's being
700  * dropped.
701  */
702 void
703 dropDatabaseDependencies(Oid databaseId)
704 {
705         Relation        sdepRel;
706         ScanKeyData key[1];
707         SysScanDesc scan;
708         HeapTuple       tup;
709
710         sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
711
712         /*
713          * First, delete all the entries that have the database Oid in the dbid
714          * field.
715          */
716         ScanKeyInit(&key[0],
717                                 Anum_pg_shdepend_dbid,
718                                 BTEqualStrategyNumber, F_OIDEQ,
719                                 ObjectIdGetDatum(databaseId));
720         /* We leave the other index fields unspecified */
721
722         scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
723                                                           SnapshotNow, 1, key);
724
725         while (HeapTupleIsValid(tup = systable_getnext(scan)))
726         {
727                 simple_heap_delete(sdepRel, &tup->t_self);
728         }
729
730         systable_endscan(scan);
731
732         /* Now delete all entries corresponding to the database itself */
733         shdepDropDependency(sdepRel, DatabaseRelationId, databaseId,
734                                                 InvalidOid, InvalidOid,
735                                                 SHARED_DEPENDENCY_INVALID);
736
737         heap_close(sdepRel, RowExclusiveLock);
738 }
739
740 /*
741  * deleteSharedDependencyRecordsFor
742  *
743  * Delete all pg_shdepend entries corresponding to an object that's being
744  * dropped or modified.  The object is assumed to be either a shared object
745  * or local to the current database (the classId tells us which).
746  */
747 void
748 deleteSharedDependencyRecordsFor(Oid classId, Oid objectId)
749 {
750         Relation        sdepRel;
751
752         sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
753
754         shdepDropDependency(sdepRel, classId, objectId,
755                                                 InvalidOid, InvalidOid,
756                                                 SHARED_DEPENDENCY_INVALID);
757
758         heap_close(sdepRel, RowExclusiveLock);
759 }
760
761 /*
762  * shdepAddDependency
763  *              Internal workhorse for inserting into pg_shdepend
764  *
765  * sdepRel must be the pg_shdepend relation, already opened and suitably
766  * locked.
767  */
768 static void
769 shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
770                                    Oid refclassId, Oid refobjId,
771                                    SharedDependencyType deptype)
772 {
773         HeapTuple       tup;
774         Datum           values[Natts_pg_shdepend];
775         bool            nulls[Natts_pg_shdepend];
776
777         /*
778          * Make sure the object doesn't go away while we record the dependency on
779          * it.  DROP routines should lock the object exclusively before they check
780          * shared dependencies.
781          */
782         shdepLockAndCheckObject(refclassId, refobjId);
783
784         memset(nulls, false, sizeof(nulls));
785
786         /*
787          * Form the new tuple and record the dependency.
788          */
789         values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId));
790         values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId);
791         values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId);
792
793         values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
794         values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
795         values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
796
797         tup = heap_form_tuple(sdepRel->rd_att, values, nulls);
798
799         simple_heap_insert(sdepRel, tup);
800
801         /* keep indexes current */
802         CatalogUpdateIndexes(sdepRel, tup);
803
804         /* clean up */
805         heap_freetuple(tup);
806 }
807
808 /*
809  * shdepDropDependency
810  *              Internal workhorse for deleting entries from pg_shdepend.
811  *
812  * We drop entries having the following properties:
813  *      dependent object is the one identified by classId/objectId
814  *      if refclassId isn't InvalidOid, it must match the entry's refclassid
815  *      if refobjId isn't InvalidOid, it must match the entry's refobjid
816  *      if deptype isn't SHARED_DEPENDENCY_INVALID, it must match entry's deptype
817  *
818  * sdepRel must be the pg_shdepend relation, already opened and suitably
819  * locked.
820  */
821 static void
822 shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
823                                         Oid refclassId, Oid refobjId,
824                                         SharedDependencyType deptype)
825 {
826         ScanKeyData key[3];
827         SysScanDesc scan;
828         HeapTuple       tup;
829
830         /* Scan for entries matching the dependent object */
831         ScanKeyInit(&key[0],
832                                 Anum_pg_shdepend_dbid,
833                                 BTEqualStrategyNumber, F_OIDEQ,
834                                 ObjectIdGetDatum(classIdGetDbId(classId)));
835         ScanKeyInit(&key[1],
836                                 Anum_pg_shdepend_classid,
837                                 BTEqualStrategyNumber, F_OIDEQ,
838                                 ObjectIdGetDatum(classId));
839         ScanKeyInit(&key[2],
840                                 Anum_pg_shdepend_objid,
841                                 BTEqualStrategyNumber, F_OIDEQ,
842                                 ObjectIdGetDatum(objectId));
843
844         scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
845                                                           SnapshotNow, 3, key);
846
847         while (HeapTupleIsValid(tup = systable_getnext(scan)))
848         {
849                 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
850
851                 /* Filter entries according to additional parameters */
852                 if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId)
853                         continue;
854                 if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId)
855                         continue;
856                 if (deptype != SHARED_DEPENDENCY_INVALID &&
857                         shdepForm->deptype != deptype)
858                         continue;
859
860                 /* OK, delete it */
861                 simple_heap_delete(sdepRel, &tup->t_self);
862         }
863
864         systable_endscan(scan);
865 }
866
867 /*
868  * classIdGetDbId
869  *
870  * Get the database Id that should be used in pg_shdepend, given the OID
871  * of the catalog containing the object.  For shared objects, it's 0
872  * (InvalidOid); for all other objects, it's the current database Id.
873  *
874  * XXX it's awfully tempting to hard-wire this instead of doing a syscache
875  * lookup ... but resist the temptation, unless you can prove it's a
876  * bottleneck.
877  */
878 static Oid
879 classIdGetDbId(Oid classId)
880 {
881         Oid                     dbId;
882         HeapTuple       tup;
883
884         tup = SearchSysCache(RELOID,
885                                                  ObjectIdGetDatum(classId),
886                                                  0, 0, 0);
887         if (!HeapTupleIsValid(tup))
888                 elog(ERROR, "cache lookup failed for relation %u", classId);
889
890         if (((Form_pg_class) GETSTRUCT(tup))->relisshared)
891                 dbId = InvalidOid;
892         else
893                 dbId = MyDatabaseId;
894
895         ReleaseSysCache(tup);
896
897         return dbId;
898 }
899
900 /*
901  * shdepLockAndCheckObject
902  *
903  * Lock the object that we are about to record a dependency on.
904  * After it's locked, verify that it hasn't been dropped while we
905  * weren't looking.  If the object has been dropped, this function
906  * does not return!
907  */
908 static void
909 shdepLockAndCheckObject(Oid classId, Oid objectId)
910 {
911         /* AccessShareLock should be OK, since we are not modifying the object */
912         LockSharedObject(classId, objectId, 0, AccessShareLock);
913
914         /*
915          * We have to recognize sinval updates here, else our local syscache may
916          * still contain the object even if it was just dropped.
917          */
918         AcceptInvalidationMessages();
919
920         switch (classId)
921         {
922                 case AuthIdRelationId:
923                         if (!SearchSysCacheExists(AUTHOID,
924                                                                           ObjectIdGetDatum(objectId),
925                                                                           0, 0, 0))
926                                 ereport(ERROR,
927                                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
928                                                  errmsg("role %u was concurrently dropped",
929                                                                 objectId)));
930                         break;
931
932                         /*
933                          * Currently, this routine need not support any other shared
934                          * object types besides roles.  If we wanted to record explicit
935                          * dependencies on databases or tablespaces, we'd need code along
936                          * these lines:
937                          */
938 #ifdef NOT_USED
939                 case TableSpaceRelationId:
940                         {
941                                 /* For lack of a syscache on pg_tablespace, do this: */
942                                 char       *tablespace = get_tablespace_name(objectId);
943
944                                 if (tablespace == NULL)
945                                         ereport(ERROR,
946                                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
947                                                          errmsg("tablespace %u was concurrently dropped",
948                                                                         objectId)));
949                                 pfree(tablespace);
950                                 break;
951                         }
952 #endif
953
954                 default:
955                         elog(ERROR, "unrecognized shared classId: %u", classId);
956         }
957 }
958
959
960 /*
961  * storeObjectDescription
962  *              Append the description of a dependent object to "descs"
963  *
964  * While searching for dependencies of a shared object, we stash the
965  * descriptions of dependent objects we find in a single string, which we
966  * later pass to ereport() in the DETAIL field when somebody attempts to
967  * drop a referenced shared object.
968  *
969  * When type is LOCAL_OBJECT or SHARED_OBJECT, we expect object to be the
970  * dependent object, deptype is the dependency type, and count is not used.
971  * When type is REMOTE_OBJECT, we expect object to be the database object,
972  * and count to be nonzero; deptype is not used in this case.
973  */
974 static void
975 storeObjectDescription(StringInfo descs, objectType type,
976                                            ObjectAddress *object,
977                                            SharedDependencyType deptype,
978                                            int count)
979 {
980         char       *objdesc = getObjectDescription(object);
981
982         /* separate entries with a newline */
983         if (descs->len != 0)
984                 appendStringInfoChar(descs, '\n');
985
986         switch (type)
987         {
988                 case LOCAL_OBJECT:
989                 case SHARED_OBJECT:
990                         if (deptype == SHARED_DEPENDENCY_OWNER)
991                                 appendStringInfo(descs, _("owner of %s"), objdesc);
992                         else if (deptype == SHARED_DEPENDENCY_ACL)
993                                 appendStringInfo(descs, _("access to %s"), objdesc);
994                         else
995                                 elog(ERROR, "unrecognized dependency type: %d",
996                                          (int) deptype);
997                         break;
998
999                 case REMOTE_OBJECT:
1000                         /* translator: %s will always be "database %s" */
1001                         appendStringInfo(descs, _("%d objects in %s"), count, objdesc);
1002                         break;
1003
1004                 default:
1005                         elog(ERROR, "unrecognized object type: %d", type);
1006         }
1007
1008         pfree(objdesc);
1009 }
1010
1011
1012 /*
1013  * isSharedObjectPinned
1014  *              Return whether a given shared object has a SHARED_DEPENDENCY_PIN entry.
1015  *
1016  * sdepRel must be the pg_shdepend relation, already opened and suitably
1017  * locked.
1018  */
1019 static bool
1020 isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
1021 {
1022         bool            result = false;
1023         ScanKeyData key[2];
1024         SysScanDesc scan;
1025         HeapTuple       tup;
1026
1027         ScanKeyInit(&key[0],
1028                                 Anum_pg_shdepend_refclassid,
1029                                 BTEqualStrategyNumber, F_OIDEQ,
1030                                 ObjectIdGetDatum(classId));
1031         ScanKeyInit(&key[1],
1032                                 Anum_pg_shdepend_refobjid,
1033                                 BTEqualStrategyNumber, F_OIDEQ,
1034                                 ObjectIdGetDatum(objectId));
1035
1036         scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1037                                                           SnapshotNow, 2, key);
1038
1039         /*
1040          * Since we won't generate additional pg_shdepend entries for pinned
1041          * objects, there can be at most one entry referencing a pinned object.
1042          * Hence, it's sufficient to look at the first returned tuple; we don't
1043          * need to loop.
1044          */
1045         tup = systable_getnext(scan);
1046         if (HeapTupleIsValid(tup))
1047         {
1048                 Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
1049
1050                 if (shdepForm->deptype == SHARED_DEPENDENCY_PIN)
1051                         result = true;
1052         }
1053
1054         systable_endscan(scan);
1055
1056         return result;
1057 }
1058
1059 /*
1060  * shdepDropOwned
1061  *
1062  * Drop the objects owned by any one of the given RoleIds.      If a role has
1063  * access to an object, the grant will be removed as well (but the object
1064  * will not, of course.)
1065  */
1066 void
1067 shdepDropOwned(List *roleids, DropBehavior behavior)
1068 {
1069         Relation        sdepRel;
1070         ListCell   *cell;
1071
1072         sdepRel = heap_open(SharedDependRelationId, AccessExclusiveLock);
1073
1074         /*
1075          * For each role, find the dependent objects and drop them using the
1076          * regular (non-shared) dependency management.
1077          */
1078         foreach(cell, roleids)
1079         {
1080                 Oid                     roleid = lfirst_oid(cell);
1081                 ScanKeyData key[2];
1082                 SysScanDesc scan;
1083                 HeapTuple       tuple;
1084
1085                 /* Doesn't work for pinned objects */
1086                 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1087                 {
1088                         ObjectAddress obj;
1089
1090                         obj.classId = AuthIdRelationId;
1091                         obj.objectId = roleid;
1092                         obj.objectSubId = 0;
1093
1094                         ereport(ERROR,
1095                                         (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1096                                    errmsg("cannot drop objects owned by %s because they are "
1097                                                   "required by the database system",
1098                                                   getObjectDescription(&obj))));
1099                 }
1100
1101                 ScanKeyInit(&key[0],
1102                                         Anum_pg_shdepend_refclassid,
1103                                         BTEqualStrategyNumber, F_OIDEQ,
1104                                         ObjectIdGetDatum(AuthIdRelationId));
1105                 ScanKeyInit(&key[1],
1106                                         Anum_pg_shdepend_refobjid,
1107                                         BTEqualStrategyNumber, F_OIDEQ,
1108                                         ObjectIdGetDatum(roleid));
1109
1110                 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1111                                                                   SnapshotNow, 2, key);
1112
1113                 while ((tuple = systable_getnext(scan)) != NULL)
1114                 {
1115                         Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1116
1117                         /* We only operate on objects on the current database */
1118                         if (sdepForm->dbid != MyDatabaseId)
1119                                 continue;
1120
1121                         switch (sdepForm->deptype)
1122                         {
1123                                         ObjectAddress obj;
1124                                         GrantObjectType objtype;
1125
1126                                         /* Shouldn't happen */
1127                                 case SHARED_DEPENDENCY_PIN:
1128                                 case SHARED_DEPENDENCY_INVALID:
1129                                         elog(ERROR, "unexpected dependency type");
1130                                         break;
1131                                 case SHARED_DEPENDENCY_ACL:
1132                                         switch (sdepForm->classid)
1133                                         {
1134                                                 case RelationRelationId:
1135                                                         objtype = ACL_OBJECT_RELATION;
1136                                                         break;
1137                                                 case DatabaseRelationId:
1138                                                         objtype = ACL_OBJECT_DATABASE;
1139                                                         break;
1140                                                 case ProcedureRelationId:
1141                                                         objtype = ACL_OBJECT_FUNCTION;
1142                                                         break;
1143                                                 case LanguageRelationId:
1144                                                         objtype = ACL_OBJECT_LANGUAGE;
1145                                                         break;
1146                                                 case NamespaceRelationId:
1147                                                         objtype = ACL_OBJECT_NAMESPACE;
1148                                                         break;
1149                                                 case TableSpaceRelationId:
1150                                                         objtype = ACL_OBJECT_TABLESPACE;
1151                                                         break;
1152                                                 default:
1153                                                         elog(ERROR, "unexpected object type %d",
1154                                                                  sdepForm->classid);
1155                                                         /* keep compiler quiet */
1156                                                         objtype = (GrantObjectType) 0;
1157                                                         break;
1158                                         }
1159
1160                                         ExecGrantStmt_oids(false, objtype,
1161                                                                            list_make1_oid(sdepForm->objid), true,
1162                                                                            ACL_NO_RIGHTS, list_make1_oid(roleid),
1163                                                                            false, DROP_CASCADE);
1164                                         break;
1165                                 case SHARED_DEPENDENCY_OWNER:
1166
1167                                         /*
1168                                          * If there's a regular (non-shared) dependency on this
1169                                          * object marked with DEPENDENCY_INTERNAL, skip this
1170                                          * object.      We will drop the referencer object instead.
1171                                          */
1172                                         if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid))
1173                                                 continue;
1174
1175                                         /* Drop the object */
1176                                         obj.classId = sdepForm->classid;
1177                                         obj.objectId = sdepForm->objid;
1178                                         obj.objectSubId = 0;
1179                                         performDeletion(&obj, behavior);
1180                                         break;
1181                         }
1182                 }
1183
1184                 systable_endscan(scan);
1185         }
1186
1187         heap_close(sdepRel, AccessExclusiveLock);
1188 }
1189
1190 /*
1191  * shdepReassignOwned
1192  *
1193  * Change the owner of objects owned by any of the roles in roleids to
1194  * newrole.  Grants are not touched.
1195  */
1196 void
1197 shdepReassignOwned(List *roleids, Oid newrole)
1198 {
1199         Relation        sdepRel;
1200         ListCell   *cell;
1201
1202         sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
1203
1204         foreach(cell, roleids)
1205         {
1206                 SysScanDesc scan;
1207                 ScanKeyData key[2];
1208                 HeapTuple       tuple;
1209                 Oid                     roleid = lfirst_oid(cell);
1210
1211                 /* Refuse to work on pinned roles */
1212                 if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
1213                 {
1214                         ObjectAddress obj;
1215
1216                         obj.classId = AuthIdRelationId;
1217                         obj.objectId = roleid;
1218                         obj.objectSubId = 0;
1219
1220                         ereport(ERROR,
1221                                         (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1222                                    errmsg("cannot drop objects owned by %s because they are "
1223                                                   "required by the database system",
1224                                                   getObjectDescription(&obj))));
1225
1226                         /*
1227                          * There's no need to tell the whole truth, which is that we
1228                          * didn't track these dependencies at all ...
1229                          */
1230                 }
1231
1232                 ScanKeyInit(&key[0],
1233                                         Anum_pg_shdepend_refclassid,
1234                                         BTEqualStrategyNumber, F_OIDEQ,
1235                                         ObjectIdGetDatum(AuthIdRelationId));
1236                 ScanKeyInit(&key[1],
1237                                         Anum_pg_shdepend_refobjid,
1238                                         BTEqualStrategyNumber, F_OIDEQ,
1239                                         ObjectIdGetDatum(roleid));
1240
1241                 scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
1242                                                                   SnapshotNow, 2, key);
1243
1244                 while ((tuple = systable_getnext(scan)) != NULL)
1245                 {
1246                         Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
1247
1248                         /* We only operate on objects on the current database */
1249                         if (sdepForm->dbid != MyDatabaseId)
1250                                 continue;
1251
1252                         /* Unexpected because we checked for pins above */
1253                         if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
1254                                 elog(ERROR, "unexpected shared pin");
1255
1256                         /* We leave non-owner dependencies alone */
1257                         if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
1258                                 continue;
1259
1260                         /*
1261                          * If there's a regular (non-shared) dependency on this object
1262                          * marked with DEPENDENCY_INTERNAL, skip this object.  We will
1263                          * alter the referencer object instead.
1264                          */
1265                         if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid))
1266                                 continue;
1267
1268                         /* Issue the appropiate ALTER OWNER call */
1269                         switch (sdepForm->classid)
1270                         {
1271                                 case ConversionRelationId:
1272                                         AlterConversionOwner_oid(sdepForm->objid, newrole);
1273                                         break;
1274
1275                                 case TypeRelationId:
1276                                         AlterTypeOwnerInternal(sdepForm->objid, newrole);
1277                                         break;
1278
1279                                 case OperatorRelationId:
1280                                         AlterOperatorOwner_oid(sdepForm->objid, newrole);
1281                                         break;
1282
1283                                 case NamespaceRelationId:
1284                                         AlterSchemaOwner_oid(sdepForm->objid, newrole);
1285                                         break;
1286
1287                                 case RelationRelationId:
1288                                         ATExecChangeOwner(sdepForm->objid, newrole, false);
1289                                         break;
1290
1291                                 case ProcedureRelationId:
1292                                         AlterFunctionOwner_oid(sdepForm->objid, newrole);
1293                                         break;
1294
1295                                 default:
1296                                         elog(ERROR, "unexpected classid %d", sdepForm->classid);
1297                                         break;
1298                         }
1299                         /* Make sure the next iteration will see my changes */
1300                         CommandCounterIncrement();
1301                 }
1302
1303                 systable_endscan(scan);
1304         }
1305
1306         heap_close(sdepRel, AccessShareLock);
1307 }