1 /*-------------------------------------------------------------------------
2  *
3  * foreign_key_relationship.c
4  *   This file contains functions for creating foreign key relationship graph
5  *   between distributed tables. Created relationship graph will be hold by
6  *   a static variable defined in this file until an invalidation comes in.
7  *
8  * Copyright (c) Citus Data, Inc.
9  *
10  *-------------------------------------------------------------------------
11  */
12 
13 #include "postgres.h"
14 
15 #include "distributed/pg_version_constants.h"
16 
17 #include "access/genam.h"
18 #include "access/htup_details.h"
19 #include "access/stratnum.h"
20 #include "access/table.h"
21 #include "catalog/pg_constraint.h"
22 #include "distributed/commands.h"
23 #include "distributed/foreign_key_relationship.h"
24 #include "distributed/hash_helpers.h"
25 #include "distributed/listutils.h"
26 #include "distributed/metadata_cache.h"
27 #include "distributed/version_compat.h"
28 #include "nodes/pg_list.h"
29 #include "storage/lockdefs.h"
30 #include "utils/fmgroids.h"
31 #include "utils/hsearch.h"
32 #if PG_VERSION_NUM >= PG_VERSION_13
33 #include "common/hashfn.h"
34 #endif
35 #include "utils/inval.h"
36 #include "utils/memutils.h"
37 
38 
39 /*
40  * ForeignConstraintRelationshipGraph holds the graph data structure for foreign constraint relationship
41  * between relations. We will only have single static instance of that struct and it
42  * will be invalidated after change on any foreign constraint.
43  */
44 typedef struct ForeignConstraintRelationshipGraph
45 {
46 	HTAB *nodeMap;
47 	bool isValid;
48 }ForeignConstraintRelationshipGraph;
49 
50 /*
51  * ForeignConstraintRelationshipNode holds the data for each node of the ForeignConstraintRelationshipGraph
52  * For each node we have relation id, which is the Oid of that relation, visiting
53  * information for that node in the latest DFS and the list of adjacency nodes.
54  * Note that we also hold back adjacency nodes for getting referenced node over
55  * that one.
56  */
57 typedef struct ForeignConstraintRelationshipNode
58 {
59 	Oid relationId;
60 	List *adjacencyList;
61 	List *backAdjacencyList;
62 }ForeignConstraintRelationshipNode;
63 
64 
65 /*
66  * ForeignConstraintRelationshipEdge will only be used while creating the ForeignConstraintRelationshipGraph.
67  * It won't show edge information on the graph, yet will be used in the pre-processing
68  * phase.
69  */
70 typedef struct ForeignConstraintRelationshipEdge
71 {
72 	Oid referencingRelationOID;
73 	Oid referencedRelationOID;
74 }ForeignConstraintRelationshipEdge;
75 
76 
77 static ForeignConstraintRelationshipGraph *fConstraintRelationshipGraph = NULL;
78 
79 static List * GetRelationshipNodesForFKeyConnectedRelations(
80 	ForeignConstraintRelationshipNode *relationshipNode);
81 static List * GetAllNeighboursList(ForeignConstraintRelationshipNode *relationshipNode);
82 static ForeignConstraintRelationshipNode * GetRelationshipNodeForRelationId(Oid
83 																			relationId,
84 																			bool *isFound);
85 static void CreateForeignConstraintRelationshipGraph(void);
86 static bool IsForeignConstraintRelationshipGraphValid(void);
87 static List * GetNeighbourList(ForeignConstraintRelationshipNode *relationshipNode,
88 							   bool isReferencing);
89 static List * GetRelationIdsFromRelationshipNodeList(List *fKeyRelationshipNodeList);
90 static void PopulateAdjacencyLists(void);
91 static int CompareForeignConstraintRelationshipEdges(const void *leftElement,
92 													 const void *rightElement);
93 static void AddForeignConstraintRelationshipEdge(Oid referencingOid, Oid referencedOid);
94 static ForeignConstraintRelationshipNode * CreateOrFindNode(HTAB *adjacencyLists, Oid
95 															relid);
96 static List * GetConnectedListHelper(ForeignConstraintRelationshipNode *node,
97 									 bool isReferencing);
98 static List * GetForeignConstraintRelationshipHelper(Oid relationId, bool isReferencing);
99 
100 
101 /*
102  * GetForeignKeyConnectedRelationIdList returns a list of relation id's for
103  * relations that are connected to relation with relationId via a foreign
104  * key graph.
105  */
106 List *
GetForeignKeyConnectedRelationIdList(Oid relationId)107 GetForeignKeyConnectedRelationIdList(Oid relationId)
108 {
109 	/* use ShareRowExclusiveLock to prevent concurent foreign key creation */
110 	LOCKMODE lockMode = ShareRowExclusiveLock;
111 	Relation relation = try_relation_open(relationId, lockMode);
112 	if (!RelationIsValid(relation))
113 	{
114 		ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
115 						errmsg("relation with OID %d does not exist",
116 							   relationId)));
117 	}
118 
119 	relation_close(relation, NoLock);
120 
121 	bool foundInFKeyGraph = false;
122 	ForeignConstraintRelationshipNode *relationshipNode =
123 		GetRelationshipNodeForRelationId(relationId, &foundInFKeyGraph);
124 	if (!foundInFKeyGraph)
125 	{
126 		/*
127 		 * Relation could not be found in foreign key graph, then it has no
128 		 * foreign key relationships.
129 		 */
130 		return NIL;
131 	}
132 
133 	List *fKeyConnectedRelationshipNodeList =
134 		GetRelationshipNodesForFKeyConnectedRelations(relationshipNode);
135 	List *fKeyConnectedRelationIdList =
136 		GetRelationIdsFromRelationshipNodeList(fKeyConnectedRelationshipNodeList);
137 	return fKeyConnectedRelationIdList;
138 }
139 
140 
141 /*
142  * ConnectedToReferenceTableViaFKey returns true if given relationId is
143  * connected to a reference table via its foreign key subgraph.
144  */
145 bool
ConnectedToReferenceTableViaFKey(Oid relationId)146 ConnectedToReferenceTableViaFKey(Oid relationId)
147 {
148 	/*
149 	 * As we will operate on foreign key connected relations, here we
150 	 * invalidate foreign key graph so that we act on fresh graph.
151 	 */
152 	InvalidateForeignKeyGraph();
153 
154 	List *fkeyConnectedRelations = GetForeignKeyConnectedRelationIdList(relationId);
155 	return RelationIdListHasReferenceTable(fkeyConnectedRelations);
156 }
157 
158 
159 /*
160  * GetRelationshipNodesForFKeyConnectedRelations performs breadth-first search
161  * starting from input ForeignConstraintRelationshipNode and returns a list
162  * of ForeignConstraintRelationshipNode objects for relations that are connected
163  * to given relation node via a foreign key relationhip graph.
164  */
165 static List *
GetRelationshipNodesForFKeyConnectedRelations(ForeignConstraintRelationshipNode * relationshipNode)166 GetRelationshipNodesForFKeyConnectedRelations(
167 	ForeignConstraintRelationshipNode *relationshipNode)
168 {
169 	HTAB *oidVisitedMap = CreateOidVisitedHashSet();
170 
171 	VisitOid(oidVisitedMap, relationshipNode->relationId);
172 	List *relationshipNodeList = list_make1(relationshipNode);
173 
174 	ForeignConstraintRelationshipNode *currentNode = NULL;
175 	foreach_ptr_append(currentNode, relationshipNodeList)
176 	{
177 		List *allNeighboursList = GetAllNeighboursList(currentNode);
178 		ForeignConstraintRelationshipNode *neighbourNode = NULL;
179 		foreach_ptr(neighbourNode, allNeighboursList)
180 		{
181 			Oid neighbourRelationId = neighbourNode->relationId;
182 			if (OidVisited(oidVisitedMap, neighbourRelationId))
183 			{
184 				continue;
185 			}
186 
187 			VisitOid(oidVisitedMap, neighbourRelationId);
188 			relationshipNodeList = lappend(relationshipNodeList, neighbourNode);
189 		}
190 	}
191 
192 	return relationshipNodeList;
193 }
194 
195 
196 /*
197  * GetAllNeighboursList returns a list of ForeignConstraintRelationshipNode
198  * objects by concatenating both (referencing & referenced) adjacency lists
199  * of given relationship node.
200  */
201 static List *
GetAllNeighboursList(ForeignConstraintRelationshipNode * relationshipNode)202 GetAllNeighboursList(ForeignConstraintRelationshipNode *relationshipNode)
203 {
204 	bool isReferencing = false;
205 	List *referencedNeighboursList = GetNeighbourList(relationshipNode, isReferencing);
206 
207 	isReferencing = true;
208 	List *referencingNeighboursList = GetNeighbourList(relationshipNode, isReferencing);
209 
210 	/*
211 	 * GetNeighbourList returns list from graph as is, so first copy it as
212 	 * list_concat might invalidate it.
213 	 */
214 	List *allNeighboursList = list_copy(referencedNeighboursList);
215 	allNeighboursList = list_concat_unique_ptr(allNeighboursList,
216 											   referencingNeighboursList);
217 	return allNeighboursList;
218 }
219 
220 
221 /*
222  * ReferencedRelationIdList is a wrapper function around GetForeignConstraintRelationshipHelper
223  * to get list of relation IDs which are referenced by the given relation id.
224  *
225  * Note that, if relation A is referenced by relation B and relation B is referenced
226  * by relation C, then the result list for relation A consists of the relation
227  * IDs of relation B and relation C.
228  */
229 List *
ReferencedRelationIdList(Oid relationId)230 ReferencedRelationIdList(Oid relationId)
231 {
232 	return GetForeignConstraintRelationshipHelper(relationId, false);
233 }
234 
235 
236 /*
237  * ReferencingRelationIdList is a wrapper function around GetForeignConstraintRelationshipHelper
238  * to get list of relation IDs which are referencing to given relation id.
239  *
240  * Note that, if relation A is referenced by relation B and relation B is referenced
241  * by relation C, then the result list for relation C consists of the relation
242  * IDs of relation A and relation B.
243  */
244 List *
ReferencingRelationIdList(Oid relationId)245 ReferencingRelationIdList(Oid relationId)
246 {
247 	return GetForeignConstraintRelationshipHelper(relationId, true);
248 }
249 
250 
251 /*
252  * GetForeignConstraintRelationshipHelper returns the list of oids referenced or
253  * referencing given relation id. It is a helper function for providing results
254  * to public functions ReferencedRelationIdList and ReferencingRelationIdList.
255  */
256 static List *
GetForeignConstraintRelationshipHelper(Oid relationId,bool isReferencing)257 GetForeignConstraintRelationshipHelper(Oid relationId, bool isReferencing)
258 {
259 	bool isFound = false;
260 	ForeignConstraintRelationshipNode *relationshipNode =
261 		GetRelationshipNodeForRelationId(relationId, &isFound);
262 
263 	if (!isFound)
264 	{
265 		/*
266 		 * If there is no node with the given relation id, that means given table
267 		 * is not referencing and is not referenced by any table
268 		 */
269 		return NIL;
270 	}
271 
272 	List *connectedNodeList = GetConnectedListHelper(relationshipNode, isReferencing);
273 	List *relationIdList = GetRelationIdsFromRelationshipNodeList(connectedNodeList);
274 	return relationIdList;
275 }
276 
277 
278 /*
279  * GetRelationshipNodeForRelationId searches foreign key graph for relation
280  * with relationId and returns ForeignConstraintRelationshipNode object for
281  * relation if it exists in graph. Otherwise, sets isFound to false.
282  *
283  * Also before searching foreign key graph, this function implicitly builds
284  * foreign key graph if it's invalid or not built yet.
285  */
286 static ForeignConstraintRelationshipNode *
GetRelationshipNodeForRelationId(Oid relationId,bool * isFound)287 GetRelationshipNodeForRelationId(Oid relationId, bool *isFound)
288 {
289 	CreateForeignConstraintRelationshipGraph();
290 
291 	ForeignConstraintRelationshipNode *relationshipNode =
292 		(ForeignConstraintRelationshipNode *) hash_search(
293 			fConstraintRelationshipGraph->nodeMap, &relationId,
294 			HASH_FIND, isFound);
295 
296 	return relationshipNode;
297 }
298 
299 
300 /*
301  * CreateForeignConstraintRelationshipGraph creates the foreign constraint relation graph using
302  * foreign constraint provided by pg_constraint metadata table.
303  */
304 static void
CreateForeignConstraintRelationshipGraph()305 CreateForeignConstraintRelationshipGraph()
306 {
307 	HASHCTL info;
308 
309 	/* if we have already created the graph, use it */
310 	if (IsForeignConstraintRelationshipGraphValid())
311 	{
312 		return;
313 	}
314 
315 	ClearForeignConstraintRelationshipGraphContext();
316 
317 	MemoryContext fConstraintRelationshipMemoryContext = AllocSetContextCreateExtended(
318 		CacheMemoryContext,
319 		"Forign Constraint Relationship Graph Context",
320 		ALLOCSET_DEFAULT_MINSIZE,
321 		ALLOCSET_DEFAULT_INITSIZE,
322 		ALLOCSET_DEFAULT_MAXSIZE);
323 
324 	MemoryContext oldContext = MemoryContextSwitchTo(
325 		fConstraintRelationshipMemoryContext);
326 
327 	fConstraintRelationshipGraph = (ForeignConstraintRelationshipGraph *) palloc(
328 		sizeof(ForeignConstraintRelationshipGraph));
329 	fConstraintRelationshipGraph->isValid = false;
330 
331 	/* create (oid) -> [ForeignConstraintRelationshipNode] hash */
332 	memset(&info, 0, sizeof(info));
333 	info.keysize = sizeof(Oid);
334 	info.entrysize = sizeof(ForeignConstraintRelationshipNode);
335 	info.hash = oid_hash;
336 	info.hcxt = CurrentMemoryContext;
337 	uint32 hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
338 
339 	fConstraintRelationshipGraph->nodeMap = hash_create(
340 		"foreign key relationship map (oid)",
341 		32, &info, hashFlags);
342 
343 	PopulateAdjacencyLists();
344 
345 	fConstraintRelationshipGraph->isValid = true;
346 	MemoryContextSwitchTo(oldContext);
347 }
348 
349 
350 /*
351  * IsForeignConstraintGraphValid check whether there is a valid graph.
352  */
353 static bool
IsForeignConstraintRelationshipGraphValid()354 IsForeignConstraintRelationshipGraphValid()
355 {
356 	/*
357 	 * We might have some concurrent metadata changes. In order to get the changes,
358 	 * we first need to accept the cache invalidation messages.
359 	 */
360 	AcceptInvalidationMessages();
361 
362 	if (fConstraintRelationshipGraph != NULL && fConstraintRelationshipGraph->isValid)
363 	{
364 		return true;
365 	}
366 
367 	return false;
368 }
369 
370 
371 /*
372  * SetForeignConstraintGraphInvalid sets the validity of the graph to false.
373  */
374 void
SetForeignConstraintRelationshipGraphInvalid()375 SetForeignConstraintRelationshipGraphInvalid()
376 {
377 	if (fConstraintRelationshipGraph != NULL)
378 	{
379 		fConstraintRelationshipGraph->isValid = false;
380 	}
381 }
382 
383 
384 /*
385  * GetConnectedListHelper returns list of ForeignConstraintRelationshipNode
386  * objects for relations referenced by or referencing to given relation
387  * according to isReferencing flag.
388  *
389  */
390 static List *
GetConnectedListHelper(ForeignConstraintRelationshipNode * node,bool isReferencing)391 GetConnectedListHelper(ForeignConstraintRelationshipNode *node, bool isReferencing)
392 {
393 	HTAB *oidVisitedMap = CreateOidVisitedHashSet();
394 
395 	List *connectedNodeList = NIL;
396 
397 	List *relationshipNodeStack = list_make1(node);
398 	while (list_length(relationshipNodeStack) != 0)
399 	{
400 		/*
401 		 * Note that this loop considers leftmost element of
402 		 * relationshipNodeStack as top of the stack.
403 		 */
404 
405 		/* pop top element from stack */
406 		ForeignConstraintRelationshipNode *currentNode = linitial(relationshipNodeStack);
407 		relationshipNodeStack = list_delete_first(relationshipNodeStack);
408 
409 		Oid currentRelationId = currentNode->relationId;
410 		if (!OidVisited(oidVisitedMap, currentRelationId))
411 		{
412 			connectedNodeList = lappend(connectedNodeList, currentNode);
413 			VisitOid(oidVisitedMap, currentRelationId);
414 		}
415 
416 		List *neighbourList = GetNeighbourList(currentNode, isReferencing);
417 		ForeignConstraintRelationshipNode *neighbourNode = NULL;
418 		foreach_ptr(neighbourNode, neighbourList)
419 		{
420 			Oid neighbourRelationId = neighbourNode->relationId;
421 			if (!OidVisited(oidVisitedMap, neighbourRelationId))
422 			{
423 				/* push to stack */
424 				relationshipNodeStack = lcons(neighbourNode, relationshipNodeStack);
425 			}
426 		}
427 	}
428 
429 	hash_destroy(oidVisitedMap);
430 
431 	/* finally remove yourself from list */
432 	connectedNodeList = list_delete_first(connectedNodeList);
433 	return connectedNodeList;
434 }
435 
436 
437 /*
438  * CreateOidVisitedHashSet creates and returns an hash-set object in
439  * CurrentMemoryContext to store visited oid's.
440  * As hash_create allocates memory in heap, callers are responsible to call
441  * hash_destroy when appropriate.
442  */
443 HTAB *
CreateOidVisitedHashSet(void)444 CreateOidVisitedHashSet(void)
445 {
446 	HASHCTL info = { 0 };
447 
448 	info.keysize = sizeof(Oid);
449 	info.hash = oid_hash;
450 	info.hcxt = CurrentMemoryContext;
451 
452 	/* we don't have value field as it's a set */
453 	info.entrysize = info.keysize;
454 
455 	uint32 hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
456 
457 	HTAB *oidVisitedMap = hash_create("oid visited hash map", 32, &info, hashFlags);
458 	return oidVisitedMap;
459 }
460 
461 
462 /*
463  * OidVisited returns true if given oid is visited according to given oid hash-set.
464  */
465 bool
OidVisited(HTAB * oidVisitedMap,Oid oid)466 OidVisited(HTAB *oidVisitedMap, Oid oid)
467 {
468 	bool found = false;
469 	hash_search(oidVisitedMap, &oid, HASH_FIND, &found);
470 	return found;
471 }
472 
473 
474 /*
475  * VisitOid sets given oid as visited in given hash-set.
476  */
477 void
VisitOid(HTAB * oidVisitedMap,Oid oid)478 VisitOid(HTAB *oidVisitedMap, Oid oid)
479 {
480 	bool found = false;
481 	hash_search(oidVisitedMap, &oid, HASH_ENTER, &found);
482 }
483 
484 
485 /*
486  * GetNeighbourList returns copy of relevant adjacency list of given
487  * ForeignConstraintRelationshipNode object depending on the isReferencing
488  * flag.
489  */
490 static List *
GetNeighbourList(ForeignConstraintRelationshipNode * relationshipNode,bool isReferencing)491 GetNeighbourList(ForeignConstraintRelationshipNode *relationshipNode, bool isReferencing)
492 {
493 	if (isReferencing)
494 	{
495 		return relationshipNode->backAdjacencyList;
496 	}
497 	else
498 	{
499 		return relationshipNode->adjacencyList;
500 	}
501 }
502 
503 
504 /*
505  * GetRelationIdsFromRelationshipNodeList returns list of relationId's for
506  * given ForeignConstraintRelationshipNode object list.
507  */
508 static List *
GetRelationIdsFromRelationshipNodeList(List * fKeyRelationshipNodeList)509 GetRelationIdsFromRelationshipNodeList(List *fKeyRelationshipNodeList)
510 {
511 	List *relationIdList = NIL;
512 
513 	ForeignConstraintRelationshipNode *fKeyRelationshipNode = NULL;
514 	foreach_ptr(fKeyRelationshipNode, fKeyRelationshipNodeList)
515 	{
516 		Oid relationId = fKeyRelationshipNode->relationId;
517 		relationIdList = lappend_oid(relationIdList, relationId);
518 	}
519 
520 	return relationIdList;
521 }
522 
523 
524 /*
525  * PopulateAdjacencyLists gets foreign constraint relationship information from pg_constraint
526  * metadata table and populates them to the foreign constraint relation graph.
527  */
528 static void
PopulateAdjacencyLists(void)529 PopulateAdjacencyLists(void)
530 {
531 	HeapTuple tuple;
532 	ScanKeyData scanKey[1];
533 	int scanKeyCount = 1;
534 
535 	Oid prevReferencingOid = InvalidOid;
536 	Oid prevReferencedOid = InvalidOid;
537 	List *frelEdgeList = NIL;
538 
539 	Relation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);
540 
541 	ScanKeyInit(&scanKey[0], Anum_pg_constraint_contype, BTEqualStrategyNumber, F_CHAREQ,
542 				CharGetDatum(CONSTRAINT_FOREIGN));
543 	SysScanDesc scanDescriptor = systable_beginscan(pgConstraint, InvalidOid, false,
544 													NULL, scanKeyCount, scanKey);
545 
546 	while (HeapTupleIsValid(tuple = systable_getnext(scanDescriptor)))
547 	{
548 		Form_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(tuple);
549 
550 		ForeignConstraintRelationshipEdge *currentFConstraintRelationshipEdge = palloc(
551 			sizeof(ForeignConstraintRelationshipEdge));
552 		currentFConstraintRelationshipEdge->referencingRelationOID =
553 			constraintForm->conrelid;
554 		currentFConstraintRelationshipEdge->referencedRelationOID =
555 			constraintForm->confrelid;
556 
557 		frelEdgeList = lappend(frelEdgeList, currentFConstraintRelationshipEdge);
558 	}
559 
560 	/*
561 	 * Since there is no index on columns we are planning to sort tuples
562 	 * sorting tuples manually instead of using scan keys
563 	 */
564 	frelEdgeList = SortList(frelEdgeList, CompareForeignConstraintRelationshipEdges);
565 
566 	ForeignConstraintRelationshipEdge *currentFConstraintRelationshipEdge = NULL;
567 	foreach_ptr(currentFConstraintRelationshipEdge, frelEdgeList)
568 	{
569 		/* we just saw this edge, no need to add it twice */
570 		if (currentFConstraintRelationshipEdge->referencingRelationOID ==
571 			prevReferencingOid &&
572 			currentFConstraintRelationshipEdge->referencedRelationOID ==
573 			prevReferencedOid)
574 		{
575 			continue;
576 		}
577 
578 		AddForeignConstraintRelationshipEdge(
579 			currentFConstraintRelationshipEdge->referencingRelationOID,
580 			currentFConstraintRelationshipEdge->
581 			referencedRelationOID);
582 
583 		prevReferencingOid = currentFConstraintRelationshipEdge->referencingRelationOID;
584 		prevReferencedOid = currentFConstraintRelationshipEdge->referencedRelationOID;
585 	}
586 
587 	systable_endscan(scanDescriptor);
588 	table_close(pgConstraint, AccessShareLock);
589 }
590 
591 
592 /*
593  * CompareForeignConstraintRelationshipEdges is a helper function to compare two
594  * ForeignConstraintRelationshipEdge using referencing and referenced ids respectively.
595  */
596 static int
CompareForeignConstraintRelationshipEdges(const void * leftElement,const void * rightElement)597 CompareForeignConstraintRelationshipEdges(const void *leftElement,
598 										  const void *rightElement)
599 {
600 	const ForeignConstraintRelationshipEdge *leftEdge =
601 		*((const ForeignConstraintRelationshipEdge **) leftElement);
602 	const ForeignConstraintRelationshipEdge *rightEdge =
603 		*((const ForeignConstraintRelationshipEdge **) rightElement);
604 
605 	int referencingDiff = leftEdge->referencingRelationOID -
606 						  rightEdge->referencingRelationOID;
607 	int referencedDiff = leftEdge->referencedRelationOID -
608 						 rightEdge->referencedRelationOID;
609 
610 	if (referencingDiff != 0)
611 	{
612 		return referencingDiff;
613 	}
614 
615 	return referencedDiff;
616 }
617 
618 
619 /*
620  * AddForeignConstraintRelationshipEdge adds edge between the nodes having given OIDs
621  * by adding referenced node to the adjacency list referencing node and adding
622  * referencing node to the back adjacency list of referenced node.
623  */
624 static void
AddForeignConstraintRelationshipEdge(Oid referencingOid,Oid referencedOid)625 AddForeignConstraintRelationshipEdge(Oid referencingOid, Oid referencedOid)
626 {
627 	ForeignConstraintRelationshipNode *referencingNode = CreateOrFindNode(
628 		fConstraintRelationshipGraph->nodeMap, referencingOid);
629 	ForeignConstraintRelationshipNode *referencedNode = CreateOrFindNode(
630 		fConstraintRelationshipGraph->nodeMap, referencedOid);
631 
632 	referencingNode->adjacencyList = lappend(referencingNode->adjacencyList,
633 											 referencedNode);
634 	referencedNode->backAdjacencyList = lappend(referencedNode->backAdjacencyList,
635 												referencingNode);
636 }
637 
638 
639 /*
640  * CreateOrFindNode either gets or adds new node to the foreign constraint relation graph
641  */
642 static ForeignConstraintRelationshipNode *
CreateOrFindNode(HTAB * adjacencyLists,Oid relid)643 CreateOrFindNode(HTAB *adjacencyLists, Oid relid)
644 {
645 	bool found = false;
646 	ForeignConstraintRelationshipNode *node =
647 		(ForeignConstraintRelationshipNode *) hash_search(adjacencyLists,
648 														  &relid, HASH_ENTER,
649 														  &found);
650 
651 	if (!found)
652 	{
653 		node->adjacencyList = NIL;
654 		node->backAdjacencyList = NIL;
655 	}
656 
657 	return node;
658 }
659 
660 
661 /*
662  * ClearForeignConstraintRelationshipGraphContext clear all the allocated memory obtained
663  * for foreign constraint relationship graph. Since all the variables of relationship
664  * graph was obtained within the same context, destroying hash map is enough as
665  * it deletes the context.
666  */
667 void
ClearForeignConstraintRelationshipGraphContext()668 ClearForeignConstraintRelationshipGraphContext()
669 {
670 	if (fConstraintRelationshipGraph == NULL)
671 	{
672 		return;
673 	}
674 
675 	hash_destroy(fConstraintRelationshipGraph->nodeMap);
676 	fConstraintRelationshipGraph = NULL;
677 }
678