From bc1fb67df034ae626d8101b603aba51478f594c7 Mon Sep 17 00:00:00 2001 From: steve-chavez Date: Mon, 3 Apr 2023 13:26:59 -0500 Subject: [PATCH] drop: Prefer: params=multiple-objects on RPC BREAKING CHANGE A function with a JSON array or object parameter should be used instead --- CHANGELOG.md | 4 +++ src/PostgREST/ApiRequest/Preferences.hs | 5 +--- src/PostgREST/Plan.hs | 1 - src/PostgREST/Plan/CallPlan.hs | 1 - src/PostgREST/Query.hs | 2 -- src/PostgREST/Query/QueryBuilder.hs | 4 +-- src/PostgREST/Query/Statements.hs | 6 ++--- test/spec/Feature/Query/PlanSpec.hs | 16 ----------- test/spec/Feature/Query/RpcSpec.hs | 36 ------------------------- 9 files changed, 10 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc69b23a2..7d85fc974 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). + Other methods should use the `limit/offset` query parameters for sub-ranges + `PUT` requests no longer return an error when this header is present (using `limit/offset` still triggers the error) +### Changed + + - #2733, Remove bulk RPC call with the `Prefer: params=multiple-objects` header. A function with a JSON array or object parameter should be used instead. + ## [10.1.2] - 2023-02-01 ### Fixed diff --git a/src/PostgREST/ApiRequest/Preferences.hs b/src/PostgREST/ApiRequest/Preferences.hs index c21bdb55b..302d7a00e 100644 --- a/src/PostgREST/ApiRequest/Preferences.hs +++ b/src/PostgREST/ApiRequest/Preferences.hs @@ -112,7 +112,7 @@ fromHeaders headers = Preferences { preferResolution = parsePrefs [MergeDuplicates, IgnoreDuplicates] , preferRepresentation = fromMaybe None $ parsePrefs [Full, None, HeadersOnly] - , preferParameters = parsePrefs [SingleObject, MultipleObjects] + , preferParameters = parsePrefs [SingleObject] , preferCount = parsePrefs [ExactCount, PlannedCount, EstimatedCount] , preferTransaction = parsePrefs [Commit, Rollback] , preferMissing = parsePrefs [ApplyDefaults, ApplyNulls] @@ -176,13 +176,10 @@ instance ToHeaderValue PreferRepresentation where -- | How to pass parameters to stored procedures. data PreferParameters = SingleObject -- ^ Pass all parameters as a single json object to a stored procedure. - | MultipleObjects -- ^ Pass an array of json objects as params to a stored procedure. deriving Eq --- TODO: Deprecate params=multiple-objects in next major version instance ToHeaderValue PreferParameters where toHeaderValue SingleObject = "params=single-object" - toHeaderValue MultipleObjects = "params=multiple-objects" -- | How to determine the count of (expected) results data PreferCount diff --git a/src/PostgREST/Plan.hs b/src/PostgREST/Plan.hs index a1ae8753b..d421008d2 100644 --- a/src/PostgREST/Plan.hs +++ b/src/PostgREST/Plan.hs @@ -548,7 +548,6 @@ callPlan proc ApiRequest{iPreferences=Preferences{..}} paramKeys args readReq = , funCParams = callParams , funCArgs = Just args , funCScalar = procReturnsScalar proc -, funCMultipleCall = preferParameters == Just MultipleObjects , funCReturning = inferColsEmbedNeeds readReq [] } where diff --git a/src/PostgREST/Plan/CallPlan.hs b/src/PostgREST/Plan/CallPlan.hs index 12d99fedc..8dbb446be 100644 --- a/src/PostgREST/Plan/CallPlan.hs +++ b/src/PostgREST/Plan/CallPlan.hs @@ -21,7 +21,6 @@ data CallPlan = FunctionCall , funCParams :: CallParams , funCArgs :: Maybe LBS.ByteString , funCScalar :: Bool - , funCMultipleCall :: Bool , funCReturning :: [FieldName] } diff --git a/src/PostgREST/Query.hs b/src/PostgREST/Query.hs index a2fa37ca8..e2f30f653 100644 --- a/src/PostgREST/Query.hs +++ b/src/PostgREST/Query.hs @@ -38,7 +38,6 @@ import Data.Scientific (FPFormat (..), formatScientific, isInteger) import PostgREST.ApiRequest (ApiRequest (..)) import PostgREST.ApiRequest.Preferences (PreferCount (..), - PreferParameters (..), PreferTransaction (..), Preferences (..), shouldCount) @@ -165,7 +164,6 @@ invokeQuery proc CallReadPlan{crReadPlan, crCallPlan, crBinField} apiReq@ApiRequ (QueryBuilder.readPlanToCountQuery crReadPlan) (shouldCount preferCount) iAcceptMediaType - (preferParameters == Just MultipleObjects) crBinField configDbPreparedStatements diff --git a/src/PostgREST/Query/QueryBuilder.hs b/src/PostgREST/Query/QueryBuilder.hs index 88304c9a5..7f1804e91 100644 --- a/src/PostgREST/Query/QueryBuilder.hs +++ b/src/PostgREST/Query/QueryBuilder.hs @@ -164,14 +164,14 @@ mutatePlanToQuery (Delete mainQi logicForest range ordts returnings) (whereRangeIdF, rangeIdF) = mutRangeF mainQi (fst . otTerm <$> ordts) callPlanToQuery :: CallPlan -> SQL.Snippet -callPlanToQuery (FunctionCall qi params args returnsScalar multipleCall returnings) = +callPlanToQuery (FunctionCall qi params args returnsScalar returnings) = "SELECT " <> (if returnsScalar then "pgrst_call AS pgrst_scalar " else returnedColumns) <> " " <> fromCall where fromCall = case params of OnePosParam prm -> "FROM " <> callIt (singleParameter args $ encodeUtf8 $ ppType prm) KeyParams [] -> "FROM " <> callIt mempty - KeyParams prms -> fromJsonBodyF args ((\p -> TypedField (ppName p) (ppType p) Nothing) <$> prms) False (not multipleCall) False <> ", " <> + KeyParams prms -> fromJsonBodyF args ((\p -> TypedField (ppName p) (ppType p) Nothing) <$> prms) False True False <> ", " <> "LATERAL " <> callIt (fmtParams prms) callIt :: SQL.Snippet -> SQL.Snippet diff --git a/src/PostgREST/Query/Statements.hs b/src/PostgREST/Query/Statements.hs index be90e8a46..9ba90bd4b 100644 --- a/src/PostgREST/Query/Statements.hs +++ b/src/PostgREST/Query/Statements.hs @@ -131,9 +131,9 @@ prepareRead selectQuery countQuery countTotal mt binaryField = _ -> HD.singleRow $ standardRow True prepareCall :: Bool -> Bool -> SQL.Snippet -> SQL.Snippet -> SQL.Snippet -> Bool -> - MediaType -> Bool -> Maybe FieldName -> Bool -> + MediaType -> Maybe FieldName -> Bool -> SQL.Statement () ResultSet -prepareCall returnsScalar returnsSingle callProcQuery selectQuery countQuery countTotal mt multObjects binaryField = +prepareCall returnsScalar returnsSingle callProcQuery selectQuery countQuery countTotal mt binaryField = SQL.dynamicallyParameterized (mtSnippet mt snippet) decodeIt where snippet = @@ -156,7 +156,7 @@ prepareCall returnsScalar returnsSingle callProcQuery selectQuery countQuery cou | getMediaType mt == MTGeoJSON = asGeoJsonF | isJust binaryField && getMediaType mt == MTTextXML = asXmlF $ fromJust binaryField | isJust binaryField = asBinaryF $ fromJust binaryField - | returnsSingle && not multObjects = asJsonSingleF returnsScalar + | returnsSingle = asJsonSingleF returnsScalar | otherwise = asJsonF returnsScalar decodeIt :: HD.Result ResultSet diff --git a/test/spec/Feature/Query/PlanSpec.hs b/test/spec/Feature/Query/PlanSpec.hs index 977c8e512..eb21d95c5 100644 --- a/test/spec/Feature/Query/PlanSpec.hs +++ b/test/spec/Feature/Query/PlanSpec.hs @@ -15,7 +15,6 @@ import Network.HTTP.Types import Test.Hspec hiding (pendingWith) import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Text.Heredoc import PostgREST.Config.PgVersion (PgVersion, pgVersion120, pgVersion130) @@ -331,21 +330,6 @@ spec actualPgVersion = do liftIO $ planCost r `shouldSatisfy` (< 1.18) - context "params=multiple-objects" $ do - it "should not exceed cost when calling setof composite proc" $ do - r <- request methodPost "/rpc/get_projects_below" - [planHdr, ("Prefer", "params=multiple-objects")] - [str| [{"id": 1}, {"id": 4}] |] - - liftIO $ planCost r `shouldSatisfy` (< 4503.4) - - it "should not exceed cost when calling scalar proc" $ do - r <- request methodPost "/rpc/add_them" - [planHdr, ("Prefer", "params=multiple-objects")] - [str| [{"a": 3, "b": 4}, {"a": 1, "b": 2}, {"a": 8, "b": 7}] |] - - liftIO $ planCost r `shouldSatisfy` (< 5.85) - context "function inlining" $ do it "should inline a zero argument function(the function won't appear in the plan tree)" $ do r <- request methodGet "/rpc/getallusers?id=eq.1" diff --git a/test/spec/Feature/Query/RpcSpec.hs b/test/spec/Feature/Query/RpcSpec.hs index 54c65baf1..090a04156 100644 --- a/test/spec/Feature/Query/RpcSpec.hs +++ b/test/spec/Feature/Query/RpcSpec.hs @@ -930,42 +930,6 @@ spec actualPgVersion = `shouldRespondWith` "3" { matchHeaders = [matchContentTypeJson] } - context "bulk RPC with params=multiple-objects" $ do - it "works with a scalar function an returns a json array" $ - request methodPost "/rpc/add_them" [("Prefer", "params=multiple-objects")] - [json|[ - {"a": 1, "b": 2}, - {"a": 4, "b": 6}, - {"a": 100, "b": 200} ]|] - `shouldRespondWith` - [json| - [3, 10, 300] - |] { matchHeaders = [matchContentTypeJson] } - - it "works with a scalar function an returns a json array when posting CSV" $ - request methodPost "/rpc/add_them" [("Content-Type", "text/csv"), ("Prefer", "params=multiple-objects")] - "a,b\n1,2\n4,6\n100,200" - `shouldRespondWith` - [json| - [3, 10, 300] - |] - { matchStatus = 200 - , matchHeaders = [matchContentTypeJson] - } - - it "works with a non-scalar result" $ - request methodPost "/rpc/get_projects_below?select=id,name" [("Prefer", "params=multiple-objects")] - [json|[ - {"id": 1}, - {"id": 5} ]|] - `shouldRespondWith` - [json| - [{"id":1,"name":"Windows 7"}, - {"id":2,"name":"Windows 10"}, - {"id":3,"name":"IOS"}, - {"id":4,"name":"OSX"}] - |] { matchHeaders = [matchContentTypeJson] } - context "HTTP request env vars" $ do it "custom header is set" $ request methodPost "/rpc/get_guc_value"