diff --git a/src/PostgREST/Error.hs b/src/PostgREST/Error.hs index a1dbd5927..6df900d42 100644 --- a/src/PostgREST/Error.hs +++ b/src/PostgREST/Error.hs @@ -369,7 +369,7 @@ compressedRel Relationship{..} = "cardinality" .= ("many-to-one" :: Text) , "relationship" .= (cons <> " using " <> qiName relTable <> fmtEls (fst <$> relColumns) <> " and " <> qiName relForeignTable <> fmtEls (snd <$> relColumns)) ] - O2O cons relColumns -> [ + O2O cons relColumns _ -> [ "cardinality" .= ("one-to-one" :: Text) , "relationship" .= (cons <> " using " <> qiName relTable <> fmtEls (fst <$> relColumns) <> " and " <> qiName relForeignTable <> fmtEls (snd <$> relColumns)) ] @@ -386,7 +386,7 @@ relHint rels = T.intercalate ", " (hintList <$> rels) case relCardinality of M2M Junction{..} -> buildHint (qiName junTable) M2O cons _ -> buildHint cons - O2O cons _ -> buildHint cons + O2O cons _ _ -> buildHint cons O2M cons _ -> buildHint cons -- An ambiguousness error cannot happen for computed relationships TODO refactor so this mempty is not needed hintList ComputedRelationship{} = mempty diff --git a/src/PostgREST/Plan.hs b/src/PostgREST/Plan.hs index 4639149eb..db52d3633 100644 --- a/src/PostgREST/Plan.hs +++ b/src/PostgREST/Plan.hs @@ -508,7 +508,7 @@ getJoinConditions tblAlias parentAlias Relationship{relTable=qi,relForeignTable= toJoinCondition parentAlias tblAlias tN ftN <$> cols M2O _ cols -> toJoinCondition parentAlias tblAlias tN ftN <$> cols - O2O _ cols -> + O2O _ cols _ -> toJoinCondition parentAlias tblAlias tN ftN <$> cols where QualifiedIdentifier{qiSchema=tSchema, qiName=tN} = qi @@ -533,20 +533,20 @@ findRel schema allRels origin target hint = rs -> Left $ AmbiguousRelBetween origin target rs where matchFKSingleCol hint_ card = case card of - O2M _ [(col, _)] -> hint_ == col - M2O _ [(col, _)] -> hint_ == col - O2O _ [(col, _)] -> hint_ == col - _ -> False + O2M{relColumns=[(col, _)]} -> hint_ == col + M2O{relColumns=[(col, _)]} -> hint_ == col + O2O{relColumns=[(col, _)]} -> hint_ == col + _ -> False matchFKRefSingleCol hint_ card = case card of - O2M _ [(_, fCol)] -> hint_ == fCol - M2O _ [(_, fCol)] -> hint_ == fCol - O2O _ [(_, fCol)] -> hint_ == fCol - _ -> False + O2M{relColumns=[(_, fCol)]} -> hint_ == fCol + M2O{relColumns=[(_, fCol)]} -> hint_ == fCol + O2O{relColumns=[(_, fCol)]} -> hint_ == fCol + _ -> False matchConstraint tar card = case card of - O2M cons _ -> tar == cons - M2O cons _ -> tar == cons - O2O cons _ -> tar == cons - _ -> False + O2M{relCons} -> tar == relCons + M2O{relCons} -> tar == relCons + O2O{relCons} -> tar == relCons + _ -> False matchJunction hint_ card = case card of M2M Junction{junTable} -> hint_ == qiName junTable _ -> False @@ -990,7 +990,7 @@ inferColsEmbedNeeds (Node ReadPlan{select} forest) pkCols Just $ fst <$> cols Node ReadPlan{relToParent=Just Relationship{relCardinality=M2O _ cols}} _ -> Just $ fst <$> cols - Node ReadPlan{relToParent=Just Relationship{relCardinality=O2O _ cols}} _ -> + Node ReadPlan{relToParent=Just Relationship{relCardinality=O2O _ cols _}} _ -> Just $ fst <$> cols Node ReadPlan{relToParent=Just Relationship{relCardinality=M2M Junction{junColsSource=cols}}} _ -> Just $ fst <$> cols diff --git a/src/PostgREST/Response/OpenAPI.hs b/src/PostgREST/Response/OpenAPI.hs index 8d939cd14..d93f34236 100644 --- a/src/PostgREST/Response/OpenAPI.hs +++ b/src/PostgREST/Response/OpenAPI.hs @@ -114,9 +114,9 @@ makeProperty tbl rels col = (colName col, Inline s) relsSortedByIsView = sortOn relFTableIsView [ r | r@Relationship{} <- searchedRels] -- Finds the relationship that has a single column foreign key rel = find (\case - Relationship{relCardinality=(M2O _ relColumns)} -> [colName col] == (fst <$> relColumns) - Relationship{relCardinality=(O2O _ relColumns)} -> [colName col] == (fst <$> relColumns) - _ -> False + Relationship{relCardinality=(M2O _ relColumns)} -> [colName col] == (fst <$> relColumns) + Relationship{relCardinality=(O2O _ relColumns False)} -> [colName col] == (fst <$> relColumns) + _ -> False ) relsSortedByIsView fCol = (headMay . (\r -> snd <$> relColumns (relCardinality r)) =<< rel) fTbl = qiName . relForeignTable <$> rel diff --git a/src/PostgREST/SchemaCache.hs b/src/PostgREST/SchemaCache.hs index f1f28ce7e..3811a7b8e 100644 --- a/src/PostgREST/SchemaCache.hs +++ b/src/PostgREST/SchemaCache.hs @@ -252,7 +252,7 @@ decodeRels :: HD.Result [Relationship] decodeRels = HD.rowList relRow where - relRow = (\(qi1, qi2, isSelf, constr, cols, isOneToOne) -> Relationship qi1 qi2 isSelf ((if isOneToOne then O2O else M2O) constr cols) False False) <$> row + relRow = (\(qi1, qi2, isSelf, constr, cols, isOneToOne)-> Relationship qi1 qi2 isSelf (if isOneToOne then O2O constr cols False else M2O constr cols) False False) <$> row row = (,,,,,) <$> (QualifiedIdentifier <$> column HD.text <*> column HD.text) <*> @@ -518,13 +518,14 @@ addViewM2OAndO2ORels :: [ViewKeyDependency] -> [Relationship] -> [Relationship] addViewM2OAndO2ORels keyDeps rels = rels ++ concatMap viewRels rels where - isM2O card = case card of {M2O _ _ -> True; _ -> False;} - isO2O card = case card of {O2O _ _ -> True; _ -> False;} + isM2O card = case card of {M2O _ _ -> True; _ -> False;} + isO2O card = case card of {O2O _ _ False -> True; _ -> False;} viewRels Relationship{relTable,relForeignTable,relCardinality=card} = if isM2O card || isO2O card then let cons = relCons card relCols = relColumns card + buildCard cns cls = if isM2O card then M2O cns cls else O2O cns cls False viewTableRels = filter (\ViewKeyDependency{keyDepTable, keyDepCons, keyDepType} -> keyDepTable == relTable && keyDepCons == cons && keyDepType == FKDep) keyDeps tableViewRels = filter (\ViewKeyDependency{keyDepTable, keyDepCons, keyDepType} -> keyDepTable == relForeignTable && keyDepCons == cons && keyDepType == FKDepRef) keyDeps in @@ -532,7 +533,7 @@ addViewM2OAndO2ORels keyDeps rels = (keyDepView vwTbl) relForeignTable False - ((if isM2O card then M2O else O2O) cons $ zipWith (\(_, vCol) (_, fCol)-> (vCol, fCol)) keyDepColsVwTbl relCols) + (buildCard cons $ zipWith (\(_, vCol) (_, fCol)-> (vCol, fCol)) keyDepColsVwTbl relCols) True False | vwTbl <- viewTableRels @@ -542,7 +543,7 @@ addViewM2OAndO2ORels keyDeps rels = relTable (keyDepView tblVw) False - ((if isM2O card then M2O else O2O) cons $ zipWith (\(tCol, _) (_, vCol) -> (tCol, vCol)) relCols keyDepColsTblVw) + (buildCard cons $ zipWith (\(tCol, _) (_, vCol) -> (tCol, vCol)) relCols keyDepColsTblVw) False True | tblVw <- tableViewRels @@ -557,7 +558,7 @@ addViewM2OAndO2ORels keyDeps rels = vw1 vw2 (vw1 == vw2) - ((if isM2O card then M2O else O2O) cons $ zipWith (\(_, vcol1) (_, vcol2) -> (vcol1, vcol2)) keyDepColsVwTbl keyDepColsTblVw) + (buildCard cons $ zipWith (\(_, vcol1) (_, vcol2) -> (vcol1, vcol2)) keyDepColsVwTbl keyDepColsTblVw) True True | vwTbl <- viewTableRels @@ -572,7 +573,7 @@ addInverseRels :: [Relationship] -> [Relationship] addInverseRels rels = rels ++ [ Relationship ft t isSelf (O2M cons (swap <$> cols)) fTableIsView tableIsView | Relationship t ft isSelf (M2O cons cols) tableIsView fTableIsView <- rels ] ++ - [ Relationship ft t isSelf (O2O cons (swap <$> cols)) fTableIsView tableIsView | Relationship t ft isSelf (O2O cons cols) tableIsView fTableIsView <- rels ] + [ Relationship ft t isSelf (O2O cons (swap <$> cols) (not isParent)) fTableIsView tableIsView | Relationship t ft isSelf (O2O cons cols isParent) tableIsView fTableIsView <- rels ] -- | Adds a m2m relationship if a table has FKs to two other tables and the FK columns are part of the PK columns addM2MRels :: TablesMap -> [Relationship] -> [Relationship] diff --git a/src/PostgREST/SchemaCache/Relationship.hs b/src/PostgREST/SchemaCache/Relationship.hs index 8b7c7c605..f51471237 100644 --- a/src/PostgREST/SchemaCache/Relationship.hs +++ b/src/PostgREST/SchemaCache/Relationship.hs @@ -44,8 +44,8 @@ data Cardinality -- ^ one-to-many | M2O {relCons :: FKConstraint, relColumns :: [(FieldName, FieldName)]} -- ^ many-to-one - | O2O {relCons :: FKConstraint, relColumns :: [(FieldName, FieldName)]} - -- ^ one-to-one, this is a refinement over M2O so operating on it is pretty much the same as M2O + | O2O {relCons :: FKConstraint, relColumns :: [(FieldName, FieldName)], isParent :: Bool} + -- ^ one-to-one, this is a refinement over M2O, operating on it is pretty much the same as M2O when isParent == False | M2M Junction -- ^ many-to-many deriving (Eq, Show, Ord, Generic, JSON.ToJSON) @@ -67,7 +67,7 @@ type RelationshipsMap = HM.HashMap (QualifiedIdentifier, Schema) [Relationship] relIsToOne :: Relationship -> Bool relIsToOne rel = case rel of - Relationship{relCardinality=M2O _ _} -> True - Relationship{relCardinality=O2O _ _} -> True - ComputedRelationship{relToOne=True} -> True - _ -> False + Relationship{relCardinality=M2O {}} -> True + Relationship{relCardinality=O2O {}} -> True + ComputedRelationship{relToOne=True} -> True + _ -> False