first fully functioning build, some tests are failing (limit,order,csv not implemented yet)

This commit is contained in:
Ruslan Talpa
2015-09-24 11:45:34 +03:00
parent fb92b76a1a
commit 448a81dff8
3 changed files with 107 additions and 45 deletions
+53 -28
View File
@@ -81,33 +81,13 @@ app dbstructure conf reqBody role req =
([table], "GET") -> ([table], "GET") ->
if range == Just emptyRange if range == Just emptyRange
then return $ responseLBS status416 [] "HTTP Range error" then return $ responseLBS status416 [] "HTTP Range error"
else do else
let apiRequest = parseGetRequest req case query of
dbRequest = first formatParserError apiRequest Left e -> return $ responseLBS status200 [("Content-Type", "text/plain")] $ cs e
>>= traverse (requestNodeToQuery schema allTables allColumns) Right qs -> do
>>= addRelations allRelations Nothing let q = B.Stmt qs V.empty True
>>= addJoinConditions allColumns row <- H.maybeEx q
where formatParserError = pack.show let (tableTotal, queryTotal, body) = fromMaybe (0::Int, 0::Int, Just "" :: Maybe Text) row
query = dbRequestToQuery <$> dbRequest
body = show query
return $ responseLBS status200 [] $ cs body
{--
let qt = qualify table
from = fromMaybe 0 $ rangeOffset <$> range
query = B.Stmt "select " V.empty True <>
parentheticT (
whereT qt qq $ countRows qt
) <> commaq <> (
bodyForAccept contentType qt
. limitT range
. orderT (orderParse qq)
. whereT qt qq
$ select qt qq
)
row <- H.maybeEx query
let (tableTotal, queryTotal, body) =
fromMaybe (0, 0, Just "" :: Maybe Text) row
to = from+queryTotal-1 to = from+queryTotal-1
contentRange = contentRangeH from to tableTotal contentRange = contentRangeH from to tableTotal
status = rangeStatus from to tableTotal status = rangeStatus from to tableTotal
@@ -116,6 +96,8 @@ app dbstructure conf reqBody role req =
. map (join (***) cs) . map (join (***) cs)
. parseSimpleQuery . parseSimpleQuery
$ rawQueryString req $ rawQueryString req
return $ responseLBS status return $ responseLBS status
[contentTypeH, contentRange, [contentTypeH, contentRange,
("Content-Location", ("Content-Location",
@@ -123,7 +105,50 @@ app dbstructure conf reqBody role req =
if Prelude.null canonical then "" else "?" <> cs canonical if Prelude.null canonical then "" else "?" <> cs canonical
) )
] (cs $ fromMaybe "[]" body) ] (cs $ fromMaybe "[]" body)
--}
where
from = fromMaybe 0 $ rangeOffset <$> range
apiRequest = parseGetRequest req
dbRequest = first formatParserError apiRequest
>>= traverse (requestNodeToQuery schema allTables allColumns)
>>= addRelations allRelations Nothing
>>= addJoinConditions allColumns
where formatParserError = pack.show
query = dbRequestToQuery <$> dbRequest
--
-- let qt = qualify table
-- from = fromMaybe 0 $ rangeOffset <$> range
-- query = B.Stmt "select " V.empty True <>
-- parentheticT (
-- whereT qt qq $ countRows qt
-- ) <> commaq <> (
-- bodyForAccept contentType qt
-- . limitT range
-- . orderT (orderParse qq)
-- . whereT qt qq
-- $ select qt qq
-- )
-- row <- H.maybeEx query
-- let (tableTotal, queryTotal, body) =
-- fromMaybe (0, 0, Just "" :: Maybe Text) row
-- to = from+queryTotal-1
-- contentRange = contentRangeH from to tableTotal
-- status = rangeStatus from to tableTotal
-- canonical = urlEncodeVars
-- . sortBy (comparing fst)
-- . map (join (***) cs)
-- . parseSimpleQuery
-- $ rawQueryString req
-- return $ responseLBS status
-- [contentTypeH, contentRange,
-- ("Content-Location",
-- "/" <> cs table <>
-- if Prelude.null canonical then "" else "?" <> cs canonical
-- )
-- ] (cs $ fromMaybe "[]" body)
(["postgrest", "users"], "POST") -> do (["postgrest", "users"], "POST") -> do
let user = decode reqBody :: Maybe AuthUser let user = decode reqBody :: Maybe AuthUser
+27 -7
View File
@@ -6,9 +6,9 @@ import PostgREST.Types
import Control.Error import Control.Error
import Data.List (find) import Data.List (find)
import Data.Tree import Data.Tree
import Data.Text hiding (find, foldr, map, null, last) import Data.Text hiding (find, foldr, map, null, last, head)
import Data.Monoid import Data.Monoid
import PostgREST.PgQuery (pgFmtOperator, pgFmtValue, pgFmtIdent, pgFmtLit, fromQi, QualifiedIdentifier(..)) import PostgREST.PgQuery (pgFmtOperator, pgFmtValue, pgFmtIdent, pgFmtLit, fromQi, whiteList, QualifiedIdentifier(..))
findColumn :: [Column] -> Text -> Text -> Text -> Either Text Column findColumn :: [Column] -> Text -> Text -> Text -> Either Text Column
@@ -95,10 +95,22 @@ addJoinConditions allColumns (Node query@(Select{qRelation=relation}) forest) =
addCond q con = q{qWhere=con:qWhere q} addCond q con = q{qWhere=con:qWhere q}
dbRequestToCountQuery :: DbRequest -> Text
dbRequestToCountQuery (Node (Select mainTable columns tables conditions relation) forest) =
Data.Text.unwords [
"SELECT pg_catalog.count(1)",
"FROM ", pgFmtTable mainTable,
("WHERE " <> intercalate " AND " ( map pgFmtCondition conditions )) `emptyOnNull` conditions
]
where emptyOnNull val x = if null x then "" else val
dbRequestToQuery :: DbRequest -> Text dbRequestToQuery :: DbRequest -> Text
dbRequestToQuery (Node (Select mainTable columns tables conditions relation) forest) = dbRequestToQuery r@(Node (Select mainTable columns tables conditions relation) forest) =
case relation of case relation of
Nothing -> "SELECT " Nothing -> "SELECT "
<> "("
<> dbRequestToCountQuery r
<> "),"
<> "pg_catalog.count(t)," <> "pg_catalog.count(t),"
<> "array_to_json(array_agg(row_to_json(t)))::CHARACTER VARYING AS json " <> "array_to_json(array_agg(row_to_json(t)))::CHARACTER VARYING AS json "
<> "FROM (" <> "FROM ("
@@ -139,12 +151,20 @@ dbRequestToQuery (Node (Select mainTable columns tables conditions relation) for
getQueryParts (Node (Select{qRelation=(Just (Relation {relType=_}))}) _) _ = undefined getQueryParts (Node (Select{qRelation=(Just (Relation {relType=_}))}) _) _ = undefined
pgFmtCondition :: Condition -> Text pgFmtCondition :: Condition -> Text
pgFmtCondition (Condition (col,jp) ops val) = pgFmtColumn col <> pgFmtJsonPath jp <> opToStr op <> valToStr val pgFmtCondition (Condition (col,jp) ops val) =
notOp <> " " <> pgFmtColumn col <> pgFmtJsonPath jp <> " " <> pgFmtOperator opCode <> " " <>
if opCode `elem` ["is","isnot"] then whiteList (getInner val) else sqlValue
where where
op = pack ops headPredicate:rest = split (=='.') $ pack ops
opToStr o = pgFmtOperator o hasNot caseTrue caseFalse = if headPredicate == "not" then caseTrue else caseFalse
opCode = hasNot (head rest) headPredicate
notOp = hasNot headPredicate ""
sqlValue = valToStr val
getInner v = case v of
VText s -> s
_ -> ""
valToStr v = case v of valToStr v = case v of
VText s -> pgFmtValue op s VText s -> pgFmtValue opCode s
VForeignKey (Relation{relFTable=table, relFColumn=column}) -> table <> "." <> column VForeignKey (Relation{relFTable=table, relFColumn=column}) -> table <> "." <> column
pgFmtColumn :: Column -> Text pgFmtColumn :: Column -> Text
+19 -2
View File
@@ -35,7 +35,7 @@ parseGetRequest httpRequest =
rootTableName = cs $ head $ pathInfo httpRequest -- TODO unsafe head rootTableName = cs $ head $ pathInfo httpRequest -- TODO unsafe head
qString = [(cs k, cs <$> v)|(k,v) <- queryString httpRequest] qString = [(cs k, cs <$> v)|(k,v) <- queryString httpRequest]
selectStr = fromMaybe "*" $ fromMaybe (Just "*") $ lookup "select" qString --in case the parametre is missing or empty we default to * selectStr = fromMaybe "*" $ fromMaybe (Just "*") $ lookup "select" qString --in case the parametre is missing or empty we default to *
whereFilters = [ (k, fromJust v) | (k,v) <- qString, k `notElem` ["select"], isJust v ] whereFilters = [ (k, fromJust v) | (k,v) <- qString, k `notElem` ["select", "order"], isJust v ]
pRequestSelect :: String -> Parser ApiRequest pRequestSelect :: String -> Parser ApiRequest
pRequestSelect rootNodeName = do pRequestSelect rootNodeName = do
@@ -136,6 +136,19 @@ pOperator :: Parser Operator
pOperator = try (string "eq") pOperator = try (string "eq")
<|> try (string "gt") <|> try (string "gt")
<|> try (string "lt") <|> try (string "lt")
<|> try (string "eq")
<|> try (string "gt")
<|> try (string "lt")
<|> try (string "gte")
<|> try (string "lte")
<|> try (string "neq")
<|> try (string "like")
<|> try (string "ilike")
<|> try (string "in")
<|> try (string "notin")
<|> try (string "is" )
<|> try (string "isnot")
<|> try (string "@@")
<?> "operator (eq, gt, ...)" <?> "operator (eq, gt, ...)"
pInt :: Parser Int pInt :: Parser Int
@@ -151,4 +164,8 @@ pDelimiter :: Parser Char
pDelimiter = char '.' <?> "delimiter (.)" pDelimiter = char '.' <?> "delimiter (.)"
pOpValueExp :: Parser (Operator, FValue) pOpValueExp :: Parser (Operator, FValue)
pOpValueExp = liftA2 (,) pOperator (pDelimiter *> pValue) pOpValueExp = do
o <- ( try ( liftA2 (++) (string "not.") pOperator) <|> pOperator )
pDelimiter
v <- pValue
return (o, v)