Skip to content

Commit

Permalink
fix calling rpc with variadic argument
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfgangwalther committed Jul 14, 2020
1 parent 1f6a824 commit 20907ae
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Fixed

- #1530, Fix how the PostgREST version is shown in the help text when the `.git` directory is not available - @monacoremo
- #1552, Fix calling RPC with variadic argument by passing an array (#1470) - @wolfgangwalther
- #1094, Fix expired JWTs starting an empty transaction on the db - @steve-chavez
- #1475, Fix location header for POST request with select= without PK (#1162) - @wolfgangwalther

Expand Down
10 changes: 6 additions & 4 deletions src/PostgREST/DbStructure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,16 @@ decodeProcs =
parseArgs = mapMaybe parseArg . filter (not . isPrefixOf "OUT" . toS) . map strip . split (==',')

parseArg :: Text -> Maybe PgArg
parseArg a =
let arg = lastDef "" $ splitOn "INOUT " a
(body, def) = breakOn " DEFAULT " arg
parseArg arg =
let isVariadic = isPrefixOf "VARIADIC " $ toS arg
argNoInout = lastDef "" $ splitOn "INOUT " arg
argNoVariadic = lastDef "" $ splitOn "VARIADIC " argNoInout
(body, def) = breakOn " DEFAULT " argNoVariadic
(name, typ) = breakOn " " body in
if T.null typ
then Nothing
else Just $
PgArg (dropAround (== '"') name) (strip typ) (T.null def)
PgArg (dropAround (== '"') name) (strip typ) (T.null def) isVariadic

parseRetType :: Text -> Text -> Bool -> Char -> RetType
parseRetType schema name isSetOf typ
Expand Down
2 changes: 1 addition & 1 deletion src/PostgREST/OpenAPI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ makeProcSchema pd =
& required .~ map pgaName (filter pgaReq (pdArgs pd))

makeProcProperty :: PgArg -> (Text, Referenced Schema)
makeProcProperty (PgArg n t _) = (n, Inline s)
makeProcProperty (PgArg n t _ _) = (n, Inline s)
where
s = (mempty :: Schema)
& type_ ?~ toSwaggerType t
Expand Down
15 changes: 10 additions & 5 deletions src/PostgREST/QueryBuilder.hs
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,20 @@ requestToCallProcQuery qi pgArgs returnsScalar preferParams returnings =
unwords [
normalizedBody <> ",",
"pgrst_args AS (",
"SELECT * FROM json_to_recordset(" <> selectBody <> ") AS _(" <> fmtArgs (\a -> " " <> pgaType a) <> ")",
"SELECT * FROM json_to_recordset(" <> selectBody <> ") AS _(" <> fmtArgs (const "") (\a -> " " <> pgaType a) <> ")",
")"]
, if paramsAsMultipleObjects
then fmtArgs (\a -> " := pgrst_args." <> pgFmtIdent (pgaName a))
else fmtArgs (\a -> " := (SELECT " <> pgFmtIdent (pgaName a) <> " FROM pgrst_args LIMIT 1)")
then fmtArgs varadicPrefix (\a -> " := pgrst_args." <> pgFmtIdent (pgaName a))
else fmtArgs varadicPrefix (\a -> " := (SELECT " <> pgFmtIdent (pgaName a) <> " FROM pgrst_args LIMIT 1)")
)

fmtArgs :: (PgArg -> SqlFragment) -> SqlFragment
fmtArgs argFrag = intercalate ", " ((\a -> pgFmtIdent (pgaName a) <> argFrag a) <$> pgArgs)
fmtArgs :: (PgArg -> SqlFragment) -> (PgArg -> SqlFragment) -> SqlFragment
fmtArgs argFragPre argFragSuf = intercalate ", " ((\a -> argFragPre a <> pgFmtIdent (pgaName a) <> argFragSuf a) <$> pgArgs)

varadicPrefix :: PgArg -> SqlFragment
varadicPrefix a
| pgaVar a = "VARIADIC "
| otherwise = ""

sourceBody :: SqlFragment
sourceBody
Expand Down
3 changes: 2 additions & 1 deletion src/PostgREST/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ data PgArg = PgArg {
pgaName :: Text
, pgaType :: Text
, pgaReq :: Bool
, pgaVar :: Bool
} deriving (Show, Eq, Ord)

data PgType = Scalar QualifiedIdentifier | Composite QualifiedIdentifier deriving (Eq, Show, Ord)
Expand Down Expand Up @@ -179,7 +180,7 @@ specifiedProcArgs keys proc =
let
args = maybe [] pdArgs proc
in
(\k -> fromMaybe (PgArg k "text" True) (find ((==) k . pgaName) args)) <$> S.toList keys
(\k -> fromMaybe (PgArg k "text" True False) (find ((==) k . pgaName) args)) <$> S.toList keys

procReturnsScalar :: ProcDescription -> Bool
procReturnsScalar proc = case proc of
Expand Down
14 changes: 14 additions & 0 deletions test/Feature/RpcSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,20 @@ spec actualPgVersion =
[json|"Hi"|]
{ matchHeaders = [matchContentTypeJson] }

it "works with inout argument" $
post "/rpc/inout_argument"
[json| { "arg": true } |]
`shouldRespondWith`
[json| true |]
{ matchHeaders = [matchContentTypeJson] }

it "works with variadic arguments" $
post "/rpc/variadic_argument"
[json| { "v": ["hello"] } |]
`shouldRespondWith`
[json|"Hi"|]
{ matchHeaders = [matchContentTypeJson] }

it "parses embedded JSON arguments as JSON" $
post "/rpc/json_argument"
[json| { "arg": { "key": 3 } } |]
Expand Down
8 changes: 4 additions & 4 deletions test/QueryCost.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ main = do
context "call proc query" $ do
it "should not exceed cost when calling setof composite proc" $ do
cost <- exec pool [str| {"id": 3} |] $
requestToCallProcQuery (QualifiedIdentifier "test" "get_projects_below") [PgArg "id" "int" True] False Nothing []
requestToCallProcQuery (QualifiedIdentifier "test" "get_projects_below") [PgArg "id" "int" True False] False Nothing []
liftIO $
cost `shouldSatisfy` (< Just 40)

Expand All @@ -41,22 +41,22 @@ main = do

it "should not exceed cost when calling scalar proc" $ do
cost <- exec pool [str| {"a": 3, "b": 4} |] $
requestToCallProcQuery (QualifiedIdentifier "test" "add_them") [PgArg "a" "int" True, PgArg "b" "int" True] True Nothing []
requestToCallProcQuery (QualifiedIdentifier "test" "add_them") [PgArg "a" "int" True False, PgArg "b" "int" True False] True Nothing []
liftIO $
cost `shouldSatisfy` (< Just 10)

context "params=multiple-objects" $ do
it "should not exceed cost when calling setof composite proc" $ do
cost <- exec pool [str| [{"id": 1}, {"id": 4}] |] $
requestToCallProcQuery (QualifiedIdentifier "test" "get_projects_below") [PgArg "id" "int" True] False (Just MultipleObjects) []
requestToCallProcQuery (QualifiedIdentifier "test" "get_projects_below") [PgArg "id" "int" True False] False (Just MultipleObjects) []
liftIO $ do
-- lower bound needed for now to make sure that cost is not Nothing
cost `shouldSatisfy` (> Just 2000)
cost `shouldSatisfy` (< Just 2100)

it "should not exceed cost when calling scalar proc" $ do
cost <- exec pool [str| [{"a": 3, "b": 4}, {"a": 1, "b": 2}, {"a": 8, "b": 7}] |] $
requestToCallProcQuery (QualifiedIdentifier "test" "add_them") [PgArg "a" "int" True, PgArg "b" "int" True] True Nothing []
requestToCallProcQuery (QualifiedIdentifier "test" "add_them") [PgArg "a" "int" True False, PgArg "b" "int" True False] True Nothing []
liftIO $
cost `shouldSatisfy` (< Just 10)

Expand Down
12 changes: 12 additions & 0 deletions test/fixtures/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,18 @@ $_$An RPC function
Just a test for RPC function arguments$_$;


CREATE FUNCTION inout_argument(INOUT arg BOOLEAN)
LANGUAGE SQL AS $_$
SELECT arg
$_$;


CREATE FUNCTION variadic_argument(VARIADIC v TEXT[]) RETURNS text
LANGUAGE SQL AS $_$
SELECT 'Hi'::text
$_$;


CREATE FUNCTION json_argument(arg json) RETURNS text

LANGUAGE sql
Expand Down

0 comments on commit 20907ae

Please sign in to comment.