Skip to content

Commit 180a96c

Browse files
authored
remove support for Prefer: params=single-object (PostgREST#3757)
BREAKING CHANGE Using this preference was deprecated in 6c3d7a9, in favor of Functions with an array of JSON objects.
1 parent da0f48e commit 180a96c

File tree

10 files changed

+28
-139
lines changed

10 files changed

+28
-139
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
55

66
## Unreleased
77

8+
### Removed
9+
10+
- #3757, Remove support for `Prefer: params=single-object` - @joelonsql
11+
+ This preference was deprecated in favor of Functions with an array of JSON objects
812

913
### Added
1014

docs/references/api/functions.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,6 @@ For this the ``Content-Type: application/json`` header must be included in the r
131131

132132
If an overloaded function has a single ``json`` or ``jsonb`` unnamed parameter, PostgREST will call this function as a fallback provided that no other overloaded function is found with the parameters sent in the POST request.
133133

134-
.. warning::
135-
136-
Sending the JSON request body as a single argument is also possible with :ref:`Prefer: params=single-object <prefer_params>` but this method is **deprecated**.
137-
138134
.. _function_single_unnamed:
139135

140136
Functions with a single unnamed parameter

docs/references/api/preferences.rst

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ The following preferences are supported.
1515
- ``Prefer: missing``. See :ref:`bulk_insert_default`.
1616
- ``Prefer: max-affected``, See :ref:`prefer_max_affected`.
1717
- ``Prefer: tx``. See :ref:`prefer_tx`.
18-
- ``Prefer: params``. See :ref:`prefer_params`.
1918

2019
.. _prefer_handling:
2120

@@ -224,31 +223,3 @@ To illustrate the use of this preference, consider the following scenario where
224223
"details": "The query affects 14 rows",
225224
"hint": null
226225
}
227-
228-
.. _prefer_params:
229-
230-
Single JSON object as Function Parameter
231-
----------------------------------------
232-
233-
.. warning::
234-
235-
Using this preference is **deprecated** in favor of :ref:`function_single_json`.
236-
237-
:code:`Prefer: params=single-object` allows sending the JSON request body as the single argument of a :ref:`function <functions>`.
238-
239-
.. code-block:: postgres
240-
241-
CREATE FUNCTION mult_them(param json) RETURNS int AS $$
242-
SELECT (param->>'x')::int * (param->>'y')::int
243-
$$ LANGUAGE SQL;
244-
245-
.. code-block:: bash
246-
247-
curl "http://localhost:3000/rpc/mult_them" \
248-
-X POST -H "Content-Type: application/json" \
249-
-H "Prefer: params=single-object" \
250-
-d '{ "x": 4, "y": 2 }'
251-
252-
.. code-block:: json
253-
254-
8

src/PostgREST/ApiRequest/Preferences.hs

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ module PostgREST.ApiRequest.Preferences
1212
, PreferCount(..)
1313
, PreferHandling(..)
1414
, PreferMissing(..)
15-
, PreferParameters(..)
1615
, PreferRepresentation(..)
1716
, PreferResolution(..)
1817
, PreferTransaction(..)
@@ -37,7 +36,6 @@ import Protolude
3736
-- >>> import Text.Pretty.Simple (pPrint)
3837
-- >>> deriving instance Show PreferResolution
3938
-- >>> deriving instance Show PreferRepresentation
40-
-- >>> deriving instance Show PreferParameters
4139
-- >>> deriving instance Show PreferCount
4240
-- >>> deriving instance Show PreferTransaction
4341
-- >>> deriving instance Show PreferMissing
@@ -51,7 +49,6 @@ data Preferences
5149
= Preferences
5250
{ preferResolution :: Maybe PreferResolution
5351
, preferRepresentation :: Maybe PreferRepresentation
54-
, preferParameters :: Maybe PreferParameters
5552
, preferCount :: Maybe PreferCount
5653
, preferTransaction :: Maybe PreferTransaction
5754
, preferMissing :: Maybe PreferMissing
@@ -71,7 +68,6 @@ data Preferences
7168
-- Preferences
7269
-- { preferResolution = Just IgnoreDuplicates
7370
-- , preferRepresentation = Nothing
74-
-- , preferParameters = Nothing
7571
-- , preferCount = Just ExactCount
7672
-- , preferTransaction = Nothing
7773
-- , preferMissing = Nothing
@@ -89,7 +85,6 @@ data Preferences
8985
-- Preferences
9086
-- { preferResolution = Just IgnoreDuplicates
9187
-- , preferRepresentation = Nothing
92-
-- , preferParameters = Nothing
9388
-- , preferCount = Just ExactCount
9489
-- , preferTransaction = Nothing
9590
-- , preferMissing = Just ApplyNulls
@@ -122,7 +117,6 @@ data Preferences
122117
-- Preferences
123118
-- { preferResolution = Nothing
124119
-- , preferRepresentation = Just Full
125-
-- , preferParameters = Nothing
126120
-- , preferCount = Just ExactCount
127121
-- , preferTransaction = Just Commit
128122
-- , preferMissing = Just ApplyDefaults
@@ -137,7 +131,6 @@ fromHeaders allowTxDbOverride acceptedTzNames headers =
137131
Preferences
138132
{ preferResolution = parsePrefs [MergeDuplicates, IgnoreDuplicates]
139133
, preferRepresentation = parsePrefs [Full, None, HeadersOnly]
140-
, preferParameters = parsePrefs [SingleObject]
141134
, preferCount = parsePrefs [ExactCount, PlannedCount, EstimatedCount]
142135
, preferTransaction = if allowTxDbOverride then parsePrefs [Commit, Rollback] else Nothing
143136
, preferMissing = parsePrefs [ApplyDefaults, ApplyNulls]
@@ -151,7 +144,6 @@ fromHeaders allowTxDbOverride acceptedTzNames headers =
151144
mapToHeadVal = map toHeaderValue
152145
acceptedPrefs = mapToHeadVal [MergeDuplicates, IgnoreDuplicates] ++
153146
mapToHeadVal [Full, None, HeadersOnly] ++
154-
mapToHeadVal [SingleObject] ++
155147
mapToHeadVal [ExactCount, PlannedCount, EstimatedCount] ++
156148
mapToHeadVal [Commit, Rollback] ++
157149
mapToHeadVal [ApplyDefaults, ApplyNulls] ++
@@ -179,7 +171,7 @@ fromHeaders allowTxDbOverride acceptedTzNames headers =
179171
prefMap = Map.fromList . fmap (\pref -> (toHeaderValue pref, pref))
180172

181173
prefAppliedHeader :: Preferences -> Maybe HTTP.Header
182-
prefAppliedHeader Preferences {preferResolution, preferRepresentation, preferParameters, preferCount, preferTransaction, preferMissing, preferHandling, preferTimezone, preferMaxAffected } =
174+
prefAppliedHeader Preferences {preferResolution, preferRepresentation, preferCount, preferTransaction, preferMissing, preferHandling, preferTimezone, preferMaxAffected } =
183175
if null prefsVals
184176
then Nothing
185177
else Just (HTTP.hPreferenceApplied, combined)
@@ -189,7 +181,6 @@ prefAppliedHeader Preferences {preferResolution, preferRepresentation, preferPar
189181
toHeaderValue <$> preferResolution
190182
, toHeaderValue <$> preferMissing
191183
, toHeaderValue <$> preferRepresentation
192-
, toHeaderValue <$> preferParameters
193184
, toHeaderValue <$> preferCount
194185
, toHeaderValue <$> preferTransaction
195186
, toHeaderValue <$> preferHandling
@@ -231,15 +222,6 @@ instance ToHeaderValue PreferRepresentation where
231222
toHeaderValue None = "return=minimal"
232223
toHeaderValue HeadersOnly = "return=headers-only"
233224

234-
-- | How to pass parameters to stored procedures.
235-
-- TODO: deprecated. Remove on next major version.
236-
data PreferParameters
237-
= SingleObject -- ^ Pass all parameters as a single json object to a stored procedure.
238-
deriving Eq
239-
240-
instance ToHeaderValue PreferParameters where
241-
toHeaderValue SingleObject = "params=single-object"
242-
243225
-- | How to determine the count of (expected) results
244226
data PreferCount
245227
= ExactCount -- ^ Exact count (slower).

src/PostgREST/ApiRequest/Types.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ data ApiRequestError
8181
| LimitNoOrderError
8282
| NotFound
8383
| NoRelBetween Text Text (Maybe Text) Text RelationshipsMap
84-
| NoRpc Text Text [Text] Bool MediaType Bool [QualifiedIdentifier] [Routine]
84+
| NoRpc Text Text [Text] MediaType Bool [QualifiedIdentifier] [Routine]
8585
| NotEmbedded Text
8686
| PutLimitNotAllowedError
8787
| QueryParamError QPError

src/PostgREST/Error.hs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -221,24 +221,23 @@ instance JSON.ToJSON ApiRequestError where
221221
(Just $ JSON.toJSONList (compressedRel <$> rels))
222222
(Just $ JSON.String $ "Try changing '" <> child <> "' to one of the following: " <> relHint rels <> ". Find the desired relationship in the 'details' key.")
223223

224-
toJSON (NoRpc schema procName argumentKeys hasPreferSingleObject contentType isInvPost allProcs overloadedProcs) =
224+
toJSON (NoRpc schema procName argumentKeys contentType isInvPost allProcs overloadedProcs) =
225225
let func = schema <> "." <> procName
226226
prms = T.intercalate ", " argumentKeys
227227
prmsMsg = "(" <> prms <> ")"
228228
prmsDet = " with parameter" <> (if length argumentKeys > 1 then "s " else " ") <> prms
229229
fmtPrms p = if null argumentKeys then " without parameters" else p
230-
onlySingleParams = hasPreferSingleObject || (isInvPost && contentType `elem` [MTTextPlain, MTTextXML, MTOctetStream])
230+
onlySingleParams = isInvPost && contentType `elem` [MTTextPlain, MTTextXML, MTOctetStream]
231231
in toJsonPgrstError
232232
SchemaCacheErrorCode02
233233
("Could not find the function " <> func <> (if onlySingleParams then "" else fmtPrms prmsMsg) <> " in the schema cache")
234234
(Just $ JSON.String $ "Searched for the function " <> func <>
235-
(case (hasPreferSingleObject, isInvPost, contentType) of
236-
(True, _, _) -> " with a single json/jsonb parameter"
237-
(_, True, MTTextPlain) -> " with a single unnamed text parameter"
238-
(_, True, MTTextXML) -> " with a single unnamed xml parameter"
239-
(_, True, MTOctetStream) -> " with a single unnamed bytea parameter"
240-
(_, True, MTApplicationJSON) -> fmtPrms prmsDet <> " or with a single unnamed json/jsonb parameter"
241-
_ -> fmtPrms prmsDet) <>
235+
(case (isInvPost, contentType) of
236+
(True, MTTextPlain) -> " with a single unnamed text parameter"
237+
(True, MTTextXML) -> " with a single unnamed xml parameter"
238+
(True, MTOctetStream) -> " with a single unnamed bytea parameter"
239+
(True, MTApplicationJSON) -> fmtPrms prmsDet <> " or with a single unnamed json/jsonb parameter"
240+
_ -> fmtPrms prmsDet) <>
242241
", but no matches were found in the schema cache.")
243242
-- The hint will be null in the case of single unnamed parameter functions
244243
(if onlySingleParams

src/PostgREST/Plan.hs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,12 @@ mutateReadPlan mutation apiRequest@ApiRequest{iPreferences=Preferences{..},..}
166166
return $ MutateReadPlan rPlan mPlan SQL.Write handler mediaType mutation identifier
167167

168168
callReadPlan :: QualifiedIdentifier -> AppConfig -> SchemaCache -> ApiRequest -> InvokeMethod -> Either Error CallReadPlan
169-
callReadPlan identifier conf sCache apiRequest@ApiRequest{iPreferences=Preferences{..},..} invMethod = do
169+
callReadPlan identifier conf sCache apiRequest@ApiRequest{iPreferences=Preferences{preferHandling, invalidPrefs},..} invMethod = do
170170
let paramKeys = case invMethod of
171171
InvRead _ -> S.fromList $ fst <$> qsParams'
172172
Inv -> iColumns
173173
proc@Function{..} <- mapLeft ApiRequestError $
174-
findProc identifier paramKeys (preferParameters == Just SingleObject) (dbRoutines sCache) iContentMediaType (invMethod == Inv)
174+
findProc identifier paramKeys (dbRoutines sCache) iContentMediaType (invMethod == Inv)
175175
let relIdentifier = QualifiedIdentifier pdSchema (fromMaybe pdName $ Routine.funcTableName proc) -- done so a set returning function can embed other relations
176176
rPlan <- readPlan relIdentifier conf sCache apiRequest
177177
let args = case (invMethod, iContentMediaType) of
@@ -207,10 +207,10 @@ inspectPlan apiRequest headersOnly schema = do
207207
Search a pg proc by matching name and arguments keys to parameters. Since a function can be overloaded,
208208
the name is not enough to find it. An overloaded function can have a different volatility or even a different return type.
209209
-}
210-
findProc :: QualifiedIdentifier -> S.Set Text -> Bool -> RoutineMap -> MediaType -> Bool -> Either ApiRequestError Routine
211-
findProc qi argumentsKeys paramsAsSingleObject allProcs contentMediaType isInvPost =
210+
findProc :: QualifiedIdentifier -> S.Set Text -> RoutineMap -> MediaType -> Bool -> Either ApiRequestError Routine
211+
findProc qi argumentsKeys allProcs contentMediaType isInvPost =
212212
case matchProc of
213-
([], []) -> Left $ NoRpc (qiSchema qi) (qiName qi) (S.toList argumentsKeys) paramsAsSingleObject contentMediaType isInvPost (HM.keys allProcs) lookupProcName
213+
([], []) -> Left $ NoRpc (qiSchema qi) (qiName qi) (S.toList argumentsKeys) contentMediaType isInvPost (HM.keys allProcs) lookupProcName
214214
-- If there are no functions with named arguments, fallback to the single unnamed argument function
215215
([], [proc]) -> Right proc
216216
([], procs) -> Left $ AmbiguousRpc (toList procs)
@@ -241,13 +241,9 @@ findProc qi argumentsKeys paramsAsSingleObject allProcs contentMediaType isInvPo
241241
matchesParams proc =
242242
let
243243
params = pdParams proc
244-
firstType = (ppType <$> headMay params)
245244
in
246-
-- exceptional case for Prefer: params=single-object
247-
if paramsAsSingleObject
248-
then length params == 1 && (firstType == Just "json" || firstType == Just "jsonb")
249245
-- If the function has no parameters, the arguments keys must be empty as well
250-
else if null params
246+
if null params
251247
then null argumentsKeys && not (isInvPost && contentMediaType `elem` [MTOctetStream, MTTextPlain, MTTextXML])
252248
-- A function has optional and required parameters. Optional parameters have a default value and
253249
-- don't require arguments for the function to be executed, required parameters must have an argument present.
@@ -972,7 +968,7 @@ resolveOrError ctx (Just table) field =
972968
cf -> Right $ withJsonParse ctx cf
973969

974970
callPlan :: Routine -> ApiRequest -> S.Set FieldName -> CallArgs -> ReadPlanTree -> CallPlan
975-
callPlan proc ApiRequest{iPreferences=Preferences{..}} paramKeys args readReq = FunctionCall {
971+
callPlan proc ApiRequest{} paramKeys args readReq = FunctionCall {
976972
funCQi = QualifiedIdentifier (pdSchema proc) (pdName proc)
977973
, funCParams = callParams
978974
, funCArgs = args
@@ -982,11 +978,9 @@ callPlan proc ApiRequest{iPreferences=Preferences{..}} paramKeys args readReq =
982978
, funCReturning = inferColsEmbedNeeds readReq []
983979
}
984980
where
985-
paramsAsSingleObject = preferParameters == Just SingleObject
986981
specifiedParams = filter (\x -> ppName x `S.member` paramKeys)
987982
callParams = case pdParams proc of
988-
[prm] | paramsAsSingleObject -> OnePosParam prm
989-
| ppName prm == mempty -> OnePosParam prm
983+
[prm] | ppName prm == mempty -> OnePosParam prm
990984
| otherwise -> KeyParams $ specifiedParams [prm]
991985
prms -> KeyParams $ specifiedParams prms
992986

src/PostgREST/Response.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ actionResponse (DbCrudResult WrappedReadPlan{wrMedia, wrHdrsOnly=headersOnly, cr
6969
RSStandard{..} -> do
7070
let
7171
(status, contentRange) = RangeQuery.rangeStatusHeader iTopLevelRange rsQueryTotal rsTableTotal
72-
prefHeader = maybeToList . prefAppliedHeader $ Preferences Nothing Nothing Nothing preferCount preferTransaction Nothing preferHandling preferTimezone Nothing []
72+
prefHeader = maybeToList . prefAppliedHeader $ Preferences Nothing Nothing preferCount preferTransaction Nothing preferHandling preferTimezone Nothing []
7373
headers =
7474
[ contentRange
7575
, ( "Content-Location"
@@ -99,7 +99,7 @@ actionResponse (DbCrudResult MutateReadPlan{mrMutation=MutationCreate, mrMutateP
9999
pkCols = case mrMutatePlan of { Insert{insPkCols} -> insPkCols; _ -> mempty;}
100100
prefHeader = prefAppliedHeader $
101101
Preferences (if null pkCols && isNothing (qsOnConflict iQueryParams) then Nothing else preferResolution)
102-
preferRepresentation Nothing preferCount preferTransaction preferMissing preferHandling preferTimezone Nothing []
102+
preferRepresentation preferCount preferTransaction preferMissing preferHandling preferTimezone Nothing []
103103
headers =
104104
catMaybes
105105
[ if null rsLocation then
@@ -139,7 +139,7 @@ actionResponse (DbCrudResult MutateReadPlan{mrMutation=MutationUpdate, mrMedia}
139139
contentRangeHeader =
140140
Just . RangeQuery.contentRangeH 0 (rsQueryTotal - 1) $
141141
if shouldCount preferCount then Just rsQueryTotal else Nothing
142-
prefHeader = prefAppliedHeader $ Preferences Nothing preferRepresentation Nothing preferCount preferTransaction preferMissing preferHandling preferTimezone preferMaxAffected []
142+
prefHeader = prefAppliedHeader $ Preferences Nothing preferRepresentation preferCount preferTransaction preferMissing preferHandling preferTimezone preferMaxAffected []
143143
headers = catMaybes [contentRangeHeader, prefHeader]
144144

145145
let (status, headers', body) =
@@ -158,7 +158,7 @@ actionResponse (DbCrudResult MutateReadPlan{mrMutation=MutationUpdate, mrMedia}
158158
actionResponse (DbCrudResult MutateReadPlan{mrMutation=MutationSingleUpsert, mrMedia} resultSet) ctxApiRequest@ApiRequest{iPreferences=Preferences{..}} _ _ _ _ _ = case resultSet of
159159
RSStandard {..} -> do
160160
let
161-
prefHeader = maybeToList . prefAppliedHeader $ Preferences Nothing preferRepresentation Nothing preferCount preferTransaction Nothing preferHandling preferTimezone Nothing []
161+
prefHeader = maybeToList . prefAppliedHeader $ Preferences Nothing preferRepresentation preferCount preferTransaction Nothing preferHandling preferTimezone Nothing []
162162
cTHeader = contentTypeHeaders mrMedia ctxApiRequest
163163

164164
let isInsertIfGTZero i = if i > 0 then HTTP.status201 else HTTP.status200
@@ -181,7 +181,7 @@ actionResponse (DbCrudResult MutateReadPlan{mrMutation=MutationDelete, mrMedia}
181181
contentRangeHeader =
182182
RangeQuery.contentRangeH 1 0 $
183183
if shouldCount preferCount then Just rsQueryTotal else Nothing
184-
prefHeader = maybeToList . prefAppliedHeader $ Preferences Nothing preferRepresentation Nothing preferCount preferTransaction Nothing preferHandling preferTimezone preferMaxAffected []
184+
prefHeader = maybeToList . prefAppliedHeader $ Preferences Nothing preferRepresentation preferCount preferTransaction Nothing preferHandling preferTimezone preferMaxAffected []
185185
headers = contentRangeHeader : prefHeader
186186

187187
let (status, headers', body) =
@@ -206,7 +206,7 @@ actionResponse (DbCallResult CallReadPlan{crMedia, crInvMthd=invMethod, crProc=p
206206
then Error.errorPayload $ Error.ApiRequestError $ ApiRequestTypes.InvalidRange
207207
$ ApiRequestTypes.OutOfBounds (show $ RangeQuery.rangeOffset iTopLevelRange) (maybe "0" show rsTableTotal)
208208
else LBS.fromStrict rsBody
209-
prefHeader = maybeToList . prefAppliedHeader $ Preferences Nothing Nothing preferParameters preferCount preferTransaction Nothing preferHandling preferTimezone preferMaxAffected []
209+
prefHeader = maybeToList . prefAppliedHeader $ Preferences Nothing Nothing preferCount preferTransaction Nothing preferHandling preferTimezone preferMaxAffected []
210210
headers = contentRange : prefHeader
211211

212212
let (status', headers', body) =

src/PostgREST/Response/OpenAPI.hs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ makePreferParam ts =
175175
val :: Text -> [Text]
176176
val = \case
177177
"count" -> ["count=none"]
178-
"params" -> ["params=single-object"]
179178
"return" -> ["return=representation", "return=minimal", "return=none"]
180179
"resolution" -> ["resolution=ignore-duplicates", "resolution=merge-duplicates"]
181180
_ -> []

0 commit comments

Comments
 (0)