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