mirror of https://github.com/citusdata/citus.git
656 lines
20 KiB
C
656 lines
20 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* foreign_key_relationship.c
|
|
* This file contains functions for creating foreign key relationship graph
|
|
* between distributed tables. Created relationship graph will be hold by
|
|
* a static variable defined in this file until an invalidation comes in.
|
|
*
|
|
* Copyright (c) Citus Data, Inc.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "distributed/pg_version_constants.h"
|
|
|
|
#include "access/genam.h"
|
|
#include "access/htup_details.h"
|
|
#include "access/stratnum.h"
|
|
#include "access/table.h"
|
|
#include "catalog/pg_constraint.h"
|
|
#include "distributed/commands.h"
|
|
#include "distributed/hash_helpers.h"
|
|
#include "distributed/foreign_key_relationship.h"
|
|
#include "distributed/hash_helpers.h"
|
|
#include "distributed/listutils.h"
|
|
#include "distributed/metadata_cache.h"
|
|
#include "distributed/version_compat.h"
|
|
#include "nodes/pg_list.h"
|
|
#include "storage/lockdefs.h"
|
|
#include "utils/catcache.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/hsearch.h"
|
|
#include "common/hashfn.h"
|
|
#include "utils/inval.h"
|
|
#include "utils/memutils.h"
|
|
|
|
|
|
/*
|
|
* ForeignConstraintRelationshipGraph holds the graph data structure for foreign constraint relationship
|
|
* between relations. We will only have single static instance of that struct and it
|
|
* will be invalidated after change on any foreign constraint.
|
|
*/
|
|
typedef struct ForeignConstraintRelationshipGraph
|
|
{
|
|
HTAB *nodeMap;
|
|
bool isValid;
|
|
}ForeignConstraintRelationshipGraph;
|
|
|
|
/*
|
|
* ForeignConstraintRelationshipNode holds the data for each node of the ForeignConstraintRelationshipGraph
|
|
* For each node we have relation id, which is the Oid of that relation, visiting
|
|
* information for that node in the latest DFS and the list of adjacency nodes.
|
|
* Note that we also hold back adjacency nodes for getting referenced node over
|
|
* that one.
|
|
*/
|
|
typedef struct ForeignConstraintRelationshipNode
|
|
{
|
|
Oid relationId;
|
|
List *adjacencyList;
|
|
List *backAdjacencyList;
|
|
}ForeignConstraintRelationshipNode;
|
|
|
|
|
|
/*
|
|
* ForeignConstraintRelationshipEdge will only be used while creating the ForeignConstraintRelationshipGraph.
|
|
* It won't show edge information on the graph, yet will be used in the pre-processing
|
|
* phase.
|
|
*/
|
|
typedef struct ForeignConstraintRelationshipEdge
|
|
{
|
|
Oid referencingRelationOID;
|
|
Oid referencedRelationOID;
|
|
}ForeignConstraintRelationshipEdge;
|
|
|
|
|
|
static ForeignConstraintRelationshipGraph *fConstraintRelationshipGraph = NULL;
|
|
|
|
static List * GetRelationshipNodesForFKeyConnectedRelations(
|
|
ForeignConstraintRelationshipNode *relationshipNode);
|
|
static List * GetAllNeighboursList(ForeignConstraintRelationshipNode *relationshipNode);
|
|
static ForeignConstraintRelationshipNode * GetRelationshipNodeForRelationId(Oid
|
|
relationId,
|
|
bool *isFound);
|
|
static void CreateForeignConstraintRelationshipGraph(void);
|
|
static bool IsForeignConstraintRelationshipGraphValid(void);
|
|
static List * GetNeighbourList(ForeignConstraintRelationshipNode *relationshipNode,
|
|
bool isReferencing);
|
|
static List * GetRelationIdsFromRelationshipNodeList(List *fKeyRelationshipNodeList);
|
|
static void PopulateAdjacencyLists(void);
|
|
static int CompareForeignConstraintRelationshipEdges(const void *leftElement,
|
|
const void *rightElement);
|
|
static void AddForeignConstraintRelationshipEdge(Oid referencingOid, Oid referencedOid);
|
|
static ForeignConstraintRelationshipNode * CreateOrFindNode(HTAB *adjacencyLists, Oid
|
|
relid);
|
|
static List * GetConnectedListHelper(ForeignConstraintRelationshipNode *node,
|
|
bool isReferencing);
|
|
static List * GetForeignConstraintRelationshipHelper(Oid relationId, bool isReferencing);
|
|
|
|
MemoryContext ForeignConstraintRelationshipMemoryContext = NULL;
|
|
|
|
|
|
/*
|
|
* GetForeignKeyConnectedRelationIdList returns a list of relation id's for
|
|
* relations that are connected to relation with relationId via a foreign
|
|
* key graph.
|
|
*/
|
|
List *
|
|
GetForeignKeyConnectedRelationIdList(Oid relationId)
|
|
{
|
|
/* use ShareRowExclusiveLock to prevent concurent foreign key creation */
|
|
LOCKMODE lockMode = ShareRowExclusiveLock;
|
|
Relation relation = try_relation_open(relationId, lockMode);
|
|
if (!RelationIsValid(relation))
|
|
{
|
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("relation with OID %d does not exist",
|
|
relationId)));
|
|
}
|
|
|
|
relation_close(relation, NoLock);
|
|
|
|
bool foundInFKeyGraph = false;
|
|
ForeignConstraintRelationshipNode *relationshipNode =
|
|
GetRelationshipNodeForRelationId(relationId, &foundInFKeyGraph);
|
|
if (!foundInFKeyGraph)
|
|
{
|
|
/*
|
|
* Relation could not be found in foreign key graph, then it has no
|
|
* foreign key relationships.
|
|
*/
|
|
return NIL;
|
|
}
|
|
|
|
List *fKeyConnectedRelationshipNodeList =
|
|
GetRelationshipNodesForFKeyConnectedRelations(relationshipNode);
|
|
List *fKeyConnectedRelationIdList =
|
|
GetRelationIdsFromRelationshipNodeList(fKeyConnectedRelationshipNodeList);
|
|
return fKeyConnectedRelationIdList;
|
|
}
|
|
|
|
|
|
/*
|
|
* ShouldUndistributeCitusLocalTable returns true if given relationId needs
|
|
* to be undistributed. Here we do not undistribute table if it's converted by the user,
|
|
* or connected to a table converted by the user, or a reference table, via foreign keys.
|
|
*/
|
|
bool
|
|
ShouldUndistributeCitusLocalTable(Oid relationId)
|
|
{
|
|
CitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);
|
|
if (!cacheEntry->autoConverted)
|
|
{
|
|
/*
|
|
* The relation is not added to metadata automatically,
|
|
* we shouldn't undistribute it.
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* As we will operate on foreign key connected relations, here we
|
|
* invalidate foreign key graph so that we act on fresh graph.
|
|
*/
|
|
InvalidateForeignKeyGraph();
|
|
|
|
List *fkeyConnectedRelations = GetForeignKeyConnectedRelationIdList(relationId);
|
|
|
|
return !RelationIdListHasReferenceTable(fkeyConnectedRelations);
|
|
}
|
|
|
|
|
|
/*
|
|
* GetRelationshipNodesForFKeyConnectedRelations performs breadth-first search
|
|
* starting from input ForeignConstraintRelationshipNode and returns a list
|
|
* of ForeignConstraintRelationshipNode objects for relations that are connected
|
|
* to given relation node via a foreign key relationhip graph.
|
|
*/
|
|
static List *
|
|
GetRelationshipNodesForFKeyConnectedRelations(
|
|
ForeignConstraintRelationshipNode *relationshipNode)
|
|
{
|
|
HTAB *oidVisitedMap = CreateSimpleHashSetWithName(Oid, "oid visited hash set");
|
|
|
|
VisitOid(oidVisitedMap, relationshipNode->relationId);
|
|
List *relationshipNodeList = list_make1(relationshipNode);
|
|
|
|
ForeignConstraintRelationshipNode *currentNode = NULL;
|
|
foreach_ptr_append(currentNode, relationshipNodeList)
|
|
{
|
|
List *allNeighboursList = GetAllNeighboursList(currentNode);
|
|
ForeignConstraintRelationshipNode *neighbourNode = NULL;
|
|
foreach_ptr(neighbourNode, allNeighboursList)
|
|
{
|
|
Oid neighbourRelationId = neighbourNode->relationId;
|
|
if (OidVisited(oidVisitedMap, neighbourRelationId))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
VisitOid(oidVisitedMap, neighbourRelationId);
|
|
relationshipNodeList = lappend(relationshipNodeList, neighbourNode);
|
|
}
|
|
}
|
|
|
|
return relationshipNodeList;
|
|
}
|
|
|
|
|
|
/*
|
|
* GetAllNeighboursList returns a list of ForeignConstraintRelationshipNode
|
|
* objects by concatenating both (referencing & referenced) adjacency lists
|
|
* of given relationship node.
|
|
*/
|
|
static List *
|
|
GetAllNeighboursList(ForeignConstraintRelationshipNode *relationshipNode)
|
|
{
|
|
bool isReferencing = false;
|
|
List *referencedNeighboursList = GetNeighbourList(relationshipNode, isReferencing);
|
|
|
|
isReferencing = true;
|
|
List *referencingNeighboursList = GetNeighbourList(relationshipNode, isReferencing);
|
|
|
|
/*
|
|
* GetNeighbourList returns list from graph as is, so first copy it as
|
|
* list_concat might invalidate it.
|
|
*/
|
|
List *allNeighboursList = list_copy(referencedNeighboursList);
|
|
allNeighboursList = list_concat_unique_ptr(allNeighboursList,
|
|
referencingNeighboursList);
|
|
return allNeighboursList;
|
|
}
|
|
|
|
|
|
/*
|
|
* ReferencedRelationIdList is a wrapper function around GetForeignConstraintRelationshipHelper
|
|
* to get list of relation IDs which are referenced by the given relation id.
|
|
*
|
|
* Note that, if relation A is referenced by relation B and relation B is referenced
|
|
* by relation C, then the result list for relation A consists of the relation
|
|
* IDs of relation B and relation C.
|
|
*/
|
|
List *
|
|
ReferencedRelationIdList(Oid relationId)
|
|
{
|
|
return GetForeignConstraintRelationshipHelper(relationId, false);
|
|
}
|
|
|
|
|
|
/*
|
|
* ReferencingRelationIdList is a wrapper function around GetForeignConstraintRelationshipHelper
|
|
* to get list of relation IDs which are referencing to given relation id.
|
|
*
|
|
* Note that, if relation A is referenced by relation B and relation B is referenced
|
|
* by relation C, then the result list for relation C consists of the relation
|
|
* IDs of relation A and relation B.
|
|
*/
|
|
List *
|
|
ReferencingRelationIdList(Oid relationId)
|
|
{
|
|
return GetForeignConstraintRelationshipHelper(relationId, true);
|
|
}
|
|
|
|
|
|
/*
|
|
* GetForeignConstraintRelationshipHelper returns the list of oids referenced or
|
|
* referencing given relation id. It is a helper function for providing results
|
|
* to public functions ReferencedRelationIdList and ReferencingRelationIdList.
|
|
*/
|
|
static List *
|
|
GetForeignConstraintRelationshipHelper(Oid relationId, bool isReferencing)
|
|
{
|
|
bool isFound = false;
|
|
ForeignConstraintRelationshipNode *relationshipNode =
|
|
GetRelationshipNodeForRelationId(relationId, &isFound);
|
|
|
|
if (!isFound)
|
|
{
|
|
/*
|
|
* If there is no node with the given relation id, that means given table
|
|
* is not referencing and is not referenced by any table
|
|
*/
|
|
return NIL;
|
|
}
|
|
|
|
List *connectedNodeList = GetConnectedListHelper(relationshipNode, isReferencing);
|
|
List *relationIdList = GetRelationIdsFromRelationshipNodeList(connectedNodeList);
|
|
return relationIdList;
|
|
}
|
|
|
|
|
|
/*
|
|
* GetRelationshipNodeForRelationId searches foreign key graph for relation
|
|
* with relationId and returns ForeignConstraintRelationshipNode object for
|
|
* relation if it exists in graph. Otherwise, sets isFound to false.
|
|
*
|
|
* Also before searching foreign key graph, this function implicitly builds
|
|
* foreign key graph if it's invalid or not built yet.
|
|
*/
|
|
static ForeignConstraintRelationshipNode *
|
|
GetRelationshipNodeForRelationId(Oid relationId, bool *isFound)
|
|
{
|
|
CreateForeignConstraintRelationshipGraph();
|
|
|
|
ForeignConstraintRelationshipNode *relationshipNode =
|
|
(ForeignConstraintRelationshipNode *) hash_search(
|
|
fConstraintRelationshipGraph->nodeMap, &relationId,
|
|
HASH_FIND, isFound);
|
|
|
|
return relationshipNode;
|
|
}
|
|
|
|
|
|
/*
|
|
* CreateForeignConstraintRelationshipGraph creates the foreign constraint relation graph using
|
|
* foreign constraint provided by pg_constraint metadata table.
|
|
*/
|
|
static void
|
|
CreateForeignConstraintRelationshipGraph()
|
|
{
|
|
/* if we have already created the graph, use it */
|
|
if (IsForeignConstraintRelationshipGraphValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Lazily create our memory context once and reset on every reuse.
|
|
* Since we have cleared and invalidated the fConstraintRelationshipGraph, right
|
|
* before we can simply reset the context if it was already existing.
|
|
*/
|
|
if (ForeignConstraintRelationshipMemoryContext == NULL)
|
|
{
|
|
/* make sure we've initialized CacheMemoryContext */
|
|
if (CacheMemoryContext == NULL)
|
|
{
|
|
CreateCacheMemoryContext();
|
|
}
|
|
|
|
ForeignConstraintRelationshipMemoryContext = AllocSetContextCreate(
|
|
CacheMemoryContext,
|
|
"Foreign Constraint Relationship Graph Context",
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
}
|
|
else
|
|
{
|
|
fConstraintRelationshipGraph = NULL;
|
|
MemoryContextReset(ForeignConstraintRelationshipMemoryContext);
|
|
}
|
|
|
|
Assert(fConstraintRelationshipGraph == NULL);
|
|
|
|
MemoryContext oldContext = MemoryContextSwitchTo(
|
|
ForeignConstraintRelationshipMemoryContext);
|
|
|
|
fConstraintRelationshipGraph = (ForeignConstraintRelationshipGraph *) palloc(
|
|
sizeof(ForeignConstraintRelationshipGraph));
|
|
fConstraintRelationshipGraph->isValid = false;
|
|
|
|
fConstraintRelationshipGraph->nodeMap = CreateSimpleHash(Oid,
|
|
ForeignConstraintRelationshipNode);
|
|
|
|
PopulateAdjacencyLists();
|
|
|
|
fConstraintRelationshipGraph->isValid = true;
|
|
MemoryContextSwitchTo(oldContext);
|
|
}
|
|
|
|
|
|
/*
|
|
* IsForeignConstraintGraphValid check whether there is a valid graph.
|
|
*/
|
|
static bool
|
|
IsForeignConstraintRelationshipGraphValid()
|
|
{
|
|
/*
|
|
* We might have some concurrent metadata changes. In order to get the changes,
|
|
* we first need to accept the cache invalidation messages.
|
|
*/
|
|
AcceptInvalidationMessages();
|
|
|
|
if (fConstraintRelationshipGraph != NULL && fConstraintRelationshipGraph->isValid)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* SetForeignConstraintGraphInvalid sets the validity of the graph to false.
|
|
*/
|
|
void
|
|
SetForeignConstraintRelationshipGraphInvalid()
|
|
{
|
|
if (fConstraintRelationshipGraph != NULL)
|
|
{
|
|
fConstraintRelationshipGraph->isValid = false;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* GetConnectedListHelper returns list of ForeignConstraintRelationshipNode
|
|
* objects for relations referenced by or referencing to given relation
|
|
* according to isReferencing flag.
|
|
*
|
|
*/
|
|
static List *
|
|
GetConnectedListHelper(ForeignConstraintRelationshipNode *node, bool isReferencing)
|
|
{
|
|
HTAB *oidVisitedMap = CreateSimpleHashSetWithName(Oid, "oid visited hash set");
|
|
|
|
List *connectedNodeList = NIL;
|
|
|
|
List *relationshipNodeStack = list_make1(node);
|
|
while (list_length(relationshipNodeStack) != 0)
|
|
{
|
|
/*
|
|
* Note that this loop considers leftmost element of
|
|
* relationshipNodeStack as top of the stack.
|
|
*/
|
|
|
|
/* pop top element from stack */
|
|
ForeignConstraintRelationshipNode *currentNode = linitial(relationshipNodeStack);
|
|
relationshipNodeStack = list_delete_first(relationshipNodeStack);
|
|
|
|
Oid currentRelationId = currentNode->relationId;
|
|
if (!OidVisited(oidVisitedMap, currentRelationId))
|
|
{
|
|
connectedNodeList = lappend(connectedNodeList, currentNode);
|
|
VisitOid(oidVisitedMap, currentRelationId);
|
|
}
|
|
|
|
List *neighbourList = GetNeighbourList(currentNode, isReferencing);
|
|
ForeignConstraintRelationshipNode *neighbourNode = NULL;
|
|
foreach_ptr(neighbourNode, neighbourList)
|
|
{
|
|
Oid neighbourRelationId = neighbourNode->relationId;
|
|
if (!OidVisited(oidVisitedMap, neighbourRelationId))
|
|
{
|
|
/* push to stack */
|
|
relationshipNodeStack = lcons(neighbourNode, relationshipNodeStack);
|
|
}
|
|
}
|
|
}
|
|
|
|
hash_destroy(oidVisitedMap);
|
|
|
|
/* finally remove yourself from list */
|
|
connectedNodeList = list_delete_first(connectedNodeList);
|
|
return connectedNodeList;
|
|
}
|
|
|
|
|
|
/*
|
|
* OidVisited returns true if given oid is visited according to given oid hash-set.
|
|
*/
|
|
bool
|
|
OidVisited(HTAB *oidVisitedMap, Oid oid)
|
|
{
|
|
bool found = false;
|
|
hash_search(oidVisitedMap, &oid, HASH_FIND, &found);
|
|
return found;
|
|
}
|
|
|
|
|
|
/*
|
|
* VisitOid sets given oid as visited in given hash-set.
|
|
*/
|
|
void
|
|
VisitOid(HTAB *oidVisitedMap, Oid oid)
|
|
{
|
|
bool found = false;
|
|
hash_search(oidVisitedMap, &oid, HASH_ENTER, &found);
|
|
}
|
|
|
|
|
|
/*
|
|
* GetNeighbourList returns copy of relevant adjacency list of given
|
|
* ForeignConstraintRelationshipNode object depending on the isReferencing
|
|
* flag.
|
|
*/
|
|
static List *
|
|
GetNeighbourList(ForeignConstraintRelationshipNode *relationshipNode, bool isReferencing)
|
|
{
|
|
if (isReferencing)
|
|
{
|
|
return relationshipNode->backAdjacencyList;
|
|
}
|
|
else
|
|
{
|
|
return relationshipNode->adjacencyList;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* GetRelationIdsFromRelationshipNodeList returns list of relationId's for
|
|
* given ForeignConstraintRelationshipNode object list.
|
|
*/
|
|
static List *
|
|
GetRelationIdsFromRelationshipNodeList(List *fKeyRelationshipNodeList)
|
|
{
|
|
List *relationIdList = NIL;
|
|
|
|
ForeignConstraintRelationshipNode *fKeyRelationshipNode = NULL;
|
|
foreach_ptr(fKeyRelationshipNode, fKeyRelationshipNodeList)
|
|
{
|
|
Oid relationId = fKeyRelationshipNode->relationId;
|
|
relationIdList = lappend_oid(relationIdList, relationId);
|
|
}
|
|
|
|
return relationIdList;
|
|
}
|
|
|
|
|
|
/*
|
|
* PopulateAdjacencyLists gets foreign constraint relationship information from pg_constraint
|
|
* metadata table and populates them to the foreign constraint relation graph.
|
|
*/
|
|
static void
|
|
PopulateAdjacencyLists(void)
|
|
{
|
|
HeapTuple tuple;
|
|
ScanKeyData scanKey[1];
|
|
int scanKeyCount = 1;
|
|
|
|
Oid prevReferencingOid = InvalidOid;
|
|
Oid prevReferencedOid = InvalidOid;
|
|
List *frelEdgeList = NIL;
|
|
|
|
Relation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);
|
|
|
|
ScanKeyInit(&scanKey[0], Anum_pg_constraint_contype, BTEqualStrategyNumber, F_CHAREQ,
|
|
CharGetDatum(CONSTRAINT_FOREIGN));
|
|
SysScanDesc scanDescriptor = systable_beginscan(pgConstraint, InvalidOid, false,
|
|
NULL, scanKeyCount, scanKey);
|
|
|
|
while (HeapTupleIsValid(tuple = systable_getnext(scanDescriptor)))
|
|
{
|
|
Form_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(tuple);
|
|
|
|
ForeignConstraintRelationshipEdge *currentFConstraintRelationshipEdge = palloc(
|
|
sizeof(ForeignConstraintRelationshipEdge));
|
|
currentFConstraintRelationshipEdge->referencingRelationOID =
|
|
constraintForm->conrelid;
|
|
currentFConstraintRelationshipEdge->referencedRelationOID =
|
|
constraintForm->confrelid;
|
|
|
|
frelEdgeList = lappend(frelEdgeList, currentFConstraintRelationshipEdge);
|
|
}
|
|
|
|
/*
|
|
* Since there is no index on columns we are planning to sort tuples
|
|
* sorting tuples manually instead of using scan keys
|
|
*/
|
|
frelEdgeList = SortList(frelEdgeList, CompareForeignConstraintRelationshipEdges);
|
|
|
|
ForeignConstraintRelationshipEdge *currentFConstraintRelationshipEdge = NULL;
|
|
foreach_ptr(currentFConstraintRelationshipEdge, frelEdgeList)
|
|
{
|
|
/* we just saw this edge, no need to add it twice */
|
|
if (currentFConstraintRelationshipEdge->referencingRelationOID ==
|
|
prevReferencingOid &&
|
|
currentFConstraintRelationshipEdge->referencedRelationOID ==
|
|
prevReferencedOid)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AddForeignConstraintRelationshipEdge(
|
|
currentFConstraintRelationshipEdge->referencingRelationOID,
|
|
currentFConstraintRelationshipEdge->
|
|
referencedRelationOID);
|
|
|
|
prevReferencingOid = currentFConstraintRelationshipEdge->referencingRelationOID;
|
|
prevReferencedOid = currentFConstraintRelationshipEdge->referencedRelationOID;
|
|
}
|
|
|
|
systable_endscan(scanDescriptor);
|
|
table_close(pgConstraint, AccessShareLock);
|
|
}
|
|
|
|
|
|
/*
|
|
* CompareForeignConstraintRelationshipEdges is a helper function to compare two
|
|
* ForeignConstraintRelationshipEdge using referencing and referenced ids respectively.
|
|
*/
|
|
static int
|
|
CompareForeignConstraintRelationshipEdges(const void *leftElement,
|
|
const void *rightElement)
|
|
{
|
|
const ForeignConstraintRelationshipEdge *leftEdge =
|
|
*((const ForeignConstraintRelationshipEdge **) leftElement);
|
|
const ForeignConstraintRelationshipEdge *rightEdge =
|
|
*((const ForeignConstraintRelationshipEdge **) rightElement);
|
|
|
|
int referencingDiff = leftEdge->referencingRelationOID -
|
|
rightEdge->referencingRelationOID;
|
|
int referencedDiff = leftEdge->referencedRelationOID -
|
|
rightEdge->referencedRelationOID;
|
|
|
|
if (referencingDiff != 0)
|
|
{
|
|
return referencingDiff;
|
|
}
|
|
|
|
return referencedDiff;
|
|
}
|
|
|
|
|
|
/*
|
|
* AddForeignConstraintRelationshipEdge adds edge between the nodes having given OIDs
|
|
* by adding referenced node to the adjacency list referencing node and adding
|
|
* referencing node to the back adjacency list of referenced node.
|
|
*/
|
|
static void
|
|
AddForeignConstraintRelationshipEdge(Oid referencingOid, Oid referencedOid)
|
|
{
|
|
ForeignConstraintRelationshipNode *referencingNode = CreateOrFindNode(
|
|
fConstraintRelationshipGraph->nodeMap, referencingOid);
|
|
ForeignConstraintRelationshipNode *referencedNode = CreateOrFindNode(
|
|
fConstraintRelationshipGraph->nodeMap, referencedOid);
|
|
|
|
referencingNode->adjacencyList = lappend(referencingNode->adjacencyList,
|
|
referencedNode);
|
|
referencedNode->backAdjacencyList = lappend(referencedNode->backAdjacencyList,
|
|
referencingNode);
|
|
}
|
|
|
|
|
|
/*
|
|
* CreateOrFindNode either gets or adds new node to the foreign constraint relation graph
|
|
*/
|
|
static ForeignConstraintRelationshipNode *
|
|
CreateOrFindNode(HTAB *adjacencyLists, Oid relid)
|
|
{
|
|
bool found = false;
|
|
ForeignConstraintRelationshipNode *node =
|
|
(ForeignConstraintRelationshipNode *) hash_search(adjacencyLists,
|
|
&relid, HASH_ENTER,
|
|
&found);
|
|
|
|
if (!found)
|
|
{
|
|
node->adjacencyList = NIL;
|
|
node->backAdjacencyList = NIL;
|
|
}
|
|
|
|
return node;
|
|
}
|