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