Bulk update (#2311)
* refactor: remove EmbedPath type from qsFiltersRoot * Rename reset items function for reusability
This commit is contained in:
@@ -143,7 +143,7 @@ spec =
|
||||
, { "id": 3, "name": "item-3" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_limited_items"
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "limited_delete_items"} |]
|
||||
`shouldRespondWith` ""
|
||||
@@ -175,7 +175,7 @@ spec =
|
||||
, { "id": 3, "name": "item-3" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_limited_items"
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "limited_delete_items"} |]
|
||||
`shouldRespondWith` ""
|
||||
@@ -233,7 +233,7 @@ spec =
|
||||
, { "id": 3, "name": "item-3" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_limited_items"
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "limited_delete_items_view"} |]
|
||||
`shouldRespondWith` ""
|
||||
@@ -265,7 +265,7 @@ spec =
|
||||
, { "id": 3, "name": "item-3" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_limited_items"
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "limited_delete_items_cpk_view"} |]
|
||||
`shouldRespondWith` ""
|
||||
@@ -297,7 +297,7 @@ spec =
|
||||
, { "id": 3, "name": "item-3" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_limited_items"
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "limited_delete_items_no_pk"} |]
|
||||
`shouldRespondWith` ""
|
||||
|
||||
@@ -93,16 +93,13 @@ spec =
|
||||
{ "id": 8, "name": "HaikuOS" } ]|]
|
||||
{ matchStatus = 201 }
|
||||
|
||||
it "doesn't affect updates" $
|
||||
it "doesn't affect updates(2 rows would be modified if it did)" $
|
||||
request methodPatch "/employees?select=first_name,last_name,occupation"
|
||||
[("Prefer", "return=representation")]
|
||||
[json| [{"occupation": "Barista"}] |]
|
||||
`shouldRespondWith`
|
||||
[json|[
|
||||
{ "first_name": "Frances M.", "last_name": "Roe", "occupation": "Barista" },
|
||||
{ "first_name": "Daniel B.", "last_name": "Lyon", "occupation": "Barista" },
|
||||
{ "first_name": "Edwin S.", "last_name": "Smith", "occupation": "Barista" } ]|]
|
||||
{ matchStatus = 200 }
|
||||
[json|[]|]
|
||||
{ matchStatus = 404 }
|
||||
|
||||
it "doesn't affect deletions" $
|
||||
request methodDelete "/employees?select=first_name,last_name"
|
||||
|
||||
@@ -65,7 +65,7 @@ spec =
|
||||
}
|
||||
|
||||
it "raises an error for multiple rows" $ do
|
||||
request methodPatch "/addresses"
|
||||
request methodPatch "/addresses?limit=4&order=id"
|
||||
[("Prefer", "tx=commit"), singular]
|
||||
[json| { address: "zzz" } |]
|
||||
`shouldRespondWith`
|
||||
@@ -81,7 +81,7 @@ spec =
|
||||
[json|[{"id":1,"address":"address 1"}]|]
|
||||
|
||||
it "raises an error for multiple rows with return=rep" $ do
|
||||
request methodPatch "/addresses"
|
||||
request methodPatch "/addresses?limit=4&order=id"
|
||||
[("Prefer", "tx=commit"), ("Prefer", "return=representation"), singular]
|
||||
[json| { address: "zzz" } |]
|
||||
`shouldRespondWith`
|
||||
|
||||
@@ -388,6 +388,17 @@ spec = do
|
||||
}
|
||||
|
||||
context "limited update" $ do
|
||||
it "does not work when no limit query or filter is given" $
|
||||
request methodPatch "/limited_update_items"
|
||||
[("Prefer", "tx=commit"), ("Prefer", "count=exact")]
|
||||
[json| {"name": "updated-item"} |]
|
||||
`shouldRespondWith`
|
||||
""
|
||||
{ matchStatus = 404
|
||||
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||
, "Content-Range" <:> "*/0" ]
|
||||
}
|
||||
|
||||
it "works with the limit query param" $ do
|
||||
get "/limited_update_items"
|
||||
`shouldRespondWith`
|
||||
@@ -398,12 +409,13 @@ spec = do
|
||||
]|]
|
||||
|
||||
request methodPatch "/limited_update_items?order=id&limit=2"
|
||||
[("Prefer", "tx=commit")]
|
||||
[("Prefer", "tx=commit"), ("Prefer", "count=exact")]
|
||||
[json| {"name": "updated-item"} |]
|
||||
`shouldRespondWith`
|
||||
""
|
||||
{ matchStatus = 204
|
||||
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||
, "Content-Range" <:> "0-1/2"
|
||||
, "Preference-Applied" <:> "tx=commit" ]
|
||||
}
|
||||
|
||||
@@ -415,7 +427,7 @@ spec = do
|
||||
, { "id": 3, "name": "item-3" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_limited_items"
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "limited_update_items"} |]
|
||||
`shouldRespondWith` ""
|
||||
@@ -448,7 +460,7 @@ spec = do
|
||||
, { "id": 3, "name": "updated-item" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_limited_items"
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "limited_update_items"} |]
|
||||
`shouldRespondWith` ""
|
||||
@@ -481,7 +493,7 @@ spec = do
|
||||
, { "id": 3, "name": "item-3" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_limited_items"
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "limited_update_items"} |]
|
||||
`shouldRespondWith` ""
|
||||
@@ -540,7 +552,7 @@ spec = do
|
||||
, { "id": 3, "name": "item-3" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_limited_items"
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "limited_update_items_view"} |]
|
||||
`shouldRespondWith` ""
|
||||
@@ -573,7 +585,7 @@ spec = do
|
||||
, { "id": 3, "name": "item-3" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_limited_items"
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "limited_update_items_cpk_view"} |]
|
||||
`shouldRespondWith` ""
|
||||
@@ -607,8 +619,186 @@ spec = do
|
||||
, { "id": 3, "name": "item-3" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_limited_items"
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "limited_update_items_no_pk"} |]
|
||||
`shouldRespondWith` ""
|
||||
{ matchStatus = 204 }
|
||||
|
||||
context "bulk updates" $ do
|
||||
it "can update tables with simple pk" $ do
|
||||
get "/bulk_update_items"
|
||||
`shouldRespondWith`
|
||||
[json|[
|
||||
{ "id": 1, "name": "item-1", "observation": null }
|
||||
, { "id": 2, "name": "item-2", "observation": null }
|
||||
, { "id": 3, "name": "item-3", "observation": null }
|
||||
]|]
|
||||
|
||||
request methodPatch "/bulk_update_items"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json|[
|
||||
{ "id": 1, "name": "item-1 - 1st", "observation": "Lost item" }
|
||||
, { "id": 3, "name": "item-3 - 3rd", "observation": null }
|
||||
]|]
|
||||
`shouldRespondWith`
|
||||
""
|
||||
{ matchStatus = 204
|
||||
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||
, "Content-Range" <:> "0-1/*"
|
||||
, "Preference-Applied" <:> "tx=commit" ]
|
||||
}
|
||||
|
||||
get "/bulk_update_items?order=id"
|
||||
`shouldRespondWith`
|
||||
[json|[
|
||||
{ "id": 1, "name": "item-1 - 1st", "observation": "Lost item" }
|
||||
, { "id": 2, "name": "item-2", "observation": null }
|
||||
, { "id": 3, "name": "item-3 - 3rd", "observation": null }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "bulk_update_items"} |]
|
||||
`shouldRespondWith` ""
|
||||
{ matchStatus = 204 }
|
||||
|
||||
it "can update tables with composite pk" $ do
|
||||
get "/bulk_update_items_cpk"
|
||||
`shouldRespondWith`
|
||||
[json|[
|
||||
{ "id": 1, "name": "item-1", "observation": null }
|
||||
, { "id": 2, "name": "item-2", "observation": null }
|
||||
, { "id": 3, "name": "item-3", "observation": null }
|
||||
]|]
|
||||
|
||||
request methodPatch "/bulk_update_items_cpk"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json|[
|
||||
{ "id": 1, "name": "item-1", "observation": "Lost item" }
|
||||
, { "id": 2, "name": "item-2", "observation": null }
|
||||
]|]
|
||||
`shouldRespondWith`
|
||||
""
|
||||
{ matchStatus = 204
|
||||
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||
, "Content-Range" <:> "0-1/*"
|
||||
, "Preference-Applied" <:> "tx=commit" ]
|
||||
}
|
||||
|
||||
get "/bulk_update_items_cpk?order=id"
|
||||
`shouldRespondWith`
|
||||
[json|[
|
||||
{ "id": 1, "name": "item-1", "observation": "Lost item" }
|
||||
, { "id": 2, "name": "item-2", "observation": null }
|
||||
, { "id": 3, "name": "item-3", "observation": null }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "bulk_update_items_cpk"} |]
|
||||
`shouldRespondWith` ""
|
||||
{ matchStatus = 204 }
|
||||
|
||||
it "updates with filters taking only the first item in the json array body" $ do
|
||||
get "/bulk_update_items"
|
||||
`shouldRespondWith`
|
||||
[json|[
|
||||
{ "id": 1, "name": "item-1", "observation": null }
|
||||
, { "id": 2, "name": "item-2", "observation": null }
|
||||
, { "id": 3, "name": "item-3", "observation": null }
|
||||
]|]
|
||||
|
||||
request methodPatch "/bulk_update_items?id=eq.2"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json|[
|
||||
{ "id": 4, "name": "item-4", "observation": "Damaged item" }
|
||||
, { "id": 3, "name": "item-3 - 3rd", "observation": null }
|
||||
]|]
|
||||
`shouldRespondWith`
|
||||
""
|
||||
{ matchStatus = 204
|
||||
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||
, "Content-Range" <:> "0-0/*"
|
||||
, "Preference-Applied" <:> "tx=commit" ]
|
||||
}
|
||||
|
||||
get "/bulk_update_items?order=id"
|
||||
`shouldRespondWith`
|
||||
[json|[
|
||||
{ "id": 1, "name": "item-1", "observation": null }
|
||||
, { "id": 3, "name": "item-3", "observation": null }
|
||||
, { "id": 4, "name": "item-4", "observation": "Damaged item" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "bulk_update_items"} |]
|
||||
`shouldRespondWith` ""
|
||||
{ matchStatus = 204 }
|
||||
|
||||
it "updates with limit and offset taking only the first item in the json array body" $ do
|
||||
get "/bulk_update_items"
|
||||
`shouldRespondWith`
|
||||
[json|[
|
||||
{ "id": 1, "name": "item-1", "observation": null }
|
||||
, { "id": 2, "name": "item-2", "observation": null }
|
||||
, { "id": 3, "name": "item-3", "observation": null }
|
||||
]|]
|
||||
|
||||
request methodPatch "/bulk_update_items?limit=2&offset=1&order=id"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json|[
|
||||
{ "name": "item-4", "observation": "Damaged item" }
|
||||
, { "name": "item-3 - 3rd", "observation": null }
|
||||
]|]
|
||||
`shouldRespondWith`
|
||||
""
|
||||
{ matchStatus = 204
|
||||
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||
, "Content-Range" <:> "0-1/*"
|
||||
, "Preference-Applied" <:> "tx=commit" ]
|
||||
}
|
||||
|
||||
get "/bulk_update_items?order=id"
|
||||
`shouldRespondWith`
|
||||
[json|[
|
||||
{ "id": 1, "name": "item-1", "observation": null }
|
||||
, { "id": 2, "name": "item-4", "observation": "Damaged item" }
|
||||
, { "id": 3, "name": "item-4", "observation": "Damaged item" }
|
||||
]|]
|
||||
|
||||
request methodPost "/rpc/reset_items_tables"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"tbl_name": "bulk_update_items"} |]
|
||||
`shouldRespondWith` ""
|
||||
{ matchStatus = 204 }
|
||||
|
||||
it "rejects a json array that isn't exclusively composed of objects" $
|
||||
request methodPatch "/bulk_update_items"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json|[
|
||||
{ "id": 1, "name": "Item 1" }
|
||||
, 2
|
||||
, "Item 2"
|
||||
, { "id": 3, "name": "Item 3" }
|
||||
]|]
|
||||
`shouldRespondWith`
|
||||
[json| {"message":"All object keys must match","code":"PGRST102","hint":null,"details":null} |]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "rejects a json array that has objects with different keys" $
|
||||
request methodPatch "/bulk_update_items"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json|[
|
||||
{ "id": 1, "name": "Item 1" }
|
||||
, { "id": 2 }
|
||||
, { "id": 3, "name": "Item 3" }
|
||||
]|]
|
||||
`shouldRespondWith`
|
||||
[json| {"message":"All object keys must match","code":"PGRST102","hint":null,"details":null} |]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
Vendored
+6
@@ -778,3 +778,9 @@ INSERT INTO private.internal_job (id, parent_id) VALUES (2, 1);
|
||||
|
||||
TRUNCATE TABLE test.test CASCADE;
|
||||
INSERT INTO test.test (id, parent_id) VALUES (1, null), (2, 1);
|
||||
|
||||
TRUNCATE TABLE test.bulk_update_items CASCADE;
|
||||
INSERT INTO test.bulk_update_items (id, name, observation) VALUES (1, 'item-1', NULL), (2, 'item-2', NULL), (3, 'item-3', NULL);
|
||||
|
||||
TRUNCATE TABLE test.bulk_update_items_cpk CASCADE;
|
||||
INSERT INTO test.bulk_update_items_cpk (id, name, observation) VALUES (1, 'item-1', NULL), (2, 'item-2', NULL), (3, 'item-3', NULL);
|
||||
|
||||
Vendored
+2
@@ -186,6 +186,8 @@ GRANT ALL ON TABLE
|
||||
, series_popularity
|
||||
, test
|
||||
, view_test
|
||||
, bulk_update_items
|
||||
, bulk_update_items_cpk
|
||||
TO postgrest_test_anonymous;
|
||||
|
||||
GRANT INSERT ON TABLE insertonly TO postgrest_test_anonymous;
|
||||
|
||||
Vendored
+16
-1
@@ -2528,7 +2528,7 @@ select *, 'static'::text as static from limited_delete_items;
|
||||
create view limited_delete_items_cpk_view as
|
||||
select * from limited_delete_items_cpk;
|
||||
|
||||
create function reset_limited_items(tbl_name text default '') returns void as $_$ begin
|
||||
create function reset_items_tables(tbl_name text default '') returns void as $_$ begin
|
||||
execute format(
|
||||
$$
|
||||
delete from %I;
|
||||
@@ -2634,3 +2634,18 @@ CREATE TABLE test.test (
|
||||
|
||||
CREATE OR REPLACE VIEW test.view_test AS
|
||||
SELECT id FROM test.test;
|
||||
|
||||
-- Tables to test bulk updates
|
||||
|
||||
CREATE TABLE test.bulk_update_items (
|
||||
id INT PRIMARY KEY,
|
||||
name TEXT,
|
||||
observation TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE test.bulk_update_items_cpk (
|
||||
id INT,
|
||||
name TEXT,
|
||||
observation TEXT,
|
||||
PRIMARY KEY (id, name)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user