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