diff --git a/src/PostgREST/DbStructure.hs b/src/PostgREST/DbStructure.hs index 01b7636b37..798abfbcbe 100644 --- a/src/PostgREST/DbStructure.hs +++ b/src/PostgREST/DbStructure.hs @@ -93,17 +93,33 @@ queryDbStructure schemas extraSearchPath prepared = do cRels <- SQL.statement mempty $ allComputedRels prepared let tabsWViewsPks = addViewPrimaryKeys tabs keyDeps - rels = relsToMap $ addO2MRels $ addM2MRels tabsWViewsPks $ addViewM2ORels keyDeps m2oRels - newRels = HM.union (HM.fromList $ map addKey cRels) rels -- TODO: currently replacing all the relationships for a single table + rels = addO2MRels $ addM2MRels tabsWViewsPks $ addViewM2ORels keyDeps m2oRels return $ removeInternal schemas $ DbStructure { dbTables = tabsWViewsPks - , dbRelationships = newRels + , dbRelationships = getOverrideRelationshipsMap rels cRels , dbProcs = procs } + +-- | overrides detected relationships with the computed relationships and gets the RelationshipsMap +getOverrideRelationshipsMap :: [Relationship] -> [Relationship] -> RelationshipsMap +getOverrideRelationshipsMap rels cRels = + sort <$> deformedRelMap patchedRels where - relsToMap = map sort . HM.fromListWith (++) . map addKey - addKey rel = ((relTable rel, qiSchema $ relForeignTable rel), [rel]) + -- there can only be a single (table_type, func_name) pair in a function definition `test.function(table_type)`, so we use HM.fromList to disallow duplicates + computedRels = HM.fromList $ relMapKey <$> cRels + -- here we override the detected relationships with the user computed relationships, HM.union makes sure computedRels prevail + patchedRels = HM.union computedRels (relsMap rels) + relsMap = HM.fromListWith (++) . fmap relMapKey + relMapKey rel = case rel of + Relationship{relTable,relForeignTable} -> ((relTable, relForeignTable), [rel]) + -- we use (relTable, relFunction) as key to override detected relationships with the function name + ComputedRelationShip{relTable,relFunction} -> ((relTable, relFunction), [rel]) + -- Since a relationship is between a table and foreign table, the logical way to index/search is by their table/ftable QualifiedIdentifier + -- However, because we allow searching a relationship by the columns of the foreign key(using the "column as target" disambiguation) we lose the + -- ability to index by the foreign table name, so we deform the key. TODO remove once support for "column as target" is gone. + deformedRelMap = HM.fromListWith (++) . fmap addDeformedRelKey . HM.toList + addDeformedRelKey ((relT, relFT), rls) = ((relT, qiSchema relFT), rls) -- | Remove db objects that belong to an internal schema(not exposed through the API) from the DbStructure. removeInternal :: [Schema] -> DbStructure -> DbStructure diff --git a/src/PostgREST/Request/DbRequestBuilder.hs b/src/PostgREST/Request/DbRequestBuilder.hs index 0715292954..0831b7fc1f 100644 --- a/src/PostgREST/Request/DbRequestBuilder.hs +++ b/src/PostgREST/Request/DbRequestBuilder.hs @@ -190,7 +190,7 @@ findRel schema allRels origin target hint = O2M _ _ -> True _ -> False rels = filter (\case - ComputedRelationShip{relForeignTable} -> target == qiName relForeignTable + ComputedRelationShip{relFunction} -> target == qiName relFunction Relationship{..} -> -- In a self-relationship we have a single foreign key but two relationships with different cardinalities: M2O/O2M. For disambiguation, we use the convention of getting: -- TODO: handle one-to-one and many-to-many self-relationships