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