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