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