drop: Prefer: params=multiple-objects on RPC

BREAKING CHANGE

A function with a JSON array or object parameter should be used instead
This commit is contained in:
steve-chavez
2023-04-04 19:55:10 -05:00
committed by Steve Chavez
parent 1442e02f5f
commit bc1fb67df0
9 changed files with 10 additions and 65 deletions
+4
View File
@@ -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 + 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) + `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 ## [10.1.2] - 2023-02-01
### Fixed ### Fixed
+1 -4
View File
@@ -112,7 +112,7 @@ fromHeaders headers =
Preferences Preferences
{ preferResolution = parsePrefs [MergeDuplicates, IgnoreDuplicates] { preferResolution = parsePrefs [MergeDuplicates, IgnoreDuplicates]
, preferRepresentation = fromMaybe None $ parsePrefs [Full, None, HeadersOnly] , preferRepresentation = fromMaybe None $ parsePrefs [Full, None, HeadersOnly]
, preferParameters = parsePrefs [SingleObject, MultipleObjects] , preferParameters = parsePrefs [SingleObject]
, preferCount = parsePrefs [ExactCount, PlannedCount, EstimatedCount] , preferCount = parsePrefs [ExactCount, PlannedCount, EstimatedCount]
, preferTransaction = parsePrefs [Commit, Rollback] , preferTransaction = parsePrefs [Commit, Rollback]
, preferMissing = parsePrefs [ApplyDefaults, ApplyNulls] , preferMissing = parsePrefs [ApplyDefaults, ApplyNulls]
@@ -176,13 +176,10 @@ instance ToHeaderValue PreferRepresentation where
-- | How to pass parameters to stored procedures. -- | How to pass parameters to stored procedures.
data PreferParameters data PreferParameters
= SingleObject -- ^ Pass all parameters as a single json object to a stored procedure. = 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 deriving Eq
-- TODO: Deprecate params=multiple-objects in next major version
instance ToHeaderValue PreferParameters where instance ToHeaderValue PreferParameters where
toHeaderValue SingleObject = "params=single-object" toHeaderValue SingleObject = "params=single-object"
toHeaderValue MultipleObjects = "params=multiple-objects"
-- | How to determine the count of (expected) results -- | How to determine the count of (expected) results
data PreferCount data PreferCount
-1
View File
@@ -548,7 +548,6 @@ callPlan proc ApiRequest{iPreferences=Preferences{..}} paramKeys args readReq =
, funCParams = callParams , funCParams = callParams
, funCArgs = Just args , funCArgs = Just args
, funCScalar = procReturnsScalar proc , funCScalar = procReturnsScalar proc
, funCMultipleCall = preferParameters == Just MultipleObjects
, funCReturning = inferColsEmbedNeeds readReq [] , funCReturning = inferColsEmbedNeeds readReq []
} }
where where
-1
View File
@@ -21,7 +21,6 @@ data CallPlan = FunctionCall
, funCParams :: CallParams , funCParams :: CallParams
, funCArgs :: Maybe LBS.ByteString , funCArgs :: Maybe LBS.ByteString
, funCScalar :: Bool , funCScalar :: Bool
, funCMultipleCall :: Bool
, funCReturning :: [FieldName] , funCReturning :: [FieldName]
} }
-2
View File
@@ -38,7 +38,6 @@ import Data.Scientific (FPFormat (..), formatScientific, isInteger)
import PostgREST.ApiRequest (ApiRequest (..)) import PostgREST.ApiRequest (ApiRequest (..))
import PostgREST.ApiRequest.Preferences (PreferCount (..), import PostgREST.ApiRequest.Preferences (PreferCount (..),
PreferParameters (..),
PreferTransaction (..), PreferTransaction (..),
Preferences (..), Preferences (..),
shouldCount) shouldCount)
@@ -165,7 +164,6 @@ invokeQuery proc CallReadPlan{crReadPlan, crCallPlan, crBinField} apiReq@ApiRequ
(QueryBuilder.readPlanToCountQuery crReadPlan) (QueryBuilder.readPlanToCountQuery crReadPlan)
(shouldCount preferCount) (shouldCount preferCount)
iAcceptMediaType iAcceptMediaType
(preferParameters == Just MultipleObjects)
crBinField crBinField
configDbPreparedStatements configDbPreparedStatements
+2 -2
View File
@@ -164,14 +164,14 @@ mutatePlanToQuery (Delete mainQi logicForest range ordts returnings)
(whereRangeIdF, rangeIdF) = mutRangeF mainQi (fst . otTerm <$> ordts) (whereRangeIdF, rangeIdF) = mutRangeF mainQi (fst . otTerm <$> ordts)
callPlanToQuery :: CallPlan -> SQL.Snippet 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) <> " " <> "SELECT " <> (if returnsScalar then "pgrst_call AS pgrst_scalar " else returnedColumns) <> " " <>
fromCall fromCall
where where
fromCall = case params of fromCall = case params of
OnePosParam prm -> "FROM " <> callIt (singleParameter args $ encodeUtf8 $ ppType prm) OnePosParam prm -> "FROM " <> callIt (singleParameter args $ encodeUtf8 $ ppType prm)
KeyParams [] -> "FROM " <> callIt mempty 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) "LATERAL " <> callIt (fmtParams prms)
callIt :: SQL.Snippet -> SQL.Snippet callIt :: SQL.Snippet -> SQL.Snippet
+3 -3
View File
@@ -131,9 +131,9 @@ prepareRead selectQuery countQuery countTotal mt binaryField =
_ -> HD.singleRow $ standardRow True _ -> HD.singleRow $ standardRow True
prepareCall :: Bool -> Bool -> SQL.Snippet -> SQL.Snippet -> SQL.Snippet -> Bool -> prepareCall :: Bool -> Bool -> SQL.Snippet -> SQL.Snippet -> SQL.Snippet -> Bool ->
MediaType -> Bool -> Maybe FieldName -> Bool -> MediaType -> Maybe FieldName -> Bool ->
SQL.Statement () ResultSet 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 SQL.dynamicallyParameterized (mtSnippet mt snippet) decodeIt
where where
snippet = snippet =
@@ -156,7 +156,7 @@ prepareCall returnsScalar returnsSingle callProcQuery selectQuery countQuery cou
| getMediaType mt == MTGeoJSON = asGeoJsonF | getMediaType mt == MTGeoJSON = asGeoJsonF
| isJust binaryField && getMediaType mt == MTTextXML = asXmlF $ fromJust binaryField | isJust binaryField && getMediaType mt == MTTextXML = asXmlF $ fromJust binaryField
| isJust binaryField = asBinaryF $ fromJust binaryField | isJust binaryField = asBinaryF $ fromJust binaryField
| returnsSingle && not multObjects = asJsonSingleF returnsScalar | returnsSingle = asJsonSingleF returnsScalar
| otherwise = asJsonF returnsScalar | otherwise = asJsonF returnsScalar
decodeIt :: HD.Result ResultSet decodeIt :: HD.Result ResultSet
-16
View File
@@ -15,7 +15,6 @@ import Network.HTTP.Types
import Test.Hspec hiding (pendingWith) import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import Text.Heredoc
import PostgREST.Config.PgVersion (PgVersion, pgVersion120, import PostgREST.Config.PgVersion (PgVersion, pgVersion120,
pgVersion130) pgVersion130)
@@ -331,21 +330,6 @@ spec actualPgVersion = do
liftIO $ planCost r `shouldSatisfy` (< 1.18) 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 context "function inlining" $ do
it "should inline a zero argument function(the function won't appear in the plan tree)" $ 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" r <- request methodGet "/rpc/getallusers?id=eq.1"
-36
View File
@@ -930,42 +930,6 @@ spec actualPgVersion =
`shouldRespondWith` "3" `shouldRespondWith` "3"
{ matchHeaders = [matchContentTypeJson] } { 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 context "HTTP request env vars" $ do
it "custom header is set" $ it "custom header is set" $
request methodPost "/rpc/get_guc_value" request methodPost "/rpc/get_guc_value"