1 /*-------------------------------------------------------------------------
2 *
3 * pg_depend.c
4 * routines to support manipulation of the pg_depend relation
5 *
6 * Portions Copyright (c) 1996-2016, 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/heapam.h"
19 #include "access/htup_details.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 #include "utils/tqual.h"
31
32
33 static bool isObjectPinned(const ObjectAddress *object, Relation rel);
34
35
36 /*
37 * Record a dependency between 2 objects via their respective objectAddress.
38 * The first argument is the dependent object, the second the one it
39 * references.
40 *
41 * This simply creates an entry in pg_depend, without any other processing.
42 */
43 void
recordDependencyOn(const ObjectAddress * depender,const ObjectAddress * referenced,DependencyType behavior)44 recordDependencyOn(const ObjectAddress *depender,
45 const ObjectAddress *referenced,
46 DependencyType behavior)
47 {
48 recordMultipleDependencies(depender, referenced, 1, behavior);
49 }
50
51 /*
52 * Record multiple dependencies (of the same kind) for a single dependent
53 * object. This has a little less overhead than recording each separately.
54 */
55 void
recordMultipleDependencies(const ObjectAddress * depender,const ObjectAddress * referenced,int nreferenced,DependencyType behavior)56 recordMultipleDependencies(const ObjectAddress *depender,
57 const ObjectAddress *referenced,
58 int nreferenced,
59 DependencyType behavior)
60 {
61 Relation dependDesc;
62 CatalogIndexState indstate;
63 HeapTuple tup;
64 int i;
65 bool nulls[Natts_pg_depend];
66 Datum values[Natts_pg_depend];
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 = heap_open(DependRelationId, RowExclusiveLock);
79
80 /* Don't open indexes unless we need to make an update */
81 indstate = NULL;
82
83 memset(nulls, false, sizeof(nulls));
84
85 for (i = 0; i < nreferenced; i++, referenced++)
86 {
87 /*
88 * If the referenced object is pinned by the system, there's no real
89 * need to record dependencies on it. This saves lots of space in
90 * pg_depend, so it's worth the time taken to check.
91 */
92 if (!isObjectPinned(referenced, dependDesc))
93 {
94 /*
95 * Record the Dependency. Note we don't bother to check for
96 * duplicate dependencies; there's no harm in them.
97 */
98 values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
99 values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
100 values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
101
102 values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
103 values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
104 values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
105
106 values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
107
108 tup = heap_form_tuple(dependDesc->rd_att, values, nulls);
109
110 simple_heap_insert(dependDesc, tup);
111
112 /* keep indexes current */
113 if (indstate == NULL)
114 indstate = CatalogOpenIndexes(dependDesc);
115
116 CatalogIndexInsert(indstate, tup);
117
118 heap_freetuple(tup);
119 }
120 }
121
122 if (indstate != NULL)
123 CatalogCloseIndexes(indstate);
124
125 heap_close(dependDesc, RowExclusiveLock);
126 }
127
128 /*
129 * If we are executing a CREATE EXTENSION operation, mark the given object
130 * as being a member of the extension. Otherwise, do nothing.
131 *
132 * This must be called during creation of any user-definable object type
133 * that could be a member of an extension.
134 *
135 * If isReplace is true, the object already existed (or might have already
136 * existed), so we must check for a pre-existing extension membership entry.
137 * Passing false is a guarantee that the object is newly created, and so
138 * could not already be a member of any extension.
139 */
140 void
recordDependencyOnCurrentExtension(const ObjectAddress * object,bool isReplace)141 recordDependencyOnCurrentExtension(const ObjectAddress *object,
142 bool isReplace)
143 {
144 /* Only whole objects can be extension members */
145 Assert(object->objectSubId == 0);
146
147 if (creating_extension)
148 {
149 ObjectAddress extension;
150
151 /* Only need to check for existing membership if isReplace */
152 if (isReplace)
153 {
154 Oid oldext;
155
156 oldext = getExtensionOfObject(object->classId, object->objectId);
157 if (OidIsValid(oldext))
158 {
159 /* If already a member of this extension, nothing to do */
160 if (oldext == CurrentExtensionObject)
161 return;
162 /* Already a member of some other extension, so reject */
163 ereport(ERROR,
164 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
165 errmsg("%s is already a member of extension \"%s\"",
166 getObjectDescription(object),
167 get_extension_name(oldext))));
168 }
169 }
170
171 /* OK, record it as a member of CurrentExtensionObject */
172 extension.classId = ExtensionRelationId;
173 extension.objectId = CurrentExtensionObject;
174 extension.objectSubId = 0;
175
176 recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
177 }
178 }
179
180 /*
181 * deleteDependencyRecordsFor -- delete all records with given depender
182 * classId/objectId. Returns the number of records deleted.
183 *
184 * This is used when redefining an existing object. Links leading to the
185 * object do not change, and links leading from it will be recreated
186 * (possibly with some differences from before).
187 *
188 * If skipExtensionDeps is true, we do not delete any dependencies that
189 * show that the given object is a member of an extension. This avoids
190 * needing a lot of extra logic to fetch and recreate that dependency.
191 */
192 long
deleteDependencyRecordsFor(Oid classId,Oid objectId,bool skipExtensionDeps)193 deleteDependencyRecordsFor(Oid classId, Oid objectId,
194 bool skipExtensionDeps)
195 {
196 long count = 0;
197 Relation depRel;
198 ScanKeyData key[2];
199 SysScanDesc scan;
200 HeapTuple tup;
201
202 depRel = heap_open(DependRelationId, RowExclusiveLock);
203
204 ScanKeyInit(&key[0],
205 Anum_pg_depend_classid,
206 BTEqualStrategyNumber, F_OIDEQ,
207 ObjectIdGetDatum(classId));
208 ScanKeyInit(&key[1],
209 Anum_pg_depend_objid,
210 BTEqualStrategyNumber, F_OIDEQ,
211 ObjectIdGetDatum(objectId));
212
213 scan = systable_beginscan(depRel, DependDependerIndexId, true,
214 NULL, 2, key);
215
216 while (HeapTupleIsValid(tup = systable_getnext(scan)))
217 {
218 if (skipExtensionDeps &&
219 ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
220 continue;
221
222 simple_heap_delete(depRel, &tup->t_self);
223 count++;
224 }
225
226 systable_endscan(scan);
227
228 heap_close(depRel, RowExclusiveLock);
229
230 return count;
231 }
232
233 /*
234 * deleteDependencyRecordsForClass -- delete all records with given depender
235 * classId/objectId, dependee classId, and deptype.
236 * Returns the number of records deleted.
237 *
238 * This is a variant of deleteDependencyRecordsFor, useful when revoking
239 * an object property that is expressed by a dependency record (such as
240 * extension membership).
241 */
242 long
deleteDependencyRecordsForClass(Oid classId,Oid objectId,Oid refclassId,char deptype)243 deleteDependencyRecordsForClass(Oid classId, Oid objectId,
244 Oid refclassId, char deptype)
245 {
246 long count = 0;
247 Relation depRel;
248 ScanKeyData key[2];
249 SysScanDesc scan;
250 HeapTuple tup;
251
252 depRel = heap_open(DependRelationId, RowExclusiveLock);
253
254 ScanKeyInit(&key[0],
255 Anum_pg_depend_classid,
256 BTEqualStrategyNumber, F_OIDEQ,
257 ObjectIdGetDatum(classId));
258 ScanKeyInit(&key[1],
259 Anum_pg_depend_objid,
260 BTEqualStrategyNumber, F_OIDEQ,
261 ObjectIdGetDatum(objectId));
262
263 scan = systable_beginscan(depRel, DependDependerIndexId, true,
264 NULL, 2, key);
265
266 while (HeapTupleIsValid(tup = systable_getnext(scan)))
267 {
268 Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
269
270 if (depform->refclassid == refclassId && depform->deptype == deptype)
271 {
272 simple_heap_delete(depRel, &tup->t_self);
273 count++;
274 }
275 }
276
277 systable_endscan(scan);
278
279 heap_close(depRel, RowExclusiveLock);
280
281 return count;
282 }
283
284 /*
285 * Adjust dependency record(s) to point to a different object of the same type
286 *
287 * classId/objectId specify the referencing object.
288 * refClassId/oldRefObjectId specify the old referenced object.
289 * newRefObjectId is the new referenced object (must be of class refClassId).
290 *
291 * Note the lack of objsubid parameters. If there are subobject references
292 * they will all be readjusted.
293 *
294 * Returns the number of records updated.
295 */
296 long
changeDependencyFor(Oid classId,Oid objectId,Oid refClassId,Oid oldRefObjectId,Oid newRefObjectId)297 changeDependencyFor(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 bool newIsPinned;
308
309 depRel = heap_open(DependRelationId, RowExclusiveLock);
310
311 /*
312 * If oldRefObjectId is pinned, there won't be any dependency entries on
313 * it --- we can't cope in that case. (This isn't really worth expending
314 * code to fix, in current usage; it just means you can't rename stuff out
315 * of pg_catalog, which would likely be a bad move anyway.)
316 */
317 objAddr.classId = refClassId;
318 objAddr.objectId = oldRefObjectId;
319 objAddr.objectSubId = 0;
320
321 if (isObjectPinned(&objAddr, depRel))
322 ereport(ERROR,
323 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
324 errmsg("cannot remove dependency on %s because it is a system object",
325 getObjectDescription(&objAddr))));
326
327 /*
328 * We can handle adding a dependency on something pinned, though, since
329 * that just means deleting the dependency entry.
330 */
331 objAddr.objectId = newRefObjectId;
332
333 newIsPinned = isObjectPinned(&objAddr, depRel);
334
335 /* Now search for dependency records */
336 ScanKeyInit(&key[0],
337 Anum_pg_depend_classid,
338 BTEqualStrategyNumber, F_OIDEQ,
339 ObjectIdGetDatum(classId));
340 ScanKeyInit(&key[1],
341 Anum_pg_depend_objid,
342 BTEqualStrategyNumber, F_OIDEQ,
343 ObjectIdGetDatum(objectId));
344
345 scan = systable_beginscan(depRel, DependDependerIndexId, true,
346 NULL, 2, key);
347
348 while (HeapTupleIsValid((tup = systable_getnext(scan))))
349 {
350 Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
351
352 if (depform->refclassid == refClassId &&
353 depform->refobjid == oldRefObjectId)
354 {
355 if (newIsPinned)
356 simple_heap_delete(depRel, &tup->t_self);
357 else
358 {
359 /* make a modifiable copy */
360 tup = heap_copytuple(tup);
361 depform = (Form_pg_depend) GETSTRUCT(tup);
362
363 depform->refobjid = newRefObjectId;
364
365 simple_heap_update(depRel, &tup->t_self, tup);
366 CatalogUpdateIndexes(depRel, tup);
367
368 heap_freetuple(tup);
369 }
370
371 count++;
372 }
373 }
374
375 systable_endscan(scan);
376
377 heap_close(depRel, RowExclusiveLock);
378
379 return count;
380 }
381
382 /*
383 * isObjectPinned()
384 *
385 * Test if an object is required for basic database functionality.
386 * Caller must already have opened pg_depend.
387 *
388 * The passed subId, if any, is ignored; we assume that only whole objects
389 * are pinned (and that this implies pinning their components).
390 */
391 static bool
isObjectPinned(const ObjectAddress * object,Relation rel)392 isObjectPinned(const ObjectAddress *object, Relation rel)
393 {
394 bool ret = false;
395 SysScanDesc scan;
396 HeapTuple tup;
397 ScanKeyData key[2];
398
399 ScanKeyInit(&key[0],
400 Anum_pg_depend_refclassid,
401 BTEqualStrategyNumber, F_OIDEQ,
402 ObjectIdGetDatum(object->classId));
403
404 ScanKeyInit(&key[1],
405 Anum_pg_depend_refobjid,
406 BTEqualStrategyNumber, F_OIDEQ,
407 ObjectIdGetDatum(object->objectId));
408
409 scan = systable_beginscan(rel, DependReferenceIndexId, true,
410 NULL, 2, key);
411
412 /*
413 * Since we won't generate additional pg_depend entries for pinned
414 * objects, there can be at most one entry referencing a pinned object.
415 * Hence, it's sufficient to look at the first returned tuple; we don't
416 * need to loop.
417 */
418 tup = systable_getnext(scan);
419 if (HeapTupleIsValid(tup))
420 {
421 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
422
423 if (foundDep->deptype == DEPENDENCY_PIN)
424 ret = true;
425 }
426
427 systable_endscan(scan);
428
429 return ret;
430 }
431
432
433 /*
434 * Various special-purpose lookups and manipulations of pg_depend.
435 */
436
437
438 /*
439 * Find the extension containing the specified object, if any
440 *
441 * Returns the OID of the extension, or InvalidOid if the object does not
442 * belong to any extension.
443 *
444 * Extension membership is marked by an EXTENSION dependency from the object
445 * to the extension. Note that the result will be indeterminate if pg_depend
446 * contains links from this object to more than one extension ... but that
447 * should never happen.
448 */
449 Oid
getExtensionOfObject(Oid classId,Oid objectId)450 getExtensionOfObject(Oid classId, Oid objectId)
451 {
452 Oid result = InvalidOid;
453 Relation depRel;
454 ScanKeyData key[2];
455 SysScanDesc scan;
456 HeapTuple tup;
457
458 depRel = heap_open(DependRelationId, AccessShareLock);
459
460 ScanKeyInit(&key[0],
461 Anum_pg_depend_classid,
462 BTEqualStrategyNumber, F_OIDEQ,
463 ObjectIdGetDatum(classId));
464 ScanKeyInit(&key[1],
465 Anum_pg_depend_objid,
466 BTEqualStrategyNumber, F_OIDEQ,
467 ObjectIdGetDatum(objectId));
468
469 scan = systable_beginscan(depRel, DependDependerIndexId, true,
470 NULL, 2, key);
471
472 while (HeapTupleIsValid((tup = systable_getnext(scan))))
473 {
474 Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
475
476 if (depform->refclassid == ExtensionRelationId &&
477 depform->deptype == DEPENDENCY_EXTENSION)
478 {
479 result = depform->refobjid;
480 break; /* no need to keep scanning */
481 }
482 }
483
484 systable_endscan(scan);
485
486 heap_close(depRel, AccessShareLock);
487
488 return result;
489 }
490
491 /*
492 * Return (possibly NIL) list of extensions that the given object depends on
493 * in DEPENDENCY_AUTO_EXTENSION mode.
494 */
495 List *
getAutoExtensionsOfObject(Oid classId,Oid objectId)496 getAutoExtensionsOfObject(Oid classId, Oid objectId)
497 {
498 List *result = NIL;
499 Relation depRel;
500 ScanKeyData key[2];
501 SysScanDesc scan;
502 HeapTuple tup;
503
504 depRel = heap_open(DependRelationId, AccessShareLock);
505
506 ScanKeyInit(&key[0],
507 Anum_pg_depend_classid,
508 BTEqualStrategyNumber, F_OIDEQ,
509 ObjectIdGetDatum(classId));
510 ScanKeyInit(&key[1],
511 Anum_pg_depend_objid,
512 BTEqualStrategyNumber, F_OIDEQ,
513 ObjectIdGetDatum(objectId));
514
515 scan = systable_beginscan(depRel, DependDependerIndexId, true,
516 NULL, 2, key);
517
518 while (HeapTupleIsValid((tup = systable_getnext(scan))))
519 {
520 Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
521
522 if (depform->refclassid == ExtensionRelationId &&
523 depform->deptype == DEPENDENCY_AUTO_EXTENSION)
524 result = lappend_oid(result, depform->refobjid);
525 }
526
527 systable_endscan(scan);
528
529 heap_close(depRel, AccessShareLock);
530
531 return result;
532 }
533
534 /*
535 * Detect whether a sequence is marked as "owned" by a column
536 *
537 * An ownership marker is an AUTO dependency from the sequence to the
538 * column. If we find one, store the identity of the owning column
539 * into *tableId and *colId and return TRUE; else return FALSE.
540 *
541 * Note: if there's more than one such pg_depend entry then you get
542 * a random one of them returned into the out parameters. This should
543 * not happen, though.
544 */
545 bool
sequenceIsOwned(Oid seqId,Oid * tableId,int32 * colId)546 sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId)
547 {
548 bool ret = false;
549 Relation depRel;
550 ScanKeyData key[2];
551 SysScanDesc scan;
552 HeapTuple tup;
553
554 depRel = heap_open(DependRelationId, AccessShareLock);
555
556 ScanKeyInit(&key[0],
557 Anum_pg_depend_classid,
558 BTEqualStrategyNumber, F_OIDEQ,
559 ObjectIdGetDatum(RelationRelationId));
560 ScanKeyInit(&key[1],
561 Anum_pg_depend_objid,
562 BTEqualStrategyNumber, F_OIDEQ,
563 ObjectIdGetDatum(seqId));
564
565 scan = systable_beginscan(depRel, DependDependerIndexId, true,
566 NULL, 2, key);
567
568 while (HeapTupleIsValid((tup = systable_getnext(scan))))
569 {
570 Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
571
572 if (depform->refclassid == RelationRelationId &&
573 depform->deptype == DEPENDENCY_AUTO)
574 {
575 *tableId = depform->refobjid;
576 *colId = depform->refobjsubid;
577 ret = true;
578 break; /* no need to keep scanning */
579 }
580 }
581
582 systable_endscan(scan);
583
584 heap_close(depRel, AccessShareLock);
585
586 return ret;
587 }
588
589 /*
590 * Remove any existing "owned" markers for the specified sequence.
591 *
592 * Note: we don't provide a special function to install an "owned"
593 * marker; just use recordDependencyOn().
594 */
595 void
markSequenceUnowned(Oid seqId)596 markSequenceUnowned(Oid seqId)
597 {
598 deleteDependencyRecordsForClass(RelationRelationId, seqId,
599 RelationRelationId, DEPENDENCY_AUTO);
600 }
601
602 /*
603 * Collect a list of OIDs of all sequences owned by the specified relation.
604 */
605 List *
getOwnedSequences(Oid relid)606 getOwnedSequences(Oid relid)
607 {
608 List *result = NIL;
609 Relation depRel;
610 ScanKeyData key[2];
611 SysScanDesc scan;
612 HeapTuple tup;
613
614 depRel = heap_open(DependRelationId, AccessShareLock);
615
616 ScanKeyInit(&key[0],
617 Anum_pg_depend_refclassid,
618 BTEqualStrategyNumber, F_OIDEQ,
619 ObjectIdGetDatum(RelationRelationId));
620 ScanKeyInit(&key[1],
621 Anum_pg_depend_refobjid,
622 BTEqualStrategyNumber, F_OIDEQ,
623 ObjectIdGetDatum(relid));
624
625 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
626 NULL, 2, key);
627
628 while (HeapTupleIsValid(tup = systable_getnext(scan)))
629 {
630 Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
631
632 /*
633 * We assume any auto dependency of a sequence on a column must be
634 * what we are looking for. (We need the relkind test because indexes
635 * can also have auto dependencies on columns.)
636 */
637 if (deprec->classid == RelationRelationId &&
638 deprec->objsubid == 0 &&
639 deprec->refobjsubid != 0 &&
640 deprec->deptype == DEPENDENCY_AUTO &&
641 get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
642 {
643 result = lappend_oid(result, deprec->objid);
644 }
645 }
646
647 systable_endscan(scan);
648
649 heap_close(depRel, AccessShareLock);
650
651 return result;
652 }
653
654
655 /*
656 * get_constraint_index
657 * Given the OID of a unique, primary-key, or exclusion constraint,
658 * return the OID of the underlying index.
659 *
660 * Return InvalidOid if the index couldn't be found; this suggests the
661 * given OID is bogus, but we leave it to caller to decide what to do.
662 */
663 Oid
get_constraint_index(Oid constraintId)664 get_constraint_index(Oid constraintId)
665 {
666 Oid indexId = InvalidOid;
667 Relation depRel;
668 ScanKeyData key[3];
669 SysScanDesc scan;
670 HeapTuple tup;
671
672 /* Search the dependency table for the dependent index */
673 depRel = heap_open(DependRelationId, AccessShareLock);
674
675 ScanKeyInit(&key[0],
676 Anum_pg_depend_refclassid,
677 BTEqualStrategyNumber, F_OIDEQ,
678 ObjectIdGetDatum(ConstraintRelationId));
679 ScanKeyInit(&key[1],
680 Anum_pg_depend_refobjid,
681 BTEqualStrategyNumber, F_OIDEQ,
682 ObjectIdGetDatum(constraintId));
683 ScanKeyInit(&key[2],
684 Anum_pg_depend_refobjsubid,
685 BTEqualStrategyNumber, F_INT4EQ,
686 Int32GetDatum(0));
687
688 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
689 NULL, 3, key);
690
691 while (HeapTupleIsValid(tup = systable_getnext(scan)))
692 {
693 Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
694
695 /*
696 * We assume any internal dependency of an index on the constraint
697 * must be what we are looking for. (The relkind test is just
698 * paranoia; there shouldn't be any such dependencies otherwise.)
699 */
700 if (deprec->classid == RelationRelationId &&
701 deprec->objsubid == 0 &&
702 deprec->deptype == DEPENDENCY_INTERNAL &&
703 get_rel_relkind(deprec->objid) == RELKIND_INDEX)
704 {
705 indexId = deprec->objid;
706 break;
707 }
708 }
709
710 systable_endscan(scan);
711 heap_close(depRel, AccessShareLock);
712
713 return indexId;
714 }
715
716 /*
717 * get_index_constraint
718 * Given the OID of an index, return the OID of the owning unique,
719 * primary-key, or exclusion constraint, or InvalidOid if there
720 * is no owning constraint.
721 */
722 Oid
get_index_constraint(Oid indexId)723 get_index_constraint(Oid indexId)
724 {
725 Oid constraintId = InvalidOid;
726 Relation depRel;
727 ScanKeyData key[3];
728 SysScanDesc scan;
729 HeapTuple tup;
730
731 /* Search the dependency table for the index */
732 depRel = heap_open(DependRelationId, AccessShareLock);
733
734 ScanKeyInit(&key[0],
735 Anum_pg_depend_classid,
736 BTEqualStrategyNumber, F_OIDEQ,
737 ObjectIdGetDatum(RelationRelationId));
738 ScanKeyInit(&key[1],
739 Anum_pg_depend_objid,
740 BTEqualStrategyNumber, F_OIDEQ,
741 ObjectIdGetDatum(indexId));
742 ScanKeyInit(&key[2],
743 Anum_pg_depend_objsubid,
744 BTEqualStrategyNumber, F_INT4EQ,
745 Int32GetDatum(0));
746
747 scan = systable_beginscan(depRel, DependDependerIndexId, true,
748 NULL, 3, key);
749
750 while (HeapTupleIsValid(tup = systable_getnext(scan)))
751 {
752 Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
753
754 /*
755 * We assume any internal dependency on a constraint must be what we
756 * are looking for.
757 */
758 if (deprec->refclassid == ConstraintRelationId &&
759 deprec->refobjsubid == 0 &&
760 deprec->deptype == DEPENDENCY_INTERNAL)
761 {
762 constraintId = deprec->refobjid;
763 break;
764 }
765 }
766
767 systable_endscan(scan);
768 heap_close(depRel, AccessShareLock);
769
770 return constraintId;
771 }
772