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