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:
committed by
Wolfgang Walther
parent
0dc1345eb4
commit
ee4bfbf253
@@ -1,5 +1,6 @@
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
{-|
|
||||
Module : PostgREST.Query.QueryBuilder
|
||||
Description : PostgREST SQL queries generating functions.
|
||||
@@ -16,8 +17,11 @@ module PostgREST.Query.QueryBuilder
|
||||
, limitedQuery
|
||||
) where
|
||||
|
||||
import qualified Data.Aeson as JSON
|
||||
import qualified Data.ByteString.Char8 as BS
|
||||
import qualified Data.HashMap.Strict as HM
|
||||
import qualified Hasql.DynamicStatements.Snippet as SQL
|
||||
import qualified Hasql.Encoders as HE
|
||||
|
||||
import Data.Maybe (fromJust)
|
||||
import Data.Tree (Tree (..))
|
||||
@@ -190,14 +194,19 @@ mutatePlanToQuery (Delete mainQi logicForest range ordts returnings)
|
||||
(whereRangeIdF, rangeIdF) = mutRangeF mainQi (cfName . coField <$> ordts)
|
||||
|
||||
callPlanToQuery :: CallPlan -> PgVersion -> SQL.Snippet
|
||||
callPlanToQuery (FunctionCall qi params args returnsScalar returnsSetOfScalar returnsCompositeAlias returnings) pgVer =
|
||||
callPlanToQuery (FunctionCall qi params arguments returnsScalar returnsSetOfScalar returnsCompositeAlias returnings) pgVer =
|
||||
"SELECT " <> (if returnsScalar || returnsSetOfScalar then "pgrst_call.pgrst_scalar" else returnedColumns) <> " " <>
|
||||
fromCall
|
||||
where
|
||||
jsonArgs = case arguments of
|
||||
DirectArgs args -> Just $ JSON.encode args
|
||||
JsonArgs json -> json
|
||||
fromCall = case params of
|
||||
OnePosParam prm -> "FROM " <> callIt (singleParameter args $ encodeUtf8 $ ppType prm)
|
||||
OnePosParam prm -> "FROM " <> callIt (singleParameter jsonArgs $ encodeUtf8 $ ppType prm)
|
||||
KeyParams [] -> "FROM " <> callIt mempty
|
||||
KeyParams prms -> fromJsonBodyF args ((\p -> CoercibleField (ppName p) mempty False (ppTypeMaxLength p) Nothing Nothing) <$> prms) False True False <> ", " <>
|
||||
KeyParams prms -> case arguments of
|
||||
DirectArgs args -> "FROM " <> callIt (fmtArgs prms args)
|
||||
JsonArgs json -> fromJsonBodyF json ((\p -> CoercibleField (ppName p) mempty False (ppTypeMaxLength p) Nothing Nothing) <$> prms) False True False <> ", " <>
|
||||
"LATERAL " <> callIt (fmtParams prms)
|
||||
|
||||
callIt :: SQL.Snippet -> SQL.Snippet
|
||||
@@ -209,6 +218,23 @@ callPlanToQuery (FunctionCall qi params args returnsScalar returnsSetOfScalar re
|
||||
fmtParams prms = intercalateSnippet ", "
|
||||
((\a -> (if ppVar a then "VARIADIC " else mempty) <> pgFmtIdent (ppName a) <> " := pgrst_body." <> pgFmtIdent (ppName a)) <$> prms)
|
||||
|
||||
fmtArgs :: [RoutineParam] -> HM.HashMap Text RpcParamValue -> SQL.Snippet
|
||||
fmtArgs prms args = intercalateSnippet ", " $ fmtArg <$> prms
|
||||
where
|
||||
fmtArg RoutineParam{..} =
|
||||
(if ppVar then "VARIADIC " else mempty) <>
|
||||
pgFmtIdent ppName <>
|
||||
" := " <>
|
||||
encodeArg (HM.lookup ppName args) <>
|
||||
"::" <>
|
||||
SQL.sql (encodeUtf8 ppTypeMaxLength)
|
||||
encodeArg :: Maybe RpcParamValue -> SQL.Snippet
|
||||
encodeArg (Just (Variadic v)) = SQL.encoderAndParam (HE.nonNullable $ HE.foldableArray $ HE.nonNullable HE.text) v
|
||||
encodeArg (Just (Fixed v)) = SQL.encoderAndParam (HE.nonNullable HE.unknown) $ encodeUtf8 v
|
||||
-- Currently not supported: Calling functions without some of their arguments without DEFAULT.
|
||||
-- We could fallback to providing this NULL value in those cases.
|
||||
encodeArg Nothing = "NULL"
|
||||
|
||||
returnedColumns :: SQL.Snippet
|
||||
returnedColumns
|
||||
| null returnings = "*"
|
||||
|
||||
Reference in New Issue
Block a user