-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix calling rpc with variadic argument #1552
Conversation
66a0d5d
to
811587a
Compare
rebased to current master. |
So I had a look at the existing This is what the test looks like: it "returns a scalar result when there is a single INOUT param" $
get "/rpc/single_inout_param?num=2" `shouldRespondWith`
[json|3|] { matchHeaders = [matchContentTypeJson] } and this is the SQL for the function create function test.single_inout_param(INOUT num int) AS $$
select num + 1;
$$ language sql; Nowhere in the function definition the volatility is set to I guess I am not understanding the PostgREST docs correctly, where it says:
So does the volatility actually have any effect on PostgREST? Or is it just that function that modify data will fail in the read-only transaction and that's it - independent of the volatility? Does it have an affect on the json schema exposed at the api root? @steve-chavez Can you help my understanding here? |
@wolfgangwalther Yes, the volatility is just a hint for the pg planner. For GET we call the function in a read only transaction, disregard the actual volatility and let pg err if the function modifies the database. For POST we do consider the volatility for the transaction. postgrest/src/PostgREST/App.hs Lines 115 to 121 in 343e41c
(For reference: the idea came from #328 (comment))
You mean if the volatility is considered for the openapi output to hint whether a function should be called with GET/POST? I believe this isn't being done now.
You're right about the docs. Perhaps we can clarify and mention that GET enforces non-mutability with the read-only tx, despite the volatility. |
Thanks for clearing that up for me and providing that background - makes a lot more sense now. Improving the docs in that regard might help others as well.
Yes, it only shows POST I guess: #1222 #1151 Now, of course I asked myself what kind of error will be thrown in case of calling a volatile proc with GET. I found this: context "only for GET rpc" $ do
it "should fail on mutating procs" $ do
get "/rpc/callcounter" `shouldRespondWith` 500
get "/rpc/setprojects?id_l=1&id_h=5&name=FreeBSD" `shouldRespondWith` 500 Instead of a generic 500, it would probably be better to return |
@wolfgangwalther I think it would be a matter of detecting the "25006 read_only_sql_transaction" pg error code in Errors.hs: postgrest/src/PostgREST/Error.hs Lines 178 to 187 in e8b4e37
(For reference https://www.postgresql.org/docs/12/errcodes-appendix.html) I was thinking if there is a case where it wouldn't make sense to map a 25006 to a Method not allowed but couldn't come up with one. So I think 405 would be good. |
329544e
to
2f392d7
Compare
@wolfgangwalther Thought you were going to do the 405 logic here, but it's actually better on another PR. Check my above review. |
Co-authored-by: Steve Chavez <[email protected]>
Yeah, that 405 stuff wasnt really related to this here - just noticed it while working on the stuff. But there might be some other code that I would add to this PR instead of creating a new one: I have the "multiple query params with the same name should make an array" in the pipeline as suggested in #1470 (comment) and realized that this might have implications on how variadic handling (should) work(s) as well. |
So as a first step, I extended the existing tests about RPC params a little bit, because I noticed passing various argument types via query string was not tested for, yet. The same thing about I noticed a couple of things - see my comments below. |
{ matchHeaders = [matchContentTypeJson] } | ||
|
||
it "accepts a variety of arguments with GET" $ | ||
-- without JSON / JSONB here, because passing those via query string is useless - they just become a "json string" all the time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So passing JSON / JSONB via query string does not seem to work. At least if I try to pass something that should match an object, it seems like this is just returned as a json string.
Looking at the tests below that ("parses xxx JSON as JSON"), where this is explicitely tested for POST, I wonder: Is this expected behaviour for GET / query string (composing json objects in a query string is really ugly...) or is this to be considered a bug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The transformation from query params to sql params happens in Haskell code. I think it may be possible to fix this, but don't think is worth it. As you mentioned is ugly to deal with json(urlencoding) in the params.
I think users that wanted to pass json on GET params have been served well by Prefer: params=single-object
http://postgrest.org/en/v7.0.0/api.html#calling-functions-with-a-single-json-parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wolfgangwalther Now that I think about it maybe the shortcoming is in the second JSON.toJSON
in:
rpcPrmsToJson = ProcessedJSON (JSON.encode $ mapFromListWithMultiple $ second JSON.toJSON <$> rpcQParams) (S.fromList $ fst <$> rpcQParams)
The fix might could be using something like this?(Not sure):
postgrest/src/PostgREST/Middleware.hs
Lines 90 to 95 in 8e4687f
unquoted :: JSON.Value -> Text | |
unquoted (JSON.String t) = t | |
unquoted (JSON.Number n) = | |
toS $ formatScientific Fixed (if isInteger n then Just 0 else Nothing) n | |
unquoted (JSON.Bool b) = show b | |
unquoted v = toS $ JSON.encode v |
it "accepts a variety of arguments from an html form" $ | ||
request methodPost "/rpc/varied_arguments" | ||
[("Content-Type", "application/x-www-form-urlencoded")] | ||
"double=3.1&varchar=hello&boolean=true&date=20190101&money=0&enum=foo&arr=%7Ba,b,c%7D&integer=43" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not try to pass in JSON here, yet, but I think that would take a slightly different route then the query params. So that might yield a different result.
@@ -452,6 +453,10 @@ spec = do | |||
"format": "enum_menagerie_type", | |||
"type": "string" | |||
}, | |||
"arr": { | |||
"format": "text[]", | |||
"type": "string" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I had to add those lines to make the tests pass, because I added the array to the varied_arguments
function. The result does not seem correct, though - I think this looks like #1405. Now I don't think it makes sense to keep this in here like this, because this is not what we want it to do.
If we just changed that here accordingly - did I accidently create a test for #1405 (comment) ?
Now to my suggestion to implement support for
Ok, to be honest - I did the implementation first, because I was curious how the query string was parsed etc. - and only then looked for a reason ;) So how did I implement this and what did I have in mind?
CREATE FUNCTION repeated(arg INT) RETURNS INT[]
LANGUAGE SQL AS $$
SELECT repeated(ARRAY[arg])
$$;
CREATE FUNCTION repeated(arg INT[]) RETURNS INT[]
LANGUAGE SQL AS $$
SELECT arg
$$; This could now be called with both For But what we might be able to do is to call the SELECT * FROM variadic_func(VARIADIC arg := CASE jsonb_typeof(args.arg) WHEN 'array' THEN args.arg ELSE ARRAY[args.arg] END) If that works, then there would suddenly be a valid use-case for |
Maybe this can be solved by putting those lists that are generated from the repeated query params into |
Couple more thoughts about the overloading example above (and the corresponding failing test): Why does this not work?
I think we currently can't use overloaded functions with arguments with the same name. This is because we are resolving which overloaded function to use in Haskell and Postgres does not have to make any choice itself. This is because of: postgrest/src/PostgREST/QueryBuilder.hs Line 140 in 8e4687f
where we cast all the arguments to postgres types (and thereby choosing which function to call). To do this, we are currently resolving the overloaded functions only by argument name, so we will only ever use one of the casts. To improve this, we would need to either:
Will think about this a bit more. |
@wolfgangwalther Yeah, that was needed to fix #1005. I think it was worth it.
This PR got a bit complicated, how about if we leave the multiple params as array for another PR? You already did solve #1470. We could merge the variadic part and improve it later. variadic parameters are not common on API functions but being compliant with pg syntax is definitely a win for us. |
Yes, you're right it's gotten a bit more. I have thought about it and I think we can make it work now - at least in parts. I would definitely keep the overloading out of this (and remove the tests in that direction for now), because that's a lot more complicated - and is an issue we need to solve more generally as well. I might be able to get the "multiple params as array" working for variadic functions by looking at the function arguments while parsing the query string already. In this case there would be no overloading involved. If it doesn't work, then I will keep the "multiple params as array" out completely for now. Would that work for you? I think it would be good if we could include that in the next release. What's your current plan: When do you want to release? So I know by when I have to get this done. Had a bit of other stuff on my plate the last couple of days. |
Yes, that would work! It would also help me in reviewing the multiple param array idea better.
I was thinking to release when we merge this one and #1546. No pressure from my part :) My current plan is to release what we have - so users can try the new reloading features and see if there's any bugs - add docs, and do PostGIS features for the next release. |
I thought about this in-depth again - and I don't think it would be a good idea to merge the variadic part I did so far as it is now. This is to avoid breaking changes later on. My reasoning is as follows:
To avoid having to break this again, if we decide to support repeated params, I suggest to just implement the variadic in the form of repeated params from the start. This also makes a lot more sense conceptually - because that's exactly what "variadic" is all about. I do already have a very good idea of how to implement this. I still need to do a smaller refactor, to have the function definition (including arguments + overloading decision) available where the query params are parsed, but that will not be a problem. I will not be able to do it this week, though. so @steve-chavez you might want to do the next release without variadic. |
@wolfgangwalther Sure, let's think better about variadic for the next release. Also, check my latest review on PostgREST/postgrest-docs#339. It would be great to merge those additions. |
closed in favor of #1603 |
Still todo: Update docs
Adding the changelog item is hard on the first commit - because you have to guess the number of the PR. Let's see whether I guessed right.
Resolves #1470