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