Allow overloaded functions if one has a single unnamed JSON param

Avoids the breaking change in #1927: If there's a function "my_func" having a single unnamed json param and other overloaded pairs(with any number of params), PostgREST won't be able to resolve a POST request to "my_func".
This commit is contained in:
Laurence Isla
2021-11-08 18:26:33 -05:00
committed by GitHub
parent 5cd7d35966
commit 40f9a6068a
4 changed files with 101 additions and 20 deletions
+25 -14
View File
@@ -490,11 +490,31 @@ rawContentTypes AppConfig{..} =
findProc :: QualifiedIdentifier -> S.Set Text -> Bool -> ProcsMap -> ContentType -> Bool -> Either ApiRequestError ProcDescription
findProc qi argumentsKeys paramsAsSingleObject allProcs contentType isInvPost =
case matchProc of
[] -> Left $ NoRpc (qiSchema qi) (qiName qi) (S.toList argumentsKeys) paramsAsSingleObject contentType isInvPost
[proc] -> Right proc
procs -> Left $ AmbiguousRpc (toList procs)
([], []) -> Left $ NoRpc (qiSchema qi) (qiName qi) (S.toList argumentsKeys) paramsAsSingleObject contentType isInvPost
-- If there are no functions with named arguments, fallback to the single unnamed argument function
([], [proc]) -> Right proc
([], procs) -> Left $ AmbiguousRpc (toList procs)
-- Matches the functions with named arguments
([proc], _) -> Right proc
(procs, _) -> Left $ AmbiguousRpc (toList procs)
where
matchProc = filter matchesParams $ M.lookupDefault mempty qi allProcs -- first find the proc by name
matchProc = overloadedProcPartition $ M.lookupDefault mempty qi allProcs -- first find the proc by name
-- The partition obtained has the form (overloadedProcs,fallbackProcs)
-- where fallbackProcs are functions with a single unnamed parameter
overloadedProcPartition procs = foldr select ([],[]) procs
select proc ~(ts,fs)
| matchesParams proc = (proc:ts,fs)
| hasSingleUnnamedParam proc = (ts,proc:fs)
| otherwise = (ts,fs)
-- If the function is called with post and has a single unnamed parameter
-- it can be called depending on content type and the parameter type
hasSingleUnnamedParam proc = isInvPost && case pdParams proc of
[ProcParam "" ppType _ _]
| contentType == CTApplicationJSON -> ppType `elem` ["json", "jsonb"]
| contentType == CTTextPlain -> ppType == "text"
| contentType == CTOctetStream -> ppType == "bytea"
| otherwise -> False
_ -> False
matchesParams proc =
let params = pdParams proc in
-- exceptional case for Prefer: params=single-object
@@ -502,16 +522,7 @@ findProc qi argumentsKeys paramsAsSingleObject allProcs contentType isInvPost =
then length params == 1 && (ppType <$> headMay params) `elem` [Just "json", Just "jsonb"]
-- If the function has no parameters, the arguments keys must be empty as well
else if null params
then null argumentsKeys
-- If the function is called with post and has a single unnamed parameter
-- it can be called depending on content type and the parameter type
else if isInvPost && length params == 1 && (ppName <$> headMay params) == Just mempty
then case headMay params of
Just prm | contentType == CTApplicationJSON -> ppType prm `elem` ["json", "jsonb"]
| contentType == CTTextPlain -> ppType prm == "text"
| contentType == CTOctetStream -> ppType prm == "bytea"
| otherwise -> False
Nothing -> False
then null argumentsKeys && contentType `notElem` [CTTextPlain, CTOctetStream]
-- A function has optional and required parameters. Optional parameters have a default value and
-- don't require arguments for the function to be executed, required parameters must have an argument present.
else case L.partition ppReq params of