fix: ignore views on col/fk as target
Also refactor self relationship findRel logic
This commit is contained in:
committed by
Steve Chavez
parent
2be63b36d6
commit
f7b173163c
@@ -149,7 +149,9 @@ decodeRels =
|
||||
(QualifiedIdentifier <$> column HD.text <*> column HD.text) <*>
|
||||
(QualifiedIdentifier <$> column HD.text <*> column HD.text) <*>
|
||||
column HD.bool <*>
|
||||
(M2O <$> column HD.text <*> compositeArrayColumn ((,) <$> compositeField HD.text <*> compositeField HD.text))
|
||||
(M2O <$> column HD.text <*> compositeArrayColumn ((,) <$> compositeField HD.text <*> compositeField HD.text)) <*>
|
||||
pure False <*>
|
||||
pure False
|
||||
|
||||
decodeViewKeyDeps :: HD.Result [ViewKeyDependency]
|
||||
decodeViewKeyDeps =
|
||||
@@ -344,6 +346,8 @@ addViewM2ORels keyDeps rels =
|
||||
relForeignTable
|
||||
False
|
||||
(M2O cons $ zipWith (\(_, vCol) (_, fCol)-> (vCol, fCol)) (keyDepCols vwTbl) relColumns)
|
||||
True
|
||||
False
|
||||
| vwTbl <- viewTableM2Os ]
|
||||
++
|
||||
[ Relationship
|
||||
@@ -351,6 +355,8 @@ addViewM2ORels keyDeps rels =
|
||||
(keyDepView tblVw)
|
||||
False
|
||||
(M2O cons $ zipWith (\(tCol, _) (_, vCol) -> (tCol, vCol)) relColumns (keyDepCols tblVw))
|
||||
False
|
||||
True
|
||||
| tblVw <- tableViewM2Os ]
|
||||
++
|
||||
[
|
||||
@@ -363,14 +369,16 @@ addViewM2ORels keyDeps rels =
|
||||
vw2
|
||||
(vw1 == vw2)
|
||||
(M2O cons $ zipWith (\(_, vcol1) (_, vcol2) -> (vcol1, vcol2)) (keyDepCols vwTbl) (keyDepCols tblVw))
|
||||
True
|
||||
True
|
||||
| vwTbl <- viewTableM2Os
|
||||
, tblVw <- tableViewM2Os ]
|
||||
viewRels _ = []
|
||||
|
||||
|
||||
addO2MRels :: [Relationship] -> [Relationship]
|
||||
addO2MRels rels = rels ++ [ Relationship ft t isSelf (O2M cons (swap <$> cols))
|
||||
| Relationship t ft isSelf (M2O cons cols) <- rels ]
|
||||
addO2MRels rels = rels ++ [ Relationship ft t isSelf (O2M cons (swap <$> cols)) fTableIsView tableIsView
|
||||
| Relationship t ft isSelf (M2O cons cols) 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]
|
||||
@@ -379,10 +387,10 @@ addM2MRels tbls rels = rels ++ catMaybes
|
||||
jtCols = S.fromList $ (fst <$> cols) ++ (fst <$> fcols)
|
||||
pkCols = S.fromList $ maybe mempty tablePKCols $ M.lookup jt1 tbls
|
||||
in if S.isSubsetOf jtCols pkCols
|
||||
then Just $ Relationship t ft (t == ft) (M2M $ Junction jt1 cons1 cons2 (swap <$> cols) (swap <$> fcols))
|
||||
then Just $ Relationship t ft (t == ft) (M2M $ Junction jt1 cons1 cons2 (swap <$> cols) (swap <$> fcols)) tblIsView fTblisView
|
||||
else Nothing
|
||||
| Relationship jt1 t _ (M2O cons1 cols) <- rels
|
||||
, Relationship jt2 ft _ (M2O cons2 fcols) <- rels
|
||||
| Relationship jt1 t _ (M2O cons1 cols) _ tblIsView <- rels
|
||||
, Relationship jt2 ft _ (M2O cons2 fcols) _ fTblisView <- rels
|
||||
, jt1 == jt2
|
||||
, cons1 /= cons2]
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ data Relationship = Relationship
|
||||
, relForeignTable :: QualifiedIdentifier
|
||||
, relIsSelf :: Bool -- ^ Whether is a self relationship
|
||||
, relCardinality :: Cardinality
|
||||
, relTableIsView :: Bool
|
||||
, relFTableIsView :: Bool
|
||||
}
|
||||
deriving (Eq, Ord, Generic, JSON.ToJSON)
|
||||
|
||||
|
||||
@@ -133,12 +133,7 @@ addRels schema allRels parentNode (Node (query@Select{from=tbl}, (nodeName, _, a
|
||||
-- /origin?select=target(*) If more than one relationship is found then the
|
||||
-- request is ambiguous and we return an error. In that case the request can
|
||||
-- be disambiguated by adding precision to the target or by using a hint:
|
||||
-- /origin?select=target!hint(*) The elements will be matched according to
|
||||
-- these rules:
|
||||
-- origin = table / view
|
||||
-- target = table / view / constraint / column-from-origin
|
||||
-- hint = table / view / constraint / column-from-origin / column-from-target
|
||||
-- (hint can take table / view values to aid in finding the junction in an m2m relationship)
|
||||
-- /origin?select=target!hint(*). The origin can be a table or view.
|
||||
findRel :: Schema -> RelationshipsMap -> NodeName -> NodeName -> Maybe Hint -> Either ApiRequestError Relationship
|
||||
findRel schema allRels origin target hint =
|
||||
case rels of
|
||||
@@ -150,7 +145,7 @@ findRel schema allRels origin target hint =
|
||||
O2M _ [(col, _)] -> hint_ == col
|
||||
M2O _ [(col, _)] -> hint_ == col
|
||||
_ -> False
|
||||
matchFKRefSingleCol hint_ card = case card of
|
||||
matchFKRefSingleCol hint_ card = case card of
|
||||
O2M _ [(_, fCol)] -> hint_ == fCol
|
||||
M2O _ [(_, fCol)] -> hint_ == fCol
|
||||
_ -> False
|
||||
@@ -161,50 +156,52 @@ findRel schema allRels origin target hint =
|
||||
matchJunction hint_ card = case card of
|
||||
M2M Junction{junTable} -> hint_ == qiName junTable
|
||||
_ -> False
|
||||
-- In a self reference we get two relationships with the same
|
||||
-- foreign key and relTable/relFtable but with different
|
||||
-- cardinalities(M2O/O2M). We use the convention of getting:
|
||||
-- + The O2M by using the table name in the target
|
||||
-- + The M2O by using the column name in the target
|
||||
-- For doing the above we ignore the M2O when using the table name in the target and
|
||||
-- we ignore the O2M when using the column name in the target
|
||||
notM2OSelfRel card isSelf = case card of
|
||||
M2O _ _ -> not isSelf
|
||||
_ -> True
|
||||
notO2MSelfRel card isSelf = case card of
|
||||
O2M _ _ -> not isSelf
|
||||
_ -> True
|
||||
isM2O card = case card of
|
||||
M2O _ _ -> True
|
||||
_ -> False
|
||||
isO2M card = case card of
|
||||
O2M _ _ -> True
|
||||
_ -> False
|
||||
rels = filter (
|
||||
\Relationship{..} ->
|
||||
case hint of
|
||||
-- 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
|
||||
if relIsSelf
|
||||
then case hint of
|
||||
Nothing ->
|
||||
-- The O2M by using the table name in the target
|
||||
target == qiName relForeignTable && isO2M relCardinality -- /family_tree?select=children:family_tree(*)
|
||||
||
|
||||
-- The M2O by using the column name in the target
|
||||
matchFKSingleCol target relCardinality && isM2O relCardinality -- /family_tree?select=parent(*)
|
||||
Just hnt ->
|
||||
-- /organizations?select=auditees:organizations!auditor(*)
|
||||
target == qiName relForeignTable && isO2M relCardinality
|
||||
&& matchFKRefSingleCol hnt relCardinality -- auditor
|
||||
else case hint of
|
||||
-- target = table / view / constraint / column-from-origin (constraint/column-from-origin can only come from tables https://github.com/PostgREST/postgrest/issues/2277)
|
||||
-- hint = table / view / constraint / column-from-origin / column-from-target (hint can take table / view values to aid in finding the junction in an m2m relationship)
|
||||
Nothing ->
|
||||
-- /projects?select=clients(*)
|
||||
target == qiName relForeignTable -- clients
|
||||
&& notM2OSelfRel relCardinality relIsSelf
|
||||
||
|
||||
-- /projects?select=projects_client_id_fkey(*)
|
||||
matchConstraint target relCardinality -- projects_client_id_fkey
|
||||
&& not relFTableIsView
|
||||
||
|
||||
-- /projects?select=client_id(*)
|
||||
matchFKSingleCol target relCardinality -- client_id
|
||||
&& notO2MSelfRel relCardinality relIsSelf
|
||||
&& not relFTableIsView
|
||||
Just hnt ->
|
||||
(
|
||||
-- /projects?select=clients(*)
|
||||
target == qiName relForeignTable && notM2OSelfRel relCardinality relIsSelf -- clients
|
||||
||
|
||||
-- /projects?select=projects_client_id_fkey(*)
|
||||
matchConstraint target relCardinality -- projects_client_id_fkey
|
||||
||
|
||||
-- /projects?select=client_id(*)
|
||||
matchFKSingleCol target relCardinality -- client_id
|
||||
) && (
|
||||
-- /projects?select=clients(*)
|
||||
target == qiName relForeignTable -- clients
|
||||
&& (
|
||||
-- /projects?select=clients!projects_client_id_fkey(*)
|
||||
matchConstraint hnt relCardinality || -- projects_client_id_fkey
|
||||
|
||||
-- /projects?select=clients!client_id(*) or /projects?select=clients!id(*)
|
||||
matchFKSingleCol hnt relCardinality || -- client_id
|
||||
matchFKRefSingleCol hnt relCardinality || -- id
|
||||
matchFKSingleCol hnt relCardinality || -- client_id
|
||||
matchFKRefSingleCol hnt relCardinality || -- id
|
||||
|
||||
-- /users?select=tasks!users_tasks(*) many-to-many between users and tasks
|
||||
matchJunction hnt relCardinality -- users_tasks
|
||||
@@ -235,7 +232,7 @@ addJoinConditions previousAlias (Node node@(query@Select{from=tbl,fromAlias=tblA
|
||||
|
||||
-- previousAlias and newAlias are used in the case of self joins
|
||||
getJoinConditions :: Maybe Alias -> Maybe Alias -> Relationship -> [JoinCondition]
|
||||
getJoinConditions previousAlias newAlias (Relationship QualifiedIdentifier{qiSchema=tSchema, qiName=tN} QualifiedIdentifier{qiName=ftN} _ card) =
|
||||
getJoinConditions previousAlias newAlias (Relationship QualifiedIdentifier{qiSchema=tSchema, qiName=tN} QualifiedIdentifier{qiName=ftN} _ card _ _) =
|
||||
case card of
|
||||
M2M (Junction QualifiedIdentifier{qiName=jtn} _ _ jcols1 jcols2) ->
|
||||
(toJoinCondition previousAlias newAlias tN jtn <$> jcols1) ++ (toJoinCondition Nothing Nothing ftN jtn <$> jcols2)
|
||||
|
||||
Reference in New Issue
Block a user