Standardize indentation
This commit is contained in:
+40
-40
@@ -65,15 +65,15 @@ app conf reqBody req =
|
|||||||
let qt = qualify table
|
let qt = qualify table
|
||||||
from = fromMaybe 0 $ rangeOffset <$> range
|
from = fromMaybe 0 $ rangeOffset <$> range
|
||||||
query = B.Stmt "select " V.empty True <>
|
query = B.Stmt "select " V.empty True <>
|
||||||
parentheticT (
|
parentheticT (
|
||||||
whereT qt qq $ countRows qt
|
whereT qt qq $ countRows qt
|
||||||
) <> commaq <> (
|
) <> commaq <> (
|
||||||
bodyForAccept contentType qt
|
bodyForAccept contentType qt
|
||||||
. limitT range
|
. limitT range
|
||||||
. orderT (orderParse qq)
|
. orderT (orderParse qq)
|
||||||
. whereT qt qq
|
. whereT qt qq
|
||||||
$ select qt qq
|
$ select qt qq
|
||||||
)
|
)
|
||||||
row <- H.maybeEx query
|
row <- H.maybeEx query
|
||||||
let (tableTotal, queryTotal, body) =
|
let (tableTotal, queryTotal, body) =
|
||||||
fromMaybe (0, 0, Just "" :: Maybe Text) row
|
fromMaybe (0, 0, Just "" :: Maybe Text) row
|
||||||
@@ -81,14 +81,14 @@ app conf reqBody req =
|
|||||||
contentRange = contentRangeH from to tableTotal
|
contentRange = contentRangeH from to tableTotal
|
||||||
status = rangeStatus from to tableTotal
|
status = rangeStatus from to tableTotal
|
||||||
canonical = urlEncodeVars
|
canonical = urlEncodeVars
|
||||||
. sortBy (comparing fst)
|
. sortBy (comparing fst)
|
||||||
. 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",
|
||||||
"/" <> cs table <>
|
"/" <> cs table <>
|
||||||
if Prelude.null canonical then "" else "?" <> cs canonical
|
if Prelude.null canonical then "" else "?" <> cs canonical
|
||||||
)
|
)
|
||||||
] (cs $ fromMaybe "[]" body)
|
] (cs $ fromMaybe "[]" body)
|
||||||
@@ -119,8 +119,7 @@ app conf reqBody req =
|
|||||||
encode . object $ [("message", String "Failed to parse user.")]
|
encode . object $ [("message", String "Failed to parse user.")]
|
||||||
Just u -> do
|
Just u -> do
|
||||||
setRole authenticator
|
setRole authenticator
|
||||||
login <- signInRole (cs $ userId u)
|
login <- signInRole (cs $ userId u) (cs $ userPass u)
|
||||||
(cs $ userPass u)
|
|
||||||
case login of
|
case login of
|
||||||
LoginSuccess role uid ->
|
LoginSuccess role uid ->
|
||||||
return $ responseLBS status201 [ jsonH ] $
|
return $ responseLBS status201 [ jsonH ] $
|
||||||
@@ -133,15 +132,15 @@ app conf reqBody req =
|
|||||||
echoRequested = lookupHeader "Prefer" == Just "return=representation"
|
echoRequested = lookupHeader "Prefer" == Just "return=representation"
|
||||||
parsed :: Either String (V.Vector Text, V.Vector (V.Vector Value))
|
parsed :: Either String (V.Vector Text, V.Vector (V.Vector Value))
|
||||||
parsed = if lookupHeader "Content-Type" == Just csvMT
|
parsed = if lookupHeader "Content-Type" == Just csvMT
|
||||||
then do
|
then do
|
||||||
rows <- CSV.decode CSV.NoHeader reqBody
|
rows <- CSV.decode CSV.NoHeader reqBody
|
||||||
if V.null rows then Left "CSV requires header"
|
if V.null rows then Left "CSV requires header"
|
||||||
else Right (V.head rows, (V.map $ V.map $ parseCsvCell . cs) (V.tail rows))
|
else Right (V.head rows, (V.map $ V.map $ parseCsvCell . cs) (V.tail rows))
|
||||||
else eitherDecode reqBody >>= \val ->
|
else eitherDecode reqBody >>= \val ->
|
||||||
case val of
|
case val of
|
||||||
Object obj -> Right . second V.singleton . V.unzip . V.fromList $
|
Object obj -> Right . second V.singleton . V.unzip . V.fromList $
|
||||||
M.toList obj
|
M.toList obj
|
||||||
_ -> Left "Expecting single JSON object or CSV rows"
|
_ -> Left "Expecting single JSON object or CSV rows"
|
||||||
case parsed of
|
case parsed of
|
||||||
Left err -> return $ responseLBS status400 [] $
|
Left err -> return $ responseLBS status400 [] $
|
||||||
encode . object $ [("message", String $ "Failed to parse JSON payload. " <> cs err)]
|
encode . object $ [("message", String $ "Failed to parse JSON payload. " <> cs err)]
|
||||||
@@ -186,7 +185,7 @@ app conf reqBody req =
|
|||||||
let specifiedKeys = map (cs . fst) qq
|
let specifiedKeys = map (cs . fst) qq
|
||||||
if S.fromList primaryKeys /= S.fromList specifiedKeys
|
if S.fromList primaryKeys /= S.fromList specifiedKeys
|
||||||
then return $ responseLBS status405 []
|
then return $ responseLBS status405 []
|
||||||
"You must speficy all and only primary keys as params"
|
"You must speficy all and only primary keys as params"
|
||||||
else do
|
else do
|
||||||
tableCols <- map (cs . colName) <$> columns qt
|
tableCols <- map (cs . colName) <$> columns qt
|
||||||
let cols = map cs $ M.keys obj
|
let cols = map cs $ M.keys obj
|
||||||
@@ -194,21 +193,21 @@ app conf reqBody req =
|
|||||||
then do
|
then do
|
||||||
let vals = M.elems obj
|
let vals = M.elems obj
|
||||||
H.unitEx $ iffNotT
|
H.unitEx $ iffNotT
|
||||||
(whereT qt qq $ update qt cols vals)
|
(whereT qt qq $ update qt cols vals)
|
||||||
(insertSelect qt cols vals)
|
(insertSelect qt cols vals)
|
||||||
return $ responseLBS status204 [ jsonH ] ""
|
return $ responseLBS status204 [ jsonH ] ""
|
||||||
|
|
||||||
else return $ if Prelude.null tableCols
|
else return $ if Prelude.null tableCols
|
||||||
then responseLBS status404 [] ""
|
then responseLBS status404 [] ""
|
||||||
else responseLBS status400 []
|
else responseLBS status400 []
|
||||||
"You must specify all columns in PUT request"
|
"You must specify all columns in PUT request"
|
||||||
|
|
||||||
([table], "PATCH") ->
|
([table], "PATCH") ->
|
||||||
handleJsonObj reqBody $ \obj -> do
|
handleJsonObj reqBody $ \obj -> do
|
||||||
let qt = qualify table
|
let qt = qualify table
|
||||||
up = returningStarT
|
up = returningStarT
|
||||||
. whereT qt qq
|
. whereT qt qq
|
||||||
$ update qt (map cs $ M.keys obj) (M.elems obj)
|
$ update qt (map cs $ M.keys obj) (M.elems obj)
|
||||||
patch = withT up "t" $ B.Stmt
|
patch = withT up "t" $ B.Stmt
|
||||||
"select count(t), array_to_json(array_agg(row_to_json(t)))::character varying"
|
"select count(t), array_to_json(array_agg(row_to_json(t)))::character varying"
|
||||||
V.empty True
|
V.empty True
|
||||||
@@ -232,8 +231,8 @@ app conf reqBody req =
|
|||||||
row <- H.maybeEx del
|
row <- H.maybeEx del
|
||||||
let (Identity deletedCount) = fromMaybe (Identity 0 :: Identity Int) row
|
let (Identity deletedCount) = fromMaybe (Identity 0 :: Identity Int) row
|
||||||
return $ if deletedCount == 0
|
return $ if deletedCount == 0
|
||||||
then responseLBS status404 [] ""
|
then responseLBS status404 [] ""
|
||||||
else responseLBS status204 [("Content-Range", "*/"<> cs (show deletedCount))] ""
|
else responseLBS status204 [("Content-Range", "*/"<> cs (show deletedCount))] ""
|
||||||
|
|
||||||
(_, _) ->
|
(_, _) ->
|
||||||
return $ responseLBS status404 [] ""
|
return $ responseLBS status404 [] ""
|
||||||
@@ -271,19 +270,20 @@ contentRangeH from to total =
|
|||||||
("Content-Range",
|
("Content-Range",
|
||||||
if total == 0 || from > total
|
if total == 0 || from > total
|
||||||
then "*/" <> cs (show total)
|
then "*/" <> cs (show total)
|
||||||
else cs (show from) <> "-"
|
else cs (show from)
|
||||||
<> cs (show to) <> "/"
|
<> "-" <> cs (show to)
|
||||||
<> cs (show total)
|
<> "/" <> cs (show total)
|
||||||
)
|
)
|
||||||
|
|
||||||
requestedSchema :: Text -> Maybe BS.ByteString -> Text
|
requestedSchema :: Text -> Maybe BS.ByteString -> Text
|
||||||
requestedSchema v1schema accept =
|
requestedSchema v1schema accept =
|
||||||
case verStr of
|
case verStr of
|
||||||
Just [[_, ver]] -> if ver == "1" then v1schema else cs ver
|
Just [[_, ver]] -> if ver == "1" then v1schema else cs ver
|
||||||
_ -> v1schema
|
_ -> v1schema
|
||||||
|
|
||||||
where verRegex = "version[ ]*=[ ]*([0-9]+)" :: BS.ByteString
|
where
|
||||||
verStr = (=~ verRegex) <$> accept :: Maybe [[BS.ByteString]]
|
verRegex = "version[ ]*=[ ]*([0-9]+)" :: BS.ByteString
|
||||||
|
verStr = (=~ verRegex) <$> accept :: Maybe [[BS.ByteString]]
|
||||||
|
|
||||||
|
|
||||||
jsonMT :: BS.ByteString
|
jsonMT :: BS.ByteString
|
||||||
|
|||||||
@@ -56,10 +56,10 @@ setRole :: Text -> H.Tx P.Postgres s ()
|
|||||||
setRole role = H.unitEx $ B.Stmt ("set local role " <> cs (pgFmtLit role)) V.empty True
|
setRole role = H.unitEx $ B.Stmt ("set local role " <> cs (pgFmtLit role)) V.empty True
|
||||||
|
|
||||||
setUserId :: Text -> H.Tx P.Postgres s ()
|
setUserId :: Text -> H.Tx P.Postgres s ()
|
||||||
setUserId uid = if uid /= "" then
|
setUserId uid =
|
||||||
H.unitEx $ B.Stmt ("set local user_vars.user_id = " <> cs (pgFmtLit uid)) V.empty True
|
if uid /= ""
|
||||||
else
|
then H.unitEx $ B.Stmt ("set local user_vars.user_id = " <> cs (pgFmtLit uid)) V.empty True
|
||||||
resetUserId
|
else resetUserId
|
||||||
|
|
||||||
resetUserId :: H.Tx P.Postgres s ()
|
resetUserId :: H.Tx P.Postgres s ()
|
||||||
resetUserId = H.unitEx [H.stmt|reset user_vars.user_id|]
|
resetUserId = H.unitEx [H.stmt|reset user_vars.user_id|]
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ data AppConfig = AppConfig {
|
|||||||
, configSecure :: Bool
|
, configSecure :: Bool
|
||||||
, configPool :: Int
|
, configPool :: Int
|
||||||
, configV1Schema :: String
|
, configV1Schema :: String
|
||||||
|
|
||||||
, configJwtSecret :: String
|
, configJwtSecret :: String
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,9 +52,9 @@ corsPolicy req = case lookup "origin" headers of
|
|||||||
corsOrigins = Just ([origin], True)
|
corsOrigins = Just ([origin], True)
|
||||||
, corsRequestHeaders = "Authentication":accHeaders
|
, corsRequestHeaders = "Authentication":accHeaders
|
||||||
, corsExposedHeaders = Just [
|
, corsExposedHeaders = Just [
|
||||||
"Content-Encoding", "Content-Location", "Content-Range", "Content-Type"
|
"Content-Encoding", "Content-Location", "Content-Range", "Content-Type"
|
||||||
, "Date", "Location", "Server", "Transfer-Encoding", "Range-Unit"
|
, "Date", "Location", "Server", "Transfer-Encoding", "Range-Unit"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Nothing -> Nothing
|
Nothing -> Nothing
|
||||||
where
|
where
|
||||||
|
|||||||
+13
-10
@@ -36,12 +36,12 @@ main = do
|
|||||||
hSetBuffering stderr NoBuffering
|
hSetBuffering stderr NoBuffering
|
||||||
|
|
||||||
let opts = info (helper <*> argParser) $
|
let opts = info (helper <*> argParser) $
|
||||||
fullDesc
|
fullDesc
|
||||||
<> progDesc (
|
<> progDesc (
|
||||||
"PostgREST "
|
"PostgREST "
|
||||||
<> prettyVersion
|
<> prettyVersion
|
||||||
<> " / create a REST API to an existing Postgres database"
|
<> " / create a REST API to an existing Postgres database"
|
||||||
)
|
)
|
||||||
parserPrefs = prefs showHelpOnError
|
parserPrefs = prefs showHelpOnError
|
||||||
conf <- customExecParser parserPrefs opts
|
conf <- customExecParser parserPrefs opts
|
||||||
let port = configPort conf
|
let port = configPort conf
|
||||||
@@ -64,12 +64,15 @@ main = do
|
|||||||
middle = logStdout . defaultMiddle (configSecure conf)
|
middle = logStdout . defaultMiddle (configSecure conf)
|
||||||
|
|
||||||
poolSettings <- maybe (fail "Improper session settings") return $
|
poolSettings <- maybe (fail "Improper session settings") return $
|
||||||
H.poolSettings (fromIntegral $ configPool conf) 30
|
H.poolSettings (fromIntegral $ configPool conf) 30
|
||||||
pool :: H.Pool P.Postgres
|
pool :: H.Pool P.Postgres <- H.acquirePool pgSettings poolSettings
|
||||||
<- H.acquirePool pgSettings poolSettings
|
|
||||||
|
|
||||||
resOrError <- H.session pool isServerVersionSupported
|
resOrError <- H.session pool isServerVersionSupported
|
||||||
either (fail . show) (\supported -> unless supported $ fail "Cannot run in this PostgreSQL version, PostgREST needs at least 9.2.0") resOrError
|
either (fail . show)
|
||||||
|
(\supported ->
|
||||||
|
unless supported $
|
||||||
|
fail "Cannot run in this PostgreSQL version, PostgREST needs at least 9.2.0"
|
||||||
|
) resOrError
|
||||||
|
|
||||||
runSettings appSettings $ middle $ \req respond -> do
|
runSettings appSettings $ middle $ \req respond -> do
|
||||||
body <- strictRequestBody req
|
body <- strictRequestBody req
|
||||||
|
|||||||
@@ -78,12 +78,12 @@ redirectInsecure app req respond = do
|
|||||||
|
|
||||||
if not (isSecure req || isHerokuSecure)
|
if not (isSecure req || isHerokuSecure)
|
||||||
then case uriM of
|
then case uriM of
|
||||||
Just uri ->
|
Just uri ->
|
||||||
respond $ responseLBS status301 [
|
respond $ responseLBS status301 [
|
||||||
(hLocation, cs . show $ uri { uriScheme = "https:" })
|
(hLocation, cs . show $ uri { uriScheme = "https:" })
|
||||||
] ""
|
] ""
|
||||||
Nothing ->
|
Nothing ->
|
||||||
respond $ responseLBS status400 [] "SSL is required"
|
respond $ responseLBS status400 [] "SSL is required"
|
||||||
else app req respond
|
else app req respond
|
||||||
|
|
||||||
unsupportedAccept :: Application -> Application
|
unsupportedAccept :: Application -> Application
|
||||||
@@ -96,6 +96,6 @@ unsupportedAccept app req respond = do
|
|||||||
|
|
||||||
defaultMiddle :: Bool -> Application -> Application
|
defaultMiddle :: Bool -> Application -> Application
|
||||||
defaultMiddle secure = (if secure then redirectInsecure else id)
|
defaultMiddle secure = (if secure then redirectInsecure else id)
|
||||||
. gzip def . cors corsPolicy
|
. gzip def . cors corsPolicy
|
||||||
. staticPolicy (only [("favicon.ico", "static/favicon.ico")])
|
. staticPolicy (only [("favicon.ico", "static/favicon.ico")])
|
||||||
. unsupportedAccept
|
. unsupportedAccept
|
||||||
|
|||||||
+78
-72
@@ -54,13 +54,13 @@ limitT r q =
|
|||||||
|
|
||||||
whereT :: QualifiedIdentifier -> Net.Query -> StatementT
|
whereT :: QualifiedIdentifier -> Net.Query -> StatementT
|
||||||
whereT table params q =
|
whereT table params q =
|
||||||
if L.null cols
|
if L.null cols
|
||||||
then q
|
then q
|
||||||
else q <> B.Stmt " where " empty True <> conjunction
|
else q <> B.Stmt " where " empty True <> conjunction
|
||||||
where
|
where
|
||||||
cols = [ col | col <- params, fst col `notElem` ["order","select"] ]
|
cols = [ col | col <- params, fst col `notElem` ["order","select"] ]
|
||||||
wherePredTable = wherePred table
|
wherePredTable = wherePred table
|
||||||
conjunction = mconcat $ L.intersperse andq (map wherePredTable cols)
|
conjunction = mconcat $ L.intersperse andq (map wherePredTable cols)
|
||||||
|
|
||||||
withT :: PStmt -> T.Text -> StatementT
|
withT :: PStmt -> T.Text -> StatementT
|
||||||
withT (B.Stmt eq ep epre) v (B.Stmt wq wp wpre) =
|
withT (B.Stmt eq ep epre) v (B.Stmt wq wp wpre) =
|
||||||
@@ -74,13 +74,13 @@ orderT ts q =
|
|||||||
then q
|
then q
|
||||||
else q <> B.Stmt " order by " empty True <> clause
|
else q <> B.Stmt " order by " empty True <> clause
|
||||||
where
|
where
|
||||||
clause = mconcat $ L.intersperse commaq (map queryTerm ts)
|
clause = mconcat $ L.intersperse commaq (map queryTerm ts)
|
||||||
queryTerm :: OrderTerm -> PStmt
|
queryTerm :: OrderTerm -> PStmt
|
||||||
queryTerm t = B.Stmt
|
queryTerm t = B.Stmt
|
||||||
(" " <> cs (pgFmtIdent $ otTerm t) <> " "
|
(" " <> cs (pgFmtIdent $ otTerm t) <> " "
|
||||||
<> cs (otDirection t) <> " "
|
<> cs (otDirection t) <> " "
|
||||||
<> maybe "" cs (otNullOrder t) <> " ")
|
<> maybe "" cs (otNullOrder t) <> " ")
|
||||||
empty True
|
empty True
|
||||||
|
|
||||||
parentheticT :: StatementT
|
parentheticT :: StatementT
|
||||||
parentheticT s =
|
parentheticT s =
|
||||||
@@ -105,7 +105,8 @@ asCsvWithCount :: QualifiedIdentifier -> StatementT
|
|||||||
asCsvWithCount table = withCount . asCsv table
|
asCsvWithCount table = withCount . asCsv table
|
||||||
|
|
||||||
asCsv :: QualifiedIdentifier -> StatementT
|
asCsv :: QualifiedIdentifier -> StatementT
|
||||||
asCsv table s = s { B.stmtTemplate =
|
asCsv table s = s {
|
||||||
|
B.stmtTemplate =
|
||||||
"(select string_agg(quote_ident(column_name::text), ',') from "
|
"(select string_agg(quote_ident(column_name::text), ',') from "
|
||||||
<> "(select column_name from information_schema.columns where quote_ident(table_schema) || '.' || table_name = '"
|
<> "(select column_name from information_schema.columns where quote_ident(table_schema) || '.' || table_name = '"
|
||||||
<> fromQi table <> "' order by ordinal_position) h) || '\r' || "
|
<> fromQi table <> "' order by ordinal_position) h) || '\r' || "
|
||||||
@@ -116,7 +117,8 @@ asJsonWithCount :: StatementT
|
|||||||
asJsonWithCount = withCount . asJson
|
asJsonWithCount = withCount . asJson
|
||||||
|
|
||||||
asJson :: StatementT
|
asJson :: StatementT
|
||||||
asJson s = s { B.stmtTemplate =
|
asJson s = s {
|
||||||
|
B.stmtTemplate =
|
||||||
"array_to_json(array_agg(row_to_json(t)))::character varying from ("
|
"array_to_json(array_agg(row_to_json(t)))::character varying from ("
|
||||||
<> B.stmtTemplate s <> ") t" }
|
<> B.stmtTemplate s <> ") t" }
|
||||||
|
|
||||||
@@ -131,26 +133,30 @@ selectStar t = B.Stmt ("select * from " <> fromQi t) empty True
|
|||||||
|
|
||||||
select :: QualifiedIdentifier -> Net.Query -> PStmt
|
select :: QualifiedIdentifier -> Net.Query -> PStmt
|
||||||
select table params =
|
select table params =
|
||||||
if L.null cols
|
if L.null cols
|
||||||
then selectStar table
|
then selectStar table
|
||||||
else B.Stmt "select " empty True <> conjunction <> B.Stmt (" from " <> fromQi table ) empty True
|
else B.Stmt "select " empty True <> conjunction <> B.Stmt (" from " <> fromQi table ) empty True
|
||||||
where
|
where
|
||||||
selectTermTable = selectTerm table
|
selectTermTable = selectTerm table
|
||||||
conjunction = mconcat $ L.intersperse commaq (map selectTermTable cols)
|
conjunction = mconcat $ L.intersperse commaq (map selectTermTable cols)
|
||||||
columnsParam = fromMaybe "" $ join (lookup "select" params)
|
columnsParam = fromMaybe "" $ join (lookup "select" params)
|
||||||
cols = filter ((>0) . T.length) $ map T.strip $ T.split (==',') $ cs columnsParam
|
cols = filter ((>0) . T.length) $ map T.strip $ T.split (==',') $ cs columnsParam
|
||||||
|
|
||||||
selectTerm :: QualifiedIdentifier -> T.Text -> PStmt
|
selectTerm :: QualifiedIdentifier -> T.Text -> PStmt
|
||||||
selectTerm table col =
|
selectTerm table col =
|
||||||
case T.splitOn "::" col of
|
case T.splitOn "::" col of
|
||||||
[colName,castTo] -> B.Stmt ("CAST (" <> pgFmtJsonbPath table (cs colName) <> " AS " <> castToSafe <> " )" <> asT (jsonbPath colName)) empty True
|
[colName,castTo] ->
|
||||||
where castToSafe = T.filter ( `elem` ['a'..'z'] ) castTo
|
B.Stmt (
|
||||||
_-> B.Stmt (pgFmtJsonbPath table (cs col) <> asT (jsonbPath col)) empty True
|
"CAST (" <> pgFmtJsonbPath table (cs colName) <> " AS "
|
||||||
where
|
<> castToSafe <> " )" <> asT (jsonbPath colName)
|
||||||
jsonbPath :: T.Text -> Maybe JsonbPath
|
) empty True
|
||||||
jsonbPath c = parseJsonbPath $ cs c
|
where castToSafe = T.filter ( `elem` ['a'..'z'] ) castTo
|
||||||
asT (Just (DoubleArrow _ (KeyIdentifier key))) = " AS " <> pgFmtIdent key
|
_ -> B.Stmt (pgFmtJsonbPath table (cs col) <> asT (jsonbPath col)) empty True
|
||||||
asT _ = ""
|
where
|
||||||
|
jsonbPath :: T.Text -> Maybe JsonbPath
|
||||||
|
jsonbPath c = parseJsonbPath $ cs c
|
||||||
|
asT (Just (DoubleArrow _ (KeyIdentifier key))) = " AS " <> pgFmtIdent key
|
||||||
|
asT _ = ""
|
||||||
|
|
||||||
returningStarT :: StatementT
|
returningStarT :: StatementT
|
||||||
returningStarT s = s { B.stmtTemplate = B.stmtTemplate s <> " RETURNING *" }
|
returningStarT s = s { B.stmtTemplate = B.stmtTemplate s <> " RETURNING *" }
|
||||||
@@ -212,39 +218,39 @@ wherePred table (col, predicate) =
|
|||||||
|
|
||||||
where
|
where
|
||||||
headPredicate:rest = T.split (=='.') $ cs $ fromMaybe "." predicate
|
headPredicate:rest = T.split (=='.') $ cs $ fromMaybe "." predicate
|
||||||
hasNot caseTrue caseFalse = if headPredicate == "not" then caseTrue else caseFalse
|
hasNot caseTrue caseFalse = if headPredicate == "not" then caseTrue else caseFalse
|
||||||
opCode = hasNot (head rest) headPredicate
|
opCode = hasNot (head rest) headPredicate
|
||||||
notOp = hasNot headPredicate ""
|
notOp = hasNot headPredicate ""
|
||||||
value = hasNot (T.intercalate "." $ tail rest) (T.intercalate "." rest)
|
value = hasNot (T.intercalate "." $ tail rest) (T.intercalate "." rest)
|
||||||
whiteList val = fromMaybe (cs (pgFmtLit val) <> "::unknown ")
|
whiteList val = fromMaybe
|
||||||
(L.find ((==) . T.toLower $ val)
|
(cs (pgFmtLit val) <> "::unknown ")
|
||||||
["null","true","false"])
|
(L.find ((==) . T.toLower $ val) ["null","true","false"])
|
||||||
star c = if c == '*' then '%' else c
|
star c = if c == '*' then '%' else c
|
||||||
unknownLiteral = (<> "::unknown ") . pgFmtLit
|
unknownLiteral = (<> "::unknown ") . pgFmtLit
|
||||||
|
|
||||||
sqlValue = case opCode of
|
sqlValue = case opCode of
|
||||||
"like" -> unknownLiteral $ T.map star value
|
"like" -> unknownLiteral $ T.map star value
|
||||||
"ilike" -> unknownLiteral $ T.map star value
|
"ilike" -> unknownLiteral $ T.map star value
|
||||||
"in" -> "(" <> T.intercalate ", " (map unknownLiteral $ T.split (==',') value) <> ") "
|
"in" -> "(" <> T.intercalate ", " (map unknownLiteral $ T.split (==',') value) <> ") "
|
||||||
"notin" -> "(" <> T.intercalate ", " (map unknownLiteral $ T.split (==',') value) <> ") "
|
"notin" -> "(" <> T.intercalate ", " (map unknownLiteral $ T.split (==',') value) <> ") "
|
||||||
"@@" -> "to_tsquery(" <> unknownLiteral value <> ") "
|
"@@" -> "to_tsquery(" <> unknownLiteral value <> ") "
|
||||||
_ -> unknownLiteral value
|
_ -> unknownLiteral value
|
||||||
|
|
||||||
op = case opCode of
|
op = case opCode of
|
||||||
"eq" -> "="
|
"eq" -> "="
|
||||||
"gt" -> ">"
|
"gt" -> ">"
|
||||||
"lt" -> "<"
|
"lt" -> "<"
|
||||||
"gte" -> ">="
|
"gte" -> ">="
|
||||||
"lte" -> "<="
|
"lte" -> "<="
|
||||||
"neq" -> "<>"
|
"neq" -> "<>"
|
||||||
"like"-> "like"
|
"like"-> "like"
|
||||||
"ilike"-> "ilike"
|
"ilike"-> "ilike"
|
||||||
"in" -> "in"
|
"in" -> "in"
|
||||||
"notin" -> "not in"
|
"notin" -> "not in"
|
||||||
"is" -> "is"
|
"is" -> "is"
|
||||||
"isnot" -> "is not"
|
"isnot" -> "is not"
|
||||||
"@@" -> "@@"
|
"@@" -> "@@"
|
||||||
_ -> "="
|
_ -> "="
|
||||||
|
|
||||||
orderParse :: Net.Query -> [OrderTerm]
|
orderParse :: Net.Query -> [OrderTerm]
|
||||||
orderParse q =
|
orderParse q =
|
||||||
@@ -255,18 +261,18 @@ orderParse q =
|
|||||||
orderParseTerm :: T.Text -> Maybe OrderTerm
|
orderParseTerm :: T.Text -> Maybe OrderTerm
|
||||||
orderParseTerm s =
|
orderParseTerm s =
|
||||||
case T.split (=='.') s of
|
case T.split (=='.') s of
|
||||||
(c:d:nls) ->
|
(c:d:nls) ->
|
||||||
if d `elem` ["asc", "desc"]
|
if d `elem` ["asc", "desc"]
|
||||||
then Just $ OrderTerm c
|
then Just $ OrderTerm c
|
||||||
( if d == "asc" then "asc" else "desc" )
|
( if d == "asc" then "asc" else "desc" )
|
||||||
( case nls of
|
( case nls of
|
||||||
[n] -> if | n == "nullsfirst" -> Just "nulls first"
|
[n] -> if | n == "nullsfirst" -> Just "nulls first"
|
||||||
| n == "nullslast" -> Just "nulls last"
|
| n == "nullslast" -> Just "nulls last"
|
||||||
| otherwise -> Nothing
|
| otherwise -> Nothing
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
)
|
)
|
||||||
else Nothing
|
else Nothing
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
|
|
||||||
commaq :: PStmt
|
commaq :: PStmt
|
||||||
commaq = B.Stmt ", " empty True
|
commaq = B.Stmt ", " empty True
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ tables :: Text -> H.Tx P.Postgres s [Table]
|
|||||||
tables schema = do
|
tables schema = do
|
||||||
rows <- H.listEx $
|
rows <- H.listEx $
|
||||||
[H.stmt|
|
[H.stmt|
|
||||||
select
|
select
|
||||||
n.nspname as table_schema,
|
n.nspname as table_schema,
|
||||||
relname as table_name,
|
relname as table_name,
|
||||||
c.relkind = 'r' or (c.relkind IN ('v', 'f')) and (pg_relation_is_updatable(c.oid::regclass, false) & 8) = 8
|
c.relkind = 'r' or (c.relkind IN ('v', 'f')) and (pg_relation_is_updatable(c.oid::regclass, false) & 8) = 8
|
||||||
or (exists (
|
or (exists (
|
||||||
@@ -52,16 +52,17 @@ tables schema = do
|
|||||||
from pg_trigger
|
from pg_trigger
|
||||||
where pg_trigger.tgrelid = c.oid and (pg_trigger.tgtype::integer & 69) = 69)
|
where pg_trigger.tgrelid = c.oid and (pg_trigger.tgtype::integer & 69) = 69)
|
||||||
) as insertable
|
) as insertable
|
||||||
from
|
from
|
||||||
pg_class c
|
pg_class c
|
||||||
join pg_namespace n on n.oid = c.relnamespace
|
join pg_namespace n on n.oid = c.relnamespace
|
||||||
where
|
where
|
||||||
c.relkind in ('v', 'r', 'm')
|
c.relkind in ('v', 'r', 'm')
|
||||||
and n.nspname = ?
|
and n.nspname = ?
|
||||||
and (
|
and (
|
||||||
pg_has_role(c.relowner, 'USAGE'::text)
|
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)
|
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
|
order by relname
|
||||||
|] schema
|
|] schema
|
||||||
return $ map tableFromRow rows
|
return $ map tableFromRow rows
|
||||||
@@ -71,31 +72,31 @@ columns :: QualifiedIdentifier -> H.Tx P.Postgres s [Column]
|
|||||||
columns table = do
|
columns table = do
|
||||||
cols <- H.listEx $ [H.stmt|
|
cols <- H.listEx $ [H.stmt|
|
||||||
select info.table_schema as schema, info.table_name as table_name,
|
select info.table_schema as schema, info.table_name as table_name,
|
||||||
info.column_name as name, info.ordinal_position as position,
|
info.column_name as name, info.ordinal_position as position,
|
||||||
info.is_nullable::boolean as nullable, info.data_type as col_type,
|
info.is_nullable::boolean as nullable, info.data_type as col_type,
|
||||||
info.is_updatable::boolean as updatable,
|
info.is_updatable::boolean as updatable,
|
||||||
info.character_maximum_length as max_len,
|
info.character_maximum_length as max_len,
|
||||||
info.numeric_precision as precision,
|
info.numeric_precision as precision,
|
||||||
info.column_default as default_value,
|
info.column_default as default_value,
|
||||||
array_to_string(enum_info.vals, ',') as enum
|
array_to_string(enum_info.vals, ',') as enum
|
||||||
from (
|
from (
|
||||||
select table_schema, table_name, column_name, ordinal_position,
|
select table_schema, table_name, column_name, ordinal_position,
|
||||||
is_nullable, data_type, is_updatable,
|
is_nullable, data_type, is_updatable,
|
||||||
character_maximum_length, numeric_precision,
|
character_maximum_length, numeric_precision,
|
||||||
column_default, udt_name
|
column_default, udt_name
|
||||||
from information_schema.columns
|
from information_schema.columns
|
||||||
where table_schema = ? and table_name = ?
|
where table_schema = ? and table_name = ?
|
||||||
) as info
|
) as info
|
||||||
left outer join (
|
left outer join (
|
||||||
select n.nspname as s,
|
select n.nspname as s,
|
||||||
t.typname as n,
|
t.typname as n,
|
||||||
array_agg(e.enumlabel ORDER BY e.enumsortorder) as vals
|
array_agg(e.enumlabel ORDER BY e.enumsortorder) as vals
|
||||||
from pg_type t
|
from pg_type t
|
||||||
join pg_enum e on t.oid = e.enumtypid
|
join pg_enum e on t.oid = e.enumtypid
|
||||||
join pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
join pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
||||||
group by s, n
|
group by s, n
|
||||||
) as enum_info
|
) as enum_info
|
||||||
on (info.udt_name = enum_info.n)
|
on (info.udt_name = enum_info.n)
|
||||||
order by position |]
|
order by position |]
|
||||||
(qiSchema table) (qiName table)
|
(qiSchema table) (qiName table)
|
||||||
|
|
||||||
|
|||||||
@@ -41,15 +41,15 @@ rangeRequested = (rangeParse =<<) . lookup hRange
|
|||||||
|
|
||||||
rangeLimit :: NonnegRange -> Maybe Int
|
rangeLimit :: NonnegRange -> Maybe Int
|
||||||
rangeLimit range =
|
rangeLimit range =
|
||||||
case [rangeLower range, rangeUpper range]
|
case [rangeLower range, rangeUpper range] of
|
||||||
of [BoundaryBelow from, BoundaryAbove to] -> Just (1 + to - from)
|
[BoundaryBelow from, BoundaryAbove to] -> Just (1 + to - from)
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
|
|
||||||
rangeOffset :: NonnegRange -> Int
|
rangeOffset :: NonnegRange -> Int
|
||||||
rangeOffset range =
|
rangeOffset range =
|
||||||
case rangeLower range
|
case rangeLower range of
|
||||||
of BoundaryBelow from -> from
|
BoundaryBelow from -> from
|
||||||
_ -> error "range without lower bound" -- should never happen
|
_ -> error "range without lower bound" -- should never happen
|
||||||
|
|
||||||
rangeGeq :: Int -> NonnegRange
|
rangeGeq :: Int -> NonnegRange
|
||||||
rangeGeq n =
|
rangeGeq n =
|
||||||
|
|||||||
Reference in New Issue
Block a user