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