From c9a60373f691caa439bc2e1240125d871915ea08 Mon Sep 17 00:00:00 2001 From: steve-chavez Date: Thu, 19 Aug 2021 17:59:21 -0500 Subject: [PATCH] refactor: PgArg to ProcParam Clarify the difference between arguments and parameters. Parameters are part of the function definition, arguments are the values passed to the function. Also clarify the findProc function comments and error message. --- src/PostgREST/App.hs | 2 +- src/PostgREST/DbStructure.hs | 5 ++- src/PostgREST/DbStructure/Proc.hs | 41 +++++++++--------- src/PostgREST/Error.hs | 16 ++++--- src/PostgREST/OpenAPI.hs | 12 +++--- src/PostgREST/Query/QueryBuilder.hs | 26 +++++------ src/PostgREST/Request/ApiRequest.hs | 67 +++++++++++++---------------- test/Feature/RpcSpec.hs | 16 +++---- test/QueryCost.hs | 8 ++-- 9 files changed, 96 insertions(+), 97 deletions(-) diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 1226aabfe..d684d880d 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -442,7 +442,7 @@ handleInvoke invMethod proc context@RequestContext{..} = do (returnsSingle iTarget) (QueryBuilder.requestToCallProcQuery (QualifiedIdentifier (pdSchema proc) (pdName proc)) - (Proc.specifiedProcArgs iColumns proc) + (Proc.specifiedProcParams iColumns proc) iPayload (returnsScalar iTarget) iPreferParameters diff --git a/src/PostgREST/DbStructure.hs b/src/PostgREST/DbStructure.hs index de7174f49..ebea78c62 100644 --- a/src/PostgREST/DbStructure.hs +++ b/src/PostgREST/DbStructure.hs @@ -43,8 +43,9 @@ import Text.InterpolatedString.Perl6 (q) import PostgREST.DbStructure.Identifiers (QualifiedIdentifier (..), Schema, TableName) -import PostgREST.DbStructure.Proc (PgArg (..), PgType (..), +import PostgREST.DbStructure.Proc (PgType (..), ProcDescription (..), + ProcParam (..), ProcVolatility (..), ProcsMap, RetType (..)) import PostgREST.DbStructure.Relationship (Cardinality (..), @@ -192,7 +193,7 @@ decodeProcs = <*> column HD.text <*> nullableColumn HD.text <*> compositeArrayColumn - (PgArg + (ProcParam <$> compositeField HD.text <*> compositeField HD.text <*> compositeField HD.bool diff --git a/src/PostgREST/DbStructure/Proc.hs b/src/PostgREST/DbStructure/Proc.hs index 1990f6fad..d2cf7a073 100644 --- a/src/PostgREST/DbStructure/Proc.hs +++ b/src/PostgREST/DbStructure/Proc.hs @@ -2,16 +2,16 @@ {-# LANGUAGE DeriveGeneric #-} module PostgREST.DbStructure.Proc - ( PgArg(..) - , PgType(..) + ( PgType(..) , ProcDescription(..) + , ProcParam(..) , ProcVolatility(..) , ProcsMap , RetType(..) , procReturnsScalar , procReturnsSingle , procTableName - , specifiedProcArgs + , specifiedProcParams ) where import qualified Data.Aeson as JSON @@ -24,15 +24,6 @@ import PostgREST.DbStructure.Identifiers (FieldName, import Protolude - -data PgArg = PgArg - { pgaName :: Text - , pgaType :: Text - , pgaReq :: Bool - , pgaVar :: Bool - } - deriving (Eq, Ord, Generic, JSON.ToJSON) - data PgType = Scalar | Composite QualifiedIdentifier @@ -53,19 +44,27 @@ data ProcDescription = ProcDescription { pdSchema :: Schema , pdName :: Text , pdDescription :: Maybe Text - , pdArgs :: [PgArg] + , pdParams :: [ProcParam] , pdReturnType :: RetType , pdVolatility :: ProcVolatility , pdHasVariadic :: Bool } deriving (Eq, Generic, JSON.ToJSON) --- Order by least number of args in the case of overloaded functions +data ProcParam = ProcParam + { ppName :: Text + , ppType :: Text + , ppReq :: Bool + , ppVar :: Bool + } + deriving (Eq, Ord, Generic, JSON.ToJSON) + +-- Order by least number of params in the case of overloaded functions instance Ord ProcDescription where - ProcDescription schema1 name1 des1 args1 rt1 vol1 hasVar1 `compare` ProcDescription schema2 name2 des2 args2 rt2 vol2 hasVar2 - | schema1 == schema2 && name1 == name2 && length args1 < length args2 = LT - | schema2 == schema2 && name1 == name2 && length args1 > length args2 = GT - | otherwise = (schema1, name1, des1, args1, rt1, vol1, hasVar1) `compare` (schema2, name2, des2, args2, rt2, vol2, hasVar2) + ProcDescription schema1 name1 des1 prms1 rt1 vol1 hasVar1 `compare` ProcDescription schema2 name2 des2 prms2 rt2 vol2 hasVar2 + | schema1 == schema2 && name1 == name2 && length prms1 < length prms2 = LT + | schema2 == schema2 && name1 == name2 && length prms1 > length prms2 = GT + | otherwise = (schema1, name1, des1, prms1, rt1, vol1, hasVar1) `compare` (schema2, name2, des2, prms2, rt2, vol2, hasVar2) -- | A map of all procs, all of which can be overloaded(one entry will have more than one ProcDescription). -- | It uses a HashMap for a faster lookup. @@ -75,9 +74,9 @@ type ProcsMap = M.HashMap QualifiedIdentifier [ProcDescription] Search the procedure parameters by matching them with the specified keys. If the key doesn't match a parameter, a parameter with a default type "text" is assumed. -} -specifiedProcArgs :: S.Set FieldName -> ProcDescription -> [PgArg] -specifiedProcArgs keys proc = - (\k -> fromMaybe (PgArg k "text" True False) (find ((==) k . pgaName) (pdArgs proc))) <$> S.toList keys +specifiedProcParams :: S.Set FieldName -> ProcDescription -> [ProcParam] +specifiedProcParams keys proc = + (\k -> fromMaybe (ProcParam k "text" True False) (find ((==) k . ppName) (pdParams proc))) <$> S.toList keys procReturnsScalar :: ProcDescription -> Bool procReturnsScalar proc = case proc of diff --git a/src/PostgREST/Error.hs b/src/PostgREST/Error.hs index dc14ddec6..689945505 100644 --- a/src/PostgREST/Error.hs +++ b/src/PostgREST/Error.hs @@ -29,8 +29,8 @@ import Network.HTTP.Types.Header (Header) import PostgREST.ContentType (ContentType (..)) import qualified PostgREST.ContentType as ContentType -import PostgREST.DbStructure.Proc (PgArg (..), - ProcDescription (..)) +import PostgREST.DbStructure.Proc (ProcDescription (..), + ProcParam (..)) import PostgREST.DbStructure.Relationship (Cardinality (..), Junction (..), Relationship (..)) @@ -99,11 +99,15 @@ instance JSON.ToJSON ApiRequestError where "message" .= ("More than one relationship was found for " <> parent <> " and " <> child :: Text), "details" .= (compressedRel <$> rels) ] toJSON (AmbiguousRpc procs) = JSON.object [ - "hint" .= ("Overloaded functions with the same argument name but different types are not supported" :: Text), - "message" .= ("Could not choose the best candidate function between: " <> T.intercalate ", " [pdSchema p <> "." <> pdName p <> "(" <> T.intercalate ", " [pgaName a <> " => " <> pgaType a | a <- pdArgs p] <> ")" | p <- procs])] + "hint" .= ("Overloaded functions with the same parameter name but different types are not supported" :: Text), + "message" .= ("Could not choose the best candidate function between: " <> T.intercalate ", " [pdSchema p <> "." <> pdName p <> "(" <> T.intercalate ", " [ppName a <> " => " <> ppType a | a <- pdParams p] <> ")" | p <- procs])] toJSON (NoRpc schema procName payloadKeys hasPreferSingleObject) = JSON.object [ - "hint" .= ("If a new function was created in the database with this name and arguments, try reloading the schema cache." :: Text), - "message" .= ("Could not find the " <> schema <> "." <> procName <> (if hasPreferSingleObject then " function with a single json or jsonb argument" else "(" <> T.intercalate ", " payloadKeys <> ")" <> " function") <> " in the schema cache")] + "hint" .= ("If a new function was created in the database with this name and parameters, try reloading the schema cache." :: Text), + "message" .= ("Could not find the " <> schema <> "." <> procName <> + (if hasPreferSingleObject + then " function with a single json or jsonb parameter" + else "(" <> T.intercalate ", " payloadKeys <> ")" <> " function") <> + " in the schema cache")] toJSON UnsupportedVerb = JSON.object [ "message" .= ("Unsupported HTTP verb" :: Text)] toJSON InvalidFilters = JSON.object [ diff --git a/src/PostgREST/OpenAPI.hs b/src/PostgREST/OpenAPI.hs index ac81306fe..9e7d771fe 100644 --- a/src/PostgREST/OpenAPI.hs +++ b/src/PostgREST/OpenAPI.hs @@ -27,8 +27,8 @@ import PostgREST.Config (AppConfig (..), Proxy (..), isMalformedProxyUri, toURI) import PostgREST.DbStructure (DbStructure (..), tableCols, tablePKCols) -import PostgREST.DbStructure.Proc (PgArg (..), - ProcDescription (..)) +import PostgREST.DbStructure.Proc (ProcDescription (..), + ProcParam (..)) import PostgREST.DbStructure.Relationship (Cardinality (..), PrimaryKey (..), Relationship (..)) @@ -130,11 +130,11 @@ makeProcSchema pd = (mempty :: Schema) & description .~ pdDescription pd & type_ ?~ SwaggerObject - & properties .~ fromList (fmap makeProcProperty (pdArgs pd)) - & required .~ fmap pgaName (filter pgaReq (pdArgs pd)) + & properties .~ fromList (fmap makeProcProperty (pdParams pd)) + & required .~ fmap ppName (filter ppReq (pdParams pd)) -makeProcProperty :: PgArg -> (Text, Referenced Schema) -makeProcProperty (PgArg n t _ _) = (n, Inline s) +makeProcProperty :: ProcParam -> (Text, Referenced Schema) +makeProcProperty (ProcParam n t _ _) = (n, Inline s) where s = (mempty :: Schema) & type_ ?~ toSwaggerType t diff --git a/src/PostgREST/Query/QueryBuilder.hs b/src/PostgREST/Query/QueryBuilder.hs index 9854c8bbb..5df062b16 100644 --- a/src/PostgREST/Query/QueryBuilder.hs +++ b/src/PostgREST/Query/QueryBuilder.hs @@ -23,7 +23,7 @@ import Data.Tree (Tree (..)) import PostgREST.DbStructure.Identifiers (FieldName, QualifiedIdentifier (..)) -import PostgREST.DbStructure.Proc (PgArg (..)) +import PostgREST.DbStructure.Proc (ProcParam (..)) import PostgREST.DbStructure.Relationship (Cardinality (..), Relationship (..)) import PostgREST.DbStructure.Table (Table (..)) @@ -118,34 +118,34 @@ mutateRequestToQuery (Delete mainQi logicForest returnings) = (if null logicForest then mempty else "WHERE " <> intercalateSnippet " AND " (map (pgFmtLogicTree mainQi) logicForest)) <> " " <> H.sql (returningF mainQi returnings) -requestToCallProcQuery :: QualifiedIdentifier -> [PgArg] -> Maybe PayloadJSON -> Bool -> Maybe PreferParameters -> [FieldName] -> H.Snippet -requestToCallProcQuery qi pgArgs pj returnsScalar preferParams returnings = - argsCTE <> sourceBody +requestToCallProcQuery :: QualifiedIdentifier -> [ProcParam] -> Maybe PayloadJSON -> Bool -> Maybe PreferParameters -> [FieldName] -> H.Snippet +requestToCallProcQuery qi procParams pj returnsScalar preferParams returnings = + prmsCTE <> sourceBody where body = pjRaw <$> pj paramsAsSingleObject = preferParams == Just SingleObject paramsAsMultipleObjects = preferParams == Just MultipleObjects - (argsCTE, args) - | null pgArgs = (mempty, mempty) + (prmsCTE, args) + | null procParams = (mempty, mempty) | paramsAsSingleObject = ("WITH pgrst_args AS (SELECT NULL)", jsonPlaceHolder body) | otherwise = ( "WITH " <> normalizedBody body <> ", " <> H.sql ( BS.unwords [ "pgrst_args AS (", - "SELECT * FROM json_to_recordset(" <> selectBody <> ") AS _(" <> fmtArgs (const mempty) (\a -> " " <> encodeUtf8 (pgaType a)) <> ")", + "SELECT * FROM json_to_recordset(" <> selectBody <> ") AS _(" <> fmtParams (const mempty) (\a -> " " <> encodeUtf8 (ppType a)) <> ")", ")"]) , H.sql $ if paramsAsMultipleObjects - then fmtArgs varadicPrefix (\a -> " := pgrst_args." <> pgFmtIdent (pgaName a)) - else fmtArgs varadicPrefix (\a -> " := (SELECT " <> pgFmtIdent (pgaName a) <> " FROM pgrst_args LIMIT 1)") + then fmtParams varadicPrefix (\a -> " := pgrst_args." <> pgFmtIdent (ppName a)) + else fmtParams varadicPrefix (\a -> " := (SELECT " <> pgFmtIdent (ppName a) <> " FROM pgrst_args LIMIT 1)") ) - fmtArgs :: (PgArg -> SqlFragment) -> (PgArg -> SqlFragment) -> SqlFragment - fmtArgs argFragPre argFragSuf = BS.intercalate ", " ((\a -> argFragPre a <> pgFmtIdent (pgaName a) <> argFragSuf a) <$> pgArgs) + fmtParams :: (ProcParam -> SqlFragment) -> (ProcParam -> SqlFragment) -> SqlFragment + fmtParams prmFragPre prmFragSuf = BS.intercalate ", " ((\a -> prmFragPre a <> pgFmtIdent (ppName a) <> prmFragSuf a) <$> procParams) - varadicPrefix :: PgArg -> SqlFragment - varadicPrefix a = if pgaVar a then "VARIADIC " else mempty + varadicPrefix :: ProcParam -> SqlFragment + varadicPrefix a = if ppVar a then "VARIADIC " else mempty sourceBody :: H.Snippet sourceBody diff --git a/src/PostgREST/Request/ApiRequest.hs b/src/PostgREST/Request/ApiRequest.hs index dbf73bf83..0f4bac427 100644 --- a/src/PostgREST/Request/ApiRequest.hs +++ b/src/PostgREST/Request/ApiRequest.hs @@ -51,9 +51,8 @@ import PostgREST.DbStructure (DbStructure (..)) import PostgREST.DbStructure.Identifiers (FieldName, QualifiedIdentifier (..), Schema) -import PostgREST.DbStructure.Proc (PgArg (..), - ProcDescription (..), - ProcsMap) +import PostgREST.DbStructure.Proc (ProcDescription (..), + ProcParam (..), ProcsMap) import PostgREST.Error (ApiRequestError (..)) import PostgREST.Query.SqlFragment (ftsOperators, operators) import PostgREST.RangeQuery (NonnegRange, allRange, @@ -119,15 +118,15 @@ instance JSON.ToJSON RpcParamValue where toJSON (Variadic v) = JSON.toJSON v toRpcParamValue :: ProcDescription -> (Text, Text) -> (Text, RpcParamValue) -toRpcParamValue proc (k, v) | argIsVariadic k = (k, Variadic [v]) +toRpcParamValue proc (k, v) | prmIsVariadic k = (k, Variadic [v]) | otherwise = (k, Fixed v) where - argIsVariadic arg = isJust $ find (\PgArg{pgaName, pgaVar} -> pgaName == arg && pgaVar) $ pdArgs proc + prmIsVariadic prm = isJust $ find (\ProcParam{ppName, ppVar} -> ppName == prm && ppVar) $ pdParams proc -- | Convert rpc params `/rpc/func?a=val1&b=val2` to json `{"a": "val1", "b": "val2"} jsonRpcParams :: ProcDescription -> [(Text, Text)] -> PayloadJSON jsonRpcParams proc prms = - if not $ pdHasVariadic proc then -- if proc has no variadic arg, save steps and directly convert to json + if not $ pdHasVariadic proc then -- if proc has no variadic param, save steps and directly convert to json ProcessedJSON (JSON.encode $ M.fromList $ second JSON.toJSON <$> prms) (S.fromList $ fst <$> prms) else let paramsMap = M.fromListWith mergeParams $ toRpcParamValue proc <$> prms in @@ -135,7 +134,7 @@ jsonRpcParams proc prms = where mergeParams :: RpcParamValue -> RpcParamValue -> RpcParamValue mergeParams (Variadic a) (Variadic b) = Variadic $ b ++ a - mergeParams v _ = v -- repeated params for non-variadic arguments are not merged + mergeParams v _ = v -- repeated params for non-variadic parameters are not merged targetToJsonRpcParams :: Maybe Target -> [(Text, Text)] -> Maybe PayloadJSON targetToJsonRpcParams target params = @@ -480,37 +479,33 @@ rawContentTypes AppConfig{..} = (ContentType.decodeContentType <$> configRawMediaTypes) `union` [CTOctetStream, CTTextPlain] {-| - Search a pg procedure by its parameters. Since a function can be overloaded, the name is not enough to find it. - An overloaded function can have a different volatility or even a different return type. + Search a pg proc by matching name and arguments keys to parameters. Since a function can be overloaded, + the name is not enough to find it. An overloaded function can have a different volatility or even a different return type. -} findProc :: QualifiedIdentifier -> S.Set Text -> Bool -> ProcsMap -> Either ApiRequestError ProcDescription -findProc qi payloadKeys paramsAsSingleObject allProcs = - case bestMatch of - [] -> Left $ NoRpc (qiSchema qi) (qiName qi) (S.toList payloadKeys) paramsAsSingleObject +findProc qi argumentsKeys paramsAsSingleObject allProcs = + case matchProc of + [] -> Left $ NoRpc (qiSchema qi) (qiName qi) (S.toList argumentsKeys) paramsAsSingleObject [proc] -> Right proc procs -> Left $ AmbiguousRpc (toList procs) where - bestMatch = - case M.lookup qi allProcs of - Nothing -> [] - Just [proc] -> [proc | matches proc] - Just procs -> filter matches procs - -- Find the exact arguments match - matches proc - | paramsAsSingleObject = case pdArgs proc of - [arg] -> pgaType arg `elem` ["json", "jsonb"] - _ -> False - | otherwise = case pdArgs proc of - [] -> null payloadKeys - args -> matchesArg args - matchesArg args = - -- The function's required arguments are separated from the ones with a default value assigned. - -- The set of names of those arguments is compared to the set of keys supplied by the client - -- 1. If only required arguments are found, the keys must be exactly the same as those arguments - -- 2. If only optional arguments are found, the keys must be a subset of those arguments - -- 3. If both required and optional arguments are found, the result of taking away the optional arguments - -- from the keys must be exactly the same as the required arguments - case L.partition pgaReq args of - (reqArgs, []) -> payloadKeys == S.fromList (pgaName <$> reqArgs) - ([], defArgs) -> payloadKeys `S.isSubsetOf` S.fromList (pgaName <$> defArgs) - (reqArgs, defArgs) -> payloadKeys `S.difference` S.fromList (pgaName <$> defArgs) == S.fromList (pgaName <$> reqArgs) + matchProc = filter matchesParams $ M.lookupDefault mempty qi allProcs -- first find the proc by name + matchesParams proc = + let params = pdParams proc in + -- here we don't match by argument key(there isn't one) but by the single parameter type + if paramsAsSingleObject then + case params of + [prm] -> ppType prm `elem` ["json", "jsonb"] + _ -> False + -- 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 + -- If the function has no parameters, the arguments keys must be empty as well + ([], []) -> null argumentsKeys + -- If the function only has required parameters, the arguments keys must match those parameters + (reqParams, []) -> argumentsKeys == S.fromList (ppName <$> reqParams) + -- If the function only has optional parameters, the arguments keys can match none or any of them(a subset) + ([], optParams) -> argumentsKeys `S.isSubsetOf` S.fromList (ppName <$> optParams) + -- If the function has required and optional parameters, the arguments keys have to match the required parameters + -- and can match any or none of the default parameters. + (reqParams, optParams) -> argumentsKeys `S.difference` S.fromList (ppName <$> optParams) == S.fromList (ppName <$> reqParams) diff --git a/test/Feature/RpcSpec.hs b/test/Feature/RpcSpec.hs index c8b8758da..2f6fbef00 100644 --- a/test/Feature/RpcSpec.hs +++ b/test/Feature/RpcSpec.hs @@ -107,7 +107,7 @@ spec actualPgVersion = it "should not ignore unknown args and fail with 404" $ get "/rpc/add_them?a=1&b=2&smthelse=blabla" `shouldRespondWith` [json| { - "hint":"If a new function was created in the database with this name and arguments, try reloading the schema cache.", + "hint":"If a new function was created in the database with this name and parameters, try reloading the schema cache.", "message":"Could not find the test.add_them(a, b, smthelse) function in the schema cache" } |] { matchStatus = 404 , matchHeaders = [matchContentTypeJson] @@ -119,8 +119,8 @@ spec actualPgVersion = [json|{}|] `shouldRespondWith` [json| { - "hint":"If a new function was created in the database with this name and arguments, try reloading the schema cache.", - "message":"Could not find the test.sayhello function with a single json or jsonb argument in the schema cache" } |] + "hint":"If a new function was created in the database with this name and parameters, try reloading the schema cache.", + "message":"Could not find the test.sayhello function with a single json or jsonb parameter in the schema cache" } |] { matchStatus = 404 , matchHeaders = [matchContentTypeJson] } @@ -128,24 +128,24 @@ spec actualPgVersion = it "should fail with 404 for overloaded functions with unknown args" $ do get "/rpc/overloaded?wrong_arg=value" `shouldRespondWith` [json| { - "hint":"If a new function was created in the database with this name and arguments, try reloading the schema cache.", + "hint":"If a new function was created in the database with this name and parameters, try reloading the schema cache.", "message":"Could not find the test.overloaded(wrong_arg) function in the schema cache" } |] { matchStatus = 404 , matchHeaders = [matchContentTypeJson] } get "/rpc/overloaded?a=1&b=2&wrong_arg=value" `shouldRespondWith` [json| { - "hint":"If a new function was created in the database with this name and arguments, try reloading the schema cache.", + "hint":"If a new function was created in the database with this name and parameters, try reloading the schema cache.", "message":"Could not find the test.overloaded(a, b, wrong_arg) function in the schema cache" } |] { matchStatus = 404 , matchHeaders = [matchContentTypeJson] } - context "ambiguous overloaded functions with same arguments but different types" $ do - it "should fail with 300 Multiple Choices without explicit argument type casts" $ + context "ambiguous overloaded functions with same parameters' names but different types" $ do + it "should fail with 300 Multiple Choices without explicit type casts" $ get "/rpc/overloaded_same_args?arg=value" `shouldRespondWith` [json| { - "hint":"Overloaded functions with the same argument name but different types are not supported", + "hint":"Overloaded functions with the same parameter name but different types are not supported", "message":"Could not choose the best candidate function between: test.overloaded_same_args(arg => integer), test.overloaded_same_args(arg => xml), test.overloaded_same_args(arg => text, num => integer)" } |] { matchStatus = 300 , matchHeaders = [matchContentTypeJson] diff --git a/test/QueryCost.hs b/test/QueryCost.hs index dc902499a..7d1ce99dd 100644 --- a/test/QueryCost.hs +++ b/test/QueryCost.hs @@ -34,7 +34,7 @@ main = do context "call proc query" $ do it "should not exceed cost when calling setof composite proc" $ do cost <- exec pool $ - requestToCallProcQuery (QualifiedIdentifier "test" "get_projects_below") [PgArg "id" "int" True False] + requestToCallProcQuery (QualifiedIdentifier "test" "get_projects_below") [ProcParam "id" "int" True False] (Just $ RawJSON [str| {"id": 3} |]) False Nothing [] liftIO $ cost `shouldSatisfy` (< Just 40) @@ -47,7 +47,7 @@ main = do it "should not exceed cost when calling scalar proc" $ do cost <- exec pool $ - requestToCallProcQuery (QualifiedIdentifier "test" "add_them") [PgArg "a" "int" True False, PgArg "b" "int" True False] + requestToCallProcQuery (QualifiedIdentifier "test" "add_them") [ProcParam "a" "int" True False, ProcParam "b" "int" True False] (Just $ RawJSON [str| {"a": 3, "b": 4} |]) True Nothing [] liftIO $ cost `shouldSatisfy` (< Just 10) @@ -55,7 +55,7 @@ main = do context "params=multiple-objects" $ do it "should not exceed cost when calling setof composite proc" $ do cost <- exec pool $ - requestToCallProcQuery (QualifiedIdentifier "test" "get_projects_below") [PgArg "id" "int" True False] + requestToCallProcQuery (QualifiedIdentifier "test" "get_projects_below") [ProcParam "id" "int" True False] (Just $ RawJSON [str| [{"id": 1}, {"id": 4}] |]) False (Just MultipleObjects) [] liftIO $ do -- lower bound needed for now to make sure that cost is not Nothing @@ -64,7 +64,7 @@ main = do it "should not exceed cost when calling scalar proc" $ do cost <- exec pool $ - requestToCallProcQuery (QualifiedIdentifier "test" "add_them") [PgArg "a" "int" True False, PgArg "b" "int" True False] + requestToCallProcQuery (QualifiedIdentifier "test" "add_them") [ProcParam "a" "int" True False, ProcParam "b" "int" True False] (Just $ RawJSON [str| [{"a": 3, "b": 4}, {"a": 1, "b": 2}, {"a": 8, "b": 7}] |]) True Nothing [] liftIO $ cost `shouldSatisfy` (< Just 10)