~ubuntu-branches/ubuntu/hardy/postgresql-8.4/hardy-backports

« back to all changes in this revision

Viewing changes to src/backend/catalog/pg_shdepend.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-03-20 12:00:13 UTC
  • Revision ID: james.westby@ubuntu.com-20090320120013-hogj7egc5mjncc5g
Tags: upstream-8.4~0cvs20090328
ImportĀ upstreamĀ versionĀ 8.4~0cvs20090328

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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$
 
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", "%d objects in %s", count), count, objdesc);
 
1053
                        break;
 
1054
 
 
1055
                default:
 
1056
                        elog(ERROR, "unrecognized object type: %d", type);
 
1057
        }
 
1058
 
 
1059
        pfree(objdesc);
 
1060
}
 
1061
 
 
1062
 
 
1063
/*
 
1064
 * isSharedObjectPinned
 
1065
 *              Return whether a given shared object has a SHARED_DEPENDENCY_PIN entry.
 
1066
 *
 
1067
 * sdepRel must be the pg_shdepend relation, already opened and suitably
 
1068
 * locked.
 
1069
 */
 
1070
static bool
 
1071
isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
 
1072
{
 
1073
        bool            result = false;
 
1074
        ScanKeyData key[2];
 
1075
        SysScanDesc scan;
 
1076
        HeapTuple       tup;
 
1077
 
 
1078
        ScanKeyInit(&key[0],
 
1079
                                Anum_pg_shdepend_refclassid,
 
1080
                                BTEqualStrategyNumber, F_OIDEQ,
 
1081
                                ObjectIdGetDatum(classId));
 
1082
        ScanKeyInit(&key[1],
 
1083
                                Anum_pg_shdepend_refobjid,
 
1084
                                BTEqualStrategyNumber, F_OIDEQ,
 
1085
                                ObjectIdGetDatum(objectId));
 
1086
 
 
1087
        scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
 
1088
                                                          SnapshotNow, 2, key);
 
1089
 
 
1090
        /*
 
1091
         * Since we won't generate additional pg_shdepend entries for pinned
 
1092
         * objects, there can be at most one entry referencing a pinned object.
 
1093
         * Hence, it's sufficient to look at the first returned tuple; we don't
 
1094
         * need to loop.
 
1095
         */
 
1096
        tup = systable_getnext(scan);
 
1097
        if (HeapTupleIsValid(tup))
 
1098
        {
 
1099
                Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
 
1100
 
 
1101
                if (shdepForm->deptype == SHARED_DEPENDENCY_PIN)
 
1102
                        result = true;
 
1103
        }
 
1104
 
 
1105
        systable_endscan(scan);
 
1106
 
 
1107
        return result;
 
1108
}
 
1109
 
 
1110
/*
 
1111
 * shdepDropOwned
 
1112
 *
 
1113
 * Drop the objects owned by any one of the given RoleIds.      If a role has
 
1114
 * access to an object, the grant will be removed as well (but the object
 
1115
 * will not, of course).
 
1116
 *
 
1117
 * We can revoke grants immediately while doing the scan, but drops are
 
1118
 * saved up and done all at once with performMultipleDeletions.  This
 
1119
 * is necessary so that we don't get failures from trying to delete
 
1120
 * interdependent objects in the wrong order.
 
1121
 */
 
1122
void
 
1123
shdepDropOwned(List *roleids, DropBehavior behavior)
 
1124
{
 
1125
        Relation        sdepRel;
 
1126
        ListCell   *cell;
 
1127
        ObjectAddresses *deleteobjs;
 
1128
 
 
1129
        deleteobjs = new_object_addresses();
 
1130
 
 
1131
        /*
 
1132
         * We don't need this strong a lock here, but we'll call routines that
 
1133
         * acquire RowExclusiveLock.  Better get that right now to avoid potential
 
1134
         * deadlock failures.
 
1135
         */
 
1136
        sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
 
1137
 
 
1138
        /*
 
1139
         * For each role, find the dependent objects and drop them using the
 
1140
         * regular (non-shared) dependency management.
 
1141
         */
 
1142
        foreach(cell, roleids)
 
1143
        {
 
1144
                Oid                     roleid = lfirst_oid(cell);
 
1145
                ScanKeyData key[2];
 
1146
                SysScanDesc scan;
 
1147
                HeapTuple       tuple;
 
1148
 
 
1149
                /* Doesn't work for pinned objects */
 
1150
                if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
 
1151
                {
 
1152
                        ObjectAddress obj;
 
1153
 
 
1154
                        obj.classId = AuthIdRelationId;
 
1155
                        obj.objectId = roleid;
 
1156
                        obj.objectSubId = 0;
 
1157
 
 
1158
                        ereport(ERROR,
 
1159
                                        (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
 
1160
                                   errmsg("cannot drop objects owned by %s because they are "
 
1161
                                                  "required by the database system",
 
1162
                                                  getObjectDescription(&obj))));
 
1163
                }
 
1164
 
 
1165
                ScanKeyInit(&key[0],
 
1166
                                        Anum_pg_shdepend_refclassid,
 
1167
                                        BTEqualStrategyNumber, F_OIDEQ,
 
1168
                                        ObjectIdGetDatum(AuthIdRelationId));
 
1169
                ScanKeyInit(&key[1],
 
1170
                                        Anum_pg_shdepend_refobjid,
 
1171
                                        BTEqualStrategyNumber, F_OIDEQ,
 
1172
                                        ObjectIdGetDatum(roleid));
 
1173
 
 
1174
                scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
 
1175
                                                                  SnapshotNow, 2, key);
 
1176
 
 
1177
                while ((tuple = systable_getnext(scan)) != NULL)
 
1178
                {
 
1179
                        Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
 
1180
                        InternalGrant istmt;
 
1181
                        ObjectAddress obj;
 
1182
 
 
1183
                        /* We only operate on objects in the current database */
 
1184
                        if (sdepForm->dbid != MyDatabaseId)
 
1185
                                continue;
 
1186
 
 
1187
                        switch (sdepForm->deptype)
 
1188
                        {
 
1189
                                        /* Shouldn't happen */
 
1190
                                case SHARED_DEPENDENCY_PIN:
 
1191
                                case SHARED_DEPENDENCY_INVALID:
 
1192
                                        elog(ERROR, "unexpected dependency type");
 
1193
                                        break;
 
1194
                                case SHARED_DEPENDENCY_ACL:
 
1195
                                        switch (sdepForm->classid)
 
1196
                                        {
 
1197
                                                case RelationRelationId:
 
1198
                                                        /* it's OK to use RELATION for a sequence */
 
1199
                                                        istmt.objtype = ACL_OBJECT_RELATION;
 
1200
                                                        break;
 
1201
                                                case DatabaseRelationId:
 
1202
                                                        istmt.objtype = ACL_OBJECT_DATABASE;
 
1203
                                                        break;
 
1204
                                                case ProcedureRelationId:
 
1205
                                                        istmt.objtype = ACL_OBJECT_FUNCTION;
 
1206
                                                        break;
 
1207
                                                case LanguageRelationId:
 
1208
                                                        istmt.objtype = ACL_OBJECT_LANGUAGE;
 
1209
                                                        break;
 
1210
                                                case NamespaceRelationId:
 
1211
                                                        istmt.objtype = ACL_OBJECT_NAMESPACE;
 
1212
                                                        break;
 
1213
                                                case TableSpaceRelationId:
 
1214
                                                        istmt.objtype = ACL_OBJECT_TABLESPACE;
 
1215
                                                        break;
 
1216
                                                default:
 
1217
                                                        elog(ERROR, "unexpected object type %d",
 
1218
                                                                 sdepForm->classid);
 
1219
                                                        break;
 
1220
                                        }
 
1221
                                        istmt.is_grant = false;
 
1222
                                        istmt.objects = list_make1_oid(sdepForm->objid);
 
1223
                                        istmt.all_privs = true;
 
1224
                                        istmt.privileges = ACL_NO_RIGHTS;
 
1225
                                        istmt.col_privs = NIL;
 
1226
                                        istmt.grantees = list_make1_oid(roleid);
 
1227
                                        istmt.grant_option = false;
 
1228
                                        istmt.behavior = DROP_CASCADE;
 
1229
 
 
1230
                                        ExecGrantStmt_oids(&istmt);
 
1231
                                        break;
 
1232
                                case SHARED_DEPENDENCY_OWNER:
 
1233
                                        /* Save it for deletion below */
 
1234
                                        obj.classId = sdepForm->classid;
 
1235
                                        obj.objectId = sdepForm->objid;
 
1236
                                        obj.objectSubId = sdepForm->objsubid;
 
1237
                                        add_exact_object_address(&obj, deleteobjs);
 
1238
                                        break;
 
1239
                        }
 
1240
                }
 
1241
 
 
1242
                systable_endscan(scan);
 
1243
        }
 
1244
 
 
1245
        /* the dependency mechanism does the actual work */
 
1246
        performMultipleDeletions(deleteobjs, behavior);
 
1247
 
 
1248
        heap_close(sdepRel, RowExclusiveLock);
 
1249
 
 
1250
        free_object_addresses(deleteobjs);
 
1251
}
 
1252
 
 
1253
/*
 
1254
 * shdepReassignOwned
 
1255
 *
 
1256
 * Change the owner of objects owned by any of the roles in roleids to
 
1257
 * newrole.  Grants are not touched.
 
1258
 */
 
1259
void
 
1260
shdepReassignOwned(List *roleids, Oid newrole)
 
1261
{
 
1262
        Relation        sdepRel;
 
1263
        ListCell   *cell;
 
1264
 
 
1265
        /*
 
1266
         * We don't need this strong a lock here, but we'll call routines that
 
1267
         * acquire RowExclusiveLock.  Better get that right now to avoid potential
 
1268
         * deadlock problems.
 
1269
         */
 
1270
        sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
 
1271
 
 
1272
        foreach(cell, roleids)
 
1273
        {
 
1274
                SysScanDesc scan;
 
1275
                ScanKeyData key[2];
 
1276
                HeapTuple       tuple;
 
1277
                Oid                     roleid = lfirst_oid(cell);
 
1278
 
 
1279
                /* Refuse to work on pinned roles */
 
1280
                if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
 
1281
                {
 
1282
                        ObjectAddress obj;
 
1283
 
 
1284
                        obj.classId = AuthIdRelationId;
 
1285
                        obj.objectId = roleid;
 
1286
                        obj.objectSubId = 0;
 
1287
 
 
1288
                        ereport(ERROR,
 
1289
                                        (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
 
1290
                                   errmsg("cannot drop objects owned by %s because they are "
 
1291
                                                  "required by the database system",
 
1292
                                                  getObjectDescription(&obj))));
 
1293
 
 
1294
                        /*
 
1295
                         * There's no need to tell the whole truth, which is that we
 
1296
                         * didn't track these dependencies at all ...
 
1297
                         */
 
1298
                }
 
1299
 
 
1300
                ScanKeyInit(&key[0],
 
1301
                                        Anum_pg_shdepend_refclassid,
 
1302
                                        BTEqualStrategyNumber, F_OIDEQ,
 
1303
                                        ObjectIdGetDatum(AuthIdRelationId));
 
1304
                ScanKeyInit(&key[1],
 
1305
                                        Anum_pg_shdepend_refobjid,
 
1306
                                        BTEqualStrategyNumber, F_OIDEQ,
 
1307
                                        ObjectIdGetDatum(roleid));
 
1308
 
 
1309
                scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
 
1310
                                                                  SnapshotNow, 2, key);
 
1311
 
 
1312
                while ((tuple = systable_getnext(scan)) != NULL)
 
1313
                {
 
1314
                        Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
 
1315
 
 
1316
                        /* We only operate on objects in the current database */
 
1317
                        if (sdepForm->dbid != MyDatabaseId)
 
1318
                                continue;
 
1319
 
 
1320
                        /* Unexpected because we checked for pins above */
 
1321
                        if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
 
1322
                                elog(ERROR, "unexpected shared pin");
 
1323
 
 
1324
                        /* We leave non-owner dependencies alone */
 
1325
                        if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
 
1326
                                continue;
 
1327
 
 
1328
                        /* Issue the appropriate ALTER OWNER call */
 
1329
                        switch (sdepForm->classid)
 
1330
                        {
 
1331
                                case ConversionRelationId:
 
1332
                                        AlterConversionOwner_oid(sdepForm->objid, newrole);
 
1333
                                        break;
 
1334
 
 
1335
                                case TypeRelationId:
 
1336
                                        AlterTypeOwnerInternal(sdepForm->objid, newrole, true);
 
1337
                                        break;
 
1338
 
 
1339
                                case OperatorRelationId:
 
1340
                                        AlterOperatorOwner_oid(sdepForm->objid, newrole);
 
1341
                                        break;
 
1342
 
 
1343
                                case NamespaceRelationId:
 
1344
                                        AlterSchemaOwner_oid(sdepForm->objid, newrole);
 
1345
                                        break;
 
1346
 
 
1347
                                case RelationRelationId:
 
1348
 
 
1349
                                        /*
 
1350
                                         * Pass recursing = true so that we don't fail on indexes,
 
1351
                                         * owned sequences, etc when we happen to visit them
 
1352
                                         * before their parent table.
 
1353
                                         */
 
1354
                                        ATExecChangeOwner(sdepForm->objid, newrole, true);
 
1355
                                        break;
 
1356
 
 
1357
                                case ProcedureRelationId:
 
1358
                                        AlterFunctionOwner_oid(sdepForm->objid, newrole);
 
1359
                                        break;
 
1360
 
 
1361
                                case LanguageRelationId:
 
1362
                                        AlterLanguageOwner_oid(sdepForm->objid, newrole);
 
1363
                                        break;
 
1364
 
 
1365
                                default:
 
1366
                                        elog(ERROR, "unexpected classid %d", sdepForm->classid);
 
1367
                                        break;
 
1368
                        }
 
1369
                        /* Make sure the next iteration will see my changes */
 
1370
                        CommandCounterIncrement();
 
1371
                }
 
1372
 
 
1373
                systable_endscan(scan);
 
1374
        }
 
1375
 
 
1376
        heap_close(sdepRel, RowExclusiveLock);
 
1377
}