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