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
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 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 */
137 void
recordDependencyOnCurrentExtension(const ObjectAddress * object,bool isReplace)138 recordDependencyOnCurrentExtension(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 */
189 long
deleteDependencyRecordsFor(Oid classId,Oid objectId,bool skipExtensionDeps)190 deleteDependencyRecordsFor(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 */
239 long
deleteDependencyRecordsForClass(Oid classId,Oid objectId,Oid refclassId,char deptype)240 deleteDependencyRecordsForClass(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 */
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 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 */
406 long
changeDependenciesOf(Oid classId,Oid oldObjectId,Oid newObjectId)407 changeDependenciesOf(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 */
462 long
changeDependenciesOn(Oid refClassId,Oid oldRefObjectId,Oid newRefObjectId)463 changeDependenciesOn(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 */
551 static bool
isObjectPinned(const ObjectAddress * object,Relation rel)552 isObjectPinned(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 */
609 Oid
getExtensionOfObject(Oid classId,Oid objectId)610 getExtensionOfObject(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 * Return (possibly NIL) list of extensions that the given object depends on
653 * in DEPENDENCY_AUTO_EXTENSION mode.
654 */
655 List *
getAutoExtensionsOfObject(Oid classId,Oid objectId)656 getAutoExtensionsOfObject(Oid classId, Oid objectId)
657 {
658 List *result = NIL;
659 Relation depRel;
660 ScanKeyData key[2];
661 SysScanDesc scan;
662 HeapTuple tup;
663
664 depRel = table_open(DependRelationId, AccessShareLock);
665
666 ScanKeyInit(&key[0],
667 Anum_pg_depend_classid,
668 BTEqualStrategyNumber, F_OIDEQ,
669 ObjectIdGetDatum(classId));
670 ScanKeyInit(&key[1],
671 Anum_pg_depend_objid,
672 BTEqualStrategyNumber, F_OIDEQ,
673 ObjectIdGetDatum(objectId));
674
675 scan = systable_beginscan(depRel, DependDependerIndexId, true,
676 NULL, 2, key);
677
678 while (HeapTupleIsValid((tup = systable_getnext(scan))))
679 {
680 Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
681
682 if (depform->refclassid == ExtensionRelationId &&
683 depform->deptype == DEPENDENCY_AUTO_EXTENSION)
684 result = lappend_oid(result, depform->refobjid);
685 }
686
687 systable_endscan(scan);
688
689 table_close(depRel, AccessShareLock);
690
691 return result;
692 }
693
694 /*
695 * Detect whether a sequence is marked as "owned" by a column
696 *
697 * An ownership marker is an AUTO or INTERNAL dependency from the sequence to the
698 * column. If we find one, store the identity of the owning column
699 * into *tableId and *colId and return true; else return false.
700 *
701 * Note: if there's more than one such pg_depend entry then you get
702 * a random one of them returned into the out parameters. This should
703 * not happen, though.
704 */
705 bool
sequenceIsOwned(Oid seqId,char deptype,Oid * tableId,int32 * colId)706 sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
707 {
708 bool ret = false;
709 Relation depRel;
710 ScanKeyData key[2];
711 SysScanDesc scan;
712 HeapTuple tup;
713
714 depRel = table_open(DependRelationId, AccessShareLock);
715
716 ScanKeyInit(&key[0],
717 Anum_pg_depend_classid,
718 BTEqualStrategyNumber, F_OIDEQ,
719 ObjectIdGetDatum(RelationRelationId));
720 ScanKeyInit(&key[1],
721 Anum_pg_depend_objid,
722 BTEqualStrategyNumber, F_OIDEQ,
723 ObjectIdGetDatum(seqId));
724
725 scan = systable_beginscan(depRel, DependDependerIndexId, true,
726 NULL, 2, key);
727
728 while (HeapTupleIsValid((tup = systable_getnext(scan))))
729 {
730 Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
731
732 if (depform->refclassid == RelationRelationId &&
733 depform->deptype == deptype)
734 {
735 *tableId = depform->refobjid;
736 *colId = depform->refobjsubid;
737 ret = true;
738 break; /* no need to keep scanning */
739 }
740 }
741
742 systable_endscan(scan);
743
744 table_close(depRel, AccessShareLock);
745
746 return ret;
747 }
748
749 /*
750 * Collect a list of OIDs of all sequences owned by the specified relation,
751 * and column if specified.
752 */
753 List *
getOwnedSequences(Oid relid,AttrNumber attnum)754 getOwnedSequences(Oid relid, AttrNumber attnum)
755 {
756 List *result = NIL;
757 Relation depRel;
758 ScanKeyData key[3];
759 SysScanDesc scan;
760 HeapTuple tup;
761
762 depRel = table_open(DependRelationId, AccessShareLock);
763
764 ScanKeyInit(&key[0],
765 Anum_pg_depend_refclassid,
766 BTEqualStrategyNumber, F_OIDEQ,
767 ObjectIdGetDatum(RelationRelationId));
768 ScanKeyInit(&key[1],
769 Anum_pg_depend_refobjid,
770 BTEqualStrategyNumber, F_OIDEQ,
771 ObjectIdGetDatum(relid));
772 if (attnum)
773 ScanKeyInit(&key[2],
774 Anum_pg_depend_refobjsubid,
775 BTEqualStrategyNumber, F_INT4EQ,
776 Int32GetDatum(attnum));
777
778 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
779 NULL, attnum ? 3 : 2, key);
780
781 while (HeapTupleIsValid(tup = systable_getnext(scan)))
782 {
783 Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
784
785 /*
786 * We assume any auto or internal dependency of a sequence on a column
787 * must be what we are looking for. (We need the relkind test because
788 * indexes can also have auto dependencies on columns.)
789 */
790 if (deprec->classid == RelationRelationId &&
791 deprec->objsubid == 0 &&
792 deprec->refobjsubid != 0 &&
793 (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
794 get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
795 {
796 result = lappend_oid(result, deprec->objid);
797 }
798 }
799
800 systable_endscan(scan);
801
802 table_close(depRel, AccessShareLock);
803
804 return result;
805 }
806
807 /*
808 * Get owned sequence, error if not exactly one.
809 */
810 Oid
getOwnedSequence(Oid relid,AttrNumber attnum)811 getOwnedSequence(Oid relid, AttrNumber attnum)
812 {
813 List *seqlist = getOwnedSequences(relid, attnum);
814
815 if (list_length(seqlist) > 1)
816 elog(ERROR, "more than one owned sequence found");
817 else if (list_length(seqlist) < 1)
818 elog(ERROR, "no owned sequence found");
819
820 return linitial_oid(seqlist);
821 }
822
823 /*
824 * get_constraint_index
825 * Given the OID of a unique, primary-key, or exclusion constraint,
826 * return the OID of the underlying index.
827 *
828 * Return InvalidOid if the index couldn't be found; this suggests the
829 * given OID is bogus, but we leave it to caller to decide what to do.
830 */
831 Oid
get_constraint_index(Oid constraintId)832 get_constraint_index(Oid constraintId)
833 {
834 Oid indexId = InvalidOid;
835 Relation depRel;
836 ScanKeyData key[3];
837 SysScanDesc scan;
838 HeapTuple tup;
839
840 /* Search the dependency table for the dependent index */
841 depRel = table_open(DependRelationId, AccessShareLock);
842
843 ScanKeyInit(&key[0],
844 Anum_pg_depend_refclassid,
845 BTEqualStrategyNumber, F_OIDEQ,
846 ObjectIdGetDatum(ConstraintRelationId));
847 ScanKeyInit(&key[1],
848 Anum_pg_depend_refobjid,
849 BTEqualStrategyNumber, F_OIDEQ,
850 ObjectIdGetDatum(constraintId));
851 ScanKeyInit(&key[2],
852 Anum_pg_depend_refobjsubid,
853 BTEqualStrategyNumber, F_INT4EQ,
854 Int32GetDatum(0));
855
856 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
857 NULL, 3, key);
858
859 while (HeapTupleIsValid(tup = systable_getnext(scan)))
860 {
861 Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
862
863 /*
864 * We assume any internal dependency of an index on the constraint
865 * must be what we are looking for.
866 */
867 if (deprec->classid == RelationRelationId &&
868 deprec->objsubid == 0 &&
869 deprec->deptype == DEPENDENCY_INTERNAL)
870 {
871 char relkind = get_rel_relkind(deprec->objid);
872
873 /*
874 * This is pure paranoia; there shouldn't be any other relkinds
875 * dependent on a constraint.
876 */
877 if (relkind != RELKIND_INDEX &&
878 relkind != RELKIND_PARTITIONED_INDEX)
879 continue;
880
881 indexId = deprec->objid;
882 break;
883 }
884 }
885
886 systable_endscan(scan);
887 table_close(depRel, AccessShareLock);
888
889 return indexId;
890 }
891
892 /*
893 * get_index_constraint
894 * Given the OID of an index, return the OID of the owning unique,
895 * primary-key, or exclusion constraint, or InvalidOid if there
896 * is no owning constraint.
897 */
898 Oid
get_index_constraint(Oid indexId)899 get_index_constraint(Oid indexId)
900 {
901 Oid constraintId = InvalidOid;
902 Relation depRel;
903 ScanKeyData key[3];
904 SysScanDesc scan;
905 HeapTuple tup;
906
907 /* Search the dependency table for the index */
908 depRel = table_open(DependRelationId, AccessShareLock);
909
910 ScanKeyInit(&key[0],
911 Anum_pg_depend_classid,
912 BTEqualStrategyNumber, F_OIDEQ,
913 ObjectIdGetDatum(RelationRelationId));
914 ScanKeyInit(&key[1],
915 Anum_pg_depend_objid,
916 BTEqualStrategyNumber, F_OIDEQ,
917 ObjectIdGetDatum(indexId));
918 ScanKeyInit(&key[2],
919 Anum_pg_depend_objsubid,
920 BTEqualStrategyNumber, F_INT4EQ,
921 Int32GetDatum(0));
922
923 scan = systable_beginscan(depRel, DependDependerIndexId, true,
924 NULL, 3, key);
925
926 while (HeapTupleIsValid(tup = systable_getnext(scan)))
927 {
928 Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
929
930 /*
931 * We assume any internal dependency on a constraint must be what we
932 * are looking for.
933 */
934 if (deprec->refclassid == ConstraintRelationId &&
935 deprec->refobjsubid == 0 &&
936 deprec->deptype == DEPENDENCY_INTERNAL)
937 {
938 constraintId = deprec->refobjid;
939 break;
940 }
941 }
942
943 systable_endscan(scan);
944 table_close(depRel, AccessShareLock);
945
946 return constraintId;
947 }
948
949 /*
950 * get_index_ref_constraints
951 * Given the OID of an index, return the OID of all foreign key
952 * constraints which reference the index.
953 */
954 List *
get_index_ref_constraints(Oid indexId)955 get_index_ref_constraints(Oid indexId)
956 {
957 List *result = NIL;
958 Relation depRel;
959 ScanKeyData key[3];
960 SysScanDesc scan;
961 HeapTuple tup;
962
963 /* Search the dependency table for the index */
964 depRel = table_open(DependRelationId, AccessShareLock);
965
966 ScanKeyInit(&key[0],
967 Anum_pg_depend_refclassid,
968 BTEqualStrategyNumber, F_OIDEQ,
969 ObjectIdGetDatum(RelationRelationId));
970 ScanKeyInit(&key[1],
971 Anum_pg_depend_refobjid,
972 BTEqualStrategyNumber, F_OIDEQ,
973 ObjectIdGetDatum(indexId));
974 ScanKeyInit(&key[2],
975 Anum_pg_depend_refobjsubid,
976 BTEqualStrategyNumber, F_INT4EQ,
977 Int32GetDatum(0));
978
979 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
980 NULL, 3, key);
981
982 while (HeapTupleIsValid(tup = systable_getnext(scan)))
983 {
984 Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
985
986 /*
987 * We assume any normal dependency from a constraint must be what we
988 * are looking for.
989 */
990 if (deprec->classid == ConstraintRelationId &&
991 deprec->objsubid == 0 &&
992 deprec->deptype == DEPENDENCY_NORMAL)
993 {
994 result = lappend_oid(result, deprec->objid);
995 }
996 }
997
998 systable_endscan(scan);
999 table_close(depRel, AccessShareLock);
1000
1001 return result;
1002 }
1003