perf: Pass arguments to RPCs called via GET directly

Previously they were passed as a JSON payload. This results in a LATERAL
join for the calling expression, which prevents LIMIT from being pushed
into the inlined function call, making some requests really slow.

Resolves #2858
This commit is contained in:
Wolfgang Walther
2024-07-09 08:29:13 +02:00
committed by Wolfgang Walther
parent 0dc1345eb4
commit ee4bfbf253
5 changed files with 59 additions and 26 deletions
+22 -15
View File
@@ -2,7 +2,9 @@
module PostgREST.Plan.CallPlan
( CallPlan(..)
, CallParams(..)
, jsonRpcParams
, CallArgs(..)
, RpcParamValue(..)
, toRpcParams
)
where
@@ -19,7 +21,7 @@ import Protolude
data CallPlan = FunctionCall
{ funCQi :: QualifiedIdentifier
, funCParams :: CallParams
, funCArgs :: Maybe LBS.ByteString
, funCArgs :: CallArgs
, funCScalar :: Bool
, funCSetOfScalar :: Bool
, funCRetCompositeAlias :: Bool
@@ -30,14 +32,26 @@ data CallParams
= KeyParams [RoutineParam] -- ^ Call with key params: func(a := val1, b:= val2)
| OnePosParam RoutineParam -- ^ Call with positional params(only one supported): func(val)
data CallArgs
= DirectArgs (HM.HashMap Text RpcParamValue)
| JsonArgs (Maybe LBS.ByteString)
-- | RPC query param value `/rpc/func?v=<value>`, used for VARIADIC functions on form-urlencoded POST and GETs
-- | It can be fixed `?v=1` or repeated `?v=1&v=2&v=3.
data RpcParamValue = Fixed Text | Variadic [Text]
instance JSON.ToJSON RpcParamValue where
toJSON (Fixed v) = JSON.toJSON v
-- Not possible to get here anymore. Variadic arguments are only supported for
-- true variadic arguments, but the toJSON instance is only used for the "single unnamed json argument" case.
toJSON (Variadic v) = JSON.toJSON v
-- | Convert rpc params `/rpc/func?a=val1&b=val2` to json `{"a": "val1", "b": "val2"}
jsonRpcParams :: Routine -> [(Text, Text)] -> LBS.ByteString
jsonRpcParams proc prms =
if not $ pdHasVariadic proc then -- if proc has no variadic param, save steps and directly convert to json
JSON.encode $ HM.fromList $ second JSON.toJSON <$> prms
toRpcParams :: Routine -> [(Text, Text)] -> HM.HashMap Text RpcParamValue
toRpcParams proc prms =
if not $ pdHasVariadic proc then -- if proc has no variadic param, save steps and directly convert to map
HM.fromList $ second Fixed <$> prms
else
let paramsMap = HM.fromListWith mergeParams $ toRpcParamValue proc <$> prms in
JSON.encode paramsMap
HM.fromListWith mergeParams $ toRpcParamValue proc <$> prms
where
mergeParams :: RpcParamValue -> RpcParamValue -> RpcParamValue
mergeParams (Variadic a) (Variadic b) = Variadic $ b ++ a
@@ -48,10 +62,3 @@ toRpcParamValue proc (k, v) | prmIsVariadic k = (k, Variadic [v])
| otherwise = (k, Fixed v)
where
prmIsVariadic prm = isJust $ find (\RoutineParam{ppName, ppVar} -> ppName == prm && ppVar) $ pdParams proc
-- | RPC query param value `/rpc/func?v=<value>`, used for VARIADIC functions on form-urlencoded POST and GETs
-- | It can be fixed `?v=1` or repeated `?v=1&v=2&v=3.
data RpcParamValue = Fixed Text | Variadic [Text]
instance JSON.ToJSON RpcParamValue where
toJSON (Fixed v) = JSON.toJSON v
toJSON (Variadic v) = JSON.toJSON v