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 428037 : recordDependencyOn(const ObjectAddress *depender,
48 : const ObjectAddress *referenced,
49 : DependencyType behavior)
50 : {
51 428037 : recordMultipleDependencies(depender, referenced, 1, behavior);
52 428037 : }
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 655720 : 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 655720 : if (nreferenced <= 0)
73 26221 : 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 629499 : if (IsBootstrapProcessingMode())
83 34527 : return;
84 :
85 594972 : 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 594972 : max_slots = Min(nreferenced,
92 : MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_depend));
93 594972 : slot = palloc_array(TupleTableSlot *, max_slots);
94 :
95 : /* Don't open indexes unless we need to make an update */
96 594972 : indstate = NULL;
97 :
98 : /* number of slots currently storing tuples */
99 594972 : slot_stored_count = 0;
100 : /* number of slots currently initialized */
101 594972 : slot_init_count = 0;
102 1729566 : 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 1134594 : if (isObjectPinned(referenced))
110 827414 : continue;
111 :
112 307180 : if (slot_init_count < max_slots)
113 : {
114 307180 : slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
115 : &TTSOpsHeapTuple);
116 307180 : slot_init_count++;
117 : }
118 :
119 307180 : 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 307180 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
126 307180 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
127 307180 : slot[slot_stored_count]->tts_values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
128 307180 : slot[slot_stored_count]->tts_values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
129 307180 : slot[slot_stored_count]->tts_values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
130 307180 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
131 307180 : slot[slot_stored_count]->tts_values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
132 :
133 307180 : memset(slot[slot_stored_count]->tts_isnull, false,
134 307180 : slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
135 :
136 307180 : ExecStoreVirtualTuple(slot[slot_stored_count]);
137 307180 : slot_stored_count++;
138 :
139 : /* If slots are full, insert a batch of tuples */
140 307180 : if (slot_stored_count == max_slots)
141 : {
142 : /* fetch index info only when we know we need it */
143 214355 : if (indstate == NULL)
144 214355 : indstate = CatalogOpenIndexes(dependDesc);
145 :
146 214355 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
147 : indstate);
148 214355 : slot_stored_count = 0;
149 : }
150 : }
151 :
152 : /* Insert any tuples left in the buffer */
153 594972 : if (slot_stored_count > 0)
154 : {
155 : /* fetch index info only when we know we need it */
156 48450 : if (indstate == NULL)
157 48450 : indstate = CatalogOpenIndexes(dependDesc);
158 :
159 48450 : CatalogTuplesMultiInsertWithInfo(dependDesc, slot, slot_stored_count,
160 : indstate);
161 : }
162 :
163 594972 : if (indstate != NULL)
164 262805 : CatalogCloseIndexes(indstate);
165 :
166 594972 : table_close(dependDesc, RowExclusiveLock);
167 :
168 : /* Drop only the number of slots used */
169 902152 : for (i = 0; i < slot_init_count; i++)
170 307180 : ExecDropSingleTupleTableSlot(slot[i]);
171 594972 : 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 167517 : recordDependencyOnCurrentExtension(const ObjectAddress *object,
196 : bool isReplace)
197 : {
198 : /* Only whole objects can be extension members */
199 : Assert(object->objectSubId == 0);
200 :
201 167517 : if (creating_extension)
202 : {
203 : ObjectAddress extension;
204 :
205 : /* Only need to check for existing membership if isReplace */
206 5699 : 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 397 : oldext = getExtensionOfObject(object->classId, object->objectId);
217 397 : if (OidIsValid(oldext))
218 : {
219 : /* If already a member of this extension, nothing to do */
220 393 : if (oldext == CurrentExtensionObject)
221 393 : 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 4 : 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 5302 : extension.classId = ExtensionRelationId;
240 5302 : extension.objectId = CurrentExtensionObject;
241 5302 : extension.objectSubId = 0;
242 :
243 5302 : 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 69 : 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 69 : if (creating_extension)
273 : {
274 : Oid oldext;
275 :
276 14 : oldext = getExtensionOfObject(object->classId, object->objectId);
277 : /* If already a member of this extension, OK */
278 14 : if (oldext == CurrentExtensionObject)
279 7 : return;
280 : /* Else complain */
281 7 : 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 11079 : deleteDependencyRecordsFor(Oid classId, Oid objectId,
304 : bool skipExtensionDeps)
305 : {
306 11079 : long count = 0;
307 : Relation depRel;
308 : ScanKeyData key[2];
309 : SysScanDesc scan;
310 : HeapTuple tup;
311 :
312 11079 : depRel = table_open(DependRelationId, RowExclusiveLock);
313 :
314 11079 : ScanKeyInit(&key[0],
315 : Anum_pg_depend_classid,
316 : BTEqualStrategyNumber, F_OIDEQ,
317 : ObjectIdGetDatum(classId));
318 11079 : ScanKeyInit(&key[1],
319 : Anum_pg_depend_objid,
320 : BTEqualStrategyNumber, F_OIDEQ,
321 : ObjectIdGetDatum(objectId));
322 :
323 11079 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
324 : NULL, 2, key);
325 :
326 17898 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
327 : {
328 6819 : if (skipExtensionDeps &&
329 5294 : ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
330 629 : continue;
331 :
332 6190 : CatalogTupleDelete(depRel, &tup->t_self);
333 6190 : count++;
334 : }
335 :
336 11079 : systable_endscan(scan);
337 :
338 11079 : table_close(depRel, RowExclusiveLock);
339 :
340 11079 : 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 7678 : deleteDependencyRecordsForClass(Oid classId, Oid objectId,
354 : Oid refclassId, char deptype)
355 : {
356 7678 : long count = 0;
357 : Relation depRel;
358 : ScanKeyData key[2];
359 : SysScanDesc scan;
360 : HeapTuple tup;
361 :
362 7678 : depRel = table_open(DependRelationId, RowExclusiveLock);
363 :
364 7678 : ScanKeyInit(&key[0],
365 : Anum_pg_depend_classid,
366 : BTEqualStrategyNumber, F_OIDEQ,
367 : ObjectIdGetDatum(classId));
368 7678 : ScanKeyInit(&key[1],
369 : Anum_pg_depend_objid,
370 : BTEqualStrategyNumber, F_OIDEQ,
371 : ObjectIdGetDatum(objectId));
372 :
373 7678 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
374 : NULL, 2, key);
375 :
376 12619 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
377 : {
378 4941 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
379 :
380 4941 : if (depform->refclassid == refclassId && depform->deptype == deptype)
381 : {
382 1109 : CatalogTupleDelete(depRel, &tup->t_self);
383 1109 : count++;
384 : }
385 : }
386 :
387 7678 : systable_endscan(scan);
388 :
389 7678 : table_close(depRel, RowExclusiveLock);
390 :
391 7678 : 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 43 : deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype,
401 : Oid refclassId, Oid refobjectId)
402 : {
403 43 : long count = 0;
404 : Relation depRel;
405 : ScanKeyData key[2];
406 : SysScanDesc scan;
407 : HeapTuple tup;
408 :
409 43 : depRel = table_open(DependRelationId, RowExclusiveLock);
410 :
411 43 : ScanKeyInit(&key[0],
412 : Anum_pg_depend_classid,
413 : BTEqualStrategyNumber, F_OIDEQ,
414 : ObjectIdGetDatum(classId));
415 43 : ScanKeyInit(&key[1],
416 : Anum_pg_depend_objid,
417 : BTEqualStrategyNumber, F_OIDEQ,
418 : ObjectIdGetDatum(objectId));
419 :
420 43 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
421 : NULL, 2, key);
422 :
423 233 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
424 : {
425 190 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
426 :
427 190 : if (depform->refclassid == refclassId &&
428 43 : depform->refobjid == refobjectId &&
429 43 : depform->deptype == deptype)
430 : {
431 43 : CatalogTupleDelete(depRel, &tup->t_self);
432 43 : count++;
433 : }
434 : }
435 :
436 43 : systable_endscan(scan);
437 :
438 43 : table_close(depRel, RowExclusiveLock);
439 :
440 43 : 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 187 : changeDependencyFor(Oid classId, Oid objectId,
460 : Oid refClassId, Oid oldRefObjectId,
461 : Oid newRefObjectId)
462 : {
463 187 : 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 187 : objAddr.classId = refClassId;
480 187 : objAddr.objectId = oldRefObjectId;
481 187 : objAddr.objectSubId = 0;
482 :
483 187 : oldIsPinned = isObjectPinned(&objAddr);
484 :
485 187 : objAddr.objectId = newRefObjectId;
486 :
487 187 : newIsPinned = isObjectPinned(&objAddr);
488 :
489 187 : 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 28 : 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 28 : depAddr.classId = classId;
503 28 : depAddr.objectId = objectId;
504 28 : depAddr.objectSubId = 0;
505 28 : recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
506 :
507 28 : return 1;
508 : }
509 :
510 159 : depRel = table_open(DependRelationId, RowExclusiveLock);
511 :
512 : /* There should be existing dependency record(s), so search. */
513 159 : ScanKeyInit(&key[0],
514 : Anum_pg_depend_classid,
515 : BTEqualStrategyNumber, F_OIDEQ,
516 : ObjectIdGetDatum(classId));
517 159 : ScanKeyInit(&key[1],
518 : Anum_pg_depend_objid,
519 : BTEqualStrategyNumber, F_OIDEQ,
520 : ObjectIdGetDatum(objectId));
521 :
522 159 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
523 : NULL, 2, key);
524 :
525 425 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
526 : {
527 266 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
528 :
529 266 : if (depform->refclassid == refClassId &&
530 159 : depform->refobjid == oldRefObjectId)
531 : {
532 159 : if (newIsPinned)
533 33 : CatalogTupleDelete(depRel, &tup->t_self);
534 : else
535 : {
536 : /* make a modifiable copy */
537 126 : tup = heap_copytuple(tup);
538 126 : depform = (Form_pg_depend) GETSTRUCT(tup);
539 :
540 126 : depform->refobjid = newRefObjectId;
541 :
542 126 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
543 :
544 126 : heap_freetuple(tup);
545 : }
546 :
547 159 : count++;
548 : }
549 : }
550 :
551 159 : systable_endscan(scan);
552 :
553 159 : table_close(depRel, RowExclusiveLock);
554 :
555 159 : 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 534 : changeDependenciesOf(Oid classId, Oid oldObjectId,
568 : Oid newObjectId)
569 : {
570 534 : long count = 0;
571 : Relation depRel;
572 : ScanKeyData key[2];
573 : SysScanDesc scan;
574 : HeapTuple tup;
575 :
576 534 : depRel = table_open(DependRelationId, RowExclusiveLock);
577 :
578 534 : ScanKeyInit(&key[0],
579 : Anum_pg_depend_classid,
580 : BTEqualStrategyNumber, F_OIDEQ,
581 : ObjectIdGetDatum(classId));
582 534 : ScanKeyInit(&key[1],
583 : Anum_pg_depend_objid,
584 : BTEqualStrategyNumber, F_OIDEQ,
585 : ObjectIdGetDatum(oldObjectId));
586 :
587 534 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
588 : NULL, 2, key);
589 :
590 1472 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
591 : {
592 : Form_pg_depend depform;
593 :
594 : /* make a modifiable copy */
595 938 : tup = heap_copytuple(tup);
596 938 : depform = (Form_pg_depend) GETSTRUCT(tup);
597 :
598 938 : depform->objid = newObjectId;
599 :
600 938 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
601 :
602 938 : heap_freetuple(tup);
603 :
604 938 : count++;
605 : }
606 :
607 534 : systable_endscan(scan);
608 :
609 534 : table_close(depRel, RowExclusiveLock);
610 :
611 534 : 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 534 : changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
624 : Oid newRefObjectId)
625 : {
626 534 : long count = 0;
627 : Relation depRel;
628 : ScanKeyData key[2];
629 : SysScanDesc scan;
630 : HeapTuple tup;
631 : ObjectAddress objAddr;
632 : bool newIsPinned;
633 :
634 534 : 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 534 : objAddr.classId = refClassId;
643 534 : objAddr.objectId = oldRefObjectId;
644 534 : objAddr.objectSubId = 0;
645 :
646 534 : 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 534 : objAddr.objectId = newRefObjectId;
657 :
658 534 : newIsPinned = isObjectPinned(&objAddr);
659 :
660 : /* Now search for dependency records */
661 534 : ScanKeyInit(&key[0],
662 : Anum_pg_depend_refclassid,
663 : BTEqualStrategyNumber, F_OIDEQ,
664 : ObjectIdGetDatum(refClassId));
665 534 : ScanKeyInit(&key[1],
666 : Anum_pg_depend_refobjid,
667 : BTEqualStrategyNumber, F_OIDEQ,
668 : ObjectIdGetDatum(oldRefObjectId));
669 :
670 534 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
671 : NULL, 2, key);
672 :
673 543 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
674 : {
675 9 : if (newIsPinned)
676 0 : CatalogTupleDelete(depRel, &tup->t_self);
677 : else
678 : {
679 : Form_pg_depend depform;
680 :
681 : /* make a modifiable copy */
682 9 : tup = heap_copytuple(tup);
683 9 : depform = (Form_pg_depend) GETSTRUCT(tup);
684 :
685 9 : depform->refobjid = newRefObjectId;
686 :
687 9 : CatalogTupleUpdate(depRel, &tup->t_self, tup);
688 :
689 9 : heap_freetuple(tup);
690 : }
691 :
692 9 : count++;
693 : }
694 :
695 534 : systable_endscan(scan);
696 :
697 534 : table_close(depRel, RowExclusiveLock);
698 :
699 534 : 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 1136036 : isObjectPinned(const ObjectAddress *object)
712 : {
713 1136036 : 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 650 : getExtensionOfObject(Oid classId, Oid objectId)
735 : {
736 650 : Oid result = InvalidOid;
737 : Relation depRel;
738 : ScanKeyData key[2];
739 : SysScanDesc scan;
740 : HeapTuple tup;
741 :
742 650 : depRel = table_open(DependRelationId, AccessShareLock);
743 :
744 650 : ScanKeyInit(&key[0],
745 : Anum_pg_depend_classid,
746 : BTEqualStrategyNumber, F_OIDEQ,
747 : ObjectIdGetDatum(classId));
748 650 : ScanKeyInit(&key[1],
749 : Anum_pg_depend_objid,
750 : BTEqualStrategyNumber, F_OIDEQ,
751 : ObjectIdGetDatum(objectId));
752 :
753 650 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
754 : NULL, 2, key);
755 :
756 1548 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
757 : {
758 1470 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
759 :
760 1470 : if (depform->refclassid == ExtensionRelationId &&
761 572 : depform->deptype == DEPENDENCY_EXTENSION)
762 : {
763 572 : result = depform->refobjid;
764 572 : break; /* no need to keep scanning */
765 : }
766 : }
767 :
768 650 : systable_endscan(scan);
769 :
770 650 : table_close(depRel, AccessShareLock);
771 :
772 650 : 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 19 : getAutoExtensionsOfObject(Oid classId, Oid objectId)
781 : {
782 19 : List *result = NIL;
783 : Relation depRel;
784 : ScanKeyData key[2];
785 : SysScanDesc scan;
786 : HeapTuple tup;
787 :
788 19 : depRel = table_open(DependRelationId, AccessShareLock);
789 :
790 19 : ScanKeyInit(&key[0],
791 : Anum_pg_depend_classid,
792 : BTEqualStrategyNumber, F_OIDEQ,
793 : ObjectIdGetDatum(classId));
794 19 : ScanKeyInit(&key[1],
795 : Anum_pg_depend_objid,
796 : BTEqualStrategyNumber, F_OIDEQ,
797 : ObjectIdGetDatum(objectId));
798 :
799 19 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
800 : NULL, 2, key);
801 :
802 48 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
803 : {
804 29 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
805 :
806 29 : if (depform->refclassid == ExtensionRelationId &&
807 1 : depform->deptype == DEPENDENCY_AUTO_EXTENSION)
808 1 : result = lappend_oid(result, depform->refobjid);
809 : }
810 :
811 19 : systable_endscan(scan);
812 :
813 19 : table_close(depRel, AccessShareLock);
814 :
815 19 : 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 1 : getExtensionType(Oid extensionOid, const char *typname)
833 : {
834 1 : Oid result = InvalidOid;
835 : Relation depRel;
836 : ScanKeyData key[3];
837 : SysScanDesc scan;
838 : HeapTuple tup;
839 :
840 1 : depRel = table_open(DependRelationId, AccessShareLock);
841 :
842 1 : ScanKeyInit(&key[0],
843 : Anum_pg_depend_refclassid,
844 : BTEqualStrategyNumber, F_OIDEQ,
845 : ObjectIdGetDatum(ExtensionRelationId));
846 1 : ScanKeyInit(&key[1],
847 : Anum_pg_depend_refobjid,
848 : BTEqualStrategyNumber, F_OIDEQ,
849 : ObjectIdGetDatum(extensionOid));
850 1 : ScanKeyInit(&key[2],
851 : Anum_pg_depend_refobjsubid,
852 : BTEqualStrategyNumber, F_INT4EQ,
853 : Int32GetDatum(0));
854 :
855 1 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
856 : NULL, 3, key);
857 :
858 1 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
859 : {
860 1 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
861 :
862 1 : if (depform->classid == TypeRelationId &&
863 1 : depform->deptype == DEPENDENCY_EXTENSION)
864 : {
865 1 : Oid typoid = depform->objid;
866 : HeapTuple typtup;
867 :
868 1 : typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid));
869 1 : if (!HeapTupleIsValid(typtup))
870 0 : continue; /* should we throw an error? */
871 1 : if (strcmp(NameStr(((Form_pg_type) GETSTRUCT(typtup))->typname),
872 : typname) == 0)
873 : {
874 1 : result = typoid;
875 1 : ReleaseSysCache(typtup);
876 1 : break; /* no need to keep searching */
877 : }
878 0 : ReleaseSysCache(typtup);
879 : }
880 : }
881 :
882 1 : systable_endscan(scan);
883 :
884 1 : table_close(depRel, AccessShareLock);
885 :
886 1 : 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 441 : sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
902 : {
903 441 : bool ret = false;
904 : Relation depRel;
905 : ScanKeyData key[2];
906 : SysScanDesc scan;
907 : HeapTuple tup;
908 :
909 441 : depRel = table_open(DependRelationId, AccessShareLock);
910 :
911 441 : ScanKeyInit(&key[0],
912 : Anum_pg_depend_classid,
913 : BTEqualStrategyNumber, F_OIDEQ,
914 : ObjectIdGetDatum(RelationRelationId));
915 441 : ScanKeyInit(&key[1],
916 : Anum_pg_depend_objid,
917 : BTEqualStrategyNumber, F_OIDEQ,
918 : ObjectIdGetDatum(seqId));
919 :
920 441 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
921 : NULL, 2, key);
922 :
923 884 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
924 : {
925 449 : Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
926 :
927 449 : if (depform->refclassid == RelationRelationId &&
928 6 : depform->deptype == deptype)
929 : {
930 6 : *tableId = depform->refobjid;
931 6 : *colId = depform->refobjsubid;
932 6 : ret = true;
933 6 : break; /* no need to keep scanning */
934 : }
935 : }
936 :
937 441 : systable_endscan(scan);
938 :
939 441 : table_close(depRel, AccessShareLock);
940 :
941 441 : 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 372 : getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)
951 : {
952 372 : List *result = NIL;
953 : Relation depRel;
954 : ScanKeyData key[3];
955 : SysScanDesc scan;
956 : HeapTuple tup;
957 :
958 372 : depRel = table_open(DependRelationId, AccessShareLock);
959 :
960 372 : ScanKeyInit(&key[0],
961 : Anum_pg_depend_refclassid,
962 : BTEqualStrategyNumber, F_OIDEQ,
963 : ObjectIdGetDatum(RelationRelationId));
964 372 : ScanKeyInit(&key[1],
965 : Anum_pg_depend_refobjid,
966 : BTEqualStrategyNumber, F_OIDEQ,
967 : ObjectIdGetDatum(relid));
968 372 : if (attnum)
969 322 : ScanKeyInit(&key[2],
970 : Anum_pg_depend_refobjsubid,
971 : BTEqualStrategyNumber, F_INT4EQ,
972 : Int32GetDatum(attnum));
973 :
974 372 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
975 : NULL, attnum ? 3 : 2, key);
976 :
977 1248 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
978 : {
979 876 : 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 876 : if (deprec->classid == RelationRelationId &&
987 377 : deprec->objsubid == 0 &&
988 377 : deprec->refobjsubid != 0 &&
989 724 : (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
990 362 : get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
991 : {
992 360 : if (!deptype || deprec->deptype == deptype)
993 357 : result = lappend_oid(result, deprec->objid);
994 : }
995 : }
996 :
997 372 : systable_endscan(scan);
998 :
999 372 : table_close(depRel, AccessShareLock);
1000 :
1001 372 : 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 50 : getOwnedSequences(Oid relid)
1010 : {
1011 50 : return getOwnedSequences_internal(relid, 0, 0);
1012 : }
1013 :
1014 : /*
1015 : * Get owned identity sequence, error if not exactly one.
1016 : */
1017 : Oid
1018 322 : getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
1019 : {
1020 322 : 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 322 : if (RelationGetForm(rel)->relispartition)
1028 : {
1029 27 : List *ancestors = get_partition_ancestors(relid);
1030 27 : const char *attname = get_attname(relid, attnum, false);
1031 :
1032 27 : relid = llast_oid(ancestors);
1033 27 : attnum = get_attnum(relid, attname);
1034 27 : if (attnum == InvalidAttrNumber)
1035 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
1036 : attname, relid);
1037 27 : list_free(ancestors);
1038 : }
1039 :
1040 322 : seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL);
1041 322 : if (list_length(seqlist) > 1)
1042 0 : elog(ERROR, "more than one owned sequence found");
1043 322 : else if (seqlist == NIL)
1044 : {
1045 6 : if (missing_ok)
1046 6 : return InvalidOid;
1047 : else
1048 0 : elog(ERROR, "no owned sequence found");
1049 : }
1050 :
1051 316 : 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 7117 : get_index_constraint(Oid indexId)
1062 : {
1063 7117 : 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 7117 : depRel = table_open(DependRelationId, AccessShareLock);
1071 :
1072 7117 : ScanKeyInit(&key[0],
1073 : Anum_pg_depend_classid,
1074 : BTEqualStrategyNumber, F_OIDEQ,
1075 : ObjectIdGetDatum(RelationRelationId));
1076 7117 : ScanKeyInit(&key[1],
1077 : Anum_pg_depend_objid,
1078 : BTEqualStrategyNumber, F_OIDEQ,
1079 : ObjectIdGetDatum(indexId));
1080 7117 : ScanKeyInit(&key[2],
1081 : Anum_pg_depend_objsubid,
1082 : BTEqualStrategyNumber, F_INT4EQ,
1083 : Int32GetDatum(0));
1084 :
1085 7117 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
1086 : NULL, 3, key);
1087 :
1088 8633 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1089 : {
1090 2433 : 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 2433 : if (deprec->refclassid == ConstraintRelationId &&
1097 917 : deprec->refobjsubid == 0 &&
1098 917 : deprec->deptype == DEPENDENCY_INTERNAL)
1099 : {
1100 917 : constraintId = deprec->refobjid;
1101 917 : break;
1102 : }
1103 : }
1104 :
1105 7117 : systable_endscan(scan);
1106 7117 : table_close(depRel, AccessShareLock);
1107 :
1108 7117 : 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 267 : get_index_ref_constraints(Oid indexId)
1118 : {
1119 267 : 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 267 : depRel = table_open(DependRelationId, AccessShareLock);
1127 :
1128 267 : ScanKeyInit(&key[0],
1129 : Anum_pg_depend_refclassid,
1130 : BTEqualStrategyNumber, F_OIDEQ,
1131 : ObjectIdGetDatum(RelationRelationId));
1132 267 : ScanKeyInit(&key[1],
1133 : Anum_pg_depend_refobjid,
1134 : BTEqualStrategyNumber, F_OIDEQ,
1135 : ObjectIdGetDatum(indexId));
1136 267 : ScanKeyInit(&key[2],
1137 : Anum_pg_depend_refobjsubid,
1138 : BTEqualStrategyNumber, F_INT4EQ,
1139 : Int32GetDatum(0));
1140 :
1141 267 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
1142 : NULL, 3, key);
1143 :
1144 276 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
1145 : {
1146 9 : 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 9 : if (deprec->classid == ConstraintRelationId &&
1153 9 : deprec->objsubid == 0 &&
1154 9 : deprec->deptype == DEPENDENCY_NORMAL)
1155 : {
1156 9 : result = lappend_oid(result, deprec->objid);
1157 : }
1158 : }
1159 :
1160 267 : systable_endscan(scan);
1161 267 : table_close(depRel, AccessShareLock);
1162 :
1163 267 : return result;
1164 : }
|