Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_depend.c
4 : * routines to support manipulation of the pg_depend relation
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/catalog/pg_depend.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 "catalog/catalog.h"
21 : #include "catalog/dependency.h"
22 : #include "catalog/indexing.h"
23 : #include "catalog/pg_constraint.h"
24 : #include "catalog/pg_depend.h"
25 : #include "catalog/pg_extension.h"
26 : #include "catalog/partition.h"
27 : #include "commands/extension.h"
28 : #include "miscadmin.h"
29 : #include "utils/fmgroids.h"
30 : #include "utils/lsyscache.h"
31 : #include "utils/rel.h"
32 :
33 :
34 : static bool isObjectPinned(const ObjectAddress *object);
35 :
36 :
37 : /*
38 : * Record a dependency between 2 objects via their respective ObjectAddress.
39 : * The first argument is the dependent object, the second the one it
40 : * references.
41 : *
42 : * This simply creates an entry in pg_depend, without any other processing.
43 : */
44 : void
45 731910 : recordDependencyOn(const ObjectAddress *depender,
46 : const ObjectAddress *referenced,
47 : DependencyType behavior)
48 : {
49 731910 : recordMultipleDependencies(depender, referenced, 1, behavior);
50 731910 : }
51 :
52 : /*
53 : * Record multiple dependencies (of the same kind) for a single dependent
54 : * object. This has a little less overhead than recording each separately.
55 : */
56 : void
57 1138646 : recordMultipleDependencies(const ObjectAddress *depender,
58 : const ObjectAddress *referenced,
59 : int nreferenced,
60 : DependencyType behavior)
61 : {
62 : Relation dependDesc;
63 : CatalogIndexState indstate;
64 : TupleTableSlot **slot;
65 : int i,
66 : max_slots,
67 : slot_init_count,
68 : slot_stored_count;
69 :
70 1138646 : if (nreferenced <= 0)
71 46296 : return; /* nothing to do */
72 :
73 : /*
74 : * During bootstrap, do nothing since pg_depend may not exist yet.
75 : *
76 : * Objects created during bootstrap are most likely pinned, and the few
77 : * that are not do not have dependencies on each other, so that there
78 : * would be no need to make a pg_depend entry anyway.
79 : */
80 1092350 : if (IsBootstrapProcessingMode())
81 60390 : return;
82 :
83 1031960 : dependDesc = table_open(DependRelationId, RowExclusiveLock);
84 :
85 : /*
86 : * Allocate the slots to use, but delay costly initialization until we
87 : * know that they will be used.
88 : */
89 1031960 : max_slots = Min(nreferenced,
90 : MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
91 1031960 : slot = palloc(sizeof(TupleTableSlot *) * max_slots);
92 :
93 : /* Don't open indexes unless we need to make an update */
94 1031960 : indstate = NULL;
95 :
96 : /* number of slots currently storing tuples */
97 1031960 : slot_stored_count = 0;
98 : /* number of slots currently initialized */
99 1031960 : slot_init_count = 0;
100 3007022 : for (i = 0; i < nreferenced; i++, referenced++)
101 : {
102 : /*
103 : * If the referenced object is pinned by the system, there's no real
104 : * need to record dependencies on it. This saves lots of space in
105 : * pg_depend, so it's worth the time taken to check.
106 : */
107 1975062 : if (isObjectPinned(referenced))
108 1441466 : continue;
109 :
110 533596 : if (slot_init_count < max_slots)
111 : {
112 533596 : slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
113 : &TTSOpsHeapTuple);
114 533596 : slot_init_count++;
115 : }
116 :
117 533596 : ExecClearTuple(slot[slot_stored_count]);
118 :
119 : /*
120 : * Record the dependency. Note we don't bother to check for duplicate
121 : * dependencies; there's no harm in them.
122 : */
123 533596 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
124 533596 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
125 533596 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
126 533596 : slot[slot_stored_count]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
127 533596 : slot[slot_stored_count]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
128 533596 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
129 533596 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
130 :
131 533596 : memset(slot[slot_stored_count]->tts_isnull, false,
132 533596 : slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
133 :
134 533596 : ExecStoreVirtualTuple(slot[slot_stored_count]);
135 533596 : slot_stored_count++;
136 :
137 : /* If slots are full, insert a batch of tuples */
138 533596 : if (slot_stored_count == max_slots)
139 : {
140 : /* fetch index info only when we know we need it */
141 370206 : if (indstate == NULL)
142 370206 : indstate = CatalogOpenIndexes(dependDesc);
143 :
144 370206 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
145 : indstate);
146 370206 : slot_stored_count = 0;
147 : }
148 : }
149 :
150 : /* Insert any tuples left in the buffer */
151 1031960 : if (slot_stored_count > 0)
152 : {
153 : /* fetch index info only when we know we need it */
154 83398 : if (indstate == NULL)
155 83398 : indstate = CatalogOpenIndexes(dependDesc);
156 :
157 83398 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
158 : indstate);
159 : }
160 :
161 1031960 : if (indstate != NULL)
162 453604 : CatalogCloseIndexes(indstate);
163 :
164 1031960 : table_close(dependDesc, RowExclusiveLock);
165 :
166 : /* Drop only the number of slots used */
167 1565556 : for (i = 0; i < slot_init_count; i++)
168 533596 : ExecDropSingleTupleTableSlot(slot[i]);
169 1031960 : pfree(slot);
170 : }
171 :
172 : /*
173 : * If we are executing a CREATE EXTENSION operation, mark the given object
174 : * as being a member of the extension, or check that it already is one.
175 : * Otherwise, do nothing.
176 : *
177 : * This must be called during creation of any user-definable object type
178 : * that could be a member of an extension.
179 : *
180 : * isReplace must be true if the object already existed, and false if it is
181 : * newly created. In the former case we insist that it already be a member
182 : * of the current extension. In the latter case we can skip checking whether
183 : * it is already a member of any extension.
184 : *
185 : * Note: isReplace = true is typically used when updating an object in
186 : * CREATE OR REPLACE and similar commands. We used to allow the target
187 : * object to not already be an extension member, instead silently absorbing
188 : * it into the current extension. However, this was both error-prone
189 : * (extensions might accidentally overwrite free-standing objects) and
190 : * a security hazard (since the object would retain its previous ownership).
191 : */
192 : void
193 283398 : recordDependencyOnCurrentExtension(const ObjectAddress *object,
194 : bool isReplace)
195 : {
196 : /* Only whole objects can be extension members */
197 : Assert(object->objectSubId == 0);
198 :
199 283398 : if (creating_extension)
200 : {
201 : ObjectAddress extension;
202 :
203 : /* Only need to check for existing membership if isReplace */
204 8844 : if (isReplace)
205 : {
206 : Oid oldext;
207 :
208 : /*
209 : * Side note: these catalog lookups are safe only because the
210 : * object is a pre-existing one. In the not-isReplace case, the
211 : * caller has most likely not yet done a CommandCounterIncrement
212 : * that would make the new object visible.
213 : */
214 766 : oldext = getExtensionOfObject(object->classId, object->objectId);
215 766 : if (OidIsValid(oldext))
216 : {
217 : /* If already a member of this extension, nothing to do */
218 758 : if (oldext == CurrentExtensionObject)
219 758 : return;
220 : /* Already a member of some other extension, so reject */
221 0 : ereport(ERROR,
222 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
223 : errmsg("%s is already a member of extension \"%s\"",
224 : getObjectDescription(object, false),
225 : get_extension_name(oldext))));
226 : }
227 : /* It's a free-standing object, so reject */
228 8 : ereport(ERROR,
229 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
230 : errmsg("%s is not a member of extension \"%s\"",
231 : getObjectDescription(object, false),
232 : get_extension_name(CurrentExtensionObject)),
233 : errdetail("An extension is not allowed to replace an object that it does not own.")));
234 : }
235 :
236 : /* OK, record it as a member of CurrentExtensionObject */
237 8078 : extension.classId = ExtensionRelationId;
238 8078 : extension.objectId = CurrentExtensionObject;
239 8078 : extension.objectSubId = 0;
240 :
241 8078 : recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
242 : }
243 : }
244 :
245 : /*
246 : * If we are executing a CREATE EXTENSION operation, check that the given
247 : * object is a member of the extension, and throw an error if it isn't.
248 : * Otherwise, do nothing.
249 : *
250 : * This must be called whenever a CREATE IF NOT EXISTS operation (for an
251 : * object type that can be an extension member) has found that an object of
252 : * the desired name already exists. It is insecure for an extension to use
253 : * IF NOT EXISTS except when the conflicting object is already an extension
254 : * member; otherwise a hostile user could substitute an object with arbitrary
255 : * properties.
256 : */
257 : void
258 138 : checkMembershipInCurrentExtension(const ObjectAddress *object)
259 : {
260 : /*
261 : * This is actually the same condition tested in
262 : * recordDependencyOnCurrentExtension; but we want to issue a
263 : * differently-worded error, and anyway it would be pretty confusing to
264 : * call recordDependencyOnCurrentExtension in these circumstances.
265 : */
266 :
267 : /* Only whole objects can be extension members */
268 : Assert(object->objectSubId == 0);
269 :
270 138 : if (creating_extension)
271 : {
272 : Oid oldext;
273 :
274 28 : oldext = getExtensionOfObject(object->classId, object->objectId);
275 : /* If already a member of this extension, OK */
276 28 : if (oldext == CurrentExtensionObject)
277 14 : return;
278 : /* Else complain */
279 14 : ereport(ERROR,
280 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
281 : errmsg("%s is not a member of extension \"%s\"",
282 : getObjectDescription(object, false),
283 : get_extension_name(CurrentExtensionObject)),
284 : errdetail("An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.")));
285 : }
286 : }
287 :
288 : /*
289 : * deleteDependencyRecordsFor -- delete all records with given depender
290 : * classId/objectId. Returns the number of records deleted.
291 : *
292 : * This is used when redefining an existing object. Links leading to the
293 : * object do not change, and links leading from it will be recreated
294 : * (possibly with some differences from before).
295 : *
296 : * If skipExtensionDeps is true, we do not delete any dependencies that
297 : * show that the given object is a member of an extension. This avoids
298 : * needing a lot of extra logic to fetch and recreate that dependency.
299 : */
300 : long
301 18668 : deleteDependencyRecordsFor(Oid classId, Oid objectId,
302 : bool skipExtensionDeps)
303 : {
304 18668 : long count = 0;
305 : Relation depRel;
306 : ScanKeyData key[2];
307 : SysScanDesc scan;
308 : HeapTuple tup;
309 :
310 18668 : depRel = table_open(DependRelationId, RowExclusiveLock);
311 :
312 18668 : ScanKeyInit(&key[0],
313 : Anum_pg_depend_classid,
314 : BTEqualStrategyNumber, F_OIDEQ,
315 : ObjectIdGetDatum(classId));
316 18668 : ScanKeyInit(&key[1],
317 : Anum_pg_depend_objid,
318 : BTEqualStrategyNumber, F_OIDEQ,
319 : ObjectIdGetDatum(objectId));
320 :
321 18668 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
322 : NULL, 2, key);
323 :
324 31010 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
325 : {
326 12342 : if (skipExtensionDeps &&
327 9924 : ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
328 1230 : continue;
329 :
330 11112 : CatalogTupleDelete(depRel, &tup->t_self);
331 11112 : count++;
332 : }
333 :
334 18668 : systable_endscan(scan);
335 :
336 18668 : table_close(depRel, RowExclusiveLock);
337 :
338 18668 : return count;
339 : }
340 :
341 : /*
342 : * deleteDependencyRecordsForClass -- delete all records with given depender
343 : * classId/objectId, dependee classId, and deptype.
344 : * Returns the number of records deleted.
345 : *
346 : * This is a variant of deleteDependencyRecordsFor, useful when revoking
347 : * an object property that is expressed by a dependency record (such as
348 : * extension membership).
349 : */
350 : long
351 12558 : deleteDependencyRecordsForClass(Oid classId, Oid objectId,
352 : Oid refclassId, char deptype)
353 : {
354 12558 : long count = 0;
355 : Relation depRel;
356 : ScanKeyData key[2];
357 : SysScanDesc scan;
358 : HeapTuple tup;
359 :
360 12558 : depRel = table_open(DependRelationId, RowExclusiveLock);
361 :
362 12558 : ScanKeyInit(&key[0],
363 : Anum_pg_depend_classid,
364 : BTEqualStrategyNumber, F_OIDEQ,
365 : ObjectIdGetDatum(classId));
366 12558 : ScanKeyInit(&key[1],
367 : Anum_pg_depend_objid,
368 : BTEqualStrategyNumber, F_OIDEQ,
369 : ObjectIdGetDatum(objectId));
370 :
371 12558 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
372 : NULL, 2, key);
373 :
374 18334 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
375 : {
376 5776 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
377 :
378 5776 : if (depform->refclassid == refclassId && depform->deptype == deptype)
379 : {
380 1198 : CatalogTupleDelete(depRel, &tup->t_self);
381 1198 : count++;
382 : }
383 : }
384 :
385 12558 : systable_endscan(scan);
386 :
387 12558 : table_close(depRel, RowExclusiveLock);
388 :
389 12558 : return count;
390 : }
391 :
392 : /*
393 : * deleteDependencyRecordsForSpecific -- delete all records with given depender
394 : * classId/objectId, dependee classId/objectId, of the given deptype.
395 : * Returns the number of records deleted.
396 : */
397 : long
398 56 : deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype,
399 : Oid refclassId, Oid refobjectId)
400 : {
401 56 : long count = 0;
402 : Relation depRel;
403 : ScanKeyData key[2];
404 : SysScanDesc scan;
405 : HeapTuple tup;
406 :
407 56 : depRel = table_open(DependRelationId, RowExclusiveLock);
408 :
409 56 : ScanKeyInit(&key[0],
410 : Anum_pg_depend_classid,
411 : BTEqualStrategyNumber, F_OIDEQ,
412 : ObjectIdGetDatum(classId));
413 56 : ScanKeyInit(&key[1],
414 : Anum_pg_depend_objid,
415 : BTEqualStrategyNumber, F_OIDEQ,
416 : ObjectIdGetDatum(objectId));
417 :
418 56 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
419 : NULL, 2, key);
420 :
421 316 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
422 : {
423 260 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
424 :
425 260 : if (depform->refclassid == refclassId &&
426 56 : depform->refobjid == refobjectId &&
427 56 : depform->deptype == deptype)
428 : {
429 56 : CatalogTupleDelete(depRel, &tup->t_self);
430 56 : count++;
431 : }
432 : }
433 :
434 56 : systable_endscan(scan);
435 :
436 56 : table_close(depRel, RowExclusiveLock);
437 :
438 56 : return count;
439 : }
440 :
441 : /*
442 : * Adjust dependency record(s) to point to a different object of the same type
443 : *
444 : * classId/objectId specify the referencing object.
445 : * refClassId/oldRefObjectId specify the old referenced object.
446 : * newRefObjectId is the new referenced object (must be of class refClassId).
447 : *
448 : * Note the lack of objsubid parameters. If there are subobject references
449 : * they will all be readjusted. Also, there is an expectation that we are
450 : * dealing with NORMAL dependencies: if we have to replace an (implicit)
451 : * dependency on a pinned object with an explicit dependency on an unpinned
452 : * one, the new one will be NORMAL.
453 : *
454 : * Returns the number of records updated -- zero indicates a problem.
455 : */
456 : long
457 374 : changeDependencyFor(Oid classId, Oid objectId,
458 : Oid refClassId, Oid oldRefObjectId,
459 : Oid newRefObjectId)
460 : {
461 374 : long count = 0;
462 : Relation depRel;
463 : ScanKeyData key[2];
464 : SysScanDesc scan;
465 : HeapTuple tup;
466 : ObjectAddress objAddr;
467 : ObjectAddress depAddr;
468 : bool oldIsPinned;
469 : bool newIsPinned;
470 :
471 : /*
472 : * Check to see if either oldRefObjectId or newRefObjectId is pinned.
473 : * Pinned objects should not have any dependency entries pointing to them,
474 : * so in these cases we should add or remove a pg_depend entry, or do
475 : * nothing at all, rather than update an entry as in the normal case.
476 : */
477 374 : objAddr.classId = refClassId;
478 374 : objAddr.objectId = oldRefObjectId;
479 374 : objAddr.objectSubId = 0;
480 :
481 374 : oldIsPinned = isObjectPinned(&objAddr);
482 :
483 374 : objAddr.objectId = newRefObjectId;
484 :
485 374 : newIsPinned = isObjectPinned(&objAddr);
486 :
487 374 : if (oldIsPinned)
488 : {
489 : /*
490 : * If both are pinned, we need do nothing. However, return 1 not 0,
491 : * else callers will think this is an error case.
492 : */
493 56 : if (newIsPinned)
494 0 : return 1;
495 :
496 : /*
497 : * There is no old dependency record, but we should insert a new one.
498 : * Assume a normal dependency is wanted.
499 : */
500 56 : depAddr.classId = classId;
501 56 : depAddr.objectId = objectId;
502 56 : depAddr.objectSubId = 0;
503 56 : recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
504 :
505 56 : return 1;
506 : }
507 :
508 318 : depRel = table_open(DependRelationId, RowExclusiveLock);
509 :
510 : /* There should be existing dependency record(s), so search. */
511 318 : ScanKeyInit(&key[0],
512 : Anum_pg_depend_classid,
513 : BTEqualStrategyNumber, F_OIDEQ,
514 : ObjectIdGetDatum(classId));
515 318 : ScanKeyInit(&key[1],
516 : Anum_pg_depend_objid,
517 : BTEqualStrategyNumber, F_OIDEQ,
518 : ObjectIdGetDatum(objectId));
519 :
520 318 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
521 : NULL, 2, key);
522 :
523 862 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
524 : {
525 544 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
526 :
527 544 : if (depform->refclassid == refClassId &&
528 318 : depform->refobjid == oldRefObjectId)
529 : {
530 318 : if (newIsPinned)
531 66 : CatalogTupleDelete(depRel, &tup->t_self);
532 : else
533 : {
534 : /* make a modifiable copy */
535 252 : tup = heap_copytuple(tup);
536 252 : depform = (Form_pg_depend) GETSTRUCT(tup);
537 :
538 252 : depform->refobjid = newRefObjectId;
539 :
540 252 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
541 :
542 252 : heap_freetuple(tup);
543 : }
544 :
545 318 : count++;
546 : }
547 : }
548 :
549 318 : systable_endscan(scan);
550 :
551 318 : table_close(depRel, RowExclusiveLock);
552 :
553 318 : return count;
554 : }
555 :
556 : /*
557 : * Adjust all dependency records to come from a different object of the same type
558 : *
559 : * classId/oldObjectId specify the old referencing object.
560 : * newObjectId is the new referencing object (must be of class classId).
561 : *
562 : * Returns the number of records updated.
563 : */
564 : long
565 1004 : changeDependenciesOf(Oid classId, Oid oldObjectId,
566 : Oid newObjectId)
567 : {
568 1004 : long count = 0;
569 : Relation depRel;
570 : ScanKeyData key[2];
571 : SysScanDesc scan;
572 : HeapTuple tup;
573 :
574 1004 : depRel = table_open(DependRelationId, RowExclusiveLock);
575 :
576 1004 : ScanKeyInit(&key[0],
577 : Anum_pg_depend_classid,
578 : BTEqualStrategyNumber, F_OIDEQ,
579 : ObjectIdGetDatum(classId));
580 1004 : ScanKeyInit(&key[1],
581 : Anum_pg_depend_objid,
582 : BTEqualStrategyNumber, F_OIDEQ,
583 : ObjectIdGetDatum(oldObjectId));
584 :
585 1004 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
586 : NULL, 2, key);
587 :
588 2788 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
589 : {
590 : Form_pg_depend depform;
591 :
592 : /* make a modifiable copy */
593 1784 : tup = heap_copytuple(tup);
594 1784 : depform = (Form_pg_depend) GETSTRUCT(tup);
595 :
596 1784 : depform->objid = newObjectId;
597 :
598 1784 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
599 :
600 1784 : heap_freetuple(tup);
601 :
602 1784 : count++;
603 : }
604 :
605 1004 : systable_endscan(scan);
606 :
607 1004 : table_close(depRel, RowExclusiveLock);
608 :
609 1004 : return count;
610 : }
611 :
612 : /*
613 : * Adjust all dependency records to point to a different object of the same type
614 : *
615 : * refClassId/oldRefObjectId specify the old referenced object.
616 : * newRefObjectId is the new referenced object (must be of class refClassId).
617 : *
618 : * Returns the number of records updated.
619 : */
620 : long
621 1004 : changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
622 : Oid newRefObjectId)
623 : {
624 1004 : long count = 0;
625 : Relation depRel;
626 : ScanKeyData key[2];
627 : SysScanDesc scan;
628 : HeapTuple tup;
629 : ObjectAddress objAddr;
630 : bool newIsPinned;
631 :
632 1004 : depRel = table_open(DependRelationId, RowExclusiveLock);
633 :
634 : /*
635 : * If oldRefObjectId is pinned, there won't be any dependency entries on
636 : * it --- we can't cope in that case. (This isn't really worth expending
637 : * code to fix, in current usage; it just means you can't rename stuff out
638 : * of pg_catalog, which would likely be a bad move anyway.)
639 : */
640 1004 : objAddr.classId = refClassId;
641 1004 : objAddr.objectId = oldRefObjectId;
642 1004 : objAddr.objectSubId = 0;
643 :
644 1004 : if (isObjectPinned(&objAddr))
645 0 : ereport(ERROR,
646 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
647 : errmsg("cannot remove dependency on %s because it is a system object",
648 : getObjectDescription(&objAddr, false))));
649 :
650 : /*
651 : * We can handle adding a dependency on something pinned, though, since
652 : * that just means deleting the dependency entry.
653 : */
654 1004 : objAddr.objectId = newRefObjectId;
655 :
656 1004 : newIsPinned = isObjectPinned(&objAddr);
657 :
658 : /* Now search for dependency records */
659 1004 : ScanKeyInit(&key[0],
660 : Anum_pg_depend_refclassid,
661 : BTEqualStrategyNumber, F_OIDEQ,
662 : ObjectIdGetDatum(refClassId));
663 1004 : ScanKeyInit(&key[1],
664 : Anum_pg_depend_refobjid,
665 : BTEqualStrategyNumber, F_OIDEQ,
666 : ObjectIdGetDatum(oldRefObjectId));
667 :
668 1004 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
669 : NULL, 2, key);
670 :
671 1022 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
672 : {
673 18 : if (newIsPinned)
674 0 : CatalogTupleDelete(depRel, &tup->t_self);
675 : else
676 : {
677 : Form_pg_depend depform;
678 :
679 : /* make a modifiable copy */
680 18 : tup = heap_copytuple(tup);
681 18 : depform = (Form_pg_depend) GETSTRUCT(tup);
682 :
683 18 : depform->refobjid = newRefObjectId;
684 :
685 18 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
686 :
687 18 : heap_freetuple(tup);
688 : }
689 :
690 18 : count++;
691 : }
692 :
693 1004 : systable_endscan(scan);
694 :
695 1004 : table_close(depRel, RowExclusiveLock);
696 :
697 1004 : return count;
698 : }
699 :
700 : /*
701 : * isObjectPinned()
702 : *
703 : * Test if an object is required for basic database functionality.
704 : *
705 : * The passed subId, if any, is ignored; we assume that only whole objects
706 : * are pinned (and that this implies pinning their components).
707 : */
708 : static bool
709 1977818 : isObjectPinned(const ObjectAddress *object)
710 : {
711 1977818 : return IsPinnedObject(object->classId, object->objectId);
712 : }
713 :
714 :
715 : /*
716 : * Various special-purpose lookups and manipulations of pg_depend.
717 : */
718 :
719 :
720 : /*
721 : * Find the extension containing the specified object, if any
722 : *
723 : * Returns the OID of the extension, or InvalidOid if the object does not
724 : * belong to any extension.
725 : *
726 : * Extension membership is marked by an EXTENSION dependency from the object
727 : * to the extension. Note that the result will be indeterminate if pg_depend
728 : * contains links from this object to more than one extension ... but that
729 : * should never happen.
730 : */
731 : Oid
732 1220 : getExtensionOfObject(Oid classId, Oid objectId)
733 : {
734 1220 : Oid result = InvalidOid;
735 : Relation depRel;
736 : ScanKeyData key[2];
737 : SysScanDesc scan;
738 : HeapTuple tup;
739 :
740 1220 : depRel = table_open(DependRelationId, AccessShareLock);
741 :
742 1220 : ScanKeyInit(&key[0],
743 : Anum_pg_depend_classid,
744 : BTEqualStrategyNumber, F_OIDEQ,
745 : ObjectIdGetDatum(classId));
746 1220 : ScanKeyInit(&key[1],
747 : Anum_pg_depend_objid,
748 : BTEqualStrategyNumber, F_OIDEQ,
749 : ObjectIdGetDatum(objectId));
750 :
751 1220 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
752 : NULL, 2, key);
753 :
754 2988 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
755 : {
756 2840 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
757 :
758 2840 : if (depform->refclassid == ExtensionRelationId &&
759 1072 : depform->deptype == DEPENDENCY_EXTENSION)
760 : {
761 1072 : result = depform->refobjid;
762 1072 : break; /* no need to keep scanning */
763 : }
764 : }
765 :
766 1220 : systable_endscan(scan);
767 :
768 1220 : table_close(depRel, AccessShareLock);
769 :
770 1220 : return result;
771 : }
772 :
773 : /*
774 : * Return (possibly NIL) list of extensions that the given object depends on
775 : * in DEPENDENCY_AUTO_EXTENSION mode.
776 : */
777 : List *
778 38 : getAutoExtensionsOfObject(Oid classId, Oid objectId)
779 : {
780 38 : List *result = NIL;
781 : Relation depRel;
782 : ScanKeyData key[2];
783 : SysScanDesc scan;
784 : HeapTuple tup;
785 :
786 38 : depRel = table_open(DependRelationId, AccessShareLock);
787 :
788 38 : ScanKeyInit(&key[0],
789 : Anum_pg_depend_classid,
790 : BTEqualStrategyNumber, F_OIDEQ,
791 : ObjectIdGetDatum(classId));
792 38 : ScanKeyInit(&key[1],
793 : Anum_pg_depend_objid,
794 : BTEqualStrategyNumber, F_OIDEQ,
795 : ObjectIdGetDatum(objectId));
796 :
797 38 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
798 : NULL, 2, key);
799 :
800 96 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
801 : {
802 58 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
803 :
804 58 : if (depform->refclassid == ExtensionRelationId &&
805 2 : depform->deptype == DEPENDENCY_AUTO_EXTENSION)
806 2 : result = lappend_oid(result, depform->refobjid);
807 : }
808 :
809 38 : systable_endscan(scan);
810 :
811 38 : table_close(depRel, AccessShareLock);
812 :
813 38 : return result;
814 : }
815 :
816 : /*
817 : * Detect whether a sequence is marked as "owned" by a column
818 : *
819 : * An ownership marker is an AUTO or INTERNAL dependency from the sequence to the
820 : * column. If we find one, store the identity of the owning column
821 : * into *tableId and *colId and return true; else return false.
822 : *
823 : * Note: if there's more than one such pg_depend entry then you get
824 : * a random one of them returned into the out parameters. This should
825 : * not happen, though.
826 : */
827 : bool
828 808 : sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
829 : {
830 808 : bool ret = false;
831 : Relation depRel;
832 : ScanKeyData key[2];
833 : SysScanDesc scan;
834 : HeapTuple tup;
835 :
836 808 : depRel = table_open(DependRelationId, AccessShareLock);
837 :
838 808 : ScanKeyInit(&key[0],
839 : Anum_pg_depend_classid,
840 : BTEqualStrategyNumber, F_OIDEQ,
841 : ObjectIdGetDatum(RelationRelationId));
842 808 : ScanKeyInit(&key[1],
843 : Anum_pg_depend_objid,
844 : BTEqualStrategyNumber, F_OIDEQ,
845 : ObjectIdGetDatum(seqId));
846 :
847 808 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
848 : NULL, 2, key);
849 :
850 1620 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
851 : {
852 824 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
853 :
854 824 : if (depform->refclassid == RelationRelationId &&
855 12 : depform->deptype == deptype)
856 : {
857 12 : *tableId = depform->refobjid;
858 12 : *colId = depform->refobjsubid;
859 12 : ret = true;
860 12 : break; /* no need to keep scanning */
861 : }
862 : }
863 :
864 808 : systable_endscan(scan);
865 :
866 808 : table_close(depRel, AccessShareLock);
867 :
868 808 : return ret;
869 : }
870 :
871 : /*
872 : * Collect a list of OIDs of all sequences owned by the specified relation,
873 : * and column if specified. If deptype is not zero, then only find sequences
874 : * with the specified dependency type.
875 : */
876 : static List *
877 706 : getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)
878 : {
879 706 : List *result = NIL;
880 : Relation depRel;
881 : ScanKeyData key[3];
882 : SysScanDesc scan;
883 : HeapTuple tup;
884 :
885 706 : depRel = table_open(DependRelationId, AccessShareLock);
886 :
887 706 : ScanKeyInit(&key[0],
888 : Anum_pg_depend_refclassid,
889 : BTEqualStrategyNumber, F_OIDEQ,
890 : ObjectIdGetDatum(RelationRelationId));
891 706 : ScanKeyInit(&key[1],
892 : Anum_pg_depend_refobjid,
893 : BTEqualStrategyNumber, F_OIDEQ,
894 : ObjectIdGetDatum(relid));
895 706 : if (attnum)
896 604 : ScanKeyInit(&key[2],
897 : Anum_pg_depend_refobjsubid,
898 : BTEqualStrategyNumber, F_INT4EQ,
899 : Int32GetDatum(attnum));
900 :
901 706 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
902 : NULL, attnum ? 3 : 2, key);
903 :
904 2354 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
905 : {
906 1648 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
907 :
908 : /*
909 : * We assume any auto or internal dependency of a sequence on a column
910 : * must be what we are looking for. (We need the relkind test because
911 : * indexes can also have auto dependencies on columns.)
912 : */
913 1648 : if (deprec->classid == RelationRelationId &&
914 716 : deprec->objsubid == 0 &&
915 716 : deprec->refobjsubid != 0 &&
916 1372 : (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
917 686 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
918 : {
919 682 : if (!deptype || deprec->deptype == deptype)
920 676 : result = lappend_oid(result, deprec->objid);
921 : }
922 : }
923 :
924 706 : systable_endscan(scan);
925 :
926 706 : table_close(depRel, AccessShareLock);
927 :
928 706 : return result;
929 : }
930 :
931 : /*
932 : * Collect a list of OIDs of all sequences owned (identity or serial) by the
933 : * specified relation.
934 : */
935 : List *
936 102 : getOwnedSequences(Oid relid)
937 : {
938 102 : return getOwnedSequences_internal(relid, 0, 0);
939 : }
940 :
941 : /*
942 : * Get owned identity sequence, error if not exactly one.
943 : */
944 : Oid
945 604 : getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
946 : {
947 604 : Oid relid = RelationGetRelid(rel);
948 : List *seqlist;
949 :
950 : /*
951 : * The identity sequence is associated with the topmost partitioned table,
952 : * which might have column order different than the given partition.
953 : */
954 604 : if (RelationGetForm(rel)->relispartition)
955 : {
956 54 : List *ancestors = get_partition_ancestors(relid);
957 54 : const char *attname = get_attname(relid, attnum, false);
958 :
959 54 : relid = llast_oid(ancestors);
960 54 : attnum = get_attnum(relid, attname);
961 54 : if (attnum == InvalidAttrNumber)
962 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
963 : attname, relid);
964 54 : list_free(ancestors);
965 : }
966 :
967 604 : seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL);
968 604 : if (list_length(seqlist) > 1)
969 0 : elog(ERROR, "more than one owned sequence found");
970 604 : else if (seqlist == NIL)
971 : {
972 12 : if (missing_ok)
973 12 : return InvalidOid;
974 : else
975 0 : elog(ERROR, "no owned sequence found");
976 : }
977 :
978 592 : return linitial_oid(seqlist);
979 : }
980 :
981 : /*
982 : * get_index_constraint
983 : * Given the OID of an index, return the OID of the owning unique,
984 : * primary-key, or exclusion constraint, or InvalidOid if there
985 : * is no owning constraint.
986 : */
987 : Oid
988 12530 : get_index_constraint(Oid indexId)
989 : {
990 12530 : Oid constraintId = InvalidOid;
991 : Relation depRel;
992 : ScanKeyData key[3];
993 : SysScanDesc scan;
994 : HeapTuple tup;
995 :
996 : /* Search the dependency table for the index */
997 12530 : depRel = table_open(DependRelationId, AccessShareLock);
998 :
999 12530 : ScanKeyInit(&key[0],
1000 : Anum_pg_depend_classid,
1001 : BTEqualStrategyNumber, F_OIDEQ,
1002 : ObjectIdGetDatum(RelationRelationId));
1003 12530 : ScanKeyInit(&key[1],
1004 : Anum_pg_depend_objid,
1005 : BTEqualStrategyNumber, F_OIDEQ,
1006 : ObjectIdGetDatum(indexId));
1007 12530 : ScanKeyInit(&key[2],
1008 : Anum_pg_depend_objsubid,
1009 : BTEqualStrategyNumber, F_INT4EQ,
1010 : Int32GetDatum(0));
1011 :
1012 12530 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
1013 : NULL, 3, key);
1014 :
1015 15474 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1016 : {
1017 4432 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
1018 :
1019 : /*
1020 : * We assume any internal dependency on a constraint must be what we
1021 : * are looking for.
1022 : */
1023 4432 : if (deprec->refclassid == ConstraintRelationId &&
1024 1488 : deprec->refobjsubid == 0 &&
1025 1488 : deprec->deptype == DEPENDENCY_INTERNAL)
1026 : {
1027 1488 : constraintId = deprec->refobjid;
1028 1488 : break;
1029 : }
1030 : }
1031 :
1032 12530 : systable_endscan(scan);
1033 12530 : table_close(depRel, AccessShareLock);
1034 :
1035 12530 : return constraintId;
1036 : }
1037 :
1038 : /*
1039 : * get_index_ref_constraints
1040 : * Given the OID of an index, return the OID of all foreign key
1041 : * constraints which reference the index.
1042 : */
1043 : List *
1044 502 : get_index_ref_constraints(Oid indexId)
1045 : {
1046 502 : List *result = NIL;
1047 : Relation depRel;
1048 : ScanKeyData key[3];
1049 : SysScanDesc scan;
1050 : HeapTuple tup;
1051 :
1052 : /* Search the dependency table for the index */
1053 502 : depRel = table_open(DependRelationId, AccessShareLock);
1054 :
1055 502 : ScanKeyInit(&key[0],
1056 : Anum_pg_depend_refclassid,
1057 : BTEqualStrategyNumber, F_OIDEQ,
1058 : ObjectIdGetDatum(RelationRelationId));
1059 502 : ScanKeyInit(&key[1],
1060 : Anum_pg_depend_refobjid,
1061 : BTEqualStrategyNumber, F_OIDEQ,
1062 : ObjectIdGetDatum(indexId));
1063 502 : ScanKeyInit(&key[2],
1064 : Anum_pg_depend_refobjsubid,
1065 : BTEqualStrategyNumber, F_INT4EQ,
1066 : Int32GetDatum(0));
1067 :
1068 502 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
1069 : NULL, 3, key);
1070 :
1071 520 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1072 : {
1073 18 : Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
1074 :
1075 : /*
1076 : * We assume any normal dependency from a constraint must be what we
1077 : * are looking for.
1078 : */
1079 18 : if (deprec->classid == ConstraintRelationId &&
1080 18 : deprec->objsubid == 0 &&
1081 18 : deprec->deptype == DEPENDENCY_NORMAL)
1082 : {
1083 18 : result = lappend_oid(result, deprec->objid);
1084 : }
1085 : }
1086 :
1087 502 : systable_endscan(scan);
1088 502 : table_close(depRel, AccessShareLock);
1089 :
1090 502 : return result;
1091 : }
|