Types reference each other

This commit is contained in:
calebmer
2015-11-11 09:09:53 -05:00
parent 3a682360f3
commit e1e4fe6d5c
5 changed files with 178 additions and 194 deletions
+8 -8
View File
@@ -163,9 +163,9 @@ app db conf reqBody req =
allRels = relations db allRels = relations db
allCols = columns db allCols = columns db
allPrKeys = primaryKeys db allPrKeys = primaryKeys db
filterCol sc table (Column{colSchema=s, colTable=t}) = s==sc && table==t filterCol sc table (Column{colTable=Table{tableSchema=s, tableName=t}}) = s==sc && table==t
filterCol _ _ _ = False filterCol _ _ _ = False
filterPk sc table pk = sc == pkSchema pk && table == pkTable pk filterPk sc table pk = sc == (tableSchema . pkTable) pk && table == (tableName . pkTable) pk
path = pathInfo req path = pathInfo req
verb = requestMethod req verb = requestMethod req
hdrs = requestHeaders req hdrs = requestHeaders req
@@ -289,7 +289,7 @@ convertJson v = (,) <$> (header <$> normalized) <*> (vals <$> normalized)
a@(Array _) -> Right a a@(Array _) -> Right a
_ -> Left invalidMsg _ -> Left invalidMsg
augumentRequestWithJoin :: Text -> [Relation] -> ApiRequest -> Either Text ApiRequest augumentRequestWithJoin :: Schema -> [Relation] -> ApiRequest -> Either Text ApiRequest
augumentRequestWithJoin schema allRels request = augumentRequestWithJoin schema allRels request =
(first formatRelationError . addRelations schema allRels Nothing) request (first formatRelationError . addRelations schema allRels Nothing) request
>>= addJoinConditions schema >>= addJoinConditions schema
@@ -332,10 +332,10 @@ addFilter (path, flt) (Node rn forest) =
where maybeNode = find ((name==).fst.snd.rootLabel) forst where maybeNode = find ((name==).fst.snd.rootLabel) forst
toSourceRelation :: Text -> Relation -> Maybe Relation toSourceRelation :: Text -> Relation -> Maybe Relation
toSourceRelation mt r@(Relation _ t _ _ ft _ _ _ rt _ _) toSourceRelation mt r@(Relation t _ ft _ _ rt _ _)
| mt == t = Just $ r {relTable=sourceSubqueryName} | mt == tableName t = Just $ r {relTable=t {tableName=sourceSubqueryName}}
| mt == ft = Just $ r {relFTable=sourceSubqueryName} | mt == tableName ft = Just $ r {relFTable=t {tableName=sourceSubqueryName}}
| Just mt == rt = Just $ r {relLTable=Just sourceSubqueryName} | Just mt == (tableName <$> rt) = Just $ r {relLTable=(\tbl -> tbl {tableName=sourceSubqueryName}) <$> rt}
| otherwise = Nothing | otherwise = Nothing
data TableOptions = TableOptions { data TableOptions = TableOptions {
@@ -348,7 +348,7 @@ instance ToJSON TableOptions where
"columns" .= tblOptcolumns t "columns" .= tblOptcolumns t
, "pkey" .= tblOptpkey t ] , "pkey" .= tblOptpkey t ]
parseRequest :: Text -> [Relation] -> NodeName -> Request -> BL.ByteString -> Either Text (Text, Text, Bool) parseRequest :: Schema -> [Relation] -> NodeName -> Request -> BL.ByteString -> Either Text (Text, Text, Bool)
parseRequest schema allRels rootTableName httpRequest reqBody = parseRequest schema allRels rootTableName httpRequest reqBody =
(,,) <$> selectQuery (,,) <$> selectQuery
<*> (if method == "GET" then pure "" else mutateQuery) <*> (if method == "GET" then pure "" else mutateQuery)
+112 -133
View File
@@ -3,13 +3,17 @@
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE TypeSynonymInstances #-}
module PostgREST.DbStructure where module PostgREST.DbStructure (
createDbStructure
, doesProcExist
, doesProcReturnJWT
) where
import Control.Applicative import Control.Applicative
import Control.Monad (join) import Control.Monad (join)
import Data.Functor.Identity import Data.Functor.Identity
import Data.List (elemIndex, find, subsequences) import Data.List (elemIndex, find, subsequences)
import Data.Maybe (fromMaybe, isJust, mapMaybe) import Data.Maybe (fromMaybe, fromJust, isJust, mapMaybe)
import Data.Monoid import Data.Monoid
import Data.Text (Text, split) import Data.Text (Text, split)
import qualified Hasql as H import qualified Hasql as H
@@ -24,13 +28,13 @@ import Prelude
createDbStructure :: H.Tx P.Postgres s DbStructure createDbStructure :: H.Tx P.Postgres s DbStructure
createDbStructure = do createDbStructure = do
tabs <- allTables tabs <- allTables
rels <- allRelations cols <- allColumns tabs
cols <- allColumns rels rels <- allRelations tabs cols
keys <- allPrimaryKeys keys <- allPrimaryKeys tabs
return DbStructure { return DbStructure {
tables = tabs tables = tabs
, columns = cols , columns = addForeignKeys rels cols
, relations = rels , relations = rels
, primaryKeys = keys , primaryKeys = keys
} }
@@ -62,135 +66,79 @@ doesProcReturnJWT = doesProc [H.stmt|
AND pg_catalog.pg_get_function_result(p.oid) like '%jwt_claims' AND pg_catalog.pg_get_function_result(p.oid) like '%jwt_claims'
|] |]
tableFromRow :: (Text, Text, Bool) -> Table addForeignKeys :: [Relation] -> [Column] -> [Column]
tableFromRow (s, n, i) = Table s n i addForeignKeys rels = map addFk
where
addFk col = col { colFK = fk col }
fk col = join $ relToFk col <$> find (lookupFn col) rels
lookupFn :: Column -> Relation -> Bool
lookupFn c (Relation{relColumns=cs, relType=rty}) = c `elem` cs && rty==Child
-- lookupFn _ _ = False
relToFk col (Relation{relColumns=cols, relFColumns=colsF}) = ForeignKey <$> colF
where
pos = elemIndex col cols
colF = (colsF !!) <$> pos
columnFromRow :: (Text, Text, Text, columnFromRow :: [Table] ->
(Text, Text, Text,
Int, Bool, Text, Int, Bool, Text,
Bool, Maybe Int, Maybe Int, Bool, Maybe Int, Maybe Int,
Maybe Text, Maybe Text) Maybe Text, Maybe Text)
-> Column -> Column
columnFromRow (s, t, n, pos, nul, typ, u, l, p, d, e) = columnFromRow tabs (s, t, n, pos, nul, typ, u, l, p, d, e) =
Column s t n pos nul typ u l p d (parseEnum e) Nothing Column table n pos nul typ u l p d (parseEnum e) Nothing
where where
table = fromJust $ find (\tbl -> tableSchema tbl == s && tableName tbl == t) tabs
parseEnum :: Maybe Text -> [Text] parseEnum :: Maybe Text -> [Text]
parseEnum str = fromMaybe [] $ split (==',') <$> str parseEnum str = fromMaybe [] $ split (==',') <$> str
relationFromRow :: (Text, Text, [Text], Text, Text, [Text]) -> Relation relationFromRow :: [Table] -> [Column] -> (Text, Text, [Text], Text, Text, [Text]) -> Relation
relationFromRow (s, t, cs, fs, ft, fcs) = Relation s t cs fs ft fcs Child Nothing Nothing Nothing Nothing relationFromRow allTabs allCols (rs, rt, rcs, frs, frt, frcs) = Relation table cols tableF colsF Child Nothing Nothing Nothing
where
findTable s t = fromJust $ find (\tbl -> tableSchema tbl == s && tableName tbl == t) allTabs
findCols s t cs = filter (\col -> tableSchema (colTable col) == s && tableName (colTable col) == t && colName col `elem` cs) allCols
table = findTable rs rt
tableF = findTable frs frt
cols = findCols rs rt rcs
colsF = findCols frs frt frcs
pkFromRow :: (Text, Text, Text) -> PrimaryKey pkFromRow :: [Table] -> (Schema, Text, Text) -> PrimaryKey
pkFromRow (s, t, n) = PrimaryKey s t n pkFromRow tabs (s, t, n) = PrimaryKey table n
where
table = fromJust $ find (\tbl -> tableSchema tbl == s && tableName tbl == t) tabs
addParentRelation :: Relation -> [Relation] -> [Relation] addParentRelation :: Relation -> [Relation] -> [Relation]
addParentRelation rel@(Relation s t c fs ft fc _ _ _ _ _) rels = Relation fs ft fc s t c Parent Nothing Nothing Nothing Nothing:rel:rels addParentRelation rel@(Relation t c ft fc _ _ _ _) rels = Relation ft fc t c Parent Nothing Nothing Nothing : rel : rels
-- allTables :: H.Tx P.Postgres s [Table] allTables :: H.Tx P.Postgres s [Table]
-- allTables = do allTables = do
-- rows <- H.listEx $ [H.stmt| rows <- H.listEx $ [H.stmt|
-- SELECT SELECT
-- n.nspname AS table_schema, n.nspname AS table_schema,
-- c.relname AS table_name, c.relname AS table_name,
-- c.relkind = 'r' OR (c.relkind IN ('v','f')) c.relkind = 'r' OR (c.relkind IN ('v','f'))
-- AND (pg_relation_is_updatable(c.oid::regclass, FALSE) & 8) = 8 AND (pg_relation_is_updatable(c.oid::regclass, FALSE) & 8) = 8
-- OR (EXISTS OR (EXISTS
-- ( SELECT 1 ( SELECT 1
-- FROM pg_trigger FROM pg_trigger
-- WHERE pg_trigger.tgrelid = c.oid WHERE pg_trigger.tgrelid = c.oid
-- AND (pg_trigger.tgtype::integer & 69) = 69) ) AS insertable, AND (pg_trigger.tgtype::integer & 69) = 69) ) AS insertable
-- array_to_string(array_agg(r.rolname), ',') AS acl FROM pg_class c
-- FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
-- CROSS JOIN pg_roles r WHERE c.relkind IN ('v','r','m')
-- JOIN pg_namespace n ON n.oid = c.relnamespace AND n.nspname NOT IN ('pg_catalog', 'information_schema')
-- WHERE c.relkind IN ('v','r','m') GROUP BY table_schema, table_name, insertable
-- AND n.nspname NOT IN ('pg_catalog', 'information_schema') ORDER BY table_schema, table_name;
-- AND ( |]
-- pg_has_role(r.rolname, c.relowner, 'USAGE'::text) OR return $ map tableFromRow rows
-- has_table_privilege(r.rolname, c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER'::text) OR
-- has_any_column_privilege(r.rolname, c.oid, 'SELECT, INSERT, UPDATE, REFERENCES'::text) )
--
-- GROUP BY table_schema, table_name, insertable
-- ORDER BY table_schema, table_name
-- |]
-- return $ map tableFromRow rows
tables :: Text -> H.Tx P.Postgres s [Table] tableFromRow :: (Text, Text, Bool) -> Table
tables schema = do tableFromRow (s, n, i) = Table s n i
rows <- H.listEx $
[H.stmt|
select
n.nspname as table_schema,
relname as table_name,
c.relkind = 'r' or (c.relkind IN ('v', 'f')) and (pg_relation_is_updatable(c.oid::regclass, false) & 8) = 8
or (exists (
select 1
from pg_trigger
where pg_trigger.tgrelid = c.oid and (pg_trigger.tgtype::integer & 69) = 69)
) as insertable
from
pg_class c
join pg_namespace n on n.oid = c.relnamespace
where
c.relkind in ('v', 'r', 'm')
and n.nspname = ?
and (
pg_has_role(c.relowner, 'USAGE'::text)
or has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER'::text)
or has_any_column_privilege(c.oid, 'SELECT, INSERT, UPDATE, REFERENCES'::text)
)
order by relname
|] schema
return $ map tableFromRow rows
allRelations :: H.Tx P.Postgres s [Relation] allColumns :: [Table] -> H.Tx P.Postgres s [Column]
allRelations = do allColumns tabs = do
rels <- H.listEx $ [H.stmt|
SELECT ns1.nspname AS table_schema,
tab.relname AS table_name,
column_info.cols AS columns,
ns2.nspname AS foreign_table_schema,
other.relname AS foreign_table_name,
column_info.refs AS foreign_columns
FROM pg_constraint,
LATERAL (SELECT array_agg(cols.attname) AS cols,
array_agg(cols.attnum) AS nums,
array_agg(refs.attname) AS refs
FROM ( SELECT unnest(conkey) AS col, unnest(confkey) AS ref) k,
LATERAL (SELECT * FROM pg_attribute
WHERE attrelid = conrelid AND attnum = col)
AS cols,
LATERAL (SELECT * FROM pg_attribute
WHERE attrelid = confrelid AND attnum = ref)
AS refs)
AS column_info,
LATERAL (SELECT * FROM pg_namespace WHERE pg_namespace.oid = connamespace) AS ns1,
LATERAL (SELECT * FROM pg_class WHERE pg_class.oid = conrelid) AS tab,
LATERAL (SELECT * FROM pg_class WHERE pg_class.oid = confrelid) AS other,
LATERAL (SELECT * FROM pg_namespace WHERE pg_namespace.oid = other.relnamespace) AS ns2
WHERE confrelid != 0
ORDER BY (conrelid, column_info.nums)
|]
let simpleRelations = foldr (addParentRelation.relationFromRow) [] rels
links = join $ map (combinations 2) $ filter (not . null) $ groupWith groupFn $ filter ( (==Child). relType) simpleRelations
return $ simpleRelations ++ mapMaybe link2Relation links
where
groupFn :: Relation -> Text
groupFn (Relation{relSchema=s, relTable=t}) = s<>"_"<>t
combinations k ns = filter ((k==).length) (subsequences ns)
link2Relation [
Relation{relSchema=ls, relTable=lt, relColumns=lc1, relFSchema=s, relFTable=t, relFColumns=c},
Relation{ relColumns=lc2, relFSchema=fs, relFTable=ft, relFColumns=fc}
]
| lc1 /= lc2 && length lc1 == 1 && length lc2 == 1 = Just $ Relation s t c fs ft fc Many (Just ls) (Just lt) (Just lc1) (Just lc2)
| otherwise = Nothing
link2Relation _ = Nothing
allColumns :: [Relation] -> H.Tx P.Postgres s [Column]
allColumns rels = do
cols <- H.listEx $ [H.stmt| cols <- H.listEx $ [H.stmt|
SELECT DISTINCT SELECT DISTINCT
info.table_schema AS schema, info.table_schema AS schema,
@@ -232,22 +180,53 @@ allColumns rels = do
) AS enum_info ON (info.udt_name = enum_info.n) ) AS enum_info ON (info.udt_name = enum_info.n)
ORDER BY schema, position ORDER BY schema, position
|] |]
return $ map (addFK . columnFromRow) cols return $ map (columnFromRow tabs) cols
allRelations :: [Table] -> [Column] -> H.Tx P.Postgres s [Relation]
allRelations tabs cols = do
rels <- H.listEx $ [H.stmt|
SELECT ns1.nspname AS table_schema,
tab.relname AS table_name,
column_info.cols AS columns,
ns2.nspname AS foreign_table_schema,
other.relname AS foreign_table_name,
column_info.refs AS foreign_columns
FROM pg_constraint,
LATERAL (SELECT array_agg(cols.attname) AS cols,
array_agg(cols.attnum) AS nums,
array_agg(refs.attname) AS refs
FROM ( SELECT unnest(conkey) AS col, unnest(confkey) AS ref) k,
LATERAL (SELECT * FROM pg_attribute
WHERE attrelid = conrelid AND attnum = col)
AS cols,
LATERAL (SELECT * FROM pg_attribute
WHERE attrelid = confrelid AND attnum = ref)
AS refs)
AS column_info,
LATERAL (SELECT * FROM pg_namespace WHERE pg_namespace.oid = connamespace) AS ns1,
LATERAL (SELECT * FROM pg_class WHERE pg_class.oid = conrelid) AS tab,
LATERAL (SELECT * FROM pg_class WHERE pg_class.oid = confrelid) AS other,
LATERAL (SELECT * FROM pg_namespace WHERE pg_namespace.oid = other.relnamespace) AS ns2
WHERE confrelid != 0
ORDER BY (conrelid, column_info.nums)
|]
let simpleRelations = foldr (addParentRelation . relationFromRow tabs cols) [] rels
links = join $ map (combinations 2) $ filter (not . null) $ groupWith groupFn $ filter ( (==Child). relType) simpleRelations
return $ simpleRelations ++ mapMaybe link2Relation links
where where
addFK col = col { colFK = fk col } groupFn :: Relation -> Text
fk col = join $ relToFk (colName col) <$> find (lookupFn col) rels groupFn (Relation{relTable=Table{tableSchema=s, tableName=t}}) = s<>"_"<>t
lookupFn :: Column -> Relation -> Bool combinations k ns = filter ((k==).length) (subsequences ns)
lookupFn (Column{colSchema=cs, colTable=ct, colName=cn}) (Relation{relSchema=rs, relTable=rt, relColumns=rc, relType=rty}) = link2Relation [
cs==rs && ct==rt && cn `elem` rc && rty==Child Relation{relTable=lt, relColumns=lc1, relFTable=t, relFColumns=c},
lookupFn _ _ = False Relation{ relColumns=lc2, relFTable=ft, relFColumns=fc}
relToFk cName (Relation{relSchema=s, relFTable=t, relColumns=cs, relFColumns=fcs}) = ForeignKey s t <$> c ]
where | lc1 /= lc2 && length lc1 == 1 && length lc2 == 1 = Just $ Relation t c ft fc Many (Just lt) (Just lc1) (Just lc2)
pos = elemIndex cName cs | otherwise = Nothing
c = (fcs !!) <$> pos link2Relation _ = Nothing
allPrimaryKeys :: H.Tx P.Postgres s [PrimaryKey] allPrimaryKeys :: [Table] -> H.Tx P.Postgres s [PrimaryKey]
allPrimaryKeys = do allPrimaryKeys tabs = do
pks <- H.listEx $ [H.stmt| pks <- H.listEx $ [H.stmt|
SELECT SELECT
kc.table_schema, kc.table_schema,
@@ -263,4 +242,4 @@ allPrimaryKeys = do
kc.constraint_name = tc.constraint_name AND kc.constraint_name = tc.constraint_name AND
kc.table_schema NOT IN ('pg_catalog', 'information_schema') kc.table_schema NOT IN ('pg_catalog', 'information_schema')
|] |]
return $ map pkFromRow pks return $ map (pkFromRow tabs) pks
+2 -1
View File
@@ -281,8 +281,9 @@ pgFmtCondition table (Filter (col,jp) ops val) =
_ -> "" _ -> ""
valToStr v = case v of valToStr v = case v of
VText s -> pgFmtValue opCode s VText s -> pgFmtValue opCode s
VForeignKey (QualifiedIdentifier s _) (ForeignKey _ ft fc) -> pgFmtColumn qi fc VForeignKey (QualifiedIdentifier s _) (ForeignKey Column{colTable=Table{tableName=ft}, colName=fc}) -> pgFmtColumn qi fc
where qi = QualifiedIdentifier (if ft == sourceSubqueryName then "" else s) ft where qi = QualifiedIdentifier (if ft == sourceSubqueryName then "" else s) ft
_ -> ""
pgFmtColumn :: QualifiedIdentifier -> T.Text -> T.Text pgFmtColumn :: QualifiedIdentifier -> T.Text -> T.Text
pgFmtColumn table "*" = fromQi table <> ".*" pgFmtColumn table "*" = fromQi table <> ".*"
+13 -13
View File
@@ -7,7 +7,7 @@ import Control.Error
import Data.List (find) import Data.List (find)
import Data.Monoid import Data.Monoid
import Data.Text hiding (filter, find, foldr, head, last, map, import Data.Text hiding (filter, find, foldr, head, last, map,
null, zipWith) null, zipWith, concatMap)
import Control.Applicative import Control.Applicative
import Data.Tree import Data.Tree
import PostgREST.PgQuery (fromQi, pgFmtCondition, pgFmtSelectItem, import PostgREST.PgQuery (fromQi, pgFmtCondition, pgFmtSelectItem,
@@ -16,11 +16,11 @@ import PostgREST.PgQuery (fromQi, pgFmtCondition, pgFmtSelectItem,
import PostgREST.Types import PostgREST.Types
import qualified Data.Map as M import qualified Data.Map as M
findRelation :: [Relation] -> Text -> Text -> Text -> Maybe Relation findRelation :: [Relation] -> Schema -> Text -> Text -> Maybe Relation
findRelation allRelations s t1 t2 = findRelation allRelations s t1 t2 =
find (\r -> s == relSchema r && t1 == relTable r && t2 == relFTable r) allRelations find (\r -> s == (tableSchema . relTable) r && t1 == (tableName . relTable) r && t2 == (tableName . relFTable) r) allRelations
addRelations :: Text -> [Relation] -> Maybe ApiRequest -> ApiRequest -> Either Text ApiRequest addRelations :: Schema -> [Relation] -> Maybe ApiRequest -> ApiRequest -> Either Text ApiRequest
addRelations schema allRelations parentNode node@(Node n@(query, (table, _)) forest) = addRelations schema allRelations parentNode node@(Node n@(query, (table, _)) forest) =
case parentNode of case parentNode of
Nothing -> Node (query, (table, Nothing)) <$> updatedForest Nothing -> Node (query, (table, Nothing)) <$> updatedForest
@@ -35,14 +35,14 @@ addRelations schema allRelations parentNode node@(Node n@(query, (table, _)) for
updatedForest = mapM (addRelations schema allRelations (Just node)) forest updatedForest = mapM (addRelations schema allRelations (Just node)) forest
getJoinConditions :: Relation -> [Filter] getJoinConditions :: Relation -> [Filter]
getJoinConditions (Relation s t cs fs ft fcs typ ls lt lc1 lc2) = getJoinConditions (Relation t cs ft fcs typ _ lc1 lc2) =
case typ of case typ of
Child -> zipWith (toFilter t fs ft) cs fcs Child -> zipWith (toFilter t) cs fcs
Parent -> zipWith (toFilter t fs ft) cs fcs Parent -> zipWith (toFilter t) cs fcs
Many -> zipWith (toFilter t (fromMaybe "" ls) (fromMaybe "" lt)) cs (fromMaybe [] lc1) ++ zipWith (toFilter ft (fromMaybe "" ls) (fromMaybe "" lt)) fcs (fromMaybe [] lc2) Many -> zipWith (toFilter t) cs (fromMaybe [] lc1) ++ zipWith (toFilter ft) fcs (fromMaybe [] lc2)
where where
toFilter :: Text -> Text -> Text -> FieldName -> FieldName -> Filter toFilter :: Table -> Column -> Column -> Filter
toFilter tb fsc ftb c fc = Filter (c, Nothing) "=" (VForeignKey (QualifiedIdentifier s tb) (ForeignKey fsc ftb fc)) toFilter tb c fc = Filter (colName c, Nothing) "=" (VForeignKey (QualifiedIdentifier (tableSchema tb) (tableName tb)) (ForeignKey fc))
addJoinConditions :: Text -> ApiRequest -> Either Text ApiRequest addJoinConditions :: Text -> ApiRequest -> Either Text ApiRequest
addJoinConditions schema (Node (query, (t, r)) forest) = addJoinConditions schema (Node (query, (t, r)) forest) =
@@ -54,15 +54,15 @@ addJoinConditions schema (Node (query, (t, r)) forest) =
Node (qq, (t, r)) <$> updatedForest Node (qq, (t, r)) <$> updatedForest
where where
q = addCond updatedQuery (getJoinConditions rel) q = addCond updatedQuery (getJoinConditions rel)
qq = q{from=linkTable:from q} qq = q{from=tableName linkTable : from q}
_ -> Left "unknown relation" _ -> Left "unknown relation"
where where
-- add parentTable and parentJoinConditions to the query -- add parentTable and parentJoinConditions to the query
updatedQuery = foldr (flip addCond) (query{from = parentTables ++ from query}) parentJoinConditions updatedQuery = foldr (flip addCond) (query{from = parentTables ++ from query}) parentJoinConditions
where where
parentJoinConditions = map (getJoinConditions.snd) parents parentJoinConditions = map (getJoinConditions . snd) parents
parentTables = map fst parents parentTables = map fst parents
parents = mapMaybe (getParents.rootLabel) forest parents = mapMaybe (getParents . rootLabel) forest
getParents (_, (tbl, Just rel@(Relation{relType=Parent}))) = Just (tbl, rel) getParents (_, (tbl, Just rel@(Relation{relType=Parent}))) = Just (tbl, rel)
getParents _ = Nothing getParents _ = Nothing
updatedForest = mapM (addJoinConditions schema) forest updatedForest = mapM (addJoinConditions schema) forest
+43 -39
View File
@@ -9,66 +9,62 @@ data DbStructure = DbStructure {
columns :: [Column] columns :: [Column]
, relations :: [Relation] , relations :: [Relation]
, primaryKeys :: [PrimaryKey] , primaryKeys :: [PrimaryKey]
} } deriving (Show, Eq)
type Schema = Text
data Table = Table { data Table = Table {
tableSchema :: Text tableSchema :: Schema
, tableName :: Text , tableName :: Text
, tableInsertable :: Bool , tableInsertable :: Bool
} deriving (Show) } deriving (Show)
data ForeignKey = ForeignKey { data ForeignKey = ForeignKey { fkCol :: Column } deriving (Show, Eq)
fkSchema :: Text,
fkTable :: Text,
fkCol :: Text
} deriving (Show, Eq)
data Column =
data Column = Column { Column {
colSchema :: Text colTable :: Table
, colTable :: Text , colName :: Text
, colName :: Text , colPosition :: Int
, colPosition :: Int , colNullable :: Bool
, colNullable :: Bool , colType :: Text
, colType :: Text , colUpdatable :: Bool
, colUpdatable :: Bool , colMaxLen :: Maybe Int
, colMaxLen :: Maybe Int , colPrecision :: Maybe Int
, colPrecision :: Maybe Int , colDefault :: Maybe Text
, colDefault :: Maybe Text , colEnum :: [Text]
, colEnum :: [Text] , colFK :: Maybe ForeignKey
, colFK :: Maybe ForeignKey }
} | Star { colSchema :: Text, colTable :: Text } deriving (Show) | Star { colTable :: Table }
deriving (Show, Eq)
data PrimaryKey = PrimaryKey { data PrimaryKey = PrimaryKey {
pkSchema::Text, pkTable::Text, pkName::Text pkTable :: Table
} , pkName :: Text
} deriving (Show, Eq)
data OrderTerm = OrderTerm { data OrderTerm = OrderTerm {
otTerm :: Text otTerm :: Text
, otDirection :: BS.ByteString , otDirection :: BS.ByteString
, otNullOrder :: Maybe BS.ByteString , otNullOrder :: Maybe BS.ByteString
} deriving (Show, Eq) } deriving (Show, Eq)
data QualifiedIdentifier = QualifiedIdentifier { data QualifiedIdentifier = QualifiedIdentifier {
qiSchema :: Text qiSchema :: Schema
, qiName :: Text , qiName :: Text
} deriving (Show, Eq) } deriving (Show, Eq)
data RelationType = Child | Parent | Many deriving (Show, Eq) data RelationType = Child | Parent | Many deriving (Show, Eq)
data Relation = Relation { data Relation = Relation {
relSchema :: Text relTable :: Table
, relTable :: Text , relColumns :: [Column]
, relColumns :: [Text] , relFTable :: Table
, relFSchema :: Text , relFColumns :: [Column]
, relFTable :: Text
, relFColumns :: [Text]
, relType :: RelationType , relType :: RelationType
, relLSchema :: Maybe Text , relLTable :: Maybe Table
, relLTable :: Maybe Text , relLCols1 :: Maybe [Column]
, relLCols1 :: Maybe [Text] , relLCols2 :: Maybe [Column]
, relLCols2 :: Maybe [Text]
} deriving (Show, Eq) } deriving (Show, Eq)
@@ -92,7 +88,7 @@ type ApiRequest = Tree ApiNode
instance ToJSON Column where instance ToJSON Column where
toJSON c = object [ toJSON c = object [
"schema" .= colSchema c "schema" .= tableSchema t
, "name" .= colName c , "name" .= colName c
, "position" .= colPosition c , "position" .= colPosition c
, "nullable" .= colNullable c , "nullable" .= colNullable c
@@ -103,9 +99,17 @@ instance ToJSON Column where
, "references".= colFK c , "references".= colFK c
, "default" .= colDefault c , "default" .= colDefault c
, "enum" .= colEnum c ] , "enum" .= colEnum c ]
where
t = colTable c
instance ToJSON ForeignKey where instance ToJSON ForeignKey where
toJSON fk = object ["schema".=fkSchema fk, "table".=fkTable fk, "column".=fkCol fk] toJSON fk = object [
"schema" .= tableSchema t
, "table" .= tableName t
, "column" .= colName c ]
where
c = fkCol fk
t = colTable c
instance ToJSON Table where instance ToJSON Table where
toJSON v = object [ toJSON v = object [