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