refactor: merge Column list into Table

* Remove Table from Column
* Correct OpenAPI test where the schema was appearing for the enum unnecessarily
* Increase memory-tests consumption by 2M

Seems the Hasql decoder consumes more memory now that the allTables
results also have [Column] inside.
This commit is contained in:
steve-chavez
2022-04-22 19:04:16 -05:00
committed by Steve Chavez
parent 902e4e8921
commit 778b8d074d
6 changed files with 139 additions and 173 deletions
+1 -1
View File
@@ -485,7 +485,7 @@ handleOpenApi headersOnly tSchema (RequestContext conf@AppConfig{..} dbStructure
lift $ case configOpenApiMode of lift $ case configOpenApiMode of
OAFollowPriv -> OAFollowPriv ->
OpenAPI.encode conf dbStructure OpenAPI.encode conf dbStructure
<$> SQL.statement tSchema (DbStructure.accessibleTables ctxPgVersion configDbPreparedStatements) <$> SQL.statement [tSchema] (DbStructure.accessibleTables ctxPgVersion configDbPreparedStatements)
<*> SQL.statement tSchema (DbStructure.accessibleProcs ctxPgVersion configDbPreparedStatements) <*> SQL.statement tSchema (DbStructure.accessibleProcs ctxPgVersion configDbPreparedStatements)
<*> SQL.statement tSchema (DbStructure.schemaDescription configDbPreparedStatements) <*> SQL.statement tSchema (DbStructure.schemaDescription configDbPreparedStatements)
OAIgnorePriv -> OAIgnorePriv ->
+108 -132
View File
@@ -25,7 +25,6 @@ module PostgREST.DbStructure
, accessibleProcs , accessibleProcs
, findIfView , findIfView
, schemaDescription , schemaDescription
, tableCols
) where ) where
import qualified Data.Aeson as JSON import qualified Data.Aeson as JSON
@@ -43,7 +42,7 @@ import PostgREST.Config.PgVersion (PgVersion, pgVersion100,
pgVersion110) pgVersion110)
import PostgREST.DbStructure.Identifiers (FieldName, import PostgREST.DbStructure.Identifiers (FieldName,
QualifiedIdentifier (..), QualifiedIdentifier (..),
Schema, TableName) Schema)
import PostgREST.DbStructure.Proc (PgType (..), import PostgREST.DbStructure.Proc (PgType (..),
ProcDescription (..), ProcDescription (..),
ProcParam (..), ProcParam (..),
@@ -60,16 +59,11 @@ import Protolude
data DbStructure = DbStructure data DbStructure = DbStructure
{ dbTables :: TablesMap { dbTables :: TablesMap
, dbColumns :: [Column]
, dbRelationships :: [Relationship] , dbRelationships :: [Relationship]
, dbProcs :: ProcsMap , dbProcs :: ProcsMap
} }
deriving (Generic, JSON.ToJSON) deriving (Generic, JSON.ToJSON)
-- TODO Table could hold references to all its Columns
tableCols :: DbStructure -> Schema -> TableName -> [Column]
tableCols dbs tSchema tName = filter (\Column{colTable=Table{tableSchema=s, tableName=t}} -> s==tSchema && t==tName) $ dbColumns dbs
findIfView :: QualifiedIdentifier -> TablesMap -> Bool findIfView :: QualifiedIdentifier -> TablesMap -> Bool
findIfView identifier tbls = maybe False tableIsView $ M.lookup identifier tbls findIfView identifier tbls = maybe False tableIsView $ M.lookup identifier tbls
@@ -94,8 +88,7 @@ queryDbStructure :: [Schema] -> [Schema] -> Bool -> SQL.Transaction DbStructure
queryDbStructure schemas extraSearchPath prepared = do queryDbStructure schemas extraSearchPath prepared = do
SQL.sql "set local schema ''" -- This voids the search path. The following queries need this for getting the fully qualified name(schema.name) of every db object SQL.sql "set local schema ''" -- This voids the search path. The following queries need this for getting the fully qualified name(schema.name) of every db object
pgVer <- SQL.statement mempty pgVersionStatement pgVer <- SQL.statement mempty pgVersionStatement
tabs <- SQL.statement mempty $ allTables pgVer prepared tabs <- SQL.statement schemas $ allTables pgVer prepared
cols <- SQL.statement schemas $ allColumns tabs prepared
keyDeps <- SQL.statement (schemas, extraSearchPath) $ allViewsKeyDependencies prepared keyDeps <- SQL.statement (schemas, extraSearchPath) $ allViewsKeyDependencies prepared
m2oRels <- SQL.statement mempty $ allM2ORels pgVer prepared m2oRels <- SQL.statement mempty $ allM2ORels pgVer prepared
procs <- SQL.statement schemas $ allProcs pgVer prepared procs <- SQL.statement schemas $ allProcs pgVer prepared
@@ -105,7 +98,6 @@ queryDbStructure schemas extraSearchPath prepared = do
return $ removeInternal schemas $ DbStructure { return $ removeInternal schemas $ DbStructure {
dbTables = tabsWViewsPks dbTables = tabsWViewsPks
, dbColumns = cols
, dbRelationships = rels , dbRelationships = rels
, dbProcs = procs , dbProcs = procs
} }
@@ -115,7 +107,6 @@ removeInternal :: [Schema] -> DbStructure -> DbStructure
removeInternal schemas dbStruct = removeInternal schemas dbStruct =
DbStructure { DbStructure {
dbTables = M.filterWithKey (\(QualifiedIdentifier sch _) _ -> sch `elem` schemas) $ dbTables dbStruct dbTables = M.filterWithKey (\(QualifiedIdentifier sch _) _ -> sch `elem` schemas) $ dbTables dbStruct
, dbColumns = filter (\x -> tableSchema (colTable x) `elem` schemas) (dbColumns dbStruct)
, dbRelationships = filter (\x -> qiSchema (relTable x) `elem` schemas && , dbRelationships = filter (\x -> qiSchema (relTable x) `elem` schemas &&
qiSchema (relForeignTable x) `elem` schemas && qiSchema (relForeignTable x) `elem` schemas &&
not (hasInternalJunction x)) $ dbRelationships dbStruct not (hasInternalJunction x)) $ dbRelationships dbStruct
@@ -130,30 +121,24 @@ decodeTables :: HD.Result TablesMap
decodeTables = decodeTables =
M.fromList . map (\tbl@Table{tableSchema, tableName} -> (QualifiedIdentifier tableSchema tableName, tbl)) <$> HD.rowList tblRow M.fromList . map (\tbl@Table{tableSchema, tableName} -> (QualifiedIdentifier tableSchema tableName, tbl)) <$> HD.rowList tblRow
where where
tblRow = Table <$> column HD.text tblRow = Table
<*> column HD.text <$> column HD.text
<*> nullableColumn HD.text <*> column HD.text
<*> column HD.bool <*> nullableColumn HD.text
<*> column HD.bool <*> column HD.bool
<*> column HD.bool <*> column HD.bool
<*> column HD.bool <*> column HD.bool
<*> arrayColumn HD.text <*> column HD.bool
<*> arrayColumn HD.text
decodeColumns :: TablesMap -> HD.Result [Column] <*> compositeArrayColumn
decodeColumns tables = (Column
mapMaybe (columnFromRow tables) <$> HD.rowList colRow <$> compositeField HD.text
where <*> nullableCompositeField HD.text
colRow = <*> compositeField HD.bool
(,,,,,,,,) <*> compositeField HD.text
<$> column HD.text <*> nullableCompositeField HD.int4
<*> column HD.text <*> nullableCompositeField HD.text
<*> column HD.text <*> compositeFieldArray HD.text)
<*> nullableColumn HD.text
<*> column HD.bool
<*> column HD.text
<*> nullableColumn HD.int4
<*> nullableColumn HD.text
<*> arrayColumn HD.text
decodeRels :: HD.Result [Relationship] decodeRels :: HD.Result [Relationship]
decodeRels = decodeRels =
@@ -330,9 +315,9 @@ schemaDescription =
where where
n.nspname = $1 |] n.nspname = $1 |]
accessibleTables :: PgVersion -> Bool -> SQL.Statement Schema TablesMap accessibleTables :: PgVersion -> Bool -> SQL.Statement [Schema] TablesMap
accessibleTables pgVer = accessibleTables pgVer =
SQL.Statement sql (param HE.text) decodeTables SQL.Statement sql (arrayParam HE.text) decodeTables
where where
sql = tablesSqlQuery False pgVer sql = tablesSqlQuery False pgVer
@@ -403,9 +388,9 @@ addViewPrimaryKeys tabs keyDeps =
maybe [] (\(ViewKeyDependency _ _ _ _ pkCols) -> snd <$> pkCols) $ maybe [] (\(ViewKeyDependency _ _ _ _ pkCols) -> snd <$> pkCols) $
find (\(ViewKeyDependency _ viewQi _ dep _) -> dep == PKDep && viewQi == QualifiedIdentifier sch vw) keyDeps find (\(ViewKeyDependency _ viewQi _ dep _) -> dep == PKDep && viewQi == QualifiedIdentifier sch vw) keyDeps
allTables :: PgVersion -> Bool -> SQL.Statement () TablesMap allTables :: PgVersion -> Bool -> SQL.Statement [Schema] TablesMap
allTables pgVer = allTables pgVer =
SQL.Statement sql HE.noParams decodeTables SQL.Statement sql (arrayParam HE.text) decodeTables
where where
sql = tablesSqlQuery True pgVer sql = tablesSqlQuery True pgVer
@@ -417,6 +402,80 @@ tablesSqlQuery getAll pgVer =
-- (pg_has_role(ss.relowner, 'USAGE'::text) OR has_column_privilege(ss.roid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'::text)); -- (pg_has_role(ss.relowner, 'USAGE'::text) OR has_column_privilege(ss.roid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'::text));
[q| [q|
WITH WITH
columns AS (
SELECT
nc.nspname::name AS table_schema,
c.relname::name AS table_name,
a.attname::name AS column_name,
d.description AS description,
pg_get_expr(ad.adbin, ad.adrelid)::text AS column_default,
not (a.attnotnull OR t.typtype = 'd' AND t.typnotnull) AS is_nullable,
CASE
WHEN t.typtype = 'd' THEN
CASE
WHEN bt.typelem <> 0::oid AND bt.typlen = (-1) THEN 'ARRAY'::text
WHEN nbt.nspname = 'pg_catalog'::name THEN format_type(t.typbasetype, NULL::integer)
ELSE format_type(a.atttypid, a.atttypmod)
END
ELSE
CASE
WHEN t.typelem <> 0::oid AND t.typlen = (-1) THEN 'ARRAY'::text
WHEN nt.nspname = 'pg_catalog'::name THEN format_type(a.atttypid, NULL::integer)
ELSE format_type(a.atttypid, a.atttypmod)
END
END::text AS data_type,
information_schema._pg_char_max_length(
information_schema._pg_truetypid(a.*, t.*),
information_schema._pg_truetypmod(a.*, t.*)
)::integer AS character_maximum_length,
COALESCE(bt.typname, t.typname)::name AS udt_name,
a.attnum::integer AS position
FROM pg_attribute a
LEFT JOIN pg_description AS d
ON d.objoid = a.attrelid and d.objsubid = a.attnum
LEFT JOIN pg_attrdef ad
ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
JOIN (pg_class c JOIN pg_namespace nc ON c.relnamespace = nc.oid)
ON a.attrelid = c.oid
JOIN (pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid)
ON a.atttypid = t.oid
LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid)
ON t.typtype = 'd' AND t.typbasetype = bt.oid
LEFT JOIN (pg_collation co JOIN pg_namespace nco ON co.collnamespace = nco.oid)
ON a.attcollation = co.oid AND (nco.nspname <> 'pg_catalog'::name OR co.collname <> 'default'::name)
WHERE
NOT pg_is_other_temp_schema(nc.oid)
AND a.attnum > 0
AND NOT a.attisdropped
AND c.relkind in ('r', 'v', 'f', 'm', 'p')
AND nc.nspname = ANY($1)
),
columns_agg AS (
SELECT DISTINCT
info.table_schema AS table_schema,
info.table_name AS table_name,
array_agg(row(
info.column_name,
info.description,
info.is_nullable::boolean,
info.data_type,
info.character_maximum_length,
info.column_default,
coalesce(enum_info.vals, '{}')) order by info.position) as columns
FROM columns info
LEFT OUTER JOIN (
SELECT
n.nspname AS s,
t.typname AS n,
array_agg(e.enumlabel ORDER BY e.enumsortorder) AS vals
FROM pg_type t
JOIN pg_enum e ON t.oid = e.enumtypid
JOIN pg_namespace n ON n.oid = t.typnamespace
GROUP BY s,n
) AS enum_info ON info.udt_name = enum_info.n
WHERE info.table_schema NOT IN ('pg_catalog', 'information_schema')
GROUP BY info.table_schema, info.table_name
),
tbl_constraints AS ( tbl_constraints AS (
SELECT SELECT
c.conname::name AS constraint_name, c.conname::name AS constraint_name,
@@ -518,11 +577,13 @@ tablesSqlQuery getAll pgVer =
AND (pg_relation_is_updatable(c.oid::regclass, TRUE) & 16) = 16 AND (pg_relation_is_updatable(c.oid::regclass, TRUE) & 16) = 16
) )
) AS deletable, ) AS deletable,
coalesce(tpks.pk_cols, '{}') as pk_cols coalesce(tpks.pk_cols, '{}') as pk_cols,
coalesce(cols_agg.columns, '{}') as columns
FROM pg_class c FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace JOIN pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_description d on d.objoid = c.oid and d.objsubid = 0 LEFT JOIN pg_description d on d.objoid = c.oid and d.objsubid = 0
LEFT JOIN tbl_pk_cols tpks ON n.nspname = tpks.table_schema AND c.relname = tpks.table_name LEFT JOIN tbl_pk_cols tpks ON n.nspname = tpks.table_schema AND c.relname = tpks.table_name
LEFT JOIN columns_agg cols_agg ON n.nspname = cols_agg.table_schema AND c.relname = cols_agg.table_name
WHERE c.relkind IN ('v','r','m','f','p') WHERE c.relkind IN ('v','r','m','f','p')
AND n.nspname NOT IN ('pg_catalog', 'information_schema') |] <> AND n.nspname NOT IN ('pg_catalog', 'information_schema') |] <>
relIsPartition <> relIsPartition <>
@@ -530,7 +591,7 @@ tablesSqlQuery getAll pgVer =
"ORDER BY table_schema, table_name" "ORDER BY table_schema, table_name"
where where
fltTables = if getAll then mempty else [q| fltTables = if getAll then mempty else [q|
AND n.nspname = $1 AND n.nspname = ANY($1)
AND ( AND (
pg_has_role(c.relowner, 'USAGE') pg_has_role(c.relowner, 'USAGE')
or has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') or has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
@@ -538,97 +599,6 @@ tablesSqlQuery getAll pgVer =
)|] )|]
relIsPartition = if pgVer >= pgVersion100 then " AND not c.relispartition " else mempty relIsPartition = if pgVer >= pgVersion100 then " AND not c.relispartition " else mempty
allColumns :: TablesMap -> Bool -> SQL.Statement [Schema] [Column]
allColumns tabs =
SQL.Statement sql (arrayParam HE.text) (decodeColumns tabs)
where
-- CTE based on information_schema.columns changed: remove the owner filter
sql = [q|
WITH
columns AS (
SELECT
nc.nspname::name AS table_schema,
c.relname::name AS table_name,
a.attname::name AS column_name,
d.description AS description,
pg_get_expr(ad.adbin, ad.adrelid)::text AS column_default,
not (a.attnotnull OR t.typtype = 'd' AND t.typnotnull) AS is_nullable,
CASE
WHEN t.typtype = 'd' THEN
CASE
WHEN bt.typelem <> 0::oid AND bt.typlen = (-1) THEN 'ARRAY'::text
WHEN nbt.nspname = 'pg_catalog'::name THEN format_type(t.typbasetype, NULL::integer)
ELSE format_type(a.atttypid, a.atttypmod)
END
ELSE
CASE
WHEN t.typelem <> 0::oid AND t.typlen = (-1) THEN 'ARRAY'::text
WHEN nt.nspname = 'pg_catalog'::name THEN format_type(a.atttypid, NULL::integer)
ELSE format_type(a.atttypid, a.atttypmod)
END
END::text AS data_type,
information_schema._pg_char_max_length(
information_schema._pg_truetypid(a.*, t.*),
information_schema._pg_truetypmod(a.*, t.*)
)::integer AS character_maximum_length,
COALESCE(bt.typname, t.typname)::name AS udt_name,
a.attnum::integer AS position
FROM pg_attribute a
LEFT JOIN pg_description AS d
ON d.objoid = a.attrelid and d.objsubid = a.attnum
LEFT JOIN pg_attrdef ad
ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
JOIN (pg_class c JOIN pg_namespace nc ON c.relnamespace = nc.oid)
ON a.attrelid = c.oid
JOIN (pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid)
ON a.atttypid = t.oid
LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid)
ON t.typtype = 'd' AND t.typbasetype = bt.oid
LEFT JOIN (pg_collation co JOIN pg_namespace nco ON co.collnamespace = nco.oid)
ON a.attcollation = co.oid AND (nco.nspname <> 'pg_catalog'::name OR co.collname <> 'default'::name)
WHERE
NOT pg_is_other_temp_schema(nc.oid)
AND a.attnum > 0
AND NOT a.attisdropped
AND c.relkind in ('r', 'v', 'f', 'm', 'p')
AND nc.nspname = ANY($1)
)
SELECT DISTINCT
info.table_schema AS schema,
info.table_name AS table_name,
info.column_name AS name,
info.description AS description,
info.is_nullable::boolean AS nullable,
info.data_type AS col_type,
info.character_maximum_length AS max_len,
info.column_default AS default_value,
coalesce(enum_info.vals, '{}') AS enum,
info.position
FROM columns info
LEFT OUTER JOIN (
SELECT
n.nspname AS s,
t.typname AS n,
array_agg(e.enumlabel ORDER BY e.enumsortorder) AS vals
FROM pg_type t
JOIN pg_enum e ON t.oid = e.enumtypid
JOIN pg_namespace n ON n.oid = t.typnamespace
GROUP BY s,n
) AS enum_info ON (info.udt_name = enum_info.n)
WHERE info.table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY schema, position;
|]
columnFromRow :: TablesMap ->
(Text, Text, Text,
Maybe Text, Bool, Text,
Maybe Int32, Maybe Text, [Text])
-> Maybe Column
columnFromRow tabs (s, t, n, desc, nul, typ, l, d, e) = buildColumn <$> table
where
buildColumn tbl = Column tbl n desc nul typ l d e
table = M.lookup (QualifiedIdentifier s t) tabs
allM2ORels :: PgVersion -> Bool -> SQL.Statement () [Relationship] allM2ORels :: PgVersion -> Bool -> SQL.Statement () [Relationship]
allM2ORels pgVer = allM2ORels pgVer =
SQL.Statement sql HE.noParams decodeRels SQL.Statement sql HE.noParams decodeRels
@@ -834,6 +804,12 @@ compositeArrayColumn = arrayColumn . HD.composite
compositeField :: HD.Value a -> HD.Composite a compositeField :: HD.Value a -> HD.Composite a
compositeField = HD.field . HD.nonNullable compositeField = HD.field . HD.nonNullable
nullableCompositeField :: HD.Value a -> HD.Composite (Maybe a)
nullableCompositeField = HD.field . HD.nullable
compositeFieldArray :: HD.Value a -> HD.Composite [a]
compositeFieldArray = HD.field . HD.nonNullable . HD.listArray . HD.nonNullable
column :: HD.Value a -> HD.Row a column :: HD.Value a -> HD.Row a
column = HD.column . HD.nonNullable column = HD.column . HD.nonNullable
+3 -6
View File
@@ -28,6 +28,7 @@ data Table = Table
, tableUpdatable :: Bool , tableUpdatable :: Bool
, tableDeletable :: Bool , tableDeletable :: Bool
, tablePKCols :: [FieldName] , tablePKCols :: [FieldName]
, tableColumns :: [Column]
} }
deriving (Show, Ord, Generic, JSON.ToJSON) deriving (Show, Ord, Generic, JSON.ToJSON)
@@ -35,8 +36,7 @@ instance Eq Table where
Table{tableSchema=s1,tableName=n1} == Table{tableSchema=s2,tableName=n2} = s1 == s2 && n1 == n2 Table{tableSchema=s1,tableName=n1} == Table{tableSchema=s2,tableName=n2} = s1 == s2 && n1 == n2
data Column = Column data Column = Column
{ colTable :: Table { colName :: FieldName
, colName :: FieldName
, colDescription :: Maybe Text , colDescription :: Maybe Text
, colNullable :: Bool , colNullable :: Bool
, colType :: Text , colType :: Text
@@ -44,9 +44,6 @@ data Column = Column
, colDefault :: Maybe Text , colDefault :: Maybe Text
, colEnum :: [Text] , colEnum :: [Text]
} }
deriving (Ord, Generic, JSON.ToJSON) deriving (Eq, Show, Ord, Generic, JSON.ToJSON)
instance Eq Column where
Column{colTable=t1,colName=n1} == Column{colTable=t2,colName=n2} = t1 == t2 && n1 == n2
type TablesMap = M.HashMap QualifiedIdentifier Table type TablesMap = M.HashMap QualifiedIdentifier Table
+17 -24
View File
@@ -26,8 +26,7 @@ import Data.Swagger
import PostgREST.Config (AppConfig (..), Proxy (..), import PostgREST.Config (AppConfig (..), Proxy (..),
isMalformedProxyUri, toURI) isMalformedProxyUri, toURI)
import PostgREST.DbStructure (DbStructure (..), import PostgREST.DbStructure (DbStructure (..))
tableCols)
import PostgREST.DbStructure.Identifiers (QualifiedIdentifier (..)) import PostgREST.DbStructure.Identifiers (QualifiedIdentifier (..))
import PostgREST.DbStructure.Proc (ProcDescription (..), import PostgREST.DbStructure.Proc (ProcDescription (..),
ProcParam (..)) ProcParam (..))
@@ -47,7 +46,7 @@ encode conf dbStructure tables procs schemaDescription =
postgrestSpec postgrestSpec
(dbRelationships dbStructure) (dbRelationships dbStructure)
(concat $ M.elems procs) (concat $ M.elems procs)
(openApiTableInfo dbStructure <$> (snd <$> M.toList tables)) (snd <$> M.toList tables)
(proxyUri conf) (proxyUri conf)
schemaDescription schemaDescription
@@ -80,17 +79,17 @@ parseDefault colType colDefault =
where where
wrapInQuotations text = "\"" <> text <> "\"" wrapInQuotations text = "\"" <> text <> "\""
makeTableDef :: [Relationship] -> (Table, [Column]) -> (Text, Schema) makeTableDef :: [Relationship] -> Table -> (Text, Schema)
makeTableDef rels (t, cs) = makeTableDef rels t =
let tn = tableName t in let tn = tableName t in
(tn, (mempty :: Schema) (tn, (mempty :: Schema)
& description .~ tableDescription t & description .~ tableDescription t
& type_ ?~ SwaggerObject & type_ ?~ SwaggerObject
& properties .~ fromList (fmap (makeProperty rels) cs) & properties .~ fromList (makeProperty t rels <$> tableColumns t)
& required .~ fmap colName (filter (not . colNullable) cs)) & required .~ fmap colName (filter (not . colNullable) $ tableColumns t))
makeProperty :: [Relationship] -> Column -> (Text, Referenced Schema) makeProperty :: Table -> [Relationship] -> Column -> (Text, Referenced Schema)
makeProperty rels col = (colName col, Inline s) makeProperty tbl rels col = (colName col, Inline s)
where where
e = if null $ colEnum col then Nothing else JSON.decode $ JSON.encode $ colEnum col e = if null $ colEnum col then Nothing else JSON.decode $ JSON.encode $ colEnum col
fk :: Maybe Text fk :: Maybe Text
@@ -107,7 +106,7 @@ makeProperty rels col = (colName col, Inline s)
in in
(\(a, b) -> T.intercalate "" ["This is a Foreign Key to `", a, ".", b, "`.<fk table='", a, "' column='", b, "'/>"]) <$> fTblCol (\(a, b) -> T.intercalate "" ["This is a Foreign Key to `", a, ".", b, "`.<fk table='", a, "' column='", b, "'/>"]) <$> fTblCol
pk :: Bool pk :: Bool
pk = colName col `elem` tablePKCols (colTable col) pk = colName col `elem` tablePKCols tbl
n = catMaybes n = catMaybes
[ Just "Note:" [ Just "Note:"
, if pk then Just "This is a Primary Key.<pk/>" else Nothing , if pk then Just "This is a Primary Key.<pk/>" else Nothing
@@ -162,7 +161,7 @@ makeProcParam pd =
, Ref $ Reference "preferParams" , Ref $ Reference "preferParams"
] ]
makeParamDefs :: [(Table, [Column])] -> [(Text, Param)] makeParamDefs :: [Table] -> [(Text, Param)]
makeParamDefs ti = makeParamDefs ti =
[ ("preferParams", makePreferParam ["params=single-object"]) [ ("preferParams", makePreferParam ["params=single-object"])
, ("preferReturn", makePreferParam ["return=representation", "return=minimal", "return=none"]) , ("preferReturn", makePreferParam ["return=representation", "return=minimal", "return=none"])
@@ -218,8 +217,8 @@ makeParamDefs ti =
& in_ .~ ParamQuery & in_ .~ ParamQuery
& type_ ?~ SwaggerString)) & type_ ?~ SwaggerString))
] ]
<> concat [ makeObjectBody (tableName t) : makeRowFilters (tableName t) cs <> concat [ makeObjectBody (tableName t) : makeRowFilters (tableName t) (tableColumns t)
| (t, cs) <- ti | t <- ti
] ]
makeObjectBody :: Text -> (Text, Param) makeObjectBody :: Text -> (Text, Param)
@@ -244,8 +243,8 @@ makeRowFilter tn c =
makeRowFilters :: Text -> [Column] -> [(Text, Param)] makeRowFilters :: Text -> [Column] -> [(Text, Param)]
makeRowFilters tn = fmap (makeRowFilter tn) makeRowFilters tn = fmap (makeRowFilter tn)
makePathItem :: (Table, [Column]) -> (FilePath, PathItem) makePathItem :: Table -> (FilePath, PathItem)
makePathItem (t, cs) = ("/" ++ T.unpack tn, p $ tableInsertable t || tableUpdatable t || tableDeletable t) makePathItem t = ("/" ++ T.unpack tn, p $ tableInsertable t || tableUpdatable t || tableDeletable t)
where where
-- Use first line of table description as summary; rest as description (if present) -- Use first line of table description as summary; rest as description (if present)
-- We strip leading newlines from description so that users can include a blank line between summary and description -- We strip leading newlines from description so that users can include a blank line between summary and description
@@ -279,7 +278,7 @@ makePathItem (t, cs) = ("/" ++ T.unpack tn, p $ tableInsertable t || tableUpdata
p False = pr p False = pr
p True = pw p True = pw
tn = tableName t tn = tableName t
rs = [ T.intercalate "." ["rowFilter", tn, colName c ] | c <- cs ] rs = [ T.intercalate "." ["rowFilter", tn, colName c ] | c <- tableColumns t ]
ref = Ref . Reference ref = Ref . Reference
makeProcPathItem :: ProcDescription -> (FilePath, PathItem) makeProcPathItem :: ProcDescription -> (FilePath, PathItem)
@@ -309,7 +308,7 @@ makeRootPathItem = ("/", p)
pr = (mempty :: PathItem) & get ?~ getOp pr = (mempty :: PathItem) & get ?~ getOp
p = pr p = pr
makePathItems :: [ProcDescription] -> [(Table, [Column])] -> InsOrdHashMap FilePath PathItem makePathItems :: [ProcDescription] -> [Table] -> InsOrdHashMap FilePath PathItem
makePathItems pds ti = fromList $ makeRootPathItem : makePathItems pds ti = fromList $ makeRootPathItem :
fmap makePathItem ti ++ fmap makeProcPathItem pds fmap makePathItem ti ++ fmap makeProcPathItem pds
@@ -321,7 +320,7 @@ escapeHostName "*6" = "0.0.0.0"
escapeHostName "!6" = "0.0.0.0" escapeHostName "!6" = "0.0.0.0"
escapeHostName h = h escapeHostName h = h
postgrestSpec :: [Relationship] -> [ProcDescription] -> [(Table, [Column])] -> (Text, Text, Integer, Text) -> Maybe Text -> Swagger postgrestSpec :: [Relationship] -> [ProcDescription] -> [Table] -> (Text, Text, Integer, Text) -> Maybe Text -> Swagger
postgrestSpec rels pds ti (s, h, p, b) sd = (mempty :: Swagger) postgrestSpec rels pds ti (s, h, p, b) sd = (mempty :: Swagger)
& basePath ?~ T.unpack b & basePath ?~ T.unpack b
& schemes ?~ [s'] & schemes ?~ [s']
@@ -379,9 +378,3 @@ proxyUri AppConfig{..} =
(proxyScheme, proxyHost, proxyPort, proxyPath) (proxyScheme, proxyHost, proxyPort, proxyPath)
Nothing -> Nothing ->
("http", configServerHost, toInteger configServerPort, "/") ("http", configServerHost, toInteger configServerPort, "/")
openApiTableInfo :: DbStructure -> Table -> (Table, [Column])
openApiTableInfo dbStructure table =
( table
, tableCols dbStructure (tableSchema table) (tableName table)
)
+9 -9
View File
@@ -102,21 +102,21 @@ postJsonArrayTest(){
echo "Running memory usage tests.." echo "Running memory usage tests.."
jsonKeyTest "1M" "POST" "/rpc/leak?columns=blob" "13M" jsonKeyTest "1M" "POST" "/rpc/leak?columns=blob" "15M"
jsonKeyTest "1M" "POST" "/leak?columns=blob" "13M" jsonKeyTest "1M" "POST" "/leak?columns=blob" "15M"
jsonKeyTest "1M" "PATCH" "/leak?id=eq.1&columns=blob" "13M" jsonKeyTest "1M" "PATCH" "/leak?id=eq.1&columns=blob" "15M"
jsonKeyTest "10M" "POST" "/rpc/leak?columns=blob" "41M" jsonKeyTest "10M" "POST" "/rpc/leak?columns=blob" "43M"
jsonKeyTest "10M" "POST" "/leak?columns=blob" "41M" jsonKeyTest "10M" "POST" "/leak?columns=blob" "43M"
jsonKeyTest "10M" "PATCH" "/leak?id=eq.1&columns=blob" "41M" jsonKeyTest "10M" "PATCH" "/leak?id=eq.1&columns=blob" "43M"
jsonKeyTest "50M" "POST" "/rpc/leak?columns=blob" "171M" jsonKeyTest "50M" "POST" "/rpc/leak?columns=blob" "171M"
jsonKeyTest "50M" "POST" "/leak?columns=blob" "171M" jsonKeyTest "50M" "POST" "/leak?columns=blob" "171M"
jsonKeyTest "50M" "PATCH" "/leak?id=eq.1&columns=blob" "171M" jsonKeyTest "50M" "PATCH" "/leak?id=eq.1&columns=blob" "171M"
postJsonArrayTest "1000" "/perf_articles?columns=id,body" "11M" postJsonArrayTest "1000" "/perf_articles?columns=id,body" "13M"
postJsonArrayTest "10000" "/perf_articles?columns=id,body" "11M" postJsonArrayTest "10000" "/perf_articles?columns=id,body" "13M"
postJsonArrayTest "100000" "/perf_articles?columns=id,body" "21M" postJsonArrayTest "100000" "/perf_articles?columns=id,body" "23M"
trap - int term exit trap - int term exit
+1 -1
View File
@@ -523,7 +523,7 @@ spec actualPgVersion = describe "OpenAPI" $ do
types `shouldBe` Just [aesonQQ| types `shouldBe` Just [aesonQQ|
{ {
"format": "test.enum_menagerie_type", "format": "enum_menagerie_type",
"type": "string", "type": "string",
"enum": [ "enum": [
"foo", "foo",