Skip to content

Commit c693df0

Browse files
test: consistently analyze tables exactly once
Disabling the autovacuum daemon should also help reproducibility in theory, although I don't know of any cases where we hit a problem with that. VACUUM changes the order of rows that PostgreSQL returns for some table without explicit ordering, thus doing the latter to make it consistently reproducible. After ANALYZE estimates are 100% exact for the moment, so some requests which returned 206 Partial Response now return 200 instead. The fact that PostgREST returns 206 on an unfiltered endpoint can probably be considered a bug.
1 parent 2e652b7 commit c693df0

10 files changed

Lines changed: 33 additions & 34 deletions

File tree

nix/tools/withTools.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ let
1515
let
1616
commandName = "postgrest-with-${name}";
1717
postgresqlConf = writeText "postgresql.conf" "
18+
autovacuum = false
1819
listen_addresses = ''
1920
log_statement = all
2021
";
@@ -143,6 +144,7 @@ let
143144
load_start=$SECONDS
144145
>&2 printf "${commandName}: Loading fixtures under the postgres role..."
145146
psql -U postgres -v PGUSER="$PGUSER" -v ON_ERROR_STOP=1 -f "$_arg_fixtures" >> "$setuplog"
147+
psql -U postgres -v ON_ERROR_STOP=1 -c "VACUUM ANALYZE;" >> "$setuplog"
146148
load_end=$((SECONDS - load_start))
147149
>&2 printf " done in %ss. Running command...\n" "$load_end"
148150
fi

src/PostgREST/Config/PgVersion.hs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ module PostgREST.Config.PgVersion
44
( PgVersion(..)
55
, minimumPgVersion
66
, pgVersion150
7-
, pgVersion170
87
, pgVersion180
98
) where
109

@@ -33,8 +32,5 @@ pgVersion140 = PgVersion 140000 "14.0" "14.0"
3332
pgVersion150 :: PgVersion
3433
pgVersion150 = PgVersion 150000 "15.0" "15.0"
3534

36-
pgVersion170 :: PgVersion
37-
pgVersion170 = PgVersion 170000 "17.0" "17.0"
38-
3935
pgVersion180 :: PgVersion
4036
pgVersion180 = PgVersion 180000 "18.0" "18.0"

test/io/test_io.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -938,12 +938,12 @@ def test_log_query(level, defaultenv):
938938
response = postgrest.session.get(
939939
"/projects", headers={"Prefer": "count=estimated"}
940940
)
941-
assert response.status_code == 206
941+
assert response.status_code == 200
942942

943943
response = postgrest.session.get(
944944
"/projects", headers={"Prefer": "count=planned"}
945945
)
946-
assert response.status_code == 206
946+
assert response.status_code == 200
947947

948948
response = postgrest.session.get("/infinite_recursion")
949949
assert response.status_code == 500

test/spec/Feature/Query/AggregateFunctionsSpec.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,24 +154,24 @@ allowed =
154154
[json|[{"total_budget": 9501.06}]|]
155155
{ matchHeaders = [matchContentTypeJson] }
156156
it "supports aggregates from a spread relationships grouped by spreaded fields from other relationships" $ do
157-
get "/processes?select=...process_costs(cost.sum()),...process_categories(name)" `shouldRespondWith`
157+
get "/processes?select=...process_costs(cost.sum()),...process_categories(name)&order=process_categories(name)" `shouldRespondWith`
158158
[json|[
159159
{"sum": 400.00, "name": "Batch"},
160160
{"sum": 350.00, "name": "Mass"}]|]
161161
{ matchHeaders = [matchContentTypeJson] }
162-
get "/processes?select=...process_costs(cost_sum:cost.sum()),...process_categories(category:name)" `shouldRespondWith`
162+
get "/processes?select=...process_costs(cost_sum:cost.sum()),...process_categories(category:name)&order=process_categories(category)" `shouldRespondWith`
163163
[json|[
164164
{"cost_sum": 400.00, "category": "Batch"},
165165
{"cost_sum": 350.00, "category": "Mass"}]|]
166166
{ matchHeaders = [matchContentTypeJson] }
167167
it "supports aggregates on spreaded fields from nested relationships" $ do
168-
get "/process_supervisor?select=...processes(factory_id,...process_costs(cost.sum()))" `shouldRespondWith`
168+
get "/process_supervisor?select=...processes(factory_id,...process_costs(cost.sum()))&order=processes(factory_id).desc" `shouldRespondWith`
169169
[json|[
170170
{"factory_id": 3, "sum": 110.00},
171171
{"factory_id": 2, "sum": 500.00},
172172
{"factory_id": 1, "sum": 350.00}]|]
173173
{ matchHeaders = [matchContentTypeJson] }
174-
get "/process_supervisor?select=...processes(factory_id,...process_costs(cost_sum:cost.sum()))" `shouldRespondWith`
174+
get "/process_supervisor?select=...processes(factory_id,...process_costs(cost_sum:cost.sum()))&order=processes(factory_id).desc" `shouldRespondWith`
175175
[json|[
176176
{"factory_id": 3, "cost_sum": 110.00},
177177
{"factory_id": 2, "cost_sum": 500.00},

test/spec/Feature/Query/EmbedDisambiguationSpec.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ spec =
236236
[json|[{"createdAt":"2015-12-08T04:22:57.472738","article":{"id": 1},"user":{"name": "Angela Martin"}}]|]
237237

238238
it "can specify a view!fk" $
239-
get "/message?select=id,body,sender:person_detail!message_sender_fkey(name,sent),recipient:person_detail!message_recipient_fkey(name,received)&id=lt.4" `shouldRespondWith`
239+
get "/message?select=id,body,sender:person_detail!message_sender_fkey(name,sent),recipient:person_detail!message_recipient_fkey(name,received)&id=lt.4&order=id" `shouldRespondWith`
240240
[json|
241241
[{"id":1,"body":"Hello Jane","sender":{"name":"John","sent":2},"recipient":{"name":"Jane","received":2}},
242242
{"id":2,"body":"Hi John","sender":{"name":"Jane","sent":1},"recipient":{"name":"John","received":1}},

test/spec/Feature/Query/PlanSpec.hs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,11 @@ import Test.Hspec hiding (pendingWith)
1515
import Test.Hspec.Wai
1616
import Test.Hspec.Wai.JSON
1717

18-
import PostgREST.Config.PgVersion (PgVersion, pgVersion170)
19-
import Protolude hiding (get)
18+
import Protolude hiding (get)
2019
import SpecHelper
2120

22-
spec :: PgVersion -> SpecWith ((), Application)
23-
spec actualPgVersion = do
21+
spec :: SpecWith ((), Application)
22+
spec = do
2423
describe "read table/view plan" $ do
2524
it "outputs the total cost for a single filter on a table" $ do
2625
r <- request methodGet "/projects?id=in.(1,2,3)"
@@ -34,7 +33,7 @@ spec actualPgVersion = do
3433
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
3534
resHeaders `shouldSatisfy` notZeroContentLength
3635
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
37-
totalCost `shouldBe` (if actualPgVersion >= pgVersion170 then 11.32 else 15.63)
36+
totalCost `shouldBe` 1.11
3837

3938
it "outputs the total cost for a single filter on a view" $ do
4039
r <- request methodGet "/projects_view?id=gt.2"
@@ -47,7 +46,7 @@ spec actualPgVersion = do
4746
liftIO $ do
4847
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
4948
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
50-
totalCost `shouldBe` 24.28
49+
totalCost `shouldBe` 1.1
5150

5251
it "outputs blocks info when using the buffers option" $ do
5352
r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=buffers") ""
@@ -143,7 +142,7 @@ spec actualPgVersion = do
143142
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
144143
resHeaders `shouldSatisfy` notZeroContentLength
145144
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
146-
totalCost `shouldBe` 8.23
145+
totalCost `shouldBe` 1.13
147146

148147
it "outputs the total cost for a delete" $ do
149148
r <- request methodDelete "/projects?id=in.(1,2,3)"
@@ -157,7 +156,7 @@ spec actualPgVersion = do
157156
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
158157
resHeaders `shouldSatisfy` notZeroContentLength
159158
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
160-
totalCost `shouldBe` (if actualPgVersion >= pgVersion170 then 11.37 else 15.68)
159+
totalCost `shouldBe` 1.16
161160

162161
it "outputs the total cost for a single upsert" $ do
163162
r <- request methodPut "/tiobe_pls?name=eq.Go"
@@ -490,7 +489,7 @@ spec actualPgVersion = do
490489

491490
liftIO $ do
492491
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=analyze; charset=utf-8")
493-
totalCost `shouldSatisfy` (> 49.0)
492+
totalCost `shouldSatisfy` (> 2.0)
494493
aggregateQty `shouldSatisfy` (> 1)
495494

496495
context "functions with count=exact" $ do

test/spec/Feature/Query/QuerySpec.hs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -692,13 +692,13 @@ spec = do
692692
{ matchHeaders = [matchContentTypeJson] }
693693

694694
it "requesting data using many<->many relation defined by composite keys" $
695-
get "/users_tasks?user_id=eq.1&task_id=eq.1&select=user_id,files(filename,content)" `shouldRespondWith`
696-
[json|[{"user_id":1,"files":[{"filename":"autoexec.bat","content":"@ECHO OFF"},{"filename":"command.com","content":"#include <unix.h>"},{"filename":"README.md","content":"# make $$$!"}]}]|]
695+
get "/users_tasks?user_id=eq.1&task_id=eq.1&select=user_id,files(filename,content)&files.order=filename" `shouldRespondWith`
696+
[json|[{"user_id":1,"files":[{"filename":"README.md","content":"# make $$$!"},{"filename":"autoexec.bat","content":"@ECHO OFF"},{"filename":"command.com","content":"#include <unix.h>"}]}]|]
697697
{ matchHeaders = [matchContentTypeJson] }
698698

699699
it "requesting data using many<->many (composite keys) relation using hint" $
700-
get "/users_tasks?user_id=eq.1&task_id=eq.1&select=user_id,files!touched_files(filename,content)" `shouldRespondWith`
701-
[json|[{"user_id":1,"files":[{"filename":"autoexec.bat","content":"@ECHO OFF"},{"filename":"command.com","content":"#include <unix.h>"},{"filename":"README.md","content":"# make $$$!"}]}]|]
700+
get "/users_tasks?user_id=eq.1&task_id=eq.1&select=user_id,files!touched_files(filename,content)&files.order=filename" `shouldRespondWith`
701+
[json|[{"user_id":1,"files":[{"filename":"README.md","content":"# make $$$!"},{"filename":"autoexec.bat","content":"@ECHO OFF"},{"filename":"command.com","content":"#include <unix.h>"}]}]|]
702702
{ matchHeaders = [matchContentTypeJson] }
703703

704704
it "requesting children with composite key" $
@@ -1607,11 +1607,11 @@ spec = do
16071607
] |]
16081608
{ matchHeaders = [matchContentTypeJson] }
16091609
it "formats through join" $
1610-
get "/datarep_next_two_todos?select=id,name,first_item:datarep_todos!datarep_next_two_todos_first_item_id_fkey(label_color,due_at)" `shouldRespondWith`
1610+
get "/datarep_next_two_todos?select=id,name,first_item:datarep_todos!datarep_next_two_todos_first_item_id_fkey(label_color,due_at)&order=id" `shouldRespondWith`
16111611
[json| [{"id":1,"name":"school related","first_item":{"label_color":"#000100","due_at":"2018-01-03T00:00:00Z"}},{"id":2,"name":"do these first","first_item":{"label_color":"#000000","due_at":"2018-01-02T00:00:00Z"}}] |]
16121612
{ matchHeaders = [matchContentTypeJson] }
16131613
it "formats through join with star select" $
1614-
get "/datarep_next_two_todos?select=id,name,second_item:datarep_todos!datarep_next_two_todos_second_item_id_fkey(*)" `shouldRespondWith`
1614+
get "/datarep_next_two_todos?select=id,name,second_item:datarep_todos!datarep_next_two_todos_second_item_id_fkey(*)&order=id" `shouldRespondWith`
16151615
[json| [
16161616
{"id":1,"name":"school related","second_item":{"id":3,"name":"Algebra","label_color":"#01E240","due_at":"2018-01-01T14:12:34.123456Z","icon_image":null,"created_at":1513213350,"budget":"0.00"}},
16171617
{"id":2,"name":"do these first","second_item":{"id":3,"name":"Algebra","label_color":"#01E240","due_at":"2018-01-01T14:12:34.123456Z","icon_image":null,"created_at":1513213350,"budget":"0.00"}}
@@ -1645,7 +1645,7 @@ spec = do
16451645
] |]
16461646
{ matchHeaders = [matchContentTypeJson] }
16471647
it "uses text parser on value for filter across relations" $
1648-
get "/datarep_next_two_todos?select=id,name,datarep_todos!datarep_next_two_todos_first_item_id_fkey(label_color,due_at)&datarep_todos.label_color=neq.000100" `shouldRespondWith`
1648+
get "/datarep_next_two_todos?select=id,name,datarep_todos!datarep_next_two_todos_first_item_id_fkey(label_color,due_at)&datarep_todos.label_color=neq.000100&order=id" `shouldRespondWith`
16491649
[json| [{"id":1,"name":"school related","datarep_todos":null},{"id":2,"name":"do these first","datarep_todos":{"label_color":"#000000","due_at":"2018-01-02T00:00:00Z"}}] |]
16501650
{ matchHeaders = [matchContentTypeJson] }
16511651
-- This is not supported by data reps (would be hard to make it work with high performance). So the test just

test/spec/Feature/Query/RelatedQueriesSpec.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ spec = describe "related queries" $ do
323323
]|]
324324
{ matchStatus = 206
325325
, matchHeaders = [ matchContentTypeJson
326-
, "Content-Range" <:> "0-3/1200" ]
326+
, "Content-Range" <:> "0-3/5" ]
327327
}
328328
request methodGet "/projects?select=name,clients()&clients=is.null"
329329
[("Prefer", "count=planned")] ""
@@ -340,9 +340,9 @@ spec = describe "related queries" $ do
340340
{"id":1,"name":"Walmart"},
341341
{"id":2,"name":"Target"}
342342
]|]
343-
{ matchStatus = 206
343+
{ matchStatus = 200
344344
, matchHeaders = [ matchContentTypeJson
345-
, "Content-Range" <:> "0-1/952" ]
345+
, "Content-Range" <:> "0-1/2" ]
346346
}
347347

348348
it "works with count=estimated" $ do
@@ -357,7 +357,7 @@ spec = describe "related queries" $ do
357357
]|]
358358
{ matchStatus = 206
359359
, matchHeaders = [ matchContentTypeJson
360-
, "Content-Range" <:> "0-3/1200" ]
360+
, "Content-Range" <:> "0-3/5" ]
361361
}
362362
request methodGet "/projects?select=name,clients()&clients=is.null"
363363
[("Prefer", "count=estimated")] ""
@@ -374,7 +374,7 @@ spec = describe "related queries" $ do
374374
{"id":1,"name":"Walmart"},
375375
{"id":2,"name":"Target"}
376376
]|]
377-
{ matchStatus = 206
377+
{ matchStatus = 200
378378
, matchHeaders = [ matchContentTypeJson
379-
, "Content-Range" <:> "0-1/952" ]
379+
, "Content-Range" <:> "0-1/2" ]
380380
}

test/spec/Main.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ main = do
243243

244244
-- this test runs with db-plan-enabled = true
245245
parallel $ before planEnabledApp $
246-
describe "Feature.Query.PlanSpec.spec" $ Feature.Query.PlanSpec.spec actualPgVersion
246+
describe "Feature.Query.PlanSpec.spec" Feature.Query.PlanSpec.spec
247247

248248
-- this test runs with server-trace-header set
249249
parallel $ before obsApp $

test/spec/fixtures/data.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,3 +983,5 @@ VALUES (1, '2025-01-01 10:00','2025-01-01 11:00', 'vacation'),
983983
(2, '2024-11-01 09:00','2024-11-01 10:00', 'vacation'),
984984
(3, '2024-12-02 13:00','2024-12-02 14:00', 'vacation'),
985985
(1, '2023-01-02 20:00','2023-01-01 21:00', 'work');
986+
987+
INSERT INTO bets (id) SELECT generate_series(1,1000);

0 commit comments

Comments
 (0)