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