-
-
Notifications
You must be signed in to change notification settings - Fork 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
feat: add request.param guc #1710
base: main
Are you sure you want to change the base?
Conversation
Hm. I'm not entirely convinced that I would want to have all the request params set as GUCs. Every GUC has a performance penalty when setting it: I remember from the SQL tests with did with the
I agree with that. Taking a step back for a moment I wonder whether we can design something that would allow me to opt-in for all kind of What if we did:
Example: CREATE FUNCTION my_pre_request(method TEXT, filters JSON) RETURNS void
LANGUAGE plpgsql AS $$
... similar to the function in the opening post ...
$$; Here, we would parse the function definition together with the schema cache and would then know which arguments we need to pass in. This would have the advantage that we could choose which variables to pass / set and could leave a big chunk out in most cases - improving performance. We would still only need one round-trip to the database, it should not perform worse. Thinking more about the naming of arguments, we could support something like a json path in those, to be able to select only a sub-key: CREATE FUNCTION my_pre_request("headers.my_custom_header" TEXT, filters JSON) This would provide all filters, but only one of the headers. |
Defining the gucs in pre-request seems interesting. A drawback is that if you only want to use restricted views or tables(#915 (comment)), you'd have to define the gucs on pre-request as well, which makes the feature more complicated to use. Ideally one would only touch pre-request for the global cases. An advantage is that it could allow more GUCs per endpoint and it would not be a global config like we could do on the config file. Not opposed to the idea, need to have more thought about it. Other stuff that could be interesting for this feature:
|
New idea for filter restrictionsHow about this. If we (ab)use our in-db config for defining restrictions, we could make them static checks that don't have to hit the database. Example: CREATE TABLE clients(
id int
, name text
);
-- assuming our postgrest-contrib in place
SELECT pgrst.restrict_filter('clients', 'id'); Then a request that doesn't include a filter on GET /clients
HTTP/1.1 403 Forbidden
{"hint":null,"details":null, "code": null, "message":"id filter must be present"} To make that work, the ALTER ROLE pgrst_authenticator SET parameters_restrictions = $$
{
"clients": "id"
}
$$;
-- or to a config file as
parameters-restrictions = '{"clients": "id"}' Advantages
SELECT pgrst.restrict_filter('rpc/get_clients', 'id'); Possibilities
SELECT pgrst.restrict_method('clients', '{PATCH,DELETE}', '{id,name}');
-- only allow patch/delete if id or name filters are present.
SELECT pgrst.allow_function('clients', 'avg(salary)');
SELECT pgrst.allow_operators('clients', '{eq,gt,lt}');
SELECT pgrst.deny_operators('clients', '{fts,wfts,phtfts,like}');
SELECT pgrst.allow_group_by(global:=true);
SELECT pgrst.allow_group_by('clients');
SELECT pgrst.allow_distinct('clients');
SELECT pgrst.embedding_depth(global:=true, 5); (Just an idea, the functions form could use more work) @wolfgangwalther What do you think? Do you see any drawbacks with this approach? |
Hm. My first reaction was: Why not do that at the proxy-level? This seems out of scope for PostgREST. But giving it further thought... I actually like it. When discussing all the content negotiation stuff with We could implement this in a much more generic and flexible way. And then we could load additional config per route the kind of checks you mention. I'm not sure about using all kinds of GUCs / config options for those kind of settings, though. Maybe we should just have a |
Yeah, the line that separated proxy and PostgREST has blurred out with time. Even the previous GUC Also, we're already a proxy for pg as well, after all we reject some requests(invalid http method, invalid jwt, etc) without touching pg. Additionally, doing REST restrictions with our functions seems superior to doing it inside RLS or a security-barrier-like condition on views. They're a PostgREST concern after all, they should be stored in our config.
Sounds interesting! But I think we should keep the design that |
Hm. The table approach is kind of a hack to achieve the same thing as schema variables would do. For schema variables, we'd need to have privileges, too. Yes, I was thinking about putting it in our extension. This idea was mainly targeted at cloud-based users, who can't use GUC-based configuration. Maybe we can even extend this idea: How about making In any case, I agree with keeping the filter settings you mentioned above in a single We could create some types roughly along the lines of: -- maps a list of path segments to a route configuration
type Router = M.Map [Text] Route
data Route = Route {
rtTarget :: Target,
-- ... other settings
rtAllowedMethods :: [Method],
rtAllowedOperators :: [Operator],
rtAllowAggregation :: Boolean,
rtAllowedFunctions :: [Text],
rtRequiredFilters :: [Text]
} (Those types need to be adjusted to properly allow function overloading for RPCs, but the general idea would be the same.) When parsing the request, we would match this to one of the routes. This would imply, that we can't respond to any unknown routes anymore. I remember we discussed this elsewhere already, though. The default values for each route are generated from the schema cache. And then we add the json config option mentioned above (e.g. |
Maybe |
The above is still true. However now that we use a single json for our GUCs, |
The above ideas could also be done with a SECURITY LABEL extension, like discussed on #2442 (comment) |
The option to be able to access limit/offset - even if not other columns (controlled via config) - also helps us build RPCs that can artificially use custom pagination techniques (for my use case, currently modifying RPC to view works , combed with haproxy URL redirection for consistency - but would be nice to have a more consistent option as we predominantly rely on /rpc => /api map - which works for 80% of endpoints - except cases when RPC are not ideal - for being able to leverage keyset pagination) |
As discussed on #915 (comment). This adds the
request.param
GUC.This is the draft implementation. Not efficient because it leads to a lot of new prepared statements(no reuse).
Each query param
?select=id,name&id=eq.1&name=eq.project&order=id
will get its own parameter:Ideas
Convert all the query parameters to a single json, like mentioned in Change SET LOCAL gucs to set_config #1600 (comment). Would lead to reusing prepared statements.
How about having a single GUC for the filters only?
request.param.filters
. This would be a JSON object:{"id": "eq.1", "name": "eq.project"}
. There wouldn't be arequest.param.id
orrequest.param.name
.pre-request
. It'd be easier to validate that filters are present by having them on a single json.(this would be more flexible than doing it as config option as proposed on Cancel query if user cancels request #699 (comment))