Merge pull request #895 from steve-chavez/binary-proc

Binary output support for RPC
This commit is contained in:
Joe Nelson
2017-07-07 22:34:06 -05:00
committed by GitHub
7 changed files with 129 additions and 50 deletions
+18 -7
View File
@@ -232,15 +232,25 @@ app dbStructure conf apiRequest =
return $ responseLBS status200 [allOrigins, acceptH] ""
(ActionInvoke, TargetProc qi, Just (PayloadJSON payload)) ->
case readSqlParts of
let proc = M.lookup (qiName qi) allProcs
returnsScalar = case proc of
Just ProcDescription{pdReturnType = (Single (Scalar _))} -> True
_ -> False
rpcBinaryField = if returnsScalar
then Right Nothing
else binaryField contentType =<< fldNames
partsField = (,) <$> readSqlParts <*> rpcBinaryField in
case partsField of
Left errorResponse -> return errorResponse
Right (q, cq) -> do
Right ((q, cq), bField) -> do
let p = V.head payload
singular = contentType == CTSingularJSON
paramsAsSingleObject = iPreferSingleObjectParameter apiRequest
row <- H.query () $
callProc qi p q cq topLevelRange shouldCount singular
paramsAsSingleObject (contentType == CTTextCSV)
callProc qi p returnsScalar q cq topLevelRange shouldCount
singular paramsAsSingleObject
(contentType == CTTextCSV)
(contentType == CTOctetStream) bField
let (tableTotal, queryTotal, body) =
fromMaybe (Just 0, 0, "[]") row
(status, contentRange) = rangeHeader queryTotal tableTotal
@@ -257,7 +267,7 @@ app dbStructure conf apiRequest =
uri Nothing = ("http", host, port, "/")
uri (Just Proxy { proxyScheme = s, proxyHost = h, proxyPort = p, proxyPath = b }) = (s, h, p, b)
uri' = uri proxy
encodeApi ti = encodeOpenAPI (M.elems $ dbProcs dbStructure) ti uri'
encodeApi ti = encodeOpenAPI (M.elems allProcs) ti uri'
body <- encodeApi . toTableInfo <$> H.query schema accessibleTables
return $ responseLBS status200 [toHeader CTOpenAPI] $ toS body
@@ -276,6 +286,7 @@ app dbStructure conf apiRequest =
filterCol :: Schema -> TableName -> Column -> Bool
filterCol sc tb Column{colTable=Table{tableSchema=s, tableName=t}} = s==sc && t==tb
allPrKeys = dbPrimaryKeys dbStructure
allProcs = dbProcs dbStructure
allOrigins = ("Access-Control-Allow-Origin", "*") :: Header
shouldCount = iPreferCount apiRequest
schema = toS $ configSchema conf
@@ -287,7 +298,7 @@ app dbStructure conf apiRequest =
status = rangeStatus lower upper (toInteger <$> tableTotal)
in (status, contentRange)
readReq = readRequest (configMaxRows conf) (dbRelations dbStructure) (dbProcs dbStructure) apiRequest
readReq = readRequest (configMaxRows conf) (dbRelations dbStructure) allProcs apiRequest
fldNames = fieldNames <$> readReq
readDbRequest = DbRead <$> readReq
mutateDbRequest = DbMutate <$> (mutateRequest apiRequest =<< fldNames)
@@ -306,7 +317,7 @@ responseContentTypeOrError accepts action = serves contentTypesForRequest accept
ActionCreate -> [CTApplicationJSON, CTSingularJSON, CTTextCSV]
ActionUpdate -> [CTApplicationJSON, CTSingularJSON, CTTextCSV]
ActionDelete -> [CTApplicationJSON, CTSingularJSON, CTTextCSV]
ActionInvoke -> [CTApplicationJSON, CTSingularJSON, CTTextCSV]
ActionInvoke -> [CTApplicationJSON, CTSingularJSON, CTTextCSV, CTOctetStream]
ActionInspect -> [CTOpenAPI, CTApplicationJSON]
ActionInfo -> [CTTextCSV]
serves sProduces cAccepts =
+3 -1
View File
@@ -137,7 +137,9 @@ accessibleProcs =
qi = QualifiedIdentifier schema name
pgType = case typ of
'c' -> Composite qi
'p' -> Pseudo name
'p' -> if name == "record" -- Only pg pseudo type that is a row type is 'record'
then Composite qi
else Scalar qi
_ -> Scalar qi -- 'b'ase, 'd'omain, 'e'num, 'r'ange
parseVolatility :: Char -> ProcVolatility
+25 -22
View File
@@ -144,42 +144,45 @@ createWriteStatement selectQuery mutateQuery wantSingle wantHdrs asCsv rep pKeys
| otherwise = asJsonF
type ProcResults = (Maybe Int64, Int64, ByteString)
callProc :: QualifiedIdentifier -> JSON.Object -> SqlQuery -> SqlQuery -> NonnegRange ->
Bool -> Bool -> Bool -> Bool -> H.Query () (Maybe ProcResults)
callProc qi params selectQuery countQuery _ countTotal isSingle paramsAsJson asCsv =
callProc :: QualifiedIdentifier -> JSON.Object -> Bool -> SqlQuery -> SqlQuery -> NonnegRange ->
Bool -> Bool -> Bool -> Bool -> Bool -> Maybe FieldName -> H.Query () (Maybe ProcResults)
callProc qi params returnsScalar selectQuery countQuery _ countTotal isSingle paramsAsJson asCsv asBinary binaryField =
unicodeStatement sql HE.unit decodeProc True
where
sql = [qc|
WITH {sourceCTEName} AS ({_callSql})
SELECT
{countResultF} AS total_result_set,
pg_catalog.count(_postgrest_t) AS page_total,
case
when pg_catalog.count(*) > 1 then
{bodyF}
else
coalesce(((array_agg(row_to_json(_postgrest_t)))[1]->{_procName})::character varying, {bodyF})
sql =
if returnsScalar then [qc|
WITH {sourceCTEName} AS ({_callSql})
SELECT
{countResultF} AS total_result_set,
1 AS page_total,
{scalarBodyF} as body
FROM ({selectQuery}) _postgrest_t;|]
else [qc|
WITH {sourceCTEName} AS ({_callSql})
SELECT
{countResultF} AS total_result_set,
pg_catalog.count(_postgrest_t) AS page_total,
{bodyF} as body
FROM ({selectQuery}) _postgrest_t;|]
end as body
FROM ({selectQuery}) _postgrest_t;
|]
-- FROM (select * from {sourceCTEName} {limitF range}) t;
countResultF = if countTotal then "("<>countQuery<>")" else "null::bigint" :: Text
countResultF = if countTotal then "( "<> countQuery <> ")" else "null::bigint" :: Text
_args = if paramsAsJson
then insertableValueWithType "json" $ JSON.Object params
else intercalate "," $ map _assignment (HM.toList params)
_procName = pgFmtLit $ qiName qi
_procName = qiName qi
_assignment (n,v) = pgFmtIdent n <> ":=" <> insertableValue v
_callSql = [qc|select * from {fromQi qi}({_args}) |] :: Text
_countExpr = if countTotal
then [qc|(select pg_catalog.count(*) from {sourceCTEName})|]
else "null::bigint" :: Text
decodeProc = HD.maybeRow procRow
procRow = (,,) <$> HD.nullableValue HD.int8 <*> HD.value HD.int8
<*> HD.value HD.bytea
scalarBodyF
| asBinary = asBinaryF _procName
| otherwise = "(row_to_json(_postgrest_t)->" <> pgFmtLit _procName <> ")::character varying"
bodyF
| isSingle = asJsonSingleF
| asCsv = asCsvF
| isJust binaryField = asBinaryF $ fromJust binaryField
| otherwise = asJsonF
pgFmtIdent :: SqlFragment -> SqlFragment
+1 -1
View File
@@ -38,7 +38,7 @@ data PgArg = PgArg {
, pgaReq :: Bool
} deriving (Show, Eq)
data PgType = Scalar QualifiedIdentifier | Composite QualifiedIdentifier | Pseudo Text deriving (Eq, Show)
data PgType = Scalar QualifiedIdentifier | Composite QualifiedIdentifier deriving (Eq, Show)
data RetType = Single PgType | SetOf PgType deriving (Eq, Show)