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