Skip to content

Commit 38dd893

Browse files
fix: Return status code 200 when PATCHing without changing rows
This partially reverts PostgREST#1257 / PostgREST#1272 / 5535317 where the 404 was introduced. A 406 error is still returned when requesting a single object via accept header. Returning an error when no rows are changed can be introduced through a different syntax again, see the discussion in PostgREST#2164. Fixes PostgREST#2343 Signed-off-by: Wolfgang Walther <[email protected]>
1 parent 9be6747 commit 38dd893

File tree

3 files changed

+15
-19
lines changed

3 files changed

+15
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1515
- #2458, Fix a regression with the location header when inserting into views with PKs from multiple tables - @wolfgangwalther
1616
- #2356, Fix a regression in openapi output with mode follow-privileges - @wolfgangwalther
1717
- #2283, Fix infinite recursion when loading schema cache with self-referencing view - @wolfgangwalther
18+
- #2343, Return status code 200 for PATCH requests which don't affect any rows - @wolfgangwalther
1819

1920
### Deprecated
2021

src/PostgREST/Response.hs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import qualified Data.Aeson as JSON
1818
import qualified Data.ByteString.Char8 as BS
1919
import qualified Data.ByteString.Lazy as LBS
2020
import qualified Data.HashMap.Strict as HM
21-
import qualified Data.Set as S
2221
import Data.Text.Read (decimal)
2322
import qualified Network.HTTP.Types.Header as HTTP
2423
import qualified Network.HTTP.Types.Status as HTTP
@@ -121,21 +120,17 @@ updateResponse ctxApiRequest@ApiRequest{..} resultSet = case resultSet of
121120
RSStandard{..} -> do
122121
let
123122
response = gucResponse rsGucStatus rsGucHeaders
124-
fullRepr = iPreferRepresentation == Full
125-
updateIsNoOp = S.null iColumns
126-
status
127-
| rsQueryTotal == 0 && not updateIsNoOp = HTTP.status404
128-
| fullRepr = HTTP.status200
129-
| otherwise = HTTP.status204
130123
contentRangeHeader =
131124
RangeQuery.contentRangeH 0 (rsQueryTotal - 1) $
132125
if shouldCount iPreferCount then Just rsQueryTotal else Nothing
133126
headers = [contentRangeHeader]
134127

135-
if fullRepr then
136-
response status (headers ++ contentTypeHeaders ctxApiRequest) (LBS.fromStrict rsBody)
128+
if iPreferRepresentation == Full then
129+
response HTTP.status200
130+
(headers ++ contentTypeHeaders ctxApiRequest)
131+
(LBS.fromStrict rsBody)
137132
else
138-
response status headers mempty
133+
response HTTP.status204 headers mempty
139134

140135
RSPlan plan ->
141136
Wai.responseLBS HTTP.status200 (contentTypeHeaders ctxApiRequest) $ LBS.fromStrict plan

test/spec/Feature/Query/UpdateSpec.hs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ spec = do
2828
`shouldRespondWith` 404
2929

3030
context "on an empty table" $
31-
it "indicates no records found to update by returning 404" $
31+
it "succeeds with status code 204" $
3232
request methodPatch "/empty_table" []
3333
[json| { "extra":20 } |]
3434
`shouldRespondWith`
3535
""
36-
{ matchStatus = 404,
36+
{ matchStatus = 204,
3737
matchHeaders = [matchHeaderAbsent hContentType]
3838
}
3939

@@ -71,14 +71,14 @@ spec = do
7171
[("Prefer", "return=representation")] [json| { "id":999999 } |]
7272
`shouldRespondWith` "[]"
7373
{
74-
matchStatus = 404,
74+
matchStatus = 200,
7575
matchHeaders = []
7676
}
7777

78-
it "gives a 404 when no rows updated" $
78+
it "returns status code 200 when no rows updated" $
7979
request methodPatch "/items?id=eq.99999999" []
8080
[json| { "id": 42 } |]
81-
`shouldRespondWith` 404
81+
`shouldRespondWith` 204
8282

8383
it "returns updated object as array when return=rep" $
8484
request methodPatch "/items?id=eq.2"
@@ -137,13 +137,13 @@ spec = do
137137
matchHeaders = [matchContentTypeJson, "Content-Range" <:> "0-0/*"]
138138
}
139139

140-
it "indicates no records updated by returning 404" $
140+
it "returns empty array when no rows updated and return=rep" $
141141
request methodPatch
142142
"/items?always_true=eq.false"
143143
[("Prefer", "return=representation")]
144144
[json| { id: 100 } |]
145145
`shouldRespondWith` "[]"
146-
{ matchStatus = 404,
146+
{ matchStatus = 200,
147147
matchHeaders = []
148148
}
149149

@@ -304,9 +304,9 @@ spec = do
304304
`shouldRespondWith`
305305
[json|[{"id": 1, "body": "Some real content", "owner": "postgrest_test_anonymous"}]|]
306306

307-
it "ignores json keys and gives 404 if no record updated" $
307+
it "ignores json keys and gives 200 if no record updated" $
308308
request methodPatch "/articles?id=eq.2001&columns=body" [("Prefer", "return=representation")]
309-
[json| {"body": "Some real content", "smth": "here", "other": "stuff", "fake_id": 13} |] `shouldRespondWith` 404
309+
[json| {"body": "Some real content", "smth": "here", "other": "stuff", "fake_id": 13} |] `shouldRespondWith` 200
310310

311311
context "tables with self reference foreign keys" $ do
312312
it "embeds children after update" $

0 commit comments

Comments
 (0)