refactor: make Junction non-recursive (#1818)
Also Cardinality now includes a Junction or ConstraintName
This commit is contained in:
@@ -51,7 +51,8 @@ import PostgREST.DbStructure.Proc (PgArg (..), PgType (..),
|
|||||||
ProcVolatility (..),
|
ProcVolatility (..),
|
||||||
ProcsMap, RetType (..))
|
ProcsMap, RetType (..))
|
||||||
import PostgREST.DbStructure.Relation (Cardinality (..),
|
import PostgREST.DbStructure.Relation (Cardinality (..),
|
||||||
ForeignKey (..), Link (..),
|
ForeignKey (..),
|
||||||
|
Junction (..),
|
||||||
PrimaryKey (..),
|
PrimaryKey (..),
|
||||||
Relation (..))
|
Relation (..))
|
||||||
import PostgREST.DbStructure.Table (Column (..), Table (..))
|
import PostgREST.DbStructure.Table (Column (..), Table (..))
|
||||||
@@ -79,8 +80,6 @@ tableCols dbs tSchema tName = filter (\Column{colTable=Table{tableSchema=s, tabl
|
|||||||
tablePKCols :: DbStructure -> Schema -> TableName -> [Text]
|
tablePKCols :: DbStructure -> Schema -> TableName -> [Text]
|
||||||
tablePKCols dbs tSchema tName = pkName <$> filter (\pk -> tSchema == (tableSchema . pkTable) pk && tName == (tableName . pkTable) pk) (dbPrimaryKeys dbs)
|
tablePKCols dbs tSchema tName = pkName <$> filter (\pk -> tSchema == (tableSchema . pkTable) pk && tName == (tableName . pkTable) pk) (dbPrimaryKeys dbs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | The source table column a view column refers to
|
-- | The source table column a view column refers to
|
||||||
type SourceColumn = (Column, ViewColumn)
|
type SourceColumn = (Column, ViewColumn)
|
||||||
type ViewColumn = Column
|
type ViewColumn = Column
|
||||||
@@ -348,8 +347,10 @@ addForeignKeys rels = map addFk
|
|||||||
addFk col = col { colFK = fk col }
|
addFk col = col { colFK = fk col }
|
||||||
fk col = find (lookupFn col) rels >>= relToFk col
|
fk col = find (lookupFn col) rels >>= relToFk col
|
||||||
lookupFn :: Column -> Relation -> Bool
|
lookupFn :: Column -> Relation -> Bool
|
||||||
lookupFn c Relation{relColumns=cs, relType=rty} = c `elem` cs && rty==M2O
|
lookupFn c rel = case rel of
|
||||||
relToFk col Relation{relColumns=cols, relFColumns=colsF} = do
|
Relation{relColumns=cs, relCardinality=M2O _} -> c `elem` cs
|
||||||
|
_ -> False
|
||||||
|
relToFk col Relation{relColumns=cols, relForeignColumns=colsF} = do
|
||||||
pos <- L.elemIndex col cols
|
pos <- L.elemIndex col cols
|
||||||
colF <- atMay colsF pos
|
colF <- atMay colsF pos
|
||||||
return $ ForeignKey colF
|
return $ ForeignKey colF
|
||||||
@@ -357,7 +358,7 @@ addForeignKeys rels = map addFk
|
|||||||
{-
|
{-
|
||||||
Adds Views M2O Relations based on SourceColumns found, the logic is as follows:
|
Adds Views M2O Relations based on SourceColumns found, the logic is as follows:
|
||||||
|
|
||||||
Having a Relation{relTable=t1, relColumns=[c1], relFTable=t2, relFColumns=[c2], relType=M2O} represented by:
|
Having a Relation{relTable=t1, relColumns=[c1], relFTable=t2, relFColumns=[c2], relCardinality=M2O} represented by:
|
||||||
|
|
||||||
t1.c1------t2.c2
|
t1.c1------t2.c2
|
||||||
|
|
||||||
@@ -392,7 +393,7 @@ addViewM2ORels allSrcCols = concatMap (\rel@Relation{..} -> rel :
|
|||||||
srcColsGroupedByView relCols = L.groupBy (\(_, viewCol1) (_, viewCol2) -> colTable viewCol1 == colTable viewCol2) $
|
srcColsGroupedByView relCols = L.groupBy (\(_, viewCol1) (_, viewCol2) -> colTable viewCol1 == colTable viewCol2) $
|
||||||
filter (\(c, _) -> c `elem` relCols) allSrcCols
|
filter (\(c, _) -> c `elem` relCols) allSrcCols
|
||||||
relSrcCols = srcColsGroupedByView relColumns
|
relSrcCols = srcColsGroupedByView relColumns
|
||||||
relFSrcCols = srcColsGroupedByView relFColumns
|
relFSrcCols = srcColsGroupedByView relForeignColumns
|
||||||
getView :: [SourceColumn] -> Table
|
getView :: [SourceColumn] -> Table
|
||||||
getView = colTable . snd . unsafeHead
|
getView = colTable . snd . unsafeHead
|
||||||
srcCols `allSrcColsOf` cols = S.fromList (fst <$> srcCols) == S.fromList cols
|
srcCols `allSrcColsOf` cols = S.fromList (fst <$> srcCols) == S.fromList cols
|
||||||
@@ -404,38 +405,36 @@ addViewM2ORels allSrcCols = concatMap (\rel@Relation{..} -> rel :
|
|||||||
viewTableM2O =
|
viewTableM2O =
|
||||||
[ Relation
|
[ Relation
|
||||||
(getView srcCols) (snd <$> srcCols `sortAccordingTo` relColumns)
|
(getView srcCols) (snd <$> srcCols `sortAccordingTo` relColumns)
|
||||||
relFTable relFColumns
|
relForeignTable relForeignColumns relCardinality
|
||||||
relType relLink
|
|
||||||
| srcCols <- relSrcCols, srcCols `allSrcColsOf` relColumns ]
|
| srcCols <- relSrcCols, srcCols `allSrcColsOf` relColumns ]
|
||||||
|
|
||||||
tableViewM2O =
|
tableViewM2O =
|
||||||
[ Relation
|
[ Relation
|
||||||
relTable relColumns
|
relTable relColumns
|
||||||
(getView fSrcCols) (snd <$> fSrcCols `sortAccordingTo` relFColumns)
|
(getView fSrcCols) (snd <$> fSrcCols `sortAccordingTo` relForeignColumns)
|
||||||
relType relLink
|
relCardinality
|
||||||
| fSrcCols <- relFSrcCols, fSrcCols `allSrcColsOf` relFColumns ]
|
| fSrcCols <- relFSrcCols, fSrcCols `allSrcColsOf` relForeignColumns ]
|
||||||
|
|
||||||
viewViewM2O =
|
viewViewM2O =
|
||||||
[ Relation
|
[ Relation
|
||||||
(getView srcCols) (snd <$> srcCols `sortAccordingTo` relColumns)
|
(getView srcCols) (snd <$> srcCols `sortAccordingTo` relColumns)
|
||||||
(getView fSrcCols) (snd <$> fSrcCols `sortAccordingTo` relFColumns)
|
(getView fSrcCols) (snd <$> fSrcCols `sortAccordingTo` relForeignColumns)
|
||||||
relType relLink
|
relCardinality
|
||||||
| srcCols <- relSrcCols, srcCols `allSrcColsOf` relColumns
|
| srcCols <- relSrcCols, srcCols `allSrcColsOf` relColumns
|
||||||
, fSrcCols <- relFSrcCols, fSrcCols `allSrcColsOf` relFColumns ]
|
, fSrcCols <- relFSrcCols, fSrcCols `allSrcColsOf` relForeignColumns ]
|
||||||
|
|
||||||
in viewTableM2O ++ tableViewM2O ++ viewViewM2O)
|
in viewTableM2O ++ tableViewM2O ++ viewViewM2O)
|
||||||
|
|
||||||
addO2MRels :: [Relation] -> [Relation]
|
addO2MRels :: [Relation] -> [Relation]
|
||||||
addO2MRels rels = rels ++ [ Relation ft fc t c O2M lnk
|
addO2MRels rels = rels ++ [ Relation ft fc t c (O2M cons)
|
||||||
| Relation t c ft fc typ lnk <- rels
|
| Relation t c ft fc (M2O cons) <- rels ]
|
||||||
, typ == M2O]
|
|
||||||
|
|
||||||
addM2MRels :: [Relation] -> [Relation]
|
addM2MRels :: [Relation] -> [Relation]
|
||||||
addM2MRels rels = rels ++ [ Relation t c ft fc M2M (Junction jt1 lnk1 jc1 lnk2 jc2)
|
addM2MRels rels = rels ++ [ Relation t c ft fc (M2M $ Junction jt1 cons1 jc1 cons2 jc2)
|
||||||
| Relation jt1 jc1 t c _ lnk1 <- rels
|
| Relation jt1 jc1 t c (M2O cons1) <- rels
|
||||||
, Relation jt2 jc2 ft fc _ lnk2 <- rels
|
, Relation jt2 jc2 ft fc (M2O cons2) <- rels
|
||||||
, jt1 == jt2
|
, jt1 == jt2
|
||||||
, lnk1 /= lnk2]
|
, cons1 /= cons2]
|
||||||
|
|
||||||
addViewPrimaryKeys :: [SourceColumn] -> [PrimaryKey] -> [PrimaryKey]
|
addViewPrimaryKeys :: [SourceColumn] -> [PrimaryKey] -> [PrimaryKey]
|
||||||
addViewPrimaryKeys srcCols = concatMap (\pk ->
|
addViewPrimaryKeys srcCols = concatMap (\pk ->
|
||||||
@@ -632,7 +631,7 @@ allM2ORels tabs cols =
|
|||||||
|
|
||||||
relFromRow :: [Table] -> [Column] -> (Text, Text, Text, [Text], Text, Text, [Text]) -> Maybe Relation
|
relFromRow :: [Table] -> [Column] -> (Text, Text, Text, [Text], Text, Text, [Text]) -> Maybe Relation
|
||||||
relFromRow allTabs allCols (rs, rt, cn, rcs, frs, frt, frcs) =
|
relFromRow allTabs allCols (rs, rt, cn, rcs, frs, frt, frcs) =
|
||||||
Relation <$> table <*> cols <*> tableF <*> colsF <*> pure M2O <*> pure (Constraint cn)
|
Relation <$> table <*> cols <*> tableF <*> colsF <*> pure (M2O cn)
|
||||||
where
|
where
|
||||||
findTable s t = find (\tbl -> tableSchema tbl == s && tableName tbl == t) allTabs
|
findTable s t = find (\tbl -> tableSchema tbl == s && tableName tbl == t) allTabs
|
||||||
findCol s t c = find (\col -> tableSchema (colTable col) == s && tableName (colTable col) == t && colName col == c) allCols
|
findCol s t c = find (\col -> tableSchema (colTable col) == s && tableName (colTable col) == t && colName col == c) allCols
|
||||||
|
|||||||
@@ -3,11 +3,10 @@
|
|||||||
|
|
||||||
module PostgREST.DbStructure.Relation
|
module PostgREST.DbStructure.Relation
|
||||||
( Cardinality(..)
|
( Cardinality(..)
|
||||||
, Constraint
|
|
||||||
, ForeignKey(..)
|
, ForeignKey(..)
|
||||||
, Link(..)
|
|
||||||
, PrimaryKey(..)
|
, PrimaryKey(..)
|
||||||
, Relation(..)
|
, Relation(..)
|
||||||
|
, Junction(..)
|
||||||
, isSelfReference
|
, isSelfReference
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@@ -16,8 +15,6 @@ import qualified Data.Aeson as JSON
|
|||||||
import PostgREST.DbStructure.Table (Column (..), ForeignKey (..),
|
import PostgREST.DbStructure.Table (Column (..), ForeignKey (..),
|
||||||
Table (..))
|
Table (..))
|
||||||
|
|
||||||
import qualified GHC.Show (show)
|
|
||||||
|
|
||||||
import Protolude
|
import Protolude
|
||||||
|
|
||||||
|
|
||||||
@@ -28,49 +25,40 @@ import Protolude
|
|||||||
--
|
--
|
||||||
-- TODO merge relColumns and relFColumns to a tuple or Data.Bimap
|
-- TODO merge relColumns and relFColumns to a tuple or Data.Bimap
|
||||||
data Relation = Relation
|
data Relation = Relation
|
||||||
{ relTable :: Table
|
{ relTable :: Table
|
||||||
, relColumns :: [Column]
|
, relColumns :: [Column]
|
||||||
, relFTable :: Table
|
, relForeignTable :: Table
|
||||||
, relFColumns :: [Column]
|
, relForeignColumns :: [Column]
|
||||||
, relType :: Cardinality
|
, relCardinality :: Cardinality
|
||||||
, relLink :: Link -- ^ Constraint on O2M/M2O, Junction for M2M Cardinality
|
|
||||||
}
|
}
|
||||||
deriving (Eq, Generic, JSON.ToJSON)
|
deriving (Eq, Generic, JSON.ToJSON)
|
||||||
|
|
||||||
type ConstraintName = Text
|
-- | The relationship cardinality
|
||||||
|
-- | https://en.wikipedia.org/wiki/Cardinality_(data_modeling)
|
||||||
|
-- TODO: missing one-to-one
|
||||||
|
data Cardinality
|
||||||
|
= O2M FKConstraint -- ^ one-to-many cardinality
|
||||||
|
| M2O FKConstraint -- ^ many-to-one cardinality
|
||||||
|
| M2M Junction -- ^ many-to-many cardinality
|
||||||
|
deriving (Eq, Generic, JSON.ToJSON)
|
||||||
|
|
||||||
|
type FKConstraint = Text
|
||||||
|
|
||||||
-- | Junction table on an M2M relationship
|
-- | Junction table on an M2M relationship
|
||||||
data Link
|
data Junction = Junction
|
||||||
= Constraint
|
{ junTable :: Table
|
||||||
{ constName :: ConstraintName }
|
, junConstraint1 :: FKConstraint
|
||||||
| Junction
|
, junColumns1 :: [Column]
|
||||||
{ junTable :: Table
|
, junConstraint2 :: FKConstraint
|
||||||
, junLink1 :: Link
|
, junColumns2 :: [Column]
|
||||||
, junCols1 :: [Column]
|
}
|
||||||
, junLink2 :: Link
|
|
||||||
, junCols2 :: [Column]
|
|
||||||
}
|
|
||||||
deriving (Eq, Generic, JSON.ToJSON)
|
deriving (Eq, Generic, JSON.ToJSON)
|
||||||
|
|
||||||
|
isSelfReference :: Relation -> Bool
|
||||||
|
isSelfReference r = relTable r == relForeignTable r
|
||||||
|
|
||||||
data PrimaryKey = PrimaryKey
|
data PrimaryKey = PrimaryKey
|
||||||
{ pkTable :: Table
|
{ pkTable :: Table
|
||||||
, pkName :: Text
|
, pkName :: Text
|
||||||
}
|
}
|
||||||
deriving (Generic, JSON.ToJSON)
|
deriving (Generic, JSON.ToJSON)
|
||||||
|
|
||||||
-- | The relationship
|
|
||||||
-- [cardinality](https://en.wikipedia.org/wiki/Cardinality_(data_modeling)).
|
|
||||||
-- TODO: missing one-to-one
|
|
||||||
data Cardinality
|
|
||||||
= O2M -- ^ one-to-many, previously known as Parent
|
|
||||||
| M2O -- ^ many-to-one, previously known as Child
|
|
||||||
| M2M -- ^ many-to-many, previously known as Many
|
|
||||||
deriving (Eq, Generic, JSON.ToJSON)
|
|
||||||
|
|
||||||
instance Show Cardinality where
|
|
||||||
show O2M = "o2m"
|
|
||||||
show M2O = "m2o"
|
|
||||||
show M2M = "m2m"
|
|
||||||
|
|
||||||
isSelfReference :: Relation -> Bool
|
|
||||||
isSelfReference r = relTable r == relFTable r
|
|
||||||
|
|||||||
+14
-8
@@ -29,7 +29,8 @@ import Network.HTTP.Types.Header (Header)
|
|||||||
import PostgREST.ContentType (ContentType (..))
|
import PostgREST.ContentType (ContentType (..))
|
||||||
import qualified PostgREST.ContentType as ContentType
|
import qualified PostgREST.ContentType as ContentType
|
||||||
|
|
||||||
import PostgREST.DbStructure.Relation (Link (..), Relation (..))
|
import PostgREST.DbStructure.Relation (Cardinality (..),
|
||||||
|
Junction (..), Relation (..))
|
||||||
import PostgREST.DbStructure.Table (Column (..), Table (..))
|
import PostgREST.DbStructure.Table (Column (..), Table (..))
|
||||||
|
|
||||||
import Protolude hiding (toS)
|
import Protolude hiding (toS)
|
||||||
@@ -106,15 +107,20 @@ compressedRel Relation{..} =
|
|||||||
in
|
in
|
||||||
JSON.object $ [
|
JSON.object $ [
|
||||||
"origin" .= fmtTbl relTable
|
"origin" .= fmtTbl relTable
|
||||||
, "target" .= fmtTbl relFTable
|
, "target" .= fmtTbl relForeignTable
|
||||||
, "cardinality" .= (show relType :: Text)
|
|
||||||
] ++
|
] ++
|
||||||
case relLink of
|
case relCardinality of
|
||||||
Junction{..} -> [
|
M2M Junction{..} -> [
|
||||||
"relationship" .= (fmtTbl junTable <> fmtEls [constName junLink1] <> fmtEls [constName junLink2])
|
"cardinality" .= ("m2m" :: Text)
|
||||||
|
, "relationship" .= (fmtTbl junTable <> fmtEls [junConstraint1] <> fmtEls [junConstraint2])
|
||||||
]
|
]
|
||||||
Constraint{..} -> [
|
M2O cons -> [
|
||||||
"relationship" .= (constName <> fmtEls (colName <$> relColumns) <> fmtEls (colName <$> relFColumns))
|
"cardinality" .= ("m2o" :: Text)
|
||||||
|
, "relationship" .= (cons <> fmtEls (colName <$> relColumns) <> fmtEls (colName <$> relForeignColumns))
|
||||||
|
]
|
||||||
|
O2M cons -> [
|
||||||
|
"cardinality" .= ("o2m" :: Text)
|
||||||
|
, "relationship" .= (cons <> fmtEls (colName <$> relColumns) <> fmtEls (colName <$> relForeignColumns))
|
||||||
]
|
]
|
||||||
|
|
||||||
data PgError = PgError Authenticated P.UsageError
|
data PgError = PgError Authenticated P.UsageError
|
||||||
|
|||||||
@@ -53,10 +53,10 @@ readRequestToQuery (Node (Select colSelects mainQi tblAlias implJoins logicFores
|
|||||||
(joins, selects) = foldr getJoinsSelects ([],[]) forest
|
(joins, selects) = foldr getJoinsSelects ([],[]) forest
|
||||||
|
|
||||||
getJoinsSelects :: ReadRequest -> ([H.Snippet], [H.Snippet]) -> ([H.Snippet], [H.Snippet])
|
getJoinsSelects :: ReadRequest -> ([H.Snippet], [H.Snippet]) -> ([H.Snippet], [H.Snippet])
|
||||||
getJoinsSelects rr@(Node (_, (name, Just Relation{relType=relTyp,relTable=Table{tableName=table}}, alias, _, _)) _) (j,s) =
|
getJoinsSelects rr@(Node (_, (name, Just Relation{relCardinality=card,relTable=Table{tableName=table}}, alias, _, _)) _) (j,s) =
|
||||||
let subquery = readRequestToQuery rr in
|
let subquery = readRequestToQuery rr in
|
||||||
case relTyp of
|
case card of
|
||||||
M2O ->
|
M2O _ ->
|
||||||
let aliasOrName = fromMaybe name alias
|
let aliasOrName = fromMaybe name alias
|
||||||
localTableName = pgFmtIdent $ table <> "_" <> aliasOrName
|
localTableName = pgFmtIdent $ table <> "_" <> aliasOrName
|
||||||
sel = H.sql ("row_to_json(" <> localTableName <> ".*) AS " <> pgFmtIdent aliasOrName)
|
sel = H.sql ("row_to_json(" <> localTableName <> ".*) AS " <> pgFmtIdent aliasOrName)
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ import Data.Tree (Tree (..))
|
|||||||
import PostgREST.DbStructure.Identifiers (FieldName,
|
import PostgREST.DbStructure.Identifiers (FieldName,
|
||||||
QualifiedIdentifier (..),
|
QualifiedIdentifier (..),
|
||||||
Schema, TableName)
|
Schema, TableName)
|
||||||
import PostgREST.DbStructure.Relation (Cardinality (..), Link (..),
|
import PostgREST.DbStructure.Relation (Cardinality (..),
|
||||||
|
Junction (..),
|
||||||
Relation (..))
|
Relation (..))
|
||||||
import PostgREST.DbStructure.Table (Column (..), Table (..),
|
import PostgREST.DbStructure.Table (Column (..), Table (..),
|
||||||
tableQi)
|
tableQi)
|
||||||
@@ -125,7 +126,7 @@ addRels :: Schema -> [Relation] -> Maybe ReadRequest -> ReadRequest -> Either Ap
|
|||||||
addRels schema allRels parentNode (Node (query@Select{from=tbl}, (nodeName, _, alias, hint, depth)) forest) =
|
addRels schema allRels parentNode (Node (query@Select{from=tbl}, (nodeName, _, alias, hint, depth)) forest) =
|
||||||
case parentNode of
|
case parentNode of
|
||||||
Just (Node (Select{from=parentNodeQi}, _) _) ->
|
Just (Node (Select{from=parentNodeQi}, _) _) ->
|
||||||
let newFrom r = if qiName tbl == nodeName then tableQi (relFTable r) else tbl
|
let newFrom r = if qiName tbl == nodeName then tableQi (relForeignTable r) else tbl
|
||||||
newReadNode = (\r -> (query{from=newFrom r}, (nodeName, Just r, alias, Nothing, depth))) <$> rel
|
newReadNode = (\r -> (query{from=newFrom r}, (nodeName, Just r, alias, Nothing, depth))) <$> rel
|
||||||
rel = findRel schema allRels (qiName parentNodeQi) nodeName hint
|
rel = findRel schema allRels (qiName parentNodeQi) nodeName hint
|
||||||
in
|
in
|
||||||
@@ -152,34 +153,38 @@ findRel schema allRels origin target hint =
|
|||||||
case rel of
|
case rel of
|
||||||
[] -> Left $ NoRelBetween origin target
|
[] -> Left $ NoRelBetween origin target
|
||||||
[r] -> Right r
|
[r] -> Right r
|
||||||
rs ->
|
-- Here we handle a self reference relationship to not cause a breaking
|
||||||
-- Return error if more than one relationship is found, unless we're in a
|
-- change: In a self reference we get two relationships with the same
|
||||||
-- self reference case.
|
-- foreign key and relTable/relFtable but with different
|
||||||
--
|
-- cardinalities(m2o/o2m) We output the O2M rel, the M2O rel can be
|
||||||
-- Here we handle a self reference relationship to not cause a breaking
|
-- obtained by using the origin column as an embed hint.
|
||||||
-- change: In a self reference we get two relationships with the same
|
rs@[rel0, rel1] -> case (relCardinality rel0, relCardinality rel1, relTable rel0 == relTable rel1 && relForeignTable rel0 == relForeignTable rel1) of
|
||||||
-- foreign key and relTable/relFtable but with different
|
(O2M cons1, M2O cons2, True) -> if cons1 == cons2 then Right rel0 else Left $ AmbiguousRelBetween origin target rs
|
||||||
-- cardinalities(m2o/o2m) We output the O2M rel, the M2O rel can be
|
(M2O cons1, O2M cons2, True) -> if cons1 == cons2 then Right rel1 else Left $ AmbiguousRelBetween origin target rs
|
||||||
-- obtained by using the origin column as an embed hint.
|
_ -> Left $ AmbiguousRelBetween origin target rs
|
||||||
let [rel0, rel1] = take 2 rs in
|
rs -> Left $ AmbiguousRelBetween origin target rs
|
||||||
if length rs == 2 && relLink rel0 == relLink rel1 && relTable rel0 == relTable rel1 && relFTable rel0 == relFTable rel1
|
|
||||||
then note (NoRelBetween origin target) (find (\r -> relType r == O2M) rs)
|
|
||||||
else Left $ AmbiguousRelBetween origin target rs
|
|
||||||
where
|
where
|
||||||
matchFKSingleCol hint_ cols = length cols == 1 && hint_ == (colName <$> head cols)
|
matchFKSingleCol hint_ cols = length cols == 1 && hint_ == (colName <$> head cols)
|
||||||
|
matchConstraint tar card = case card of
|
||||||
|
O2M cons -> tar == Just cons
|
||||||
|
M2O cons -> tar == Just cons
|
||||||
|
_ -> False
|
||||||
|
matchJunction hint_ card = case card of
|
||||||
|
M2M Junction{junTable} -> hint_ == Just (tableName junTable)
|
||||||
|
_ -> False
|
||||||
rel = filter (
|
rel = filter (
|
||||||
\Relation{..} ->
|
\Relation{..} ->
|
||||||
-- Both relationship ends need to be on the exposed schema
|
-- Both relationship ends need to be on the exposed schema
|
||||||
schema == tableSchema relTable && schema == tableSchema relFTable &&
|
schema == tableSchema relTable && schema == tableSchema relForeignTable &&
|
||||||
(
|
(
|
||||||
-- /projects?select=clients(*)
|
-- /projects?select=clients(*)
|
||||||
origin == tableName relTable && -- projects
|
origin == tableName relTable && -- projects
|
||||||
target == tableName relFTable || -- clients
|
target == tableName relForeignTable || -- clients
|
||||||
|
|
||||||
-- /projects?select=projects_client_id_fkey(*)
|
-- /projects?select=projects_client_id_fkey(*)
|
||||||
(
|
(
|
||||||
origin == tableName relTable && -- projects
|
origin == tableName relTable && -- projects
|
||||||
Constraint target == relLink -- projects_client_id_fkey
|
matchConstraint (Just target) relCardinality -- projects_client_id_fkey
|
||||||
) ||
|
) ||
|
||||||
-- /projects?select=client_id(*)
|
-- /projects?select=client_id(*)
|
||||||
(
|
(
|
||||||
@@ -190,20 +195,14 @@ findRel schema allRels origin target hint =
|
|||||||
isNothing hint || -- hint is optional
|
isNothing hint || -- hint is optional
|
||||||
|
|
||||||
-- /projects?select=clients!projects_client_id_fkey(*)
|
-- /projects?select=clients!projects_client_id_fkey(*)
|
||||||
(
|
matchConstraint hint relCardinality || -- projects_client_id_fkey
|
||||||
relType /= M2M &&
|
|
||||||
hint == Just (constName relLink) -- projects_client_id_fkey
|
|
||||||
) ||
|
|
||||||
|
|
||||||
-- /projects?select=clients!client_id(*) or /projects?select=clients!id(*)
|
-- /projects?select=clients!client_id(*) or /projects?select=clients!id(*)
|
||||||
matchFKSingleCol hint relColumns || -- client_id
|
matchFKSingleCol hint relColumns || -- client_id
|
||||||
matchFKSingleCol hint relFColumns || -- id
|
matchFKSingleCol hint relForeignColumns || -- id
|
||||||
|
|
||||||
-- /users?select=tasks!users_tasks(*)
|
-- /users?select=tasks!users_tasks(*) many-to-many between users and tasks
|
||||||
(
|
matchJunction hint relCardinality -- users_tasks
|
||||||
relType == M2M && -- many-to-many between users and tasks
|
|
||||||
hint == Just (tableName $ junTable relLink) -- users_tasks
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
) allRels
|
) allRels
|
||||||
|
|
||||||
@@ -211,7 +210,7 @@ findRel schema allRels origin target hint =
|
|||||||
addJoinConditions :: Maybe Alias -> ReadRequest -> Either ApiRequestError ReadRequest
|
addJoinConditions :: Maybe Alias -> ReadRequest -> Either ApiRequestError ReadRequest
|
||||||
addJoinConditions previousAlias (Node node@(query@Select{from=tbl}, nodeProps@(_, rel, _, _, depth)) forest) =
|
addJoinConditions previousAlias (Node node@(query@Select{from=tbl}, nodeProps@(_, rel, _, _, depth)) forest) =
|
||||||
case rel of
|
case rel of
|
||||||
Just r@Relation{relType=M2M, relLink=Junction{junTable}} ->
|
Just r@Relation{relCardinality=M2M Junction{junTable}} ->
|
||||||
let rq = augmentQuery r in
|
let rq = augmentQuery r in
|
||||||
Node (rq{implicitJoins=tableQi junTable:implicitJoins rq}, nodeProps) <$> updatedForest
|
Node (rq{implicitJoins=tableQi junTable:implicitJoins rq}, nodeProps) <$> updatedForest
|
||||||
Just r -> Node (augmentQuery r, nodeProps) <$> updatedForest
|
Just r -> Node (augmentQuery r, nodeProps) <$> updatedForest
|
||||||
@@ -231,11 +230,11 @@ addJoinConditions previousAlias (Node node@(query@Select{from=tbl}, nodeProps@(_
|
|||||||
|
|
||||||
-- previousAlias and newAlias are used in the case of self joins
|
-- previousAlias and newAlias are used in the case of self joins
|
||||||
getJoinConditions :: Maybe Alias -> Maybe Alias -> Relation -> [JoinCondition]
|
getJoinConditions :: Maybe Alias -> Maybe Alias -> Relation -> [JoinCondition]
|
||||||
getJoinConditions previousAlias newAlias (Relation Table{tableSchema=tSchema, tableName=tN} cols Table{tableName=ftN} fCols _ lnk) =
|
getJoinConditions previousAlias newAlias (Relation Table{tableSchema=tSchema, tableName=tN} cols Table{tableName=ftN} fCols card) =
|
||||||
case lnk of
|
case card of
|
||||||
Junction Table{tableName=jtn} _ jc1 _ jc2 ->
|
M2M (Junction Table{tableName=jtn} _ jc1 _ jc2) ->
|
||||||
zipWith (toJoinCondition tN jtn) cols jc1 ++ zipWith (toJoinCondition ftN jtn) fCols jc2
|
zipWith (toJoinCondition tN jtn) cols jc1 ++ zipWith (toJoinCondition ftN jtn) fCols jc2
|
||||||
Constraint _ ->
|
_ ->
|
||||||
zipWith (toJoinCondition tN ftN) cols fCols
|
zipWith (toJoinCondition tN ftN) cols fCols
|
||||||
where
|
where
|
||||||
toJoinCondition :: Text -> Text -> Column -> Column -> JoinCondition
|
toJoinCondition :: Text -> Text -> Column -> Column -> JoinCondition
|
||||||
@@ -358,14 +357,10 @@ returningCols rr@(Node _ forest) pkCols
|
|||||||
-- be `RETURNING name`(see QueryBuilder). This would make the embedding
|
-- be `RETURNING name`(see QueryBuilder). This would make the embedding
|
||||||
-- fail because the following JOIN would need the "client_id" column from
|
-- fail because the following JOIN would need the "client_id" column from
|
||||||
-- projects. So this adds the foreign key columns to ensure the embedding
|
-- projects. So this adds the foreign key columns to ensure the embedding
|
||||||
-- succeeds, result would be `RETURNING name, client_id`. This also works
|
-- succeeds, result would be `RETURNING name, client_id`.
|
||||||
-- for the other relType's.
|
|
||||||
fkCols = concat $ mapMaybe (\case
|
fkCols = concat $ mapMaybe (\case
|
||||||
Node (_, (_, Just Relation{relColumns=cols, relType=relTyp}, _, _, _)) _ -> case relTyp of
|
Node (_, (_, Just Relation{relColumns=cols}, _, _, _)) _ -> Just cols
|
||||||
O2M -> Just cols
|
_ -> Nothing
|
||||||
M2O -> Just cols
|
|
||||||
M2M -> Just cols
|
|
||||||
_ -> Nothing
|
|
||||||
) forest
|
) forest
|
||||||
-- However if the "client_id" is present, e.g. mutateRequest to
|
-- However if the "client_id" is present, e.g. mutateRequest to
|
||||||
-- /projects?select=client_id,name,clients(name) we would get `RETURNING
|
-- /projects?select=client_id,name,clients(name) we would get `RETURNING
|
||||||
|
|||||||
Reference in New Issue
Block a user