Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02d83d1c01 | ||
|
|
e89e0bc255 | ||
|
|
9e20e5df90 | ||
|
|
2122dcef97 | ||
|
|
d84d00be8e | ||
|
|
3a034fcc2a | ||
|
|
a2a592a617 | ||
|
|
89fe63fce0 | ||
|
|
f21949a517 | ||
|
|
6ead34e70b | ||
|
|
e3206262d2 | ||
|
|
a3f36d6184 | ||
|
|
6cc1698c05 | ||
|
|
77d6055683 | ||
|
|
05d1e0f83d | ||
|
|
6fdc230637 | ||
|
|
a841dc9c39 | ||
|
|
18371ec372 | ||
|
|
6951e1301d | ||
|
|
75891975f5 | ||
|
|
142b54158e | ||
|
|
876dfad9d9 | ||
|
|
2ad6d239a0 | ||
|
|
426e15bbb4 | ||
|
|
7e8212870d | ||
|
|
6a9c99d54e | ||
|
|
e44178445d | ||
|
|
59a644bb99 | ||
|
|
932c4f6328 | ||
|
|
a41396c425 | ||
|
|
ca4a6d9e99 | ||
|
|
f34ca15e84 | ||
|
|
f84c44dafa | ||
|
|
549ab36c30 | ||
|
|
42754cb6c9 | ||
|
|
57e8d20811 | ||
|
|
8ede8fb0dc | ||
|
|
131df9b578 | ||
|
|
6a7ad33524 | ||
|
|
b174295bf2 | ||
|
|
6304265cd9 | ||
|
|
cb9b00d374 | ||
|
|
59081195f0 | ||
|
|
022f0faa38 | ||
|
|
f6d34fd4fc | ||
|
|
e32e2b07b5 | ||
|
|
a108d66968 | ||
|
|
e820cc7370 | ||
|
|
74c8a3580d | ||
|
|
03c67f3410 | ||
|
|
1d195bf865 | ||
|
|
473734f717 | ||
|
|
178b1d3e38 | ||
|
|
080abcb695 | ||
|
|
8faf9c0519 | ||
|
|
f81dd29cd1 | ||
|
|
c297d051dc | ||
|
|
de19b04fe4 | ||
|
|
51e9fd6efd | ||
|
|
63a2267f53 | ||
|
|
aa7d442d32 | ||
|
|
44e15e4e9b | ||
|
|
aa6c19ebda | ||
|
|
8776ece7d5 | ||
|
|
205135853d | ||
|
|
a71ab36096 | ||
|
|
490d1dc5d3 | ||
|
|
2fa8de4e52 | ||
|
|
3522917021 | ||
|
|
4fc47754c2 | ||
|
|
0bda2bcdff | ||
|
|
c03ac09dbb | ||
|
|
44edf962d8 | ||
|
|
6670b1a4b6 | ||
|
|
c73c676849 | ||
|
|
6191243c64 | ||
|
|
c5c9dc33c9 | ||
|
|
56df1dc532 | ||
|
|
06bda07db1 | ||
|
|
0bd2821937 | ||
|
|
936d9df13d | ||
|
|
75b315d077 | ||
|
|
76c1ec5b06 | ||
|
|
a836fb5e37 | ||
|
|
69d21a82c9 | ||
|
|
412c6a1d26 | ||
|
|
5a987c06fe | ||
|
|
af17a75ae1 | ||
|
|
9f3dc46502 | ||
|
|
b967a5033a | ||
|
|
18646a6c03 | ||
|
|
84e2a0c829 | ||
|
|
f1649bf6ee | ||
|
|
bc5d7a0e92 | ||
|
|
d5875c1554 | ||
|
|
576805b7b0 | ||
|
|
2b463ab1c6 | ||
|
|
6f899ee493 | ||
|
|
556a5dad47 | ||
|
|
cbaf5ac8fc | ||
|
|
76261d5bfc | ||
|
|
dcc99040d6 | ||
|
|
07a9752866 | ||
|
|
7cafa67f0a | ||
|
|
3909e85f67 | ||
|
|
e83a25a698 | ||
|
|
32706284f0 | ||
|
|
a079d23de1 | ||
|
|
d470213857 | ||
|
|
1446f11222 | ||
|
|
ef72787907 | ||
|
|
66d40c0159 | ||
|
|
2d340e8a8c | ||
|
|
94be1b7250 | ||
|
|
b32c477b1d | ||
|
|
e5eb98c8a0 | ||
|
|
bb63c3fade | ||
|
|
a0bb87d693 | ||
|
|
ba9336f402 | ||
|
|
5feb55a95b | ||
|
|
67956b3cb1 | ||
|
|
a6840f9bf9 | ||
|
|
081c4c6d70 | ||
|
|
b5f10be167 | ||
|
|
0bc9fe813f | ||
|
|
feb4853273 | ||
|
|
9e0f9bc4ee | ||
|
|
f5fd2e71b6 | ||
|
|
ee30bd03ac | ||
|
|
1a35af5024 | ||
|
|
ded9be6774 | ||
|
|
ffe4cc6f42 | ||
|
|
79e9e77e46 | ||
|
|
87b480bc0a | ||
|
|
336d32d1ce | ||
|
|
9aef6f30a8 | ||
|
|
242e5ba87e | ||
|
|
9f97147cc3 | ||
|
|
9048332b0e | ||
|
|
b6c12abf0e | ||
|
|
8fa26ee865 | ||
|
|
fae6253932 | ||
|
|
2976eb047b | ||
|
|
f24bc7092f | ||
|
|
6d87fc5879 | ||
|
|
54430c3a2c | ||
|
|
e9a016670f | ||
|
|
1ebf480258 | ||
|
|
e7c8d70333 | ||
|
|
9097752888 | ||
|
|
62503466a0 | ||
|
|
1560078ce2 | ||
|
|
8bde0ad474 | ||
|
|
21433d10c2 | ||
|
|
044d623424 | ||
|
|
0b5655b9d3 | ||
|
|
4df0654998 | ||
|
|
ad790baefb | ||
|
|
cb6fc55a2d | ||
|
|
b114bb9395 | ||
|
|
da8738b88e | ||
|
|
3ca876da62 | ||
|
|
416a15480f | ||
|
|
a149405d11 | ||
|
|
3d596e2dbc | ||
|
|
fae58174fa | ||
|
|
840d3f99ed | ||
|
|
871aa058f8 | ||
|
|
8552c66c1e | ||
|
|
097a1de67d | ||
|
|
ed4b68c364 | ||
|
|
268ab00ed9 | ||
|
|
7803960cd1 | ||
|
|
0e46474cb2 | ||
|
|
7c806a2bf9 | ||
|
|
c2d32be3c5 | ||
|
|
13c0e7061e | ||
|
|
1d6e0bd35f | ||
|
|
ff92846e64 | ||
|
|
640039a180 | ||
|
|
59dffc258a | ||
|
|
8a9f98371e | ||
|
|
5cdf2e0569 | ||
|
|
669fc9ede8 | ||
|
|
0df56f9ea8 | ||
|
|
56ad5a9324 | ||
|
|
9162cea7ba | ||
|
|
e1d547623e | ||
|
|
998d232ac0 | ||
|
|
1a6ba2072c | ||
|
|
ae00c04faf | ||
|
|
e0e14a1fe4 | ||
|
|
901624a891 | ||
|
|
913fe001e5 | ||
|
|
86d6ed10bb | ||
|
|
55f9c45899 | ||
|
|
07938aaab2 | ||
|
|
61da5dd88b | ||
|
|
d9f73ac71b | ||
|
|
e6d8d95c0a | ||
|
|
9d67febb93 | ||
|
|
6220ab3f31 | ||
|
|
1eba9855c1 | ||
|
|
a297391071 | ||
|
|
4f9bc89ab2 | ||
|
|
084a8eca55 | ||
|
|
ed344c795a | ||
|
|
c335da9334 | ||
|
|
fb24b6b37f | ||
|
|
8868d13214 | ||
|
|
c7260bddab | ||
|
|
abe3e511bf | ||
|
|
70327cf869 | ||
|
|
a9713208e4 | ||
|
|
2783ec7b51 | ||
|
|
ab43ee1fbb | ||
|
|
2ab2ea2c6c | ||
|
|
703a5535cf | ||
|
|
187ea6d546 | ||
|
|
055921eaf5 | ||
|
|
f90d7d7296 | ||
|
|
27aea13798 | ||
|
|
d75beed5dd | ||
|
|
243bbb1b61 | ||
|
|
98f8e52b46 | ||
|
|
0bba1d265a | ||
|
|
96f12f83f0 | ||
|
|
02a1de2afe | ||
|
|
beafd0b53f | ||
|
|
4c04572783 | ||
|
|
6b3ec5d9e0 | ||
|
|
6804ce3cbe | ||
|
|
42e53e84e1 | ||
|
|
8105375b64 | ||
|
|
e39740cca3 | ||
|
|
c8e7f11688 | ||
|
|
fd3f9378ac | ||
|
|
85d91e5c0a | ||
|
|
c09394517d | ||
|
|
04a0e041e4 | ||
|
|
5f6f7dca44 | ||
|
|
ea08a4d767 | ||
|
|
822f8ef802 | ||
|
|
c8439e437c | ||
|
|
dc179665dc | ||
|
|
3ec9b94c79 | ||
|
|
9b9521b75a | ||
|
|
41b86fffa5 | ||
|
|
bf758698b3 | ||
|
|
a5cc457875 | ||
|
|
f80122e12b | ||
|
|
2e5baaf296 | ||
|
|
0e0bc6a2c1 | ||
|
|
38829191a7 | ||
|
|
c9253ed056 | ||
|
|
a1ab388ab6 | ||
|
|
de4ad62562 | ||
|
|
e2a677e21d | ||
|
|
baebacf3db | ||
|
|
3b1373ec02 | ||
|
|
695678ad40 | ||
|
|
92ba6d5caa | ||
|
|
6b4022b0c1 | ||
|
|
0cb354be7a | ||
|
|
1c869bf3a6 | ||
|
|
fee820bac2 | ||
|
|
1676314fc3 | ||
|
|
49a16fe505 | ||
|
|
43447ed711 | ||
|
|
edbd2a3dca | ||
|
|
03f7234360 | ||
|
|
d79f28fd75 | ||
|
|
051b4799de | ||
|
|
1b74b93466 | ||
|
|
4ccb125e85 | ||
|
|
b6c6bb27a8 | ||
|
|
1f70e42d3b | ||
|
|
be7a42ff30 | ||
|
|
9ed7f52685 | ||
|
|
dfb3602318 | ||
|
|
f1cd9a2136 | ||
|
|
759917772c | ||
|
|
bcc8998e5e | ||
|
|
6af77360d3 | ||
|
|
f939a81ad6 | ||
|
|
bf8a52694a | ||
|
|
4fbde347f8 | ||
|
|
aca58c8571 | ||
|
|
4ca34c0695 | ||
|
|
26ce39bb46 | ||
|
|
4e8c4f218f | ||
|
|
070eb2fc3a | ||
|
|
431f8586f5 | ||
|
|
c9adaed79d | ||
|
|
381e12efa2 | ||
|
|
f4fc29855e | ||
|
|
2fb2e86473 | ||
|
|
c31aeecdcf | ||
|
|
5d4f82d70f | ||
|
|
ad907219be | ||
|
|
463b701e9f | ||
|
|
3d98f8d65b | ||
|
|
a4c1d945ee | ||
|
|
ff068aa1b7 | ||
|
|
9e48a338c3 | ||
|
|
b610b1ea51 | ||
|
|
69c6a0aa36 | ||
|
|
a5e66a4448 | ||
|
|
328598eaed | ||
|
|
d6c338d588 | ||
|
|
bd5de884fa | ||
|
|
8d5b1018d3 | ||
|
|
ae7d4d2553 | ||
|
|
88afa05cf8 | ||
|
|
748ba7f02b | ||
|
|
0b665ecd10 | ||
|
|
1b402d16ad | ||
|
|
135b77d12b | ||
|
|
effcc080e5 | ||
|
|
5e504a77fa | ||
|
|
c9c6ebb53a | ||
|
|
941c2f2d17 | ||
|
|
bf7dc485b3 | ||
|
|
622c6d3f19 | ||
|
|
12ef63370b | ||
|
|
796339172c | ||
|
|
238e9078bf | ||
|
|
1934179c64 | ||
|
|
88c365fb52 | ||
|
|
0ff8a1a37a | ||
|
|
c448712e88 | ||
|
|
d4ba41bd43 | ||
|
|
40a1fea916 | ||
|
|
1f54e2accc | ||
|
|
d5df12c9c2 | ||
|
|
af4c4157f5 | ||
|
|
5ff271592c | ||
|
|
bb4358c8bb | ||
|
|
2ab2706c8e | ||
|
|
b83fbb76a9 | ||
|
|
fb54db143d | ||
|
|
8ee8cdd7f8 | ||
|
|
bee2ea6fa6 | ||
|
|
a4aa58b4de | ||
|
|
e741c1bca7 | ||
|
|
d5694672a4 | ||
|
|
ca858f7544 | ||
|
|
cab60b9ee8 | ||
|
|
0b7f0197fc | ||
|
|
93db1a5a47 | ||
|
|
f657ac4e0b | ||
|
|
f5b9ed28db | ||
|
|
0eb2bdd8e1 | ||
|
|
58368513ab | ||
|
|
5abacba0d8 | ||
|
|
2861b35f41 | ||
|
|
4e4ae3c7a2 | ||
|
|
1c6d0816a5 | ||
|
|
341e40196d | ||
|
|
8e1f0a0471 | ||
|
|
2408cd332d | ||
|
|
58a973e664 | ||
|
|
a36963026e | ||
|
|
78f231c87a | ||
|
|
5992430e9f | ||
|
|
83dc082acf | ||
|
|
2edc44c352 | ||
|
|
02feaf087e | ||
|
|
c52fd3fd2b | ||
|
|
248b777117 | ||
|
|
66fda76644 | ||
|
|
23692ab8e7 | ||
|
|
b977ffbb90 | ||
|
|
85a313a8cc | ||
|
|
bfb4f900e7 | ||
|
|
055efe7abd | ||
|
|
ca96328142 | ||
|
|
d6816d8d2a | ||
|
|
c5d495006a | ||
|
|
abe414ad88 | ||
|
|
e95e815483 | ||
|
|
c3500bd3af | ||
|
|
99984d328e | ||
|
|
aaaf7e96bd | ||
|
|
37fc0a211b | ||
|
|
c35eb7a6c2 | ||
|
|
2bd4b07418 | ||
|
|
55cd5a23ff | ||
|
|
72a9d70f09 | ||
|
|
d10c779fc6 | ||
|
|
d031bb2df5 | ||
|
|
73a465501e | ||
|
|
6f1fe69433 | ||
|
|
246239f42a | ||
|
|
1682677297 | ||
|
|
21b8c3458a | ||
|
|
5356f4e973 | ||
|
|
00c7cb1a22 | ||
|
|
d75b1a7e96 | ||
|
|
fe0386e9c4 | ||
|
|
5a4c11c72a | ||
|
|
c15e908266 | ||
|
|
c18727ff43 | ||
|
|
886df84e87 | ||
|
|
8f5fe3f46b | ||
|
|
ffa393850a | ||
|
|
f5c7cde9b7 | ||
|
|
0018c38f79 | ||
|
|
9042457326 | ||
|
|
e988fe49ed | ||
|
|
e592d568c6 | ||
|
|
9ec5b030ce | ||
|
|
76e0e1fbff | ||
|
|
b8ca1bb04c | ||
|
|
57c5ff5db3 | ||
|
|
2b83dcc794 | ||
|
|
7c3bccf4ba | ||
|
|
d2aa6c1b8a | ||
|
|
a329bca16b | ||
|
|
388930336d | ||
|
|
d06377a37b | ||
|
|
eed018fe1d | ||
|
|
013f078bc4 | ||
|
|
2bcd336400 | ||
|
|
8f34afd66e | ||
|
|
4cdc4c4861 | ||
|
|
050554e2bb | ||
|
|
b3b7bd9327 | ||
|
|
dac8a5fb86 | ||
|
|
fd6a3bdccf | ||
|
|
60c70c5b37 | ||
|
|
c9f9b93ad7 | ||
|
|
e39b91c27b | ||
|
|
d694ad90e0 | ||
|
|
99b8246d6e | ||
|
|
90bf1762ae | ||
|
|
c9c975ce49 | ||
|
|
29c2aa015f | ||
|
|
abad47ff1b | ||
|
|
c232e97f80 | ||
|
|
ab9b00ce88 | ||
|
|
9200a98087 | ||
|
|
3b27b2220d | ||
|
|
6573953ebd | ||
|
|
0fa2f569a7 | ||
|
|
24a27d17d0 | ||
|
|
1724e4551d | ||
|
|
457f20f962 | ||
|
|
70240b0d08 | ||
|
|
50eec773bf | ||
|
|
35de13eebd | ||
|
|
3baa6b0063 | ||
|
|
6d462f762e | ||
|
|
802cce9a28 | ||
|
|
ee334cbcf8 | ||
|
|
c561a3749c | ||
|
|
f8b56736d7 | ||
|
|
7bcd46b145 | ||
|
|
d905592115 | ||
|
|
4b8dec8901 | ||
|
|
379eaec8e0 | ||
|
|
ae2d650516 | ||
|
|
4a83e79923 | ||
|
|
eb00c1991c | ||
|
|
462798dc2d | ||
|
|
1efd5781f8 | ||
|
|
08c6deba12 | ||
|
|
531848366d | ||
|
|
91abcd49e1 | ||
|
|
770c404ebe | ||
|
|
99217433b1 | ||
|
|
bccf7ac97a | ||
|
|
ed8be34b20 | ||
|
|
b6bb8382e7 | ||
|
|
893e0061a3 | ||
|
|
58efc2680e | ||
|
|
8180905890 | ||
|
|
db2be093b5 | ||
|
|
a688878236 | ||
|
|
75d4131aa6 | ||
|
|
66f84c5903 | ||
|
|
5d9b169380 | ||
|
|
eb908c696f | ||
|
|
21edcc86ce | ||
|
|
6f46233ad7 | ||
|
|
018803473c | ||
|
|
23750e644f | ||
|
|
4dff3550b3 | ||
|
|
3085cc81ec | ||
|
|
4ea983163a | ||
|
|
1d2350188c | ||
|
|
b20d369176 | ||
|
|
7968b918ec | ||
|
|
82981a1a29 | ||
|
|
41def2f0eb | ||
|
|
6aa5a09c18 | ||
|
|
66161104bf | ||
|
|
5a4e2e4dec | ||
|
|
0f7ac1bc39 | ||
|
|
1ada6e6f84 | ||
|
|
be9a0ab467 | ||
|
|
ea5db2a09a | ||
|
|
c88ddfe18d | ||
|
|
b7ad217cd3 | ||
|
|
c08b87749b | ||
|
|
07681d1b5b | ||
|
|
7ba5e32bcc | ||
|
|
3885c643ec | ||
|
|
66a8d04610 | ||
|
|
8e35d543bd | ||
|
|
041d4f8ed2 | ||
|
|
4d764500b1 | ||
|
|
7b85333b88 | ||
|
|
af6edc575d | ||
|
|
4ce859bf46 | ||
|
|
32eb6ebf19 | ||
|
|
e4bdc3bc4c | ||
|
|
c1d9728dc8 | ||
|
|
322216c810 | ||
|
|
6d8a95f607 | ||
|
|
ea2c64da54 | ||
|
|
09b088d8ff | ||
|
|
e4458abd33 | ||
|
|
8abc1559c7 | ||
|
|
b6ac0279a5 | ||
|
|
36ac916c72 | ||
|
|
253453d8a6 | ||
|
|
a9a1763328 | ||
|
|
ab2cd766c2 | ||
|
|
0478e6bbfe | ||
|
|
f2d13c242e | ||
|
|
bc01687d7c | ||
|
|
f894901ad1 | ||
|
|
d064faebb9 | ||
|
|
bcf3457c28 | ||
|
|
9f056ac756 | ||
|
|
98de226dfd | ||
|
|
4a81f6b1a6 | ||
|
|
ef3a25a626 | ||
|
|
2b8f8c4c47 | ||
|
|
6019e4890d | ||
|
|
089ffae94c | ||
|
|
52c346f952 | ||
|
|
baea47bc85 | ||
|
|
15e04903b1 | ||
|
|
a75ec75fff | ||
|
|
2d3d6256ab | ||
|
|
0acf5a30e6 | ||
|
|
4650e7299f | ||
|
|
0bb127057f | ||
|
|
59ca9e7f0d | ||
|
|
cddfb6cf5e | ||
|
|
1d2a3e8501 | ||
|
|
966a611d7f | ||
|
|
19ac284f25 | ||
|
|
d512d5c105 | ||
|
|
b265f7bf7e | ||
|
|
1dca23e7af | ||
|
|
9c07070263 | ||
|
|
1ace298a1a | ||
|
|
96d2b69af4 | ||
|
|
b2ce322d56 | ||
|
|
0844743c30 | ||
|
|
3e776213b7 | ||
|
|
00d92aa474 | ||
|
|
0d7986bd71 | ||
|
|
41b6ebea58 | ||
|
|
ef9ac61d9e | ||
|
|
043b1f97ee | ||
|
|
7a2840fba6 | ||
|
|
08a6a9d9be | ||
|
|
09c1e2b24c | ||
|
|
bf90f95443 | ||
|
|
dfe5ba3863 | ||
|
|
7657607ca5 | ||
|
|
f01d5bfeb3 | ||
|
|
0ae1cdfc66 | ||
|
|
1dce4a9321 | ||
|
|
c9272b0088 | ||
|
|
1cd8934bb7 | ||
|
|
b915968f30 | ||
|
|
0f1ca8faac | ||
|
|
7870add603 | ||
|
|
a2da255931 | ||
|
|
26bc13b57f | ||
|
|
fa09e4af0d | ||
|
|
fd127f449c | ||
|
|
ad25479185 | ||
|
|
6a37e1c37b | ||
|
|
77ff11de95 | ||
|
|
ac155a9391 | ||
|
|
96571aa0f4 | ||
|
|
aa58e37f03 | ||
|
|
5e0da40ac4 | ||
|
|
2b065553a0 | ||
|
|
cd04360a43 | ||
|
|
96bbbe35e1 | ||
|
|
161f37f1f1 | ||
|
|
25e56f32e4 | ||
|
|
66caaffbe3 | ||
|
|
2d4967c4b4 | ||
|
|
d013a1fd77 | ||
|
|
b91e241b9a | ||
|
|
84564b9e3e | ||
|
|
d4caa0f5f5 | ||
|
|
d31c873cd5 | ||
|
|
f32a8861c5 | ||
|
|
18932b7a4a | ||
|
|
067c181bb8 | ||
|
|
83c533e557 | ||
|
|
55eafdf352 | ||
|
|
70f449654f | ||
|
|
bd61cf9b60 | ||
|
|
6bf2cb09a6 | ||
|
|
fbb243ee0c | ||
|
|
7ce2b3c4e9 | ||
|
|
7e2f06d217 | ||
|
|
cedb8d9cd9 | ||
|
|
39e2a154f0 | ||
|
|
a4ab4ce2f3 | ||
|
|
cfabbe67b8 | ||
|
|
a91a222613 | ||
|
|
a183dea9e4 | ||
|
|
b28df988ef | ||
|
|
01c23a8124 | ||
|
|
e3f8a95b72 | ||
|
|
a87e31b767 | ||
|
|
4daa4533d3 | ||
|
|
e37385c893 | ||
|
|
4a0eb7febe | ||
|
|
f295130544 | ||
|
|
9d5968a45a | ||
|
|
9519278c5e | ||
|
|
a91ce4a159 | ||
|
|
969e4afb26 | ||
|
|
de25b26f3e | ||
|
|
67bd3526c2 | ||
|
|
c7e5db34e4 | ||
|
|
c8adfebb1f | ||
|
|
49de3c1ab8 | ||
|
|
e1c71c92d6 | ||
|
|
330df05891 | ||
|
|
63a492291a | ||
|
|
0b69ad0e65 | ||
|
|
bc99ec8285 | ||
|
|
7dbb8cd987 | ||
|
|
38332d9462 | ||
|
|
cba7fc950b | ||
|
|
143d23f1b8 | ||
|
|
47763df590 | ||
|
|
53604c9db2 | ||
|
|
ba7aaa8ab1 | ||
|
|
c318d46469 | ||
|
|
a2892ab1dd | ||
|
|
1879c813c8 | ||
|
|
2eb9e4a8a4 | ||
|
|
a9d4237bc2 | ||
|
|
f85113669d | ||
|
|
69072116be | ||
|
|
b2f8786821 | ||
|
|
dc46aea15e | ||
|
|
e33ce843dd | ||
|
|
f7fa717411 | ||
|
|
a409a2cb94 | ||
|
|
7e3fb2ba08 | ||
|
|
9084d8c358 | ||
|
|
4ae5db57dc | ||
|
|
c5ccf742d6 | ||
|
|
f6316b86e4 | ||
|
|
aebdc716c7 | ||
|
|
72f6036432 | ||
|
|
afc989403d | ||
|
|
ea9d4f49b2 | ||
|
|
9c40a9c6df | ||
|
|
000cb87ec1 | ||
|
|
054ee463a1 | ||
|
|
07d9747c40 | ||
|
|
2d5d5ed704 | ||
|
|
52d2bd93f5 | ||
|
|
da467fe3b6 | ||
|
|
52cf4658c5 | ||
|
|
94d6ac2f2e | ||
|
|
5f9e0a2739 | ||
|
|
7933685ffb | ||
|
|
1258ea663c | ||
|
|
520631913f | ||
|
|
10e31c4e8c | ||
|
|
a659b0005a | ||
|
|
23f11e3abb | ||
|
|
211e27a4d7 | ||
|
|
0b3c8c98d8 | ||
|
|
605d82285d | ||
|
|
07f51f323e | ||
|
|
8b63ea82ec | ||
|
|
8390df0fa5 | ||
|
|
1609e32c3a | ||
|
|
c5cba11ecb | ||
|
|
257a7d1488 | ||
|
|
bcf66253bd | ||
|
|
451676c5be |
@@ -1,42 +0,0 @@
|
||||
freebsd_instance:
|
||||
image_family: freebsd-14-3
|
||||
|
||||
build_task:
|
||||
# Don't change this name without adjusting .github/workflows/build.yaml
|
||||
name: Build FreeBSD (Stack)
|
||||
install_script: pkg install -y postgresql16-client hs-stack git
|
||||
|
||||
only_if: |
|
||||
$CIRRUS_TAG != '' || $CIRRUS_BRANCH == 'main' || $CIRRUS_BRANCH =~ 'v*' ||
|
||||
changesInclude(
|
||||
'.github/workflows/build.yaml',
|
||||
'.github/actions/artifact-from-cirrus/**',
|
||||
'.cirrus.yml',
|
||||
'postgrest.cabal',
|
||||
'stack.yaml*',
|
||||
'**.hs'
|
||||
)
|
||||
|
||||
stack_cache:
|
||||
folders: /.stack
|
||||
fingerprint_script:
|
||||
- echo $CIRRUS_OS
|
||||
- stack --version
|
||||
- md5sum postgrest.cabal
|
||||
- md5sum stack.yaml.lock
|
||||
|
||||
stack_work_cache:
|
||||
folders: .stack-work
|
||||
fingerprint_script:
|
||||
- echo $CIRRUS_OS
|
||||
- stack --version
|
||||
- md5sum postgrest.cabal
|
||||
- md5sum stack.yaml.lock
|
||||
- find main src -type f -iname '*.hs' -exec md5sum "{}" +
|
||||
|
||||
build_script: |
|
||||
stack build -j 1 --local-bin-path . --copy-bins
|
||||
strip postgrest
|
||||
|
||||
bin_artifacts:
|
||||
path: postgrest
|
||||
@@ -1,55 +0,0 @@
|
||||
# Contributing to PostgREST
|
||||
|
||||
**First:** if you're unsure or afraid of _anything_, just ask or
|
||||
submit the issue or pull request anyways. You won't be yelled at
|
||||
for giving your best effort. The worst that can happen is that
|
||||
you'll be politely asked to change something. We appreciate any
|
||||
sort of contributions, and don't want a wall of rules to get in the
|
||||
way of that.
|
||||
|
||||
However, for those individuals who want a bit more guidance on the
|
||||
best way to contribute to the project, read on. This document will
|
||||
cover what we're looking for. By addressing all the points we're
|
||||
looking for, it raises the chances we can quickly merge or address
|
||||
your contributions.
|
||||
|
||||
## Issues
|
||||
|
||||
For questions on how to use PostgREST, please use
|
||||
[GitHub discussions](https://github.com/PostgREST/postgrest/discussions).
|
||||
|
||||
### Reporting an Issue
|
||||
|
||||
* Make sure you test against the latest [stable release](https://github.com/PostgREST/postgrest/releases/latest)
|
||||
and also against the latest [nightly release](https://github.com/PostgREST/postgrest/releases/tag/nightly).
|
||||
It is possible we already fixed the bug you're experiencing.
|
||||
|
||||
* Provide steps to reproduce the issue, including your OS version and
|
||||
the specific database schema that you are using.
|
||||
|
||||
* Please include SQL logs for issues involving runtime problems. To obtain logs first
|
||||
[enable logging all statements](http://www.microhowto.info/howto/log_all_queries_to_a_postgresql_server.html),
|
||||
then [find your logs](http://blog.endpoint.com/2014/11/dear-postgresql-where-are-my-logs.html).
|
||||
|
||||
* If your database schema has changed while the PostgREST server is running,
|
||||
[send the server a `SIGUSR1` signal](http://postgrest.org/en/latest/admin.html#schema-reloading) or restart it to ensure the schema cache
|
||||
is not stale. This sometimes fixes apparent bugs.
|
||||
|
||||
## Code
|
||||
|
||||
We have a fully nix-based development environment with many tools for a smooth development workflow available.
|
||||
Check the [development docs](https://github.com/PostgREST/postgrest/blob/main/nix/README.md) on how to set it up and use it.
|
||||
|
||||
### Haskell Conventions
|
||||
|
||||
* All contributions must pass the tests before being merged. When
|
||||
you create a pull request your code will automatically be tested.
|
||||
|
||||
* All code must also pass [hlint](http://community.haskell.org/~ndm/hlint/) and [stylish-haskell](https://github.com/jaspervdj/stylish-haskell)
|
||||
with no warnings. This helps enforce a uniform style for all committers. Continuous integration will check this as well on every
|
||||
pull request. There are useful tools in the nix-shell that help with checking this locally. You can run `postgrest-check` to do this manually but
|
||||
we recommend adding it to `.git/hooks/pre-commit` as `nix-shell --run postgrest-check` to automatically check this before doing a commit.
|
||||
|
||||
### Running Tests
|
||||
|
||||
For instructions on running tests, see the [development docs](https://github.com/PostgREST/postgrest/blob/main/nix/README.md#testing).
|
||||
@@ -4,17 +4,18 @@ When submitting a new feature or fix:
|
||||
- Add a new entry to the CHANGELOG - https://github.com/PostgREST/postgrest/blob/main/CHANGELOG.md#unreleased
|
||||
- If relevant, update the docs
|
||||
- Use a prefix for the PR title or commits, e.g. "fix: description of the fix".
|
||||
+ `fix`, bug fixes
|
||||
+ `feat`, new features added
|
||||
+ `perf`, performance improvements
|
||||
+ `docs`, updating the documentation
|
||||
+ `nix`, related to the Nix development environment
|
||||
+ `ci`, related to the Continuous Integration modules
|
||||
+ `test`, related to the testing modules
|
||||
+ `refactor`, refactoring code
|
||||
+ `deprecate`, deprecating a feature
|
||||
+ `changelog`, updating the CHANGELOG
|
||||
+ `chore`, maintenance (build process, updating sponsors, etc.)
|
||||
+ `add`, Add a new feature
|
||||
+ `amend`, To amend an unrealease commit
|
||||
+ `change`, Breaking changes
|
||||
+ `chore`, Maintenance, update sponsors, changelog, readme etc
|
||||
+ `ci`, CI configuration files and scripts
|
||||
+ `docs`, Documentation
|
||||
+ `fix`, Bug fix
|
||||
+ `nix`, Related to Nix
|
||||
+ `perf`, Performance improvements
|
||||
+ `refactor`, Refactoring code
|
||||
+ `remove`, Remove a feature or fix
|
||||
+ `test`, Adding tests
|
||||
+ Other prefixes may be used if necessary
|
||||
- If there's a breaking change, add `BREAKING CHANGE` and an explanation to your commit message
|
||||
-->
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# TODO: Remove this once a new actionlint release has been cut
|
||||
# and made its way to us through nixpkgs.
|
||||
self-hosted-runner:
|
||||
labels:
|
||||
- ubuntu-24.04-arm
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Cache on main
|
||||
|
||||
description: Stores caches on main and release branches only, but restores them on all branches.
|
||||
|
||||
inputs:
|
||||
path:
|
||||
description: Path(s) to cache
|
||||
required: true
|
||||
save-prs:
|
||||
description: Whether to additionally store the cache in a pull request, too. Should only be used for very small caches.
|
||||
prefix:
|
||||
description: Cache key prefix to be used in both primary key and restore-keys.
|
||||
required: true
|
||||
suffix:
|
||||
description: Cache key suffix to be used only in primary key.
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
if: ${{ startsWith(github.ref, 'refs/heads/') || (inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
|
||||
with:
|
||||
path: ${{ inputs.path }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-${{ inputs.prefix }}-${{ inputs.suffix }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-${{ inputs.prefix }}-
|
||||
- uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
if: ${{ !startsWith(github.ref, 'refs/heads/') && !(inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
|
||||
with:
|
||||
path: ${{ inputs.path }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-${{ inputs.prefix }}-${{ inputs.suffix }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-${{ inputs.prefix }}-
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Run anywhere
|
||||
|
||||
description: Runs the same code either in a VM or on the bare machine
|
||||
|
||||
inputs:
|
||||
vm:
|
||||
description: Which VM to run on.
|
||||
envs:
|
||||
description: List of relevant environment variables, which might need to be copied into the VM.
|
||||
prepare:
|
||||
description: Code to run in a prepare step, e.g. installing dependencies.
|
||||
run:
|
||||
description: Code to run as the main action.
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- if: ${{ inputs.vm == 'freebsd' }}
|
||||
uses: vmactions/freebsd-vm@83b151f58c6047089f4c80eb5ba2039d158ce093 # v1.5.3
|
||||
with:
|
||||
envs: ${{ inputs.envs }}
|
||||
prepare: ${{ inputs.prepare }}
|
||||
# Work around https://github.com/vmactions/freebsd-vm/issues/59
|
||||
run: |
|
||||
pw user add -n action -m
|
||||
su action -c '${{ inputs.run }}'
|
||||
- if: ${{ inputs.vm == '' }}
|
||||
name: Prepare
|
||||
shell: ${{ runner.os == 'Windows' && 'pwsh' || 'bash' }}
|
||||
run: ${{ inputs.prepare }}
|
||||
- if: ${{ inputs.vm == '' }}
|
||||
name: Run
|
||||
shell: ${{ runner.os == 'Windows' && 'pwsh' || 'bash' }}
|
||||
run: ${{ inputs.run }}
|
||||
@@ -4,6 +4,9 @@ codecov:
|
||||
|
||||
comment: false
|
||||
|
||||
github_checks:
|
||||
annotations: true
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"extends": [
|
||||
"config:best-practices"
|
||||
],
|
||||
"baseBranches": [
|
||||
"baseBranchPatterns": [
|
||||
"main",
|
||||
"/^v[0-9]+/"
|
||||
],
|
||||
@@ -13,28 +13,16 @@
|
||||
},
|
||||
"packageRules": [
|
||||
{
|
||||
"matchBaseBranches": [ "/^v[0-9]+/" ],
|
||||
"matchManagers": ["haskell-cabal"],
|
||||
"matchManagers": [
|
||||
"haskell-cabal"
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"matchBaseBranches": [ "/^v[0-9]+/" ],
|
||||
"matchBaseBranches": [
|
||||
"/^v[0-9]+/"
|
||||
],
|
||||
"groupName": "all dependencies"
|
||||
},
|
||||
{
|
||||
"matchManagers": ["haskell-cabal"],
|
||||
"matchPackageNames": ["base", "bytestring", "containers", "directory", "mtl", "parsec", "process", "text"],
|
||||
"groupName": "GHC dependencies"
|
||||
},
|
||||
{
|
||||
"matchManagers": ["haskell-cabal"],
|
||||
"matchPackageNames": ["hasql", "hasql-dynamic-statements", "hasql-notifications", "hasql-transaction", "hasql-pool"],
|
||||
"groupName": "hasql"
|
||||
},
|
||||
{
|
||||
"matchManagers": ["haskell-cabal"],
|
||||
"matchPackageNames": ["fuzzyset"],
|
||||
"allowedVersions": "<0.3"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
name: Backport
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
backport:
|
||||
name: Backport
|
||||
runs-on: ubuntu-slim
|
||||
# It triggers only when PR is already merged on either:
|
||||
#
|
||||
# - The merge event itself (action != labeled) or
|
||||
# - A label event with the right label (backport ...).
|
||||
#
|
||||
# The result will be that we can add the label before or after merge,
|
||||
# but the workflow will only run once the PR had been merged.
|
||||
if: >
|
||||
github.event.pull_request.merged &&
|
||||
(
|
||||
github.event.action != 'labeled' ||
|
||||
startsWith(github.event.label.name, 'backport')
|
||||
)
|
||||
steps:
|
||||
|
||||
# This actions creates the github token using the postgrest app secrets
|
||||
- name: Create Github App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
client-id: ${{ vars.POSTGREST_CI_APP_ID }}
|
||||
private-key: ${{ secrets.POSTGREST_CI_PRIVATE_KEY }}
|
||||
permission-contents: write
|
||||
permission-pull-requests: write
|
||||
permission-workflows: write # required when backporting CI changes
|
||||
|
||||
# This is required for backport action to cherry-pick the PR
|
||||
- name: Fetch PR ref
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
allow-unsafe-pr-checkout: true
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
# Backport action that creates the PR with given settings
|
||||
- name: Create backport PR
|
||||
uses: korthout/backport-action@2e830a1d0b8269505846ddd407a70876913ad1f8 # v4.6
|
||||
with:
|
||||
github_token: ${{ steps.app-token.outputs.token }}
|
||||
pull_description: 'Backport for #${pull_number}.'
|
||||
pull_title: '${target_branch}: ${pull_title}'
|
||||
@@ -0,0 +1,199 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN:
|
||||
required: false
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- v[0-9]+
|
||||
paths:
|
||||
- .github/workflows/build.yaml
|
||||
- .github/actions/**
|
||||
- .github/scripts/**
|
||||
- .github/*
|
||||
- '*.nix'
|
||||
- nix/**
|
||||
- flake.lock
|
||||
- .cirrus.yml
|
||||
- cabal.project*
|
||||
- postgrest.cabal
|
||||
- stack.yaml*
|
||||
- '**.hs'
|
||||
- '!**.md'
|
||||
|
||||
concurrency:
|
||||
# Terminate all previous runs of the same workflow for pull requests
|
||||
group: build-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
static:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: Linux aarch64
|
||||
runs-on: ubuntu-24.04-arm
|
||||
artifact: aarch64
|
||||
- name: Linux x86-64
|
||||
runs-on: ubuntu-24.04
|
||||
artifact: x86-64
|
||||
name: Nix - ${{ matrix.name }} static
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
|
||||
- name: Build static executable
|
||||
run: nix-build -A postgrestStatic -A postgrestStatic.tests
|
||||
- name: Save built executable as artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: postgrest-linux-static-${{ matrix.artifact }}
|
||||
path: result/bin/postgrest
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build Docker image
|
||||
run: nix-build -A docker.image --out-link postgrest-docker-${{ matrix.artifact }}.tar.gz
|
||||
- name: Save built Docker image as artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: postgrest-docker-${{ matrix.artifact }}
|
||||
path: postgrest-docker-${{ matrix.artifact }}.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Test static executable with NixOS' VM test
|
||||
# GHA's ARM runner does not support KVM
|
||||
if: runner.arch == 'X64'
|
||||
run: nix-build -A nixpkgs-nixos-test
|
||||
|
||||
|
||||
macos:
|
||||
name: Nix - MacOS
|
||||
runs-on: macos-26
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- name: Install nix-build-uncached
|
||||
run: nix-env -f default.nix -iA nix-build-uncached
|
||||
|
||||
- name: Build everything (default.nix)
|
||||
run: nix-build-uncached
|
||||
|
||||
- name: Build everything (shell.nix)
|
||||
run: nix-build-uncached shell.nix
|
||||
|
||||
|
||||
stack:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: FreeBSD x86-64
|
||||
runs-on: ubuntu-24.04
|
||||
vm: freebsd
|
||||
artifact: postgrest-freebsd-x86-64
|
||||
deps: pkg install -y git postgresql16-client hs-stack
|
||||
|
||||
- name: MacOS aarch64
|
||||
runs-on: macos-14
|
||||
artifact: postgrest-macos-aarch64
|
||||
deps: brew link --force libpq
|
||||
|
||||
- name: MacOS x86-64
|
||||
runs-on: macos-15-intel
|
||||
artifact: postgrest-macos-x86-64
|
||||
deps: brew link --force libpq
|
||||
|
||||
- name: Windows
|
||||
runs-on: windows-2022
|
||||
deps: Add-Content $env:GITHUB_PATH $env:PGBIN
|
||||
artifact: postgrest-windows-x86-64
|
||||
|
||||
name: Stack - ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
env:
|
||||
# Putting .stack in the working directory helps with moving this in and out of the FreeBSD VM.
|
||||
STACK_ROOT: ${{ github.workspace }}/.stack
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- if: ${{ !matrix.vm }}
|
||||
uses: haskell-actions/setup@6037f33647c3f17758a2356c80fc4a53d7e0685d # v2.12.0
|
||||
with:
|
||||
# This must match the version in stack.yaml's resolver
|
||||
ghc-version: 9.10.3
|
||||
enable-stack: true
|
||||
stack-no-global: true
|
||||
stack-setup-ghc: true
|
||||
- name: Cache .stack
|
||||
uses: ./.github/actions/cache-on-main
|
||||
with:
|
||||
path: .stack
|
||||
prefix: ${{ matrix.vm }}${{ matrix.vm && '-' }}stack
|
||||
suffix: ${{ hashFiles('postgrest.cabal', 'stack.yaml.lock') }}
|
||||
- name: Cache .stack-work
|
||||
uses: ./.github/actions/cache-on-main
|
||||
with:
|
||||
path: .stack-work
|
||||
save-prs: true
|
||||
prefix: ${{ matrix.vm }}${{ matrix.vm && '-' }}stack-work-${{ hashFiles('postgrest.cabal', 'stack.yaml.lock') }}
|
||||
suffix: ${{ hashFiles('main/**/*.hs', 'src/**/*.hs') }}
|
||||
- name: Build with Stack
|
||||
uses: ./.github/actions/run-anywhere
|
||||
with:
|
||||
vm: ${{ matrix.vm }}
|
||||
envs: STACK_ROOT
|
||||
prepare: ${{ matrix.deps }}
|
||||
run: |
|
||||
stack build --lock-file error-on-write --local-bin-path result --copy-bins
|
||||
strip result/postgrest*
|
||||
- name: Save built executable as artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: ${{ matrix.artifact }}
|
||||
path: |
|
||||
result/postgrest
|
||||
result/postgrest.exe
|
||||
if-no-files-found: error
|
||||
|
||||
|
||||
cabal:
|
||||
strategy:
|
||||
matrix:
|
||||
ghc: ['9.10.3', '9.12.3']
|
||||
fail-fast: false
|
||||
name: Cabal - Linux x86-64 - GHC ${{ matrix.ghc }}
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: haskell-actions/setup@6037f33647c3f17758a2356c80fc4a53d7e0685d # v2.12.0
|
||||
with:
|
||||
ghc-version: ${{ matrix.ghc }}
|
||||
- name: Cache .cabal
|
||||
uses: ./.github/actions/cache-on-main
|
||||
with:
|
||||
path: |
|
||||
~/.cabal/packages
|
||||
~/.cabal/store
|
||||
prefix: cabal-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }}
|
||||
suffix: ${{ hashFiles('postgrest.cabal', 'cabal.project') }}
|
||||
- name: Cache dist-newstyle
|
||||
uses: ./.github/actions/cache-on-main
|
||||
with:
|
||||
path: dist-newstyle
|
||||
save-prs: true
|
||||
prefix: cabal-${{ matrix.ghc }}-dist-newstyle-${{ hashFiles('postgrest.cabal', 'cabal.project', 'cabal.project.freeze') }}
|
||||
suffix: ${{ hashFiles('**/*.hs') }}
|
||||
- name: Install dependencies
|
||||
run: cabal build --only-dependencies --enable-tests --enable-benchmarks
|
||||
- name: Build
|
||||
run: cabal build --enable-tests --enable-benchmarks all
|
||||
@@ -0,0 +1,53 @@
|
||||
name: Check
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN:
|
||||
required: false
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- v[0-9]+
|
||||
|
||||
concurrency:
|
||||
# Terminate all previous runs of the same workflow for pull requests
|
||||
group: style-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint-style:
|
||||
name: Lint & Style
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: style.lint.bin style.styleCheck.bin
|
||||
- name: Run linter (check locally with `nix-shell --run postgrest-lint`)
|
||||
run: postgrest-lint
|
||||
- name: Run style check (auto-format with `nix-shell --run postgrest-style`)
|
||||
run: postgrest-style-check
|
||||
|
||||
commit:
|
||||
if: github.event_name != 'push' # we don't run this on a push, a failure on push disrupts the release workflow
|
||||
name: Commit
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 100 # fetch history (last 100 commits) instead of default shallow clone history, this is deemed enough for a PR history
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: gitTools.commitCheck.bin
|
||||
- name: Run commitlint (check locally with `nix-shell --run postgrest-commitlint`)
|
||||
run: |
|
||||
# Fetch target branch explicitly
|
||||
git fetch origin ${{ github.base_ref }}
|
||||
|
||||
# Run commitlint
|
||||
postgrest-commitlint --from origin/${{ github.base_ref }} --to HEAD
|
||||
@@ -0,0 +1,69 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v[0-9]+
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
uses: ./.github/workflows/check.yaml
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
|
||||
|
||||
docs:
|
||||
name: Docs
|
||||
uses: ./.github/workflows/docs.yaml
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
|
||||
|
||||
test:
|
||||
name: Test
|
||||
uses: ./.github/workflows/test.yaml
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
|
||||
build:
|
||||
name: Build
|
||||
uses: ./.github/workflows/build.yaml
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
|
||||
|
||||
tag:
|
||||
name: Tag
|
||||
concurrency:
|
||||
# Never tag outdated commits on the main branch by skipping superseded commits
|
||||
group: ci-tag-${{ (github.ref == 'refs/heads/main' && github.ref) || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
if: vars.RELEASE_ENABLED
|
||||
runs-on: ubuntu-slim
|
||||
needs:
|
||||
- docs
|
||||
- test
|
||||
- build
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ssh-key: ${{ secrets.POSTGREST_SSH_KEY }}
|
||||
- name: Tag latest commit
|
||||
run: |
|
||||
cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
|
||||
|
||||
if [[ "$cabal_version" == *.* ]]; then
|
||||
git fetch --tags
|
||||
|
||||
if [ -z "$(git tag --list "v$cabal_version")" ]; then
|
||||
git tag "v$cabal_version"
|
||||
git push origin "v$cabal_version"
|
||||
fi
|
||||
else
|
||||
git tag -f "devel"
|
||||
git push -f origin "devel"
|
||||
fi
|
||||
@@ -14,6 +14,7 @@ on:
|
||||
- .github/actions/setup-nix/**
|
||||
- default.nix
|
||||
- nix/**
|
||||
- flake.lock
|
||||
- docs/**
|
||||
- '!**.md'
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Linkcheck
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '1 2 * * 3'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
linkcheck:
|
||||
name: Linkcheck
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: docs.linkcheck.bin
|
||||
|
||||
- name: Run Linkcheck
|
||||
id: linkcheck
|
||||
run: postgrest-docs-linkcheck
|
||||
|
||||
# This actions creates the github token using the postgrest app secrets
|
||||
- name: Create Github App Token (Runs only on linkcheck failure)
|
||||
id: app-token
|
||||
if: ${{ failure() && steps.linkcheck.outcome == 'failure' }} # only create the token on linkcheck failure
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
client-id: ${{ vars.POSTGREST_CI_APP_ID }}
|
||||
private-key: ${{ secrets.POSTGREST_CI_PRIVATE_KEY }}
|
||||
permission-issues: write # required for commenting on issues
|
||||
|
||||
- name: Notify on linkcheck failure by commenting on GH Issue 4106
|
||||
if: ${{ failure() && steps.linkcheck.outcome == 'failure' }}
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
issue-number: 4106
|
||||
body: |
|
||||
**Linkcheck Job Failed!**
|
||||
|
||||
A broken link was detected in the docs. Please check the [failed run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.
|
||||
@@ -0,0 +1,226 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- devel
|
||||
- v*
|
||||
|
||||
concurrency:
|
||||
# Terminate all previous runs of the same workflow for the same tag.
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
uses: ./.github/workflows/build.yaml
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
|
||||
|
||||
github:
|
||||
name: GitHub
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-slim
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Check the version to be released
|
||||
run: |
|
||||
cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
|
||||
|
||||
if [ "${GITHUB_REF_NAME}" != "devel" ] && [ "${GITHUB_REF_NAME}" != "v$cabal_version" ]; then
|
||||
echo "Tagged version ($GITHUB_REF_NAME) does not match the one in postgrest.cabal (v$cabal_version). Aborting release..."
|
||||
exit 1
|
||||
fi
|
||||
- name: Identify changes from CHANGELOG.md
|
||||
run: |
|
||||
if [ "${GITHUB_REF_NAME}" == "devel" ]; then
|
||||
echo "Getting unreleased changes..."
|
||||
sed -n "1,/## Unreleased/d;/## \[/q;p" CHANGELOG.md > CHANGES.md
|
||||
else
|
||||
version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
|
||||
echo "Propper release, getting changes for version $version ..."
|
||||
sed -n "1,/## \[$version\]/d;/## \[/q;p" CHANGELOG.md > CHANGES.md
|
||||
fi
|
||||
|
||||
echo "Relevant extract from CHANGELOG.md:"
|
||||
cat CHANGES.md
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Create release bundle with archives for all builds
|
||||
run: |
|
||||
find artifacts -type f -iname postgrest -exec chmod +x {} \;
|
||||
|
||||
mkdir -p release-bundle
|
||||
|
||||
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-linux-static-aarch64.tar.xz" \
|
||||
-C artifacts/postgrest-linux-static-aarch64 postgrest
|
||||
|
||||
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-linux-static-x86-64.tar.xz" \
|
||||
-C artifacts/postgrest-linux-static-x86-64 postgrest
|
||||
|
||||
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-macos-aarch64.tar.xz" \
|
||||
-C artifacts/postgrest-macos-aarch64 postgrest
|
||||
|
||||
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-macos-x86-64.tar.xz" \
|
||||
-C artifacts/postgrest-macos-x86-64 postgrest
|
||||
|
||||
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-freebsd-x86-64.tar.xz" \
|
||||
-C artifacts/postgrest-freebsd-x86-64 postgrest
|
||||
|
||||
zip --junk-paths "release-bundle/postgrest-${GITHUB_REF_NAME}-windows-x86-64.zip" \
|
||||
artifacts/postgrest-windows-x86-64/postgrest.exe
|
||||
|
||||
- name: Save release bundle
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: release-bundle
|
||||
path: release-bundle
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Publish release on GitHub
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
echo "Releasing version ${GITHUB_REF_NAME} on GitHub..."
|
||||
|
||||
if [ "${GITHUB_REF_NAME}" == "devel" ]; then
|
||||
# To replace the existing release, we must first delete the old assets,
|
||||
# then modify the release, then add the new assets.
|
||||
gh release view devel --json assets \
|
||||
| jq -r '.assets[] | .name' \
|
||||
| xargs -rn1 \
|
||||
gh release delete-asset -y devel
|
||||
gh release edit devel \
|
||||
-t devel \
|
||||
--verify-tag \
|
||||
-F CHANGES.md \
|
||||
--prerelease
|
||||
gh release upload --clobber devel release-bundle/*
|
||||
else
|
||||
gh release create "${GITHUB_REF_NAME}" \
|
||||
-t "${GITHUB_REF_NAME}" \
|
||||
--verify-tag \
|
||||
-F CHANGES.md \
|
||||
release-bundle/*
|
||||
fi
|
||||
|
||||
|
||||
docker:
|
||||
name: Docker Hub
|
||||
runs-on: ubuntu-24.04-arm
|
||||
needs:
|
||||
- github
|
||||
if: |
|
||||
vars.DOCKER_REPO && vars.DOCKER_USER
|
||||
env:
|
||||
DOCKER_REPO: ${{ vars.DOCKER_REPO }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Download aarch64 Docker image
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: postgrest-docker-aarch64
|
||||
- name: Download x86-64 Docker image
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: postgrest-docker-x86-64
|
||||
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
username: ${{ vars.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASS }}
|
||||
- name: Publish images on Docker Hub
|
||||
run: |
|
||||
docker load -i postgrest-docker-aarch64.tar.gz
|
||||
docker tag postgrest:latest "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}-linux-arm64"
|
||||
docker push "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}-linux-arm64"
|
||||
|
||||
docker load -i postgrest-docker-x86-64.tar.gz
|
||||
docker tag postgrest:latest "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}-linux-amd64"
|
||||
docker push "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}-linux-amd64"
|
||||
|
||||
docker manifest create "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}" \
|
||||
"$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}-linux-arm64" \
|
||||
"$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}-linux-amd64"
|
||||
docker manifest push "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}"
|
||||
|
||||
# Only tag 'latest' for full releases
|
||||
if [ "${GITHUB_REF_NAME}" != "devel" ]; then
|
||||
echo "Pushing to 'latest' tag for full release of ${GITHUB_REF_NAME} ..."
|
||||
docker manifest create "$DOCKER_REPO/postgrest:latest" \
|
||||
"$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}-linux-arm64" \
|
||||
"$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}-linux-amd64"
|
||||
docker manifest push "$DOCKER_REPO/postgrest:latest"
|
||||
else
|
||||
echo "Skipping push to 'latest' tag for pre-release..."
|
||||
fi
|
||||
|
||||
- uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0
|
||||
if: github.ref == 'refs/tags/devel'
|
||||
name: Docker Hub Description
|
||||
with:
|
||||
username: ${{ vars.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASS }}
|
||||
repository: ${{ vars.DOCKER_REPO }}/postgrest
|
||||
short-description: ${{ github.event.repository.description }}
|
||||
readme-filepath: ./docker-hub-readme.md
|
||||
|
||||
|
||||
ghcr:
|
||||
name: GitHub Container Registry
|
||||
runs-on: ubuntu-24.04-arm
|
||||
needs:
|
||||
- github
|
||||
permissions:
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Download aarch64 Docker image
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: postgrest-docker-aarch64
|
||||
- name: Download x86-64 Docker image
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: postgrest-docker-x86-64
|
||||
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Publish images on Docker Hub
|
||||
run: |
|
||||
docker load -i postgrest-docker-aarch64.tar.gz
|
||||
docker tag postgrest:latest "ghcr.io/${GITHUB_REPOSITORY,,}:${GITHUB_REF_NAME}-linux-arm64"
|
||||
docker push "ghcr.io/${GITHUB_REPOSITORY,,}:${GITHUB_REF_NAME}-linux-arm64"
|
||||
|
||||
docker load -i postgrest-docker-x86-64.tar.gz
|
||||
docker tag postgrest:latest "ghcr.io/${GITHUB_REPOSITORY,,}:${GITHUB_REF_NAME}-linux-amd64"
|
||||
docker push "ghcr.io/${GITHUB_REPOSITORY,,}:${GITHUB_REF_NAME}-linux-amd64"
|
||||
|
||||
docker manifest create "ghcr.io/${GITHUB_REPOSITORY,,}:${GITHUB_REF_NAME}" \
|
||||
"ghcr.io/${GITHUB_REPOSITORY,,}:${GITHUB_REF_NAME}-linux-arm64" \
|
||||
"ghcr.io/${GITHUB_REPOSITORY,,}:${GITHUB_REF_NAME}-linux-amd64"
|
||||
docker manifest push "ghcr.io/${GITHUB_REPOSITORY,,}:${GITHUB_REF_NAME}"
|
||||
|
||||
# Only tag 'latest' for full releases
|
||||
if [ "${GITHUB_REF_NAME}" != "devel" ]; then
|
||||
echo "Pushing to 'latest' tag for full release of ${GITHUB_REF_NAME} ..."
|
||||
docker manifest create "ghcr.io/${GITHUB_REPOSITORY,,}:latest" \
|
||||
"ghcr.io/${GITHUB_REPOSITORY,,}:${GITHUB_REF_NAME}-linux-arm64" \
|
||||
"ghcr.io/${GITHUB_REPOSITORY,,}:${GITHUB_REF_NAME}-linux-amd64"
|
||||
docker manifest push "ghcr.io/${GITHUB_REPOSITORY,,}:latest"
|
||||
else
|
||||
echo "Skipping push to 'latest' tag for pre-release..."
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN:
|
||||
required: false
|
||||
CODECOV_TOKEN:
|
||||
required: false
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- v[0-9]+
|
||||
paths:
|
||||
- .github/workflows/test.yaml
|
||||
- .github/workflows/report.yaml
|
||||
- .github/actions/setup-nix/**
|
||||
- default.nix
|
||||
- nix/**
|
||||
- flake.lock
|
||||
- .stylish-haskell.yaml
|
||||
- cabal.project
|
||||
- postgrest.cabal
|
||||
- '**.hs'
|
||||
- test/**
|
||||
- '!**.md'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
concurrency:
|
||||
# Terminate all previous runs of the same workflow for pull requests
|
||||
group: test-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
name: Coverage
|
||||
runs-on: ubuntu-24.04
|
||||
defaults:
|
||||
run:
|
||||
# Hack for enabling color output, see:
|
||||
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
||||
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: tests.coverage.bin tests.testDoctests.bin tests.testSpecIdempotence.bin cabalTools.update.bin
|
||||
|
||||
- run: postgrest-cabal-update
|
||||
|
||||
- name: Run coverage (IO tests and Spec tests against latest supported PostgreSQL)
|
||||
run: postgrest-coverage
|
||||
- name: Upload coverage to codecov
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
with:
|
||||
files: ./coverage/codecov.json
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- name: Run doctests
|
||||
if: always()
|
||||
run: nix-shell --run postgrest-test-doctests
|
||||
|
||||
- name: Check the spec tests for idempotence
|
||||
if: always()
|
||||
run: postgrest-test-spec-idempotence
|
||||
|
||||
|
||||
postgres:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Latest version is tested via `coverage` above.
|
||||
pgVersion: [pg-14, pg-15, pg-16, pg-17, oriole-18, pg-18]
|
||||
name: ${{ matrix.pgVersion }}
|
||||
runs-on: ubuntu-24.04
|
||||
defaults:
|
||||
run:
|
||||
# Hack for enabling color output, see:
|
||||
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
||||
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: tests.testSpec.bin tests.testObservability.bin tests.testIO.bin tests.testBigSchema.bin withTools.${{ matrix.pgVersion }}.bin cabalTools.update.bin
|
||||
|
||||
- run: postgrest-cabal-update
|
||||
|
||||
- name: Run spec tests
|
||||
if: always()
|
||||
run: postgrest-with-${{ matrix.pgVersion }} postgrest-test-spec
|
||||
|
||||
- name: Run observability tests
|
||||
if: always()
|
||||
run: postgrest-with-${{ matrix.pgVersion }} postgrest-test-observability
|
||||
|
||||
- name: Run IO tests
|
||||
if: always()
|
||||
run: postgrest-with-${{ matrix.pgVersion }} postgrest-test-io -vv
|
||||
|
||||
- name: Run IO tests on a big schema
|
||||
if: always()
|
||||
run: postgrest-with-${{ matrix.pgVersion }} postgrest-test-big-schema -vv
|
||||
|
||||
|
||||
memory:
|
||||
name: Memory
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: tests.testMemory.bin cabalTools.update.bin
|
||||
|
||||
- run: postgrest-cabal-update
|
||||
|
||||
- name: Run memory tests
|
||||
run: postgrest-test-memory
|
||||
|
||||
|
||||
loadtest:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
kind: ['mixed', 'jwt-cache']
|
||||
name: Loadtest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: loadtest.loadtestAgainst.bin loadtest.report.bin loadtest.report-load.bin cabalTools.update.bin
|
||||
|
||||
- run: postgrest-cabal-update
|
||||
|
||||
- name: Run loadtest
|
||||
env:
|
||||
TARGET_BRANCH: ${{ github.base_ref || github.ref_name }}
|
||||
run: |
|
||||
if [ "$TARGET_BRANCH" = "main" ]; then
|
||||
latest_tag=$(git tag --sort=-creatordate --list "v*" | head -n1)
|
||||
else
|
||||
latest_tag=$(git tag --merged HEAD --sort=-creatordate "v*" | head -n1)
|
||||
fi
|
||||
postgrest-loadtest-against -k ${{ matrix.kind }} "$TARGET_BRANCH" "$latest_tag"
|
||||
|
||||
- name: Report P50
|
||||
# This step checks whether any red cross indicators (:x:) are present in the step summary.
|
||||
# The loadtest reporter writes them when any of individual steps fails the performance
|
||||
# regression threshold.
|
||||
run: |
|
||||
! (postgrest-loadtest-report -g ${{ matrix.kind }} -p 50 \
|
||||
| tee "$GITHUB_STEP_SUMMARY" \
|
||||
| grep ':x:')
|
||||
|
||||
- name: Report P0
|
||||
if: always()
|
||||
run: |
|
||||
postgrest-loadtest-report -g ${{ matrix.kind }} -p 0 >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Report P90
|
||||
if: always()
|
||||
run: |
|
||||
postgrest-loadtest-report -g ${{ matrix.kind }} -p 90 >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Report P95
|
||||
if: always()
|
||||
run: |
|
||||
postgrest-loadtest-report -g ${{ matrix.kind }} -p 95 >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Report CPU/MEM
|
||||
if: always()
|
||||
run: |
|
||||
postgrest-loadtest-report-load -g ${{ matrix.kind }} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
flake:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runs-on:
|
||||
- macos-14 # aarch64-darwin
|
||||
- ubuntu-24.04 # x86_64-linux
|
||||
- ubuntu-24.04-arm # aarch64-linux
|
||||
name: Flake Check
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- name: Run flake check
|
||||
run: |
|
||||
nix flake check
|
||||
@@ -25,3 +25,9 @@ loadtest
|
||||
.history
|
||||
.docs-build
|
||||
gen_targets.http
|
||||
gen_jwks.json
|
||||
gen_private.json
|
||||
.pytest_cache
|
||||
.ruff_cache
|
||||
postgrest-module-graph.png
|
||||
.ghc.environment.*
|
||||
|
||||
@@ -7,4 +7,4 @@ python:
|
||||
build:
|
||||
os: ubuntu-24.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
python: "3.12"
|
||||
|
||||
@@ -200,7 +200,7 @@ steps:
|
||||
|
||||
# A common setting is the number of columns (parts of) code will be wrapped
|
||||
# to. Different steps take this into account. Default: 80.
|
||||
columns: 70
|
||||
columns: 80
|
||||
|
||||
# By default, line endings are converted according to the OS. You can override
|
||||
# preferred format here.
|
||||
|
||||
@@ -13,26 +13,26 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/neon.jpg">
|
||||
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
|
||||
<img width="296px" src="static/supabase.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/tembo.png">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/neon.jpg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
|
||||
<img width="296px" src="static/supabase.svg">
|
||||
<a href="https://www.bytebase.com/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/bytebase.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -93,6 +93,11 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
|
||||
<img width="222px" src="static/code-build.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/tembo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,10 +1,258 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
All notable changes to this project will be documented in this file. From version `14.0` onwards PostgREST follows a `MAJOR.PATCH` two-part versioning. Only even-numbered MAJOR versions will be released, reserving odd-numbered MAJOR versions for development.
|
||||
|
||||
## Unreleased
|
||||
|
||||
## [16.1] - 2026-08-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- JWT validation uses wrong current time due to a bug in auto-update by @mkleczek in #5159
|
||||
|
||||
## [16.0] - 2026-08-07
|
||||
|
||||
### Changes
|
||||
|
||||
#### HTTP Server
|
||||
|
||||
- [Graceful shutdown](https://docs.postgrest.org/en/v16/references/http_server.html#graceful-shutdown) by @mkleczek, @Vlix in #4702
|
||||
|
||||
- [server-reuseport](https://docs.postgrest.org/en/v16/references/configuration.html#server-reuseport) allows starting multiple PostgREST instances using the same port on supported platforms by @mkleczek in #4703, #4694
|
||||
|
||||
#### Performance
|
||||
|
||||
- Optimize schema cache domain type resolution by using [pg_basetype](https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-CATALOG) on PostgreSQL 17+ by @joelonsql in #4567
|
||||
|
||||
- [Prefer: count=exact](https://docs.postgrest.org/en/v16/references/api/pagination_count.html#exact-count) no longer does a double count on requests that do not use ranges or `db-max-rows` by @laurenceisla in #3957
|
||||
|
||||
- [Prefer: timezone](https://docs.postgrest.org/en/v16/references/api/preferences.html#prefer-timezone) no longer requires the schema cache by @steve-chavez in #5100
|
||||
+ Previously this required caching [pg_timezone_names](https://www.postgresql.org/docs/current/view-pg-timezone-names.html) which was slow in some systems
|
||||
|
||||
#### Integrations
|
||||
|
||||
- PostgREST is now tested to work with [OrioleDB](https://github.com/orioledb/orioledb/) in #4845 by @wolfgangwalther
|
||||
+ See [our guide for running OrioleDB on NixOS](https://docs.postgrest.org/en/v16/integrations/nixos.html)
|
||||
|
||||
#### JWT
|
||||
|
||||
- [JWT Role Extraction](https://docs.postgrest.org/en/v16/references/auth.html#jwt-role-extract) is now more flexible, supporting the standard JSON Path defined in RFC 9535 by @taimoorzaeem in #4984
|
||||
|
||||
#### API
|
||||
|
||||
- [Prefer: timezone](https://docs.postgrest.org/en/v16/references/api/preferences.html#timezone) now supports numeric offsets like `05:00` or `-4` by @steve-chavez in #5100
|
||||
|
||||
- Fix unexpected results when embedding and filtering the same table more than once by @laurenceisla in #4075
|
||||
+ You need to set [url-use-legacy-target-names](https://docs.postgrest.org/en/v16/references/configuration.html#url-use-legacy-target-names) to `false`.
|
||||
|
||||
- Deprecate filters, orders and limits with the name of an embedded table when it has an alias by @steve-chavez, @laurenceisla in #4075
|
||||
+ e.g. `?select=alias:table(*)&table.id=eq.1` will not be possible anymore, use `?select=alias:table(*)&alias.id=eq.1` instead.
|
||||
+ You will see a warning in the logs and a `Warning` header on the client response when this happens.
|
||||
+ You can disable this behavior now by setting `url-use-legacy-target-names = false`.
|
||||
|
||||
- Add `Vary` header to responses by @develop7 in #4609
|
||||
|
||||
- Fix automatic transaction retries on `40001 (serialization_failure)` errors to prevent replication lag by @laurenceisla in #3673
|
||||
|
||||
#### Observability
|
||||
|
||||
- [GHC runtime metrics](https://docs.postgrest.org/en/v16/references/observability.html#ghc-runtime-metrics) by @mkleczek in #4862
|
||||
- [client-error-verbosity](https://docs.postgrest.org/en/v16/references/configuration.html#client-error-verbosity) to customize responses error verbosity by @taimoorzaeem in #4088, #3980, #3824
|
||||
- [log-level](https://docs.postgrest.org/en/v16/references/configuration.html#log-level) config is now reloadable by @taimoorzaeem in #5113
|
||||
- Log error when `db-schemas` config contains schema `pg_catalog` or `information_schema` by @taimoorzaeem in #4359
|
||||
- Log schema cache queries timings on `log-level=debug` by @steve-chavez in #4805
|
||||
|
||||
#### Admin Server
|
||||
|
||||
- [admin-server-unix-socket](https://docs.postgrest.org/en/v16/references/configuration.html#admin-server-unix-socket)/[admin-server-unix-socket-mode](https://docs.postgrest.org/en/v16/references/configuration.html#admin-server-unix-socket-mode) to run the admin server on a unix socket by @wolfgangwalther in #5003
|
||||
- Fix responding with `Something went wrong` on Admin server when under EMFILE by @mkleczek in #5077
|
||||
|
||||
#### Deployment
|
||||
|
||||
- Make executable for aarch64-linux static instead of Ubuntu-based by @wolfgangwalther in #4193
|
||||
- Docker image for aarch64-linux is now built from scratch instead of being Ubuntu-based by @wolfgangwalther in #4193
|
||||
- Besides Docker Hub, docker images are now published to Github Container Registry by @wolfgangwalther in #2836
|
||||
|
||||
#### Schema Cache
|
||||
|
||||
- Fix requests failing when the schema cache fails to reload, when this happens PostgREST will continue serving requests in "best effort" by @mkleczek in #4873 #4869
|
||||
- Fix reporting 503s errors unnecessarily while the schema cache is loading at startup by @mkleczek in #4880
|
||||
- Fix schema cache dump missing RPC transaction isolation level by @taimoorzaeem in #5079
|
||||
|
||||
#### Listener
|
||||
|
||||
- Fix config `db-channel-enabled` not reloading by @taimoorzaeem in #4894
|
||||
|
||||
### Migration to v16
|
||||
|
||||
- Drop support for PostgreSQL EOL version 13 by @wolfgangwalther in #4193
|
||||
+ PostgreSQL 13 end of life was on 2025 ([ref](https://www.postgresql.org/support/versioning/))
|
||||
+ Upgrade your PostgreSQL version to at least 14 to use this new PostgREST version.
|
||||
|
||||
- Fail at startup when `db-schemas` contains schema `pg_catalog` or `information_schema` by @taimoorzaeem in #4359
|
||||
+ Previously it failed at runtime with `PGRST205` on requests related to these schemas.
|
||||
+ Remove `pg_catalog` and `information_schema` from `db-schemas`.
|
||||
|
||||
- `Prefer: timezone` no longer complies with `handling=lenient` and instead always fails by @steve-chavez in #5128
|
||||
+ Supporting this required caching `pg_timezone_names`, which was expensive.
|
||||
+ Ensure your requests always have a valid timezone.
|
||||
|
||||
- `jwt-role-claim-key` no longer uses the JSPath DSL and instead uses JSON Path by @taimoorzaeem in #4984
|
||||
+ Now all config values must start with `$` character.
|
||||
Example: `.roles.read` -> `$.roles.read`
|
||||
+ Keys with special characters, with the exception of `_` char must be quoted.
|
||||
Example: `.roles.write-role` -> `$.roles["write-role"]`
|
||||
+ String comparison operators (`^==`, `==^` and `*==`) are replaced with regular expression search.
|
||||
Example: `.roles[?(@ ^== "postgrest_test_")]` -> `$.roles[?search(@, "^postgrest_test_")]`
|
||||
+ Update the `jwt-role-claim-key` value accoring to the above rules. Also see the syntax reference: [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html#name-jsonpath-syntax-and-semanti).
|
||||
|
||||
## [14.16] - 2026-07-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix admin server crashing without a way to recover by @taimoorzaeem in #5096
|
||||
|
||||
## [14.15] - 2026-07-13
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix admin server dying silently by @Vlix, @mkleczek, @steve-chavez in #5012
|
||||
|
||||
## [14.14] - 2026-06-29
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix admin server not logging cause of failure by @taimoorzaeem in #5012
|
||||
|
||||
## [14.13] - 2026-06-04
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix connection retrying message in `PGRST000` error by @netqo in #4980
|
||||
+ Remove redundant "Retrying the connection." from message because it is logged separately
|
||||
- Fix request failures when `work_mem` is set on a role by @laurenceisla in #4955
|
||||
|
||||
## [14.12] - 2026-05-20
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix race condition in pool_available metric causing negative values during network instability by @mkleczek in #4622
|
||||
|
||||
## [14.11] - 2026-05-04
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix login with uppercase and mixed case role names by @taimoorzaeem in #4678
|
||||
- Restore Listener query shape so it can be found in `pg_stat_activity` by @mkleczek in #4857 #4859
|
||||
- The LISTEN channel now automatically recovers when it stops working due to a PostgreSQL bug @laurenceisla in #3147
|
||||
- Fix misleading "Functions" name on schema cache summary in startup logs by @taimoorzaeem in #4821
|
||||
|
||||
## [14.10] - 2026-04-16
|
||||
|
||||
### Added
|
||||
|
||||
- Log when the pool is released during schema cache reload on `log-level=debug` by @mkleczek in #4668
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix unnecessary connection pool flushes during schema cache reloading by @mkleczek in #4645
|
||||
|
||||
## [14.9] - 2026-04-10
|
||||
|
||||
### Added
|
||||
|
||||
- Log host, port and pg version of listener database connection by @mkleczek in #4617 #4618
|
||||
|
||||
### Fixed
|
||||
|
||||
- Remove red herring warp logs on default log-level, only emit them on `log-level=debug` by @steve-chavez in #4799
|
||||
|
||||
## [14.8] - 2026-04-03
|
||||
|
||||
### Added
|
||||
|
||||
- Log a `HINT` when the LISTEN channel stops working due to a PostgreSQL bug by @laurenceisla in #4581
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix invalid OpenAPI 2.0 format for integer types (`smallint`, `integer`, `bigint`) by @arturbent0 in #4641
|
||||
|
||||
## [14.7] - 2026-03-20
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix not logging SIGTERM and SIGINT by @steve-chavez in #4728
|
||||
|
||||
## [14.6] - 2026-03-06
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix leaking table and function names when calculating error hint by @taimoorzaeem in #4675
|
||||
|
||||
## [14.5] - 2026-02-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- Don't hide async exceptions in logs by @stevechavez in #4646
|
||||
|
||||
## [14.4] - 2026-01-29
|
||||
|
||||
### Fixed
|
||||
|
||||
- Ensure Listener connections are released by @mkleczek in #4614
|
||||
- Fix incorrectly filtering the returned representation for PATCH requests when using `or/and` filters by @laurenceisla in #3707
|
||||
- Fix listener running with exception masked after first failure by @mkleczek in #4615
|
||||
|
||||
## [14.3] - 2026-01-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix performance and high memory usage of relation hint calculation by @mkleczek in #4462, #4463
|
||||
|
||||
## [14.2] - 2025-12-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `hasSingleUnnamedParam` incorrectly matching functions with named parameters by @joelonsql in #4553
|
||||
+ Functions with a single named parameter (e.g., `foo(data json)`) no longer incorrectly match the single-param fallback, returning a clean `PGRST202` error instead of a confusing PostgreSQL `42883` error.
|
||||
- Fix misleading logs on unsupported PostgreSQL versions by @taimoorzaeem in #4519
|
||||
- Fix regression where the `PGRST103` error response was truncated by @laurenceisla in #4455
|
||||
+ Happened when an `offset` was greater than the rows requested and `Prefer: count=exact` was sent.
|
||||
- Fix not returning `Content-Length` on empty HTTP `201` responses by @laurenceisla in #4518
|
||||
- Fix inaccurate Server-Timing header durations by @steve-chavez in #4522
|
||||
- Fix inaccurate "Schema cache queried" logs by @steve-chavez in #4522
|
||||
|
||||
## [14.1] - 2025-11-05
|
||||
|
||||
## Fixed
|
||||
|
||||
- Fix `db-pre-config` function failing when function names are pg reserved words by @taimoorzaeem in #4380
|
||||
- Fix `server-host=!6` incorrectly binds to IPv4 address by @taimoorzaeem in #3202
|
||||
|
||||
## [14.0] - 2025-10-24
|
||||
|
||||
### Added
|
||||
|
||||
- Bounded JWT cache using the SIEVE algorithm by @mkleczek in #4084
|
||||
+ It now uses a fixed size cache instead of arbitrary sized cache.
|
||||
- Add `--ready` flag for postgrest healthcheck by @taimoorzaeem in #4239
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix not logging OpenAPI queries when `log-query=main-query` is enabled by @steve-chavez in #4226
|
||||
- Fix not logging explain query when `log-query=main-query` is enabled by @steve-chavez in #4319
|
||||
- Fix not logging transaction variables and db-pre-request function when `log-query=main-query` is enabled by @steve-chavez in #3934
|
||||
- Fix not logging the JSON message to stderr on a `PGRST002` error by @laurenceisla in #4129
|
||||
- Fix reloading the Schema Cache unnecessarily on a `PGRST002` error by @laurenceisla in #4367
|
||||
- Fix schema cache loading taking a long time for large schemas by @mkleczek in #4360, #3704
|
||||
|
||||
### Changed
|
||||
|
||||
- Drop support for PostgreSQL EOL version 12 by @wolfgangwalther in #3865
|
||||
- From now on PostgREST will follow a `MAJOR.PATCH` two-part versioning. Only even-numbered MAJOR versions will be released, reserving odd-numbered MAJOR versions for development.
|
||||
- Replaced `jwt-cache-max-lifetime` config with `jwt-cache-max-entries` by @mkleczek in #4084
|
||||
- `log-query` config now takes a boolean instead of a string value by @steve-chavez in #3934
|
||||
|
||||
## [13.0.8] - 2025-10-24
|
||||
|
||||
### Fixed
|
||||
@@ -32,7 +280,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix OpenAPI broken docs link by @taimoorzaeem in #4048
|
||||
- Fix OpenAPI broken docs link by @taimoorzaeem in #4080
|
||||
- Fix OpenAPI specification incorrectly exposing GET methods for volatile functions by @joelonsql in #4174
|
||||
- Fix empty spread embeddings return unexpected SQL error by @taimoorzaeem in #3887
|
||||
- Fix `/metrics` endpoint not responding with `Content-Type` header by @taimoorzaeem in #4271
|
||||
@@ -46,11 +294,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [13.0.3] - 2025-06-16
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `max-affected` preference not failing with RPC when `handling=strict` by @taimoorzaeem in #4100
|
||||
- Fix a property definition's type in OpenAPI not showing the correct base type of a recursive domain by @laurenceisla in #4136
|
||||
|
||||
### Fixed
|
||||
|
||||
## [13.0.2] - 2025-06-02
|
||||
|
||||
### Fixed
|
||||
@@ -118,6 +366,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- #3956, Drop `/config` endpoint of admin server - @steve-chavez
|
||||
+ The endpoint was at risk of being left unprotected when exposing it.
|
||||
+ The accompanying `admin-server-config-enabled` config was also dropped.
|
||||
- #3598, PostgREST now validates the `kid` parameter of the JWT - @wolfgangwalther
|
||||
+ If the JWT contains a ``kid`` parameter, then PostgREST will look for the JSON Web Key in the `jwt-secret`.
|
||||
+ If the JWT doesn't contain a `kid`, the behavior should be backwards compatible. PostgREST will try each key in the `jwt-secret` one by one until it finds one that works.
|
||||
- #3697, #3602, Querying non-existent table now returns `PGRST205` error instead of empty json - @taimoorzaeem
|
||||
- #3600, #3926, Improve JWT errors - @taimoorzaeem
|
||||
+ Return `PGRST301` error when `Bearer` in auth header is sent empty
|
||||
@@ -554,7 +805,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
### Added
|
||||
|
||||
- #1933, #2109, Add a minimal health check endpoint - @steve-chavez
|
||||
+ For enabling this, the `admin-server-port` config must be set explictly
|
||||
+ For enabling this, the `admin-server-port` config must be set explicitly
|
||||
+ A `<host>:<admin_server_port>/live` endpoint is available for checking if postgrest is running on its port/socket. 200 OK = alive, 503 = dead.
|
||||
+ A `<host>:<admin_server_port>/ready` endpoint is available for checking a correct internal state(the database connection plus the schema cache). 200 OK = ready, 503 = not ready.
|
||||
- #1988, Add the current user to the request log on stdout - @DavidLindbom, @wolfgangwalther
|
||||
@@ -1037,7 +1288,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Customize content negotiation per route - @begriffs
|
||||
- Allow using nulls order without explicit order direction - @steve-chavez
|
||||
- Fatal error on postgres unsupported version, format supported version in error message - @steve-chavez
|
||||
- Prevent database memory cosumption by prepared statements caches - @ruslantalpa
|
||||
- Prevent database memory consumption by prepared statements caches - @ruslantalpa
|
||||
- Use specific columns in the RETURNING section - @ruslantalpa
|
||||
- Fix columns alias for RETURNING - @steve-chavez
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# Contributing to PostgREST
|
||||
|
||||
## AI Policy
|
||||
|
||||
We adhere to [Gentoo's AI policy](https://wiki.gentoo.org/wiki/Project:Council/AI_policy):
|
||||
|
||||
> It is expressly forbidden to contribute [...] any content that has been created with the assistance of Natural Language Processing artificial intelligence tools. This motion can be revisited, should a case been made over such a tool that does not pose copyright, ethical and quality concerns.
|
||||
|
||||
You can find more about its rationale [here](https://wiki.gentoo.org/wiki/Project:Council/AI_policy#Rationale).
|
||||
|
||||
## Issues
|
||||
|
||||
For questions on how to use PostgREST, please use
|
||||
[GitHub discussions](https://github.com/PostgREST/postgrest/discussions).
|
||||
|
||||
### Reporting an Issue
|
||||
|
||||
* Make sure you test against the latest [stable release](https://github.com/PostgREST/postgrest/releases/latest)
|
||||
and also against the latest [devel release](https://github.com/PostgREST/postgrest/releases/tag/devel).
|
||||
It is possible we already fixed the bug you're experiencing.
|
||||
|
||||
* Provide steps to reproduce the issue, including your OS version and
|
||||
the specific database schema that you are using.
|
||||
|
||||
* Please include SQL logs for issues involving runtime problems. To obtain logs first
|
||||
[enable logging all statements](http://www.microhowto.info/howto/log_all_queries_to_a_postgresql_server.html),
|
||||
then [find your logs](http://blog.endpoint.com/2014/11/dear-postgresql-where-are-my-logs.html).
|
||||
|
||||
* If your database schema has changed while the PostgREST server is running,
|
||||
[send the server a `SIGUSR1` signal](http://postgrest.org/en/latest/admin.html#schema-reloading) or restart it to ensure the schema cache
|
||||
is not stale. This sometimes fixes apparent bugs.
|
||||
|
||||
## Code
|
||||
|
||||
We have a fully nix-based development environment with many tools for a smooth development workflow available.
|
||||
Check the [development docs](https://github.com/PostgREST/postgrest/blob/main/nix/README.md) on how to set it up and use it.
|
||||
|
||||
* All contributions must pass the tests before being merged. When
|
||||
you create a pull request your code will automatically be tested.
|
||||
|
||||
* All fixes or features must have a test proving the improvement.
|
||||
|
||||
* All features must document the new behavior. Critical fixes that introduce new behavior must be documented too.
|
||||
|
||||
* All code must also pass a [linter](http://community.haskell.org/~ndm/hlint/) and [styler](https://github.com/jaspervdj/stylish-haskell)
|
||||
with no warnings. This helps enforce a uniform style for all committers. Continuous integration will check this as well on every
|
||||
pull request. There are useful tools in the nix-shell that help with checking this locally. You can run `postgrest-check` to do this manually but
|
||||
we recommend adding it to `.git/hooks/pre-commit` as `nix-shell --run postgrest-check` to automatically check this before doing a commit.
|
||||
|
||||
### Running Tests
|
||||
|
||||
For instructions on running tests, see the [development docs](https://github.com/PostgREST/postgrest/blob/main/nix/README.md#testing).
|
||||
|
||||
### Structuring commits in pull requests
|
||||
|
||||
To simplify reviews, make it easy to split pull requests if deemed necessary, and to maintain clean and meaningful history of changes, you will be asked to update your PR if it does not follow the below rules:
|
||||
|
||||
* It must be possible to merge the PR branch into target using `git merge --ff-only`, ie. the source branch must be rebased on top of target.
|
||||
* No merge commits in the source branch.
|
||||
* All commits in the source branch must be self contained, meaning: it should be possible to treat each commit as a separate PR.
|
||||
* Commits in the source branch must contain only related changes (related means the changes target a single problem/goal). For example, any refactorings should be isolated from the actual change implementation into separate commits.
|
||||
* Tests, documentation, and changelog updates should be contained in the same commits as the actual code changes they relate to. An exception to this rule is when test or documentation changes are made in separate PR.
|
||||
* Commit messages must be prefixed with one of the prefixes defined in [the list used by commit verification scripts](https://github.com/PostgREST/postgrest/blob/main/nix/tools/gitTools.nix#L11).
|
||||
* Commit messages should contain a longer description of the purpose of the changes contained in the commit and, for non-trivial changes, a description of the changes themselves.
|
||||
@@ -1,5 +1,4 @@
|
||||
Copyright (c) 2014 Joe Nelson
|
||||
Copyright (c) 2019 Steve Chavez
|
||||
Copyright (c) 2014-2026 The PostgREST contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -22,26 +22,26 @@ API than you are likely to write from scratch.
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/neon.jpg">
|
||||
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
|
||||
<img width="296px" src="static/supabase.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/tembo.png">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/neon.jpg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
|
||||
<img width="296px" src="static/supabase.svg">
|
||||
<a href="https://www.bytebase.com/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/bytebase.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -52,8 +52,8 @@ Big thanks to our sponsors! You can join them by supporting PostgREST on [Patreo
|
||||
|
||||
## Usage
|
||||
|
||||
1. Download the binary ([latest release](https://github.com/PostgREST/postgrest/releases/latest))
|
||||
for your platform.
|
||||
1. See the docs for [how to install PostgREST on your platform](https://docs.postgrest.org/en/stable/explanations/install.html). You can also [use Docker](https://docs.postgrest.org/en/stable/explanations/install.html#docker).
|
||||
|
||||
2. Invoke for help:
|
||||
|
||||
```bash
|
||||
@@ -125,7 +125,7 @@ and limited with - range headers. More about
|
||||
## Data Integrity
|
||||
|
||||
Rather than relying on an Object Relational Mapper and custom
|
||||
imperative coding, this system requires you put declarative constraints
|
||||
imperative coding, this system requires you to put declarative constraints
|
||||
directly into your database. Hence no application can corrupt your
|
||||
data (including your API server).
|
||||
|
||||
@@ -142,6 +142,10 @@ You can help PostgREST ongoing maintenance and development by making a regular d
|
||||
|
||||
Every donation will be spent on making PostgREST better for the whole community.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are always welcome and appreciated. Please see the [Contributing guidelines](https://github.com/PostgREST/postgrest/blob/main/CONTRIBUTING.md).
|
||||
|
||||
## Thanks
|
||||
|
||||
The PostgREST organization is grateful to:
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
packages: postgrest.cabal
|
||||
tests: true
|
||||
package *
|
||||
ghc-options: -split-sections
|
||||
allow-newer:
|
||||
hasql:postgresql-libpq
|
||||
|
||||
-- https://github.com/martijnbastiaan/doctest-parallel/blob/main/example/README.md#cabalproject
|
||||
write-ghc-environment-files: always
|
||||
|
||||
@@ -1 +1 @@
|
||||
index-state: hackage.haskell.org 2025-10-13T04:53:27Z
|
||||
index-state: hackage.haskell.org 2026-08-10T16:58:32Z
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ system ? builtins.currentSystem
|
||||
|
||||
, compiler ? "ghc948"
|
||||
, compiler ? "ghc9123"
|
||||
|
||||
, # Commit of the Nixpkgs repository that we want to use.
|
||||
# It defaults to reading the inputs from flake.lock, which serves
|
||||
@@ -43,9 +43,7 @@ let
|
||||
allOverlays.build-toolbox
|
||||
allOverlays.checked-shell-script
|
||||
allOverlays.gitignore
|
||||
allOverlays.postgresql-libpq
|
||||
(allOverlays.haskell-packages { inherit compiler; })
|
||||
allOverlays.slocat
|
||||
];
|
||||
|
||||
# Evaluated expression of the Nixpkgs repository.
|
||||
@@ -54,16 +52,26 @@ let
|
||||
|
||||
postgresqlVersions =
|
||||
[
|
||||
{ name = "postgresql-17"; postgresql = pkgs.postgresql_17.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-16"; postgresql = pkgs.postgresql_16.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-15"; postgresql = pkgs.postgresql_15.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-14"; postgresql = pkgs.postgresql_14.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-13"; postgresql = pkgs.postgresql_13.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-12"; postgresql = pkgs.postgresql_12.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "pg-19"; postgresql = pkgs.postgresql_19.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "pg-18"; postgresql = pkgs.postgresql_18.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "pg-17"; postgresql = pkgs.postgresql_17.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "pg-16"; postgresql = pkgs.postgresql_16.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "pg-15"; postgresql = pkgs.postgresql_15.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "pg-14"; postgresql = pkgs.postgresql_14.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{
|
||||
name = "oriole-18";
|
||||
postgresql = pkgs.orioledb.withPackages (p: [ p.postgis p.pg_safeupdate ]);
|
||||
config = "
|
||||
default_table_access_method = 'orioledb'
|
||||
shared_preload_libraries = 'orioledb, pg_stat_statements'
|
||||
";
|
||||
}
|
||||
];
|
||||
|
||||
haskellPackages = pkgs.haskell.packages."${compiler}";
|
||||
|
||||
# Dynamic derivation for PostgREST
|
||||
postgrest = pkgs.lib.pipe (pkgs.haskell.packages."${compiler}".callCabal2nix name src { }) [
|
||||
postgrest = pkgs.lib.pipe (haskellPackages.callCabal2nix name src { }) [
|
||||
# To allow ghc-datasize to be used.
|
||||
lib.disableLibraryProfiling
|
||||
# We are never going to use dynamic haskell libraries anyway. "Dynamic" refers to how
|
||||
@@ -75,18 +83,33 @@ let
|
||||
|
||||
# Options passed to cabal in dev tools and tests
|
||||
devCabalOptions =
|
||||
"-f dev --test-show-detail=direct --disable-shared";
|
||||
"-f dev --test-show-detail=direct";
|
||||
|
||||
inherit (pkgs.haskell) lib;
|
||||
|
||||
nixos-lib = import (pkgs.path + "/nixos/lib") { };
|
||||
runTest = postgrest: test: (nixos-lib.runTest {
|
||||
hostPkgs = pkgs;
|
||||
# Replace the top-level `pkgs.postgrest` attribute with our current version on this branch.
|
||||
defaults.nixpkgs.overlays = [ (_: _: { inherit postgrest; }) ];
|
||||
# Speeds up evaluation a little bit; documentation is really not required for tests.
|
||||
defaults.documentation.enable = pkgs.lib.mkDefault false;
|
||||
imports = [ test ];
|
||||
}).config.result;
|
||||
in
|
||||
rec {
|
||||
inherit nixpkgs pkgs;
|
||||
|
||||
# Derivation for the PostgREST Haskell package, including the executable,
|
||||
# libraries and documentation. We disable running the test suite on Nix
|
||||
# builds, as they require a database to be set up.
|
||||
postgrestPackage =
|
||||
lib.dontCheck postgrest;
|
||||
# builds, as they require a database to be set up. We split the binary
|
||||
# into a separate output, so that the default distribution via flake.nix
|
||||
# has a much smaller closure size.
|
||||
postgrestPackage = pkgs.lib.pipe postgrest [
|
||||
lib.dontCheck
|
||||
lib.enableSeparateBinOutput
|
||||
(haskellPackages.generateOptparseApplicativeCompletions [ "postgrest" ])
|
||||
];
|
||||
|
||||
# Profiled dynamic executable.
|
||||
postgrestProfiled = pkgs.lib.pipe postgrestPackage [
|
||||
@@ -103,6 +126,9 @@ rec {
|
||||
inherit (pkgs.haskell.packages."${compiler}") ghcWithPackages;
|
||||
};
|
||||
|
||||
# Used by CI on MacOS
|
||||
inherit (pkgs) nix-build-uncached;
|
||||
|
||||
### Tools
|
||||
|
||||
cabalTools =
|
||||
@@ -113,16 +139,24 @@ rec {
|
||||
|
||||
# Development tools.
|
||||
devTools =
|
||||
pkgs.callPackage nix/tools/devTools.nix { inherit tests style devCabalOptions hsie withTools; };
|
||||
pkgs.callPackage nix/tools/devTools.nix { inherit tests style devCabalOptions hsie; };
|
||||
|
||||
# Documentation tools.
|
||||
docs =
|
||||
pkgs.callPackage nix/tools/docs.nix { };
|
||||
|
||||
# Git tools.
|
||||
gitTools =
|
||||
pkgs.callPackage nix/tools/gitTools.nix { };
|
||||
|
||||
# Load testing tools.
|
||||
loadtest =
|
||||
pkgs.callPackage nix/tools/loadtest.nix { inherit withTools; };
|
||||
|
||||
# Utility for updating the pinned version of Nixpkgs.
|
||||
nixpkgsTools =
|
||||
pkgs.callPackage nix/tools/nixpkgsTools.nix { };
|
||||
|
||||
# Scripts for publishing new releases.
|
||||
release =
|
||||
pkgs.callPackage nix/tools/release.nix { };
|
||||
@@ -147,4 +181,7 @@ rec {
|
||||
# Docker images and loading script.
|
||||
docker =
|
||||
pkgs.callPackage nix/tools/docker { postgrest = postgrestStatic; };
|
||||
|
||||
# NixOS VM tests
|
||||
nixpkgs-nixos-test = runTest postgrestStatic (pkgs.path + "/nixos/tests/postgrest.nix");
|
||||
}
|
||||
|
||||
@@ -19,26 +19,26 @@ write from scratch.
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/neon.jpg">
|
||||
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/supabase.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/tembo.png">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/neon.jpg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://supabase.io?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/supabase.svg">
|
||||
<a href="https://www.bytebase.com/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/bytebase.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -61,5 +61,3 @@ The image is built from scratch using
|
||||
no commands are listed in the image history. See the [PostgREST
|
||||
repository](https://github.com/PostgREST/postgrest/tree/main/nix/tools/docker) for
|
||||
details on the build process and how to inspect the image.
|
||||
|
||||
This does not apply to the arm64 variant, which is based on Ubuntu.
|
||||
|
||||
@@ -40,9 +40,14 @@ database "PostgreSQL" {
|
||||
|
||||
:user:
|
||||
hexagon Proxy
|
||||
:user: .r-> Proxy
|
||||
:user: .r-> Proxy : request with JWT
|
||||
HTTPAPI <.l- Proxy
|
||||
|
||||
hexagon ExternalAuth
|
||||
ExternalAuth -u[hidden]- Proxy
|
||||
:user: .r-> ExternalAuth : login
|
||||
:user: <.r- ExternalAuth : JWT
|
||||
|
||||
:operator: .d-> HTTPADMIN
|
||||
:operator: .d-> CLI
|
||||
|
||||
@@ -51,9 +56,8 @@ PostgreSQL <.developer : "\t"
|
||||
Listener -r.> "PostgreSQL"
|
||||
"Connection Pool" -r.> "PostgreSQL" : "\t\t"
|
||||
|
||||
|
||||
note bottom of Auth
|
||||
Authenticates the user request
|
||||
Validates the JWT
|
||||
end note
|
||||
|
||||
note bottom of ApiRequest
|
||||
@@ -72,6 +76,7 @@ note top of Listener
|
||||
LISTEN session
|
||||
end note
|
||||
|
||||
url of ExternalAuth is [[../explanations/external_auth.html]]
|
||||
url of Admin is [[../references/admin_server.html#admin-server]]
|
||||
url of API is [[../explanations/schema_isolation.html]]
|
||||
url of Auth is [[../references/auth.html#authn]]
|
||||
@@ -82,8 +87,8 @@ url of Authorization is [[../explanations/db_authz.html]]
|
||||
url of CLI is [[../references/cli.html#cli]]
|
||||
url of "Connection Pool" is [[../references/connection_pool.html]]
|
||||
url of Config is [[../references/configuration.html#configuration]]
|
||||
url of HTTPADMIN is [[../explanations/architecture.html#http]]
|
||||
url of HTTPAPI is [[../explanations/architecture.html#http]]
|
||||
url of HTTPADMIN is [[../references/http_server.html]]
|
||||
url of HTTPAPI is [[../references/http_server.html]]
|
||||
url of Listener is [[../references/listener.html#listener]]
|
||||
url of Proxy is [[../explanations/nginx.html]]
|
||||
url of "Schema Cache" is [[../references/schema_cache.html#schema-cache]]
|
||||
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.8 KiB |
@@ -12,7 +12,6 @@
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
@@ -49,14 +48,14 @@ source_suffix = ".rst"
|
||||
# The master toctree document.
|
||||
master_doc = "index"
|
||||
|
||||
# This is overriden by readthedocs with the version tag anyway
|
||||
version = "13.0"
|
||||
# This is overridden by readthedocs with the version tag anyway
|
||||
version = "devel"
|
||||
# To avoid repetition in <title> we set this to an empty string.
|
||||
release = ""
|
||||
|
||||
# General information about the project.
|
||||
project = "PostgREST " + version
|
||||
author = "Joe Nelson, Steve Chavez"
|
||||
author = "The PostgREST contributors"
|
||||
copyright = "2017, " + author
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
@@ -300,6 +299,8 @@ linkcheck_ignore = [
|
||||
# 403 only in CI / GitHub Actions
|
||||
r"https://www.patreon.com/postgrest",
|
||||
r"https://blog.frankel.ch/poor-man-api",
|
||||
r"https://www.cybertec-postgresql.com/.*",
|
||||
r"https://stackoverflow.com/.*",
|
||||
# Odd SSL error
|
||||
r"https://www.dripdepot.com",
|
||||
r"https://www.euronodes.com",
|
||||
|
||||
@@ -34,9 +34,10 @@ Templates
|
||||
Example Apps
|
||||
------------
|
||||
|
||||
* `archtika <https://github.com/thiloho/archtika>`_ - self‑hosted CMS
|
||||
* `archtika <https://github.com/thiloho/archtika>`_ - self-hosted CMS
|
||||
* `delibrium-postgrest <https://gitlab.com/delibrium/delibrium-postgrest/>`_ - example school API and front-end in Vue.js
|
||||
* `ETH-transactions-storage <https://github.com/Adamant-im/ETH-transactions-storage>`_ - indexer for Ethereum to get transaction list by ETH address
|
||||
* `fullstack template <https://github.com/jenstroeger/fullstack-webapp-template>`_ - a complete fullstack webapp template using PG as db and message queue, Python and Dramatiq to implement async jobs, db migrations, test runners, and more.
|
||||
* `general <https://github.com/PierreRochard/general>`_ - example auth back-end
|
||||
* `guild-operators <https://github.com/cardano-community/koios-artifacts/tree/main/files/grest>`_ - example queries and functions that the Cardano Community uses for their Guild Operators' Repository
|
||||
* `PostGUI <https://github.com/priyank-purohit/PostGUI>`_ - React Material UI admin panel
|
||||
@@ -82,6 +83,7 @@ Extensions
|
||||
Client-Side Libraries
|
||||
---------------------
|
||||
|
||||
* `efcore-postgrest <https://github.com/pedro-gilmora/EF.PostgREST.Provider>`_ - C#
|
||||
* `postgrest-csharp <https://github.com/supabase-community/postgrest-csharp>`_ - C#
|
||||
* `postgrest-dart <https://github.com/supabase/postgrest-dart>`_ - Dart
|
||||
* `postgrest-ex <https://github.com/supabase-community/postgrest-ex>`_ - Elixir
|
||||
|
||||
@@ -31,65 +31,60 @@ This section talks briefly about various important modules.
|
||||
Main
|
||||
----
|
||||
|
||||
The starting point of the program is `Main.hs <https://github.com/PostgREST/postgrest/blob/main/main/Main.hs>`_.
|
||||
The starting point of the program is `Main.hs <https://github.com/PostgREST/postgrest/blob/main/src/executable/Main.hs>`_.
|
||||
|
||||
CLI
|
||||
---
|
||||
|
||||
Main then calls `CLI.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/CLI.hs>`_, which is in charge of :ref:`cli`.
|
||||
Main then calls `CLI.hs <https://github.com/PostgREST/postgrest/blob/main/src/library/PostgREST/CLI.hs>`_, which is in charge of :ref:`cli`.
|
||||
|
||||
App
|
||||
---
|
||||
|
||||
`App.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/App.hs>`_ is then in charge of composing the different modules.
|
||||
`App.hs <https://github.com/PostgREST/postgrest/blob/main/src/library/PostgREST/App.hs>`_ is then in charge of composing the different modules.
|
||||
|
||||
Auth
|
||||
----
|
||||
|
||||
`Auth.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Auth.hs>`_ is in charge of :ref:`authn`.
|
||||
`Auth.hs <https://github.com/PostgREST/postgrest/blob/main/src/library/PostgREST/Auth.hs>`_ is in charge of :ref:`authn`.
|
||||
|
||||
Api Request
|
||||
-----------
|
||||
|
||||
`ApiRequest.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/ApiRequest.hs>`_ is in charge of parsing the URL query string (following PostgREST syntax), the request headers, and the request body.
|
||||
`ApiRequest.hs <https://github.com/PostgREST/postgrest/blob/main/src/library/PostgREST/ApiRequest.hs>`_ is in charge of parsing the URL query string (following PostgREST syntax), the request headers, and the request body.
|
||||
|
||||
A request might be rejected at this level if it's invalid. For example when providing an unknown media type to PostgREST or using an unknown HTTP method.
|
||||
|
||||
Plan
|
||||
----
|
||||
|
||||
Using the Schema Cache, `Plan.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Plan.hs>`_ generates an internal AST, filling out-of-band SQL details (like an ``ON CONFLICT (pk)`` clause) required to complete the user request.
|
||||
Using the Schema Cache, `Plan.hs <https://github.com/PostgREST/postgrest/blob/main/src/library/PostgREST/Plan.hs>`_ generates an internal AST, filling out-of-band SQL details (like an ``ON CONFLICT (pk)`` clause) required to complete the user request.
|
||||
|
||||
A request might be rejected at this level if it's invalid. For example when doing resource embedding on a nonexistent resource.
|
||||
|
||||
Query
|
||||
-----
|
||||
|
||||
`Query.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Query.hs>`_ generates the SQL queries (parametrized and prepared) required to satisfy the user request.
|
||||
`Query.hs <https://github.com/PostgREST/postgrest/blob/main/src/library/PostgREST/Query.hs>`_ generates the SQL queries (parametrized and prepared) required to satisfy the user request.
|
||||
|
||||
Only at this stage a connection from the pool might be used.
|
||||
|
||||
Schema Cache
|
||||
------------
|
||||
|
||||
`SchemaCache.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/SchemaCache.hs>`_ is in charge of :ref:`schema_cache`.
|
||||
`SchemaCache.hs <https://github.com/PostgREST/postgrest/blob/main/src/library/PostgREST/SchemaCache.hs>`_ is in charge of :ref:`schema_cache`.
|
||||
|
||||
Config
|
||||
------
|
||||
|
||||
`Config.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Config.hs>`_ is in charge of :ref:`configuration`.
|
||||
`Config.hs <https://github.com/PostgREST/postgrest/blob/main/src/library/PostgREST/Config.hs>`_ is in charge of :ref:`configuration`.
|
||||
|
||||
Admin
|
||||
-----
|
||||
|
||||
`Admin.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Admin.hs>`_ is in charge of the :ref:`admin_server`.
|
||||
|
||||
HTTP
|
||||
----
|
||||
|
||||
The HTTP server is provided by `Warp <https://aosabook.org/en/posa/warp.html>`_.
|
||||
`Admin.hs <https://github.com/PostgREST/postgrest/blob/main/src/library/PostgREST/Admin.hs>`_ is in charge of the :ref:`admin_server`.
|
||||
|
||||
Listener
|
||||
--------
|
||||
|
||||
`Listener.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Listener.hs>`_ is in charge of the :ref:`listener`.
|
||||
`Reload.hs <https://github.com/PostgREST/postgrest/blob/main/src/library/PostgREST/AppState/Reload.hs>`_ is in charge of the :ref:`listener`.
|
||||
|
||||
@@ -13,7 +13,7 @@ A role can be thought of as either a database user, or a group of database users
|
||||
Roles for Each Web User
|
||||
-----------------------
|
||||
|
||||
PostgREST can accommodate either viewpoint. If you treat a role as a single user then the :ref:`jwt_impersonation` does most of what you need. When an authenticated user makes a request PostgREST will switch into the database role for that user, which in addition to restricting queries, is available to SQL through the :code:`current_user` variable.
|
||||
PostgREST can accommodate either viewpoint. If you treat a role as a single user then :ref:`user_impersonation` does most of what you need. When an authenticated user makes a request PostgREST will switch into the database role for that user, which in addition to restricting queries, is available to SQL through the :code:`current_user` variable.
|
||||
|
||||
You can use row-level security to flexibly restrict visibility and access for the current user. Here is an `example <https://www.enterprisedb.com:443/blog/application-users-vs-row-level-security>`_ from Tomas Vondra, a chat table storing messages sent between users. Users can insert rows into it to send messages to other users, and query it to see messages sent to them by other users.
|
||||
|
||||
@@ -163,7 +163,7 @@ Another option is to define the function with the :code:`SECURITY DEFINER` optio
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- login as a user wich has privileges on the private schemas
|
||||
-- login as a user which has privileges on the private schemas
|
||||
|
||||
-- create a sample function
|
||||
create or replace function login(email text, pass text, out token text) as $$
|
||||
|
||||
@@ -16,7 +16,7 @@ Supported PostgreSQL versions
|
||||
=============================
|
||||
|
||||
=============== =================================
|
||||
**Supported** PostgreSQL >= 12
|
||||
**Supported** PostgreSQL >= 14
|
||||
=============== =================================
|
||||
|
||||
PostgREST works with all PostgreSQL versions still `officially supported <https://www.postgresql.org/support/versioning/>`_.
|
||||
@@ -75,8 +75,12 @@ You can get the `official PostgREST Docker image <https://hub.docker.com/r/postg
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# pull the latest version
|
||||
docker pull postgrest/postgrest
|
||||
|
||||
# to pull a particular version, use one of the versions on https://hub.docker.com/r/postgrest/postgrest/tags
|
||||
docker pull postgrest/postgrest:<version>
|
||||
|
||||
To configure the container image, use :ref:`env_variables_config`.
|
||||
|
||||
There are two ways to run the PostgREST container: with an existing external database, or through docker-compose.
|
||||
@@ -142,6 +146,7 @@ To avoid having to install the database at all, you can run both it and the serv
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
PGRST_SERVER_HOST: 0.0.0.0 # necessary for `postgrest --ready` flag to work
|
||||
PGRST_DB_URI: postgres://app_user:password@db:5432/app_db
|
||||
PGRST_OPENAPI_SERVER_PROXY_URI: http://127.0.0.1:3000
|
||||
depends_on:
|
||||
@@ -176,6 +181,23 @@ If you want to have a visual overview of your API in your browser you can add sw
|
||||
|
||||
With this you can see the swagger-ui in your browser on port 8080.
|
||||
|
||||
.. _docker_cpu_contraint:
|
||||
|
||||
Docker Resource Constraints
|
||||
---------------------------
|
||||
|
||||
PostgREST does not support ``--cpus`` `constraint option <https://docs.docker.com/engine/containers/resource_constraints/#configure-the-default-cfs-scheduler>`_.
|
||||
|
||||
As a workaround, you may use the `GHC RTS <https://ghc.gitlab.haskell.org/ghc/doc/users_guide/runtime_control.html#runtime-system-rts-options>`_ ``-N`` option. For instance, to limit it to 2 CPU cores, do:
|
||||
|
||||
.. code::
|
||||
|
||||
# Set environment variable GHCRTS set to "-N2"
|
||||
docker run --rm -p 3000:3000 \
|
||||
-e PGRST_DB_URI="postgres://app_user:password@10.0.0.10/postgres" \
|
||||
-e GHCRTS="-N2"
|
||||
postgrest/postgrest
|
||||
|
||||
.. _build_source:
|
||||
|
||||
Building from Source
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
.. _debugging_performance_pg_stat_statements:
|
||||
|
||||
Debugging Performance with pg_stat_statements
|
||||
=============================================
|
||||
|
||||
This how-to shows how to get a query identifier through PostgREST and then use it to inspect the same query in ``pg_stat_statements``.
|
||||
|
||||
.. important::
|
||||
|
||||
- :ref:`db-plan-enabled` must be enabled in PostgREST.
|
||||
- PostgreSQL 14 or newer with ``pg_stat_statements`` available.
|
||||
|
||||
Get the Query Identifier from PostgREST
|
||||
---------------------------------------
|
||||
|
||||
Request the plan in JSON format with the ``verbose`` option:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/projects?select=id,name&order=id" \
|
||||
-H "Accept: application/vnd.pgrst.plan+json; options=verbose"
|
||||
|
||||
The response will contain a top-level ``Query Identifier`` field:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"Plan": {
|
||||
"Node Type": "Aggregate"
|
||||
},
|
||||
"Query Identifier": -432192689578025496
|
||||
}
|
||||
]
|
||||
|
||||
Look up the query in pg_stat_statements
|
||||
---------------------------------------
|
||||
|
||||
Use that identifier against ``pg_stat_statements``:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
select
|
||||
calls,
|
||||
total_exec_time,
|
||||
mean_exec_time,
|
||||
rows,
|
||||
query
|
||||
from pg_stat_statements
|
||||
where queryid = -432192689578025496;
|
||||
|
||||
.. csv-table::
|
||||
:header: "calls", "total_exec_time", "mean_exec_time", "rows", "query"
|
||||
|
||||
"13", "0.6355850000000001", "0.04889115384615385", "13", "WITH pgrst_source AS (...)"
|
||||
|
||||
This lets you correlate a PostgREST request with PostgreSQL runtime statistics such as:
|
||||
|
||||
- how often the query ran
|
||||
- total and average execution time
|
||||
- how many rows it produced
|
||||
- the normalized SQL text recorded by PostgreSQL
|
||||
@@ -43,7 +43,7 @@ As in :ref:`sql_user_management`, we create the :code:`pgcrypto` and :code:`pgjw
|
||||
CREATE EXTENSION pgcrypto WITH SCHEMA ext_pgcrypto;
|
||||
|
||||
|
||||
Concerning the `pgjwt extension <https://github.com/michelp/pgjwt>`_, please cf. to :ref:`client_auth`.
|
||||
Concerning the `pgjwt extension <https://github.com/michelp/pgjwt>`_, please cf. to :ref:`jwt-from-sql`.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
@@ -157,7 +157,7 @@ Here we use the username instead of the email address to identify a user.
|
||||
Logins
|
||||
~~~~~~
|
||||
|
||||
As described in :ref:`client_auth`, we'll create a JWT token inside our login function. Note that you'll need to adjust the secret key which is hard-coded in this example to a secure (at least thirty-two character) secret of your choosing.
|
||||
As described in :ref:`jwt-from-sql`, we'll create a JWT token inside our login function. Note that you'll need to adjust the secret key which is hard-coded in this example to a secure (at least thirty-two character) secret of your choosing.
|
||||
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
SQL User Management
|
||||
===================
|
||||
|
||||
As mentioned on :ref:`jwt_generation`, an external service can provide user management and coordinate with the PostgREST server using JWT. It’s also possible to support logins entirely through SQL. It’s a fair bit of work, so get ready.
|
||||
As mentioned on :ref:`jwt_generation`, an external service can provide user management and coordinate with the PostgREST server using JWT. It's also possible to support logins entirely through SQL. It's a fair bit of work, so get ready.
|
||||
|
||||
Storing Users and Passwords
|
||||
---------------------------
|
||||
@@ -110,6 +110,8 @@ Then, add ``db-anon-role`` to the configuration file to allow anonymous requests
|
||||
|
||||
db-anon-role = "anon"
|
||||
|
||||
.. _jwt-from-sql:
|
||||
|
||||
JWT from SQL
|
||||
~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -318,144 +318,6 @@ You can insert a new product using a JSON object for the ``extra_info`` column:
|
||||
|
||||
To query and filter the data see :ref:`json_columns` for a complete reference.
|
||||
|
||||
.. _ww_postgis:
|
||||
|
||||
PostGIS
|
||||
-------
|
||||
|
||||
You can use the string representation for `PostGIS <https://postgis.net/>`_ data types such as ``geometry`` or ``geography`` (you need to `install PostGIS <https://postgis.net/documentation/getting_started/>`_ first).
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- Activate the postgis module in the current database
|
||||
create extension if not exists postgis;
|
||||
|
||||
create table coverage (
|
||||
id int primary key,
|
||||
name text unique,
|
||||
area geometry
|
||||
);
|
||||
|
||||
To add areas in polygon format, you can use string representation:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/coverage" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d @- << EOF
|
||||
[
|
||||
{ "id": 1, "name": "small", "area": "SRID=4326;POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))" },
|
||||
{ "id": 2, "name": "big", "area": "SRID=4326;POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))" }
|
||||
]
|
||||
EOF
|
||||
|
||||
Now, when you request the information, PostgREST will automatically cast the ``area`` column into a ``Polygon`` geometry type. Although this is useful, you may need the whole output to be in `GeoJSON <https://geojson.org/>`_ format out of the box, which can be done by including the ``Accept: application/geo+json`` in the request. This will work for PostGIS versions 3.0.0 and up and will return the output as a `FeatureCollection Object <https://www.rfc-editor.org/rfc/rfc7946#section-3.3>`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/coverage" \
|
||||
-H "Accept: application/geo+json"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[[0,0],[1,0],[1,1],[0,1],[0,0]]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"id": 1,
|
||||
"name": "small"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[[0,0],[10,0],[10,10],[0,10],[0,0]]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"id": 2,
|
||||
"name": "big"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
If you need to add an extra property, like the area in square units by using ``st_area(area)``, you could add a generated column to the table and it will appear in the ``properties`` key of each ``Feature``.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
alter table coverage
|
||||
add square_units double precision generated always as ( st_area(area) ) stored;
|
||||
|
||||
In the case that you are using older PostGIS versions, then creating a function is your best option:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create or replace function coverage_geo_collection() returns json as $$
|
||||
select
|
||||
json_build_object(
|
||||
'type', 'FeatureCollection',
|
||||
'features', json_agg(
|
||||
json_build_object(
|
||||
'type', 'Feature',
|
||||
'geometry', st_AsGeoJSON(c.area)::json,
|
||||
'properties', json_build_object('id', c.id, 'name', c.name)
|
||||
)
|
||||
)
|
||||
)
|
||||
from coverage c;
|
||||
$$ language sql;
|
||||
|
||||
Now this query will return the same results:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/rpc/coverage_geo_collection"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[[0,0],[1,0],[1,1],[0,1],[0,0]]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"id": 1,
|
||||
"name": "small"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[[0,0],[10,0],[10,10],[0,10],[0,0]]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"id": 2,
|
||||
"name": "big"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Ranges
|
||||
------
|
||||
|
||||
@@ -609,3 +471,20 @@ You can use other comparative filters and also all the `PostgreSQL special date/
|
||||
"due_date": "2022-02-27T06:00:00-05:00"
|
||||
}
|
||||
]
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<script type="text/javascript">
|
||||
let hash = window.location.hash;
|
||||
|
||||
const redirects = {
|
||||
// PostGIS
|
||||
'#postgis': '../integrations/postgis.html#postgis',
|
||||
};
|
||||
|
||||
let willRedirectTo = redirects[hash];
|
||||
|
||||
if (willRedirectTo) {
|
||||
window.location.href = willRedirectTo;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -38,6 +38,28 @@ Sponsors
|
||||
.. image:: ../static/cybertec.svg
|
||||
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/supabase-dark.svg
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
|
||||
.. container:: img-light
|
||||
|
||||
.. image:: ../static/supabase.svg
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/euronodes.svg
|
||||
:target: https://www.euronodes.com/postgrest
|
||||
|
||||
.. container:: img-light
|
||||
|
||||
.. image:: ../static/euronodes.svg
|
||||
:target: https://www.euronodes.com/postgrest
|
||||
|
||||
|
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/neon-dark.jpg
|
||||
@@ -48,30 +70,15 @@ Sponsors
|
||||
.. image:: ../static/neon.jpg
|
||||
:target: https://neon.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
.. image:: ../static/tembo.png
|
||||
:target: https://www.tembo.io/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
|
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/euronodes.svg
|
||||
:target: https://www.euronodes.com/postgrest
|
||||
.. image:: ../static/bytebase-dark.svg
|
||||
:target: https://www.bytebase.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
.. container:: img-light
|
||||
|
||||
.. image:: ../static/euronodes.svg
|
||||
:target: https://www.euronodes.com/postgrest
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/supabase-dark.svg
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
|
||||
.. container:: img-light
|
||||
|
||||
.. image:: ../static/supabase.svg
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
.. image:: ../static/bytebase.svg
|
||||
:target: https://www.bytebase.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
.. The static/empty.png(created with `convert -size 320x95 xc:#fcfcfc empty.png`) is an ugly workaround
|
||||
to create space and center the logos. It's not easy to layout with restructuredText.
|
||||
@@ -106,10 +113,20 @@ Getting Support
|
||||
|
||||
The project has a friendly and growing community. For discussions, use the Github `discussions page <https://github.com/PostgREST/postgrest/discussions>`_. You can also report or search for bugs/features on the Github `issues <https://github.com/PostgREST/postgrest/issues>`_ page.
|
||||
|
||||
Release Notes
|
||||
-------------
|
||||
Releases
|
||||
--------
|
||||
|
||||
The release notes are published on `PostgREST's GitHub release page <https://github.com/PostgREST/postgrest/releases>`_.
|
||||
PostgREST follows ``MAJOR.PATCH`` two-part versioning:
|
||||
|
||||
- ``MAJOR``: feature release, may deprecate or remove things.
|
||||
- ``PATCH``: fix/security release only, no features and no behavior changes.
|
||||
|
||||
MAJOR releases are published twice a year, with their scope and target dates tracked through `GitHub milestones <https://github.com/PostgREST/postgrest/milestones>`_.
|
||||
PATCH releases are published on an as-needed basis.
|
||||
|
||||
Starting from ``v14.0``, only even-numbered MAJOR versions are released, reserving odd-numbered MAJOR versions for development.
|
||||
|
||||
All releases are published on `PostgREST's GitHub release page <https://github.com/PostgREST/postgrest/releases>`_, along with the corresponding upgrade guides.
|
||||
|
||||
Tutorials
|
||||
---------
|
||||
@@ -257,4 +274,21 @@ Testimonials
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Please see the `Contributing guidelines <https://github.com/PostgREST/postgrest/blob/main/.github/CONTRIBUTING.md>`_ in the main PostgREST repository.
|
||||
Please see the `Contributing guidelines <https://github.com/PostgREST/postgrest/blob/main/CONTRIBUTING.md>`_ in the main PostgREST repository.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<script type="text/javascript">
|
||||
let hash = window.location.hash;
|
||||
|
||||
const redirects = {
|
||||
// Tables and Views
|
||||
'#release-notes': '#releases',
|
||||
};
|
||||
|
||||
let willRedirectTo = redirects[hash];
|
||||
|
||||
if (willRedirectTo) {
|
||||
window.location.href = willRedirectTo;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
NixOS
|
||||
=====
|
||||
|
||||
Nixpkgs contains a `NixOS module to run PostgREST <https://search.nixos.org/options?channel=unstable&query=services.postgrest&type=options>`_, which can be enabled with ``services.postgrest.enable = true``.
|
||||
|
||||
A PostgreSQL server can be enabled on the same machine with ``services.postgresql.enable = true``. Connections will use the name of the system user as user and database names by default, in this case ``postgrest``.
|
||||
|
||||
A minimal example could look like this:
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
initialScript = pkgs.writeText "init.sql" ''
|
||||
CREATE ROLE postgrest LOGIN NOINHERIT;
|
||||
CREATE ROLE anon ROLE postgrest;
|
||||
'';
|
||||
};
|
||||
|
||||
services.postgrest = {
|
||||
enable = true;
|
||||
settings.db-anon-role = "anon";
|
||||
settings.db-uri.dbname = "postgres";
|
||||
};
|
||||
}
|
||||
|
||||
This will expose the PostgREST server on localhost on the NixOS machine and allow anonymous access.
|
||||
|
||||
.. tip::
|
||||
NixOS also allows to quickly spin up different PostgreSQL versions or even forks this way. For example, to test the current beta version of `OrioleDB <https://www.orioledb.com>`_, use ``services.postgresql.package = pkgs.orioledb``.
|
||||
@@ -0,0 +1,154 @@
|
||||
.. _ww_postgis:
|
||||
|
||||
PostGIS
|
||||
=======
|
||||
|
||||
To work with `PostGIS <https://postgis.net/>`_ data types such as ``geometry`` or ``geography``, you'll need to `install PostGIS <https://postgis.net/documentation/getting_started/>`_ first.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- Activate the postgis module in the current database
|
||||
create extension if not exists postgis;
|
||||
|
||||
create table coverage (
|
||||
id int primary key,
|
||||
name text unique,
|
||||
area geometry
|
||||
);
|
||||
|
||||
insert into coverage (id, name, area) values
|
||||
(1, 'small', ST_GeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))',4326)),
|
||||
(2, 'big', ST_GeomFromText('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', 4326);
|
||||
|
||||
.. _application/geo+json:
|
||||
|
||||
``application/geo+json``
|
||||
------------------------
|
||||
|
||||
PostgREST supports the `standard <https://www.iana.org/assignments/media-types/application/geo+json>`_ ``application/geo+json`` media type which can be used to get the output in `GeoJSON <https://geojson.org/>`_ format. This will work for PostGIS versions 3.0.0 and up and will return the output as a `FeatureCollection Object <https://www.rfc-editor.org/rfc/rfc7946#section-3.3>`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/coverage" \
|
||||
-H "Accept: application/geo+json"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[[0,0],[1,0],[1,1],[0,1],[0,0]]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"id": 1,
|
||||
"name": "small"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[[0,0],[10,0],[10,10],[0,10],[0,0]]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"id": 2,
|
||||
"name": "big"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Using generated columns
|
||||
-----------------------
|
||||
|
||||
If you need to add an extra property, like the area in square units by using ``st_area(area)``, you could add a generated column to the table and it will appear in the ``properties`` key of each ``Feature``.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
alter table coverage
|
||||
add square_units double precision generated always as ( st_area(area) ) stored;
|
||||
|
||||
In the case that you are using older PostGIS versions, then creating a function is your best option:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create or replace function coverage_geo_collection() returns json as $$
|
||||
select
|
||||
json_build_object(
|
||||
'type', 'FeatureCollection',
|
||||
'features', json_agg(
|
||||
json_build_object(
|
||||
'type', 'Feature',
|
||||
'geometry', st_AsGeoJSON(c.area)::json,
|
||||
'properties', json_build_object('id', c.id, 'name', c.name)
|
||||
)
|
||||
)
|
||||
)
|
||||
from coverage c;
|
||||
$$ language sql;
|
||||
|
||||
Now this query will return the same results:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/rpc/coverage_geo_collection"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[[0,0],[1,0],[1,1],[0,1],[0,0]]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"id": 1,
|
||||
"name": "small"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[[0,0],[10,0],[10,10],[0,10],[0,0]]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"id": 2,
|
||||
"name": "big"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Using string representation
|
||||
---------------------------
|
||||
|
||||
To insert areas in polygon format, you can use string representation:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/coverage" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d @- << EOF
|
||||
[
|
||||
{ "id": 3, "name": "strip", "area": "SRID=4326;POLYGON((0 0, 50 0, 50 2, 0 2, 0 0))" },
|
||||
{ "id": 4, "name": "diamond", "area": "SRID=4326;POLYGON((5 0, 10 5, 5 10, 0 5, 5 0))" }
|
||||
]
|
||||
EOF
|
||||
|
||||
PostgREST will automatically cast the ``area`` column into a ``Polygon`` geometry type.
|
||||
@@ -1,9 +1,11 @@
|
||||
personal_ws-1.1 en 0 utf-8
|
||||
api
|
||||
autoscaling
|
||||
API's
|
||||
APIs
|
||||
APISIX
|
||||
AST
|
||||
async
|
||||
aud
|
||||
Auth
|
||||
auth
|
||||
@@ -14,6 +16,7 @@ BOM
|
||||
Bytea
|
||||
Cardano
|
||||
cd
|
||||
CDNs
|
||||
centric
|
||||
CLI
|
||||
CMS
|
||||
@@ -28,10 +31,11 @@ CSV
|
||||
durations
|
||||
DDL
|
||||
DOM
|
||||
DSL
|
||||
DevOps
|
||||
Dramatiq
|
||||
dockerize
|
||||
enum
|
||||
ECS
|
||||
Enums
|
||||
Entra
|
||||
eq
|
||||
@@ -41,7 +45,10 @@ EveryLayout
|
||||
filename
|
||||
FreeBSD
|
||||
fts
|
||||
fullstack
|
||||
GC
|
||||
GeoJSON
|
||||
GHC
|
||||
Github
|
||||
Google
|
||||
grantor
|
||||
@@ -70,7 +77,6 @@ isdistinct
|
||||
JS
|
||||
js
|
||||
JSON
|
||||
JSPath
|
||||
JWK
|
||||
JWT
|
||||
jwt
|
||||
@@ -93,6 +99,7 @@ namespaced
|
||||
Nanos
|
||||
neq
|
||||
nginx
|
||||
NixOS
|
||||
nixpkgs
|
||||
npm
|
||||
nxl
|
||||
@@ -138,11 +145,13 @@ Redux
|
||||
refactor
|
||||
reloadable
|
||||
Reloadable
|
||||
reuseport
|
||||
requester's
|
||||
RESTful
|
||||
RLS
|
||||
RPC
|
||||
RSA
|
||||
RTS
|
||||
safeupdate
|
||||
savepoint
|
||||
schemas
|
||||
@@ -188,9 +197,12 @@ verifier
|
||||
versioning
|
||||
Vondra
|
||||
Vue
|
||||
webapp
|
||||
webhooks
|
||||
websearch
|
||||
Websockets
|
||||
webuser
|
||||
wfts
|
||||
www
|
||||
debouncing
|
||||
deduplicates
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
Admin Server
|
||||
############
|
||||
|
||||
PostgREST provides an admin server that can be enabled by setting :ref:`admin-server-port`.
|
||||
PostgREST provides an admin server that can be enabled by setting :ref:`admin-server-port` or :ref:`admin-server-unix-socket`.
|
||||
|
||||
Multiple PostgREST instances can share the same public API host and port when :ref:`server-reuseport` is enabled. Admin ports are not shared: give each instance a different :ref:`admin-server-port`, otherwise the new instance will fail to start.
|
||||
|
||||
.. _health_check:
|
||||
|
||||
@@ -72,5 +74,4 @@ Provides the ``schema_cache`` endpoint that prints the runtime :ref:`schema_cach
|
||||
"dbRepresentations": ["..."],
|
||||
"dbRoutines": ["..."],
|
||||
"dbTables": ["..."],
|
||||
"dbTimezones": ["..."]
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ PostgREST exposes three database objects of a schema as resources: tables, views
|
||||
api/aggregate_functions.rst
|
||||
api/openapi.rst
|
||||
api/preferences.rst
|
||||
api/vary_header.rst
|
||||
api/*
|
||||
|
||||
.. raw:: html
|
||||
|
||||
@@ -173,4 +173,4 @@ Domain Representations avoid all the above drawbacks. Their only drawback is tha
|
||||
|
||||
Why not create a `base type <https://www.postgresql.org/docs/current/sql-createtype.html#id-1.9.3.94.5.8>`_ instead? ``CREATE TYPE app_uuid (INTERNALLENGTH = 22, INPUT = app_uuid_parser, OUTPUT = app_uuid_formatter)``.
|
||||
|
||||
Creating base types need superuser, which is restricted on cloud hosted databases. Additionally this way lets “how the data is presented” dictate “how the data is stored” which would be backwards.
|
||||
Creating base types need superuser, which is restricted on cloud hosted databases. Additionally this way lets "how the data is presented" dictate "how the data is stored" which would be backwards.
|
||||
|
||||
@@ -69,6 +69,26 @@ If the function doesn't modify the database, it will also run under the GET meth
|
||||
|
||||
The function parameter names match the JSON object keys in the POST case, for the GET case they match the query parameters ``?a=1&b=2``.
|
||||
|
||||
If the function is defined to have default values for the parameters then arguments for these parameters can be omitted in the request. For instance:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE FUNCTION greet_user(username TEXT DEFAULT 'guest')
|
||||
RETURNS TEXT AS $$
|
||||
SELECT 'Hello ' || username || '!';
|
||||
$$ LANGUAGE SQL IMMUTABLE;
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -i "http://localhost:3000/rpc/greet_user"
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Context-Type: application/json; charset=utf-8
|
||||
|
||||
"Hello guest!"
|
||||
|
||||
.. _function_single_json:
|
||||
|
||||
Functions with an array of JSON objects
|
||||
|
||||
@@ -15,7 +15,7 @@ Using these domains, :ref:`functions <functions>` can become handlers and `user-
|
||||
|
||||
.. important::
|
||||
|
||||
- PostgREST vendor media types (``application/vnd.pgrst.plan``, ``application/vnd.pgrst.object`` and ``application/vnd.pgrst.array``) cannot be overriden.
|
||||
- PostgREST vendor media types (``application/vnd.pgrst.plan``, ``application/vnd.pgrst.object`` and ``application/vnd.pgrst.array``) cannot be overridden.
|
||||
- Long media types like ``application/vnd.openxmlformats-officedocument.wordprocessingml.document`` cannot be expressed as domains since they surpass `PostgreSQL identifier length <https://www.postgresql.org/docs/current/limits.html#LIMITS-TABLE>`_.
|
||||
For these you can use the :ref:`any_handler`.
|
||||
|
||||
|
||||
@@ -62,8 +62,12 @@ The server ignores unrecognized or unfulfillable preferences by default. You can
|
||||
Timezone
|
||||
========
|
||||
|
||||
The ``timezone`` preference allows you to change the `PostgreSQL timezone <https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-TIMEZONE>`_. It accepts all time zones in `pg_timezone_names <https://www.postgresql.org/docs/current/view-pg-timezone-names.html>`_.
|
||||
.. important::
|
||||
|
||||
``handling=lenient`` is ignored for ``timezone``. Invalid time zones always return an error.
|
||||
|
||||
The ``timezone`` preference allows you to change the `PostgreSQL timezone <https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-TIMEZONE>`_.
|
||||
It accepts all time zones in `pg_timezone_names <https://www.postgresql.org/docs/current/view-pg-timezone-names.html>`_ and numeric offsets.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -84,34 +88,35 @@ The ``timezone`` preference allows you to change the `PostgreSQL timezone <https
|
||||
{"t":"2023-10-18T09:37:59.611-07:00"}
|
||||
]
|
||||
|
||||
For an invalid time zone, PostgREST returns values with the default time zone (configured on ``postgresql.conf`` or as a setting on the :ref:`authenticator <roles>`).
|
||||
Offsets are also accepted:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -i "http://localhost:3000/timestamps" \
|
||||
-H "Prefer: timezone=Jupiter/Red_Spot"
|
||||
-H "Prefer: timezone=05:30"
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=utf-8
|
||||
Preference-Applied: timezone=05:30
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{"t":"2023-10-18T12:37:59.611+00:00"},
|
||||
{"t":"2023-10-18T14:37:59.611+00:00"},
|
||||
{"t":"2023-10-18T16:37:59.611+00:00"}
|
||||
{"t":"2023-10-18T17:07:59.611+05:30"},
|
||||
{"t":"2023-10-18T19:07:59.611+05:30"},
|
||||
{"t":"2023-10-18T21:07:59.611+05:30"}
|
||||
]
|
||||
|
||||
Note that there's no ``Preference-Applied`` in the response.
|
||||
You can also use negative offsets like ``-03:00``.
|
||||
|
||||
However, with ``handling=strict``, an invalid time zone preference will throw an :ref:`error <pgrst122>`.
|
||||
For an invalid time zone, PostgREST returns a database error.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -i "http://localhost:3000/timestamps" \
|
||||
-H "Prefer: handling=strict, timezone=Jupiter/Red_Spot"
|
||||
-H "Prefer: timezone=Jupiter/Red_Spot"
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ Since the table name is plural, we can be more accurate by making it singular wi
|
||||
One-to-many relationships
|
||||
-------------------------
|
||||
|
||||
The **foreign key reference** establishes the inverse one-to-many relationship. In this case, ``films`` returns as a JSON array because of the “to-many” end.
|
||||
The **foreign key reference** establishes the inverse one-to-many relationship. In this case, ``films`` returns as a JSON array because of the "to-many" end.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -251,6 +251,12 @@ Computed Relationships
|
||||
|
||||
You can manually define relationships by using functions. This is useful for database objects that can't define foreign keys, like `Foreign Data Wrappers <https://wiki.postgresql.org/wiki/Foreign_data_wrappers>`_.
|
||||
|
||||
Computed relationships have good performance as their intended design enable `function inlining <https://wiki.postgresql.org/wiki/Inlining_of_SQL_functions#Inlining_conditions_for_table_functions>`_.
|
||||
|
||||
.. important::
|
||||
|
||||
- Always use ``SETOF`` when creating computed relationships. Functions can return a table without using ``SETOF``, but bear in mind that PostgreSQL will not inline them. e.g. ``RETURNS <table_name>`` is not inlinable.
|
||||
|
||||
Assuming there's a foreign table ``premieres`` that we want to relate to ``films``.
|
||||
|
||||
.. code-block:: postgres
|
||||
@@ -283,6 +289,10 @@ The name of the function ``film`` is arbitrary and can be used to do the embeddi
|
||||
".."
|
||||
]
|
||||
|
||||
.. warning::
|
||||
|
||||
- Make sure to correctly label the ``to-one`` part of the relationship. When using the ``ROWS 1`` estimation, PostgREST will expect a single row to be returned. If that is not the case, it will unnest the embedding and return repeated values for the top level resource.
|
||||
|
||||
Now let's define the opposite one-to-many relationship.
|
||||
|
||||
.. code-block:: postgres
|
||||
@@ -331,12 +341,6 @@ Thanks to overloaded functions, you can use the same function name for different
|
||||
|
||||
Computed relationships have good performance as their intended design enable `function inlining <https://wiki.postgresql.org/wiki/Inlining_of_SQL_functions#Inlining_conditions_for_table_functions>`_.
|
||||
|
||||
.. warning::
|
||||
|
||||
- Always use ``SETOF`` when creating computed relationships. Functions can return a table without using ``SETOF``, but bear in mind that PostgreSQL will not inline them.
|
||||
|
||||
- Make sure to correctly label the ``to-one`` part of the relationship. When using the ``ROWS 1`` estimation, PostgREST will expect a single row to be returned. If that is not the case, it will unnest the embedding and return repeated values for the top level resource.
|
||||
|
||||
.. _embed_disamb:
|
||||
.. _target_disamb:
|
||||
.. _hint_disamb:
|
||||
@@ -1206,7 +1210,7 @@ Spread on resources forming :ref:`one-to-many <one-to-many>` and :ref:`many-to-m
|
||||
}
|
||||
]
|
||||
|
||||
Note that ``films`` is no longer an array of objects, unlike regularly embedding :ref:`one-to-many`. The embedded columns become arrays and they're correlated—in the above result, we can say that "Pulp Fiction" premiered in 1994 and "Reservoir Dogs" in 1992.
|
||||
Note that ``films`` is no longer an array of objects, unlike regularly embedding :ref:`one-to-many`. The embedded columns become arrays and they're correlated-in the above result, we can say that "Pulp Fiction" premiered in 1994 and "Reservoir Dogs" in 1992.
|
||||
|
||||
Order in spread to-many
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -1240,7 +1244,7 @@ You can order the correlated arrays explicitly. For example, to order by the fil
|
||||
|
||||
.. warning::
|
||||
|
||||
Aliasing spreaded columns is recommended since JSON allows duplicate keys. Example:
|
||||
Aliasing spread columns is recommended since JSON allows duplicate keys. Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -1302,7 +1306,7 @@ You can use multiple spreads at any level. For example, let's spread ``technical
|
||||
|
||||
Note that:
|
||||
|
||||
- All the ``film_*`` arrays are correlated—"Reservoir Dogs" premiered in 1992, its runtime is 1:39:00 and it has the following characters: ``[ "Mr. Pink", "Mr. White" ]``.
|
||||
- All the ``film_*`` arrays are correlated-"Reservoir Dogs" premiered in 1992, its runtime is 1:39:00 and it has the following characters: ``[ "Mr. Pink", "Mr. White" ]``.
|
||||
- The ``film_*`` arrays are ordered by ``year`` (due to ``films.order=year``).
|
||||
- The bottom level array ``film_characters`` is ordered (due to ``films.roles.order=character``).
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ Builtin handlers are offered for common standard media types.
|
||||
|
||||
* ``text/csv`` and ``application/json``, for all API endpoints. See :ref:`tables_views` and :ref:`functions`.
|
||||
* ``application/openapi+json``, for the root endpoint. See :ref:`open-api`.
|
||||
* ``application/geo+json``, see :ref:`ww_postgis`.
|
||||
* ``application/geo+json``, see :ref:`application/geo+json`.
|
||||
* ``*/*``, resolves to ``application/json`` for API endpoints and to ``application/openapi+json`` for the root endpoint.
|
||||
|
||||
The following vendor media types handlers are also supported.
|
||||
|
||||
@@ -5,6 +5,10 @@ Schemas
|
||||
|
||||
PostgREST can expose a single or multiple schema's tables, views and functions. The :ref:`active database role <roles>` must have the usage privilege on the schemas to access them.
|
||||
|
||||
.. important::
|
||||
|
||||
``pg_catalog`` and ``information_schema`` are not allowed in :ref:`db-schemas`. This is done to prevent leaking sensitive information and hence they cannot be accessed directly. If you wish to expose objects of these schemas, expose another schema that contains wrapper views or functions over ``pg_catalog`` or ``information_schema`` objects.
|
||||
|
||||
Single schema
|
||||
-------------
|
||||
|
||||
|
||||
@@ -217,6 +217,15 @@ This allows using the ``fts`` operator on ``text`` and ``json`` types out of the
|
||||
curl --get "http://localhost:3000/people" \
|
||||
-d "my_json_column=not.phfts(english).The%20Fat%20Cats"
|
||||
|
||||
.. important::
|
||||
|
||||
To ensure this operation is fast, you need to create an index on the expression:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE INDEX idx_people_col ON people
|
||||
USING GIN (to_tsvector('french', my_text_column));
|
||||
|
||||
.. _v_filter:
|
||||
|
||||
Vertical Filtering
|
||||
@@ -630,7 +639,7 @@ However, it can work with surrogate primary keys (e.g. ``id serial primary key``
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/employees?colums=id,name,salary" \
|
||||
curl "http://localhost:3000/employees?columns=id,name,salary" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-H "Prefer: resolution=merge-duplicates, missing=default" \
|
||||
-d @- << EOF
|
||||
|
||||
@@ -14,7 +14,7 @@ Custom Queries
|
||||
|
||||
The PostgREST URL grammar limits the kinds of queries clients can perform. It prevents arbitrary, potentially poorly constructed and slow client queries. It's good for quality of service, but means database administrators must create custom views and functions to provide richer endpoints. The most common causes for custom endpoints are
|
||||
|
||||
* Table unions
|
||||
* SET operators like `UNION, INTERSECT and EXCEPT <https://www.postgresql.org/docs/current/queries-union.html>`_.
|
||||
* More complicated joins than those provided by :ref:`resource_embedding`.
|
||||
* Geo-spatial queries that require an argument, like "points near (lat,lon)"
|
||||
|
||||
@@ -51,7 +51,7 @@ You can request table/columns with spaces in them by percent encoding the spaces
|
||||
Reserved characters
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If filters include PostgREST reserved characters(``,``, ``.``, ``:``, ``()``) you'll have to surround them in percent encoded double quotes ``%22`` for correct processing.
|
||||
If filters include PostgREST reserved characters(``,``, ``.``, ``:``, ``*``, ``(``, ``)``) you'll have to surround them in percent encoded double quotes ``%22`` for correct processing.
|
||||
|
||||
Here ``Hebdon,John`` and ``Williams,Mary`` are values.
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
.. _vary_header:
|
||||
|
||||
Vary Header
|
||||
===========
|
||||
|
||||
In order to assist caching proxies and CDNs, PostgREST includes a ``Vary`` header of value
|
||||
``Accept, Prefer, Range`` in its responses which should fit most of the bills. As any other
|
||||
response header, it's available for override
|
||||
by updating ``response.headers`` GUC variable accordingly, for example:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- Override the Vary header to include Accept, Prefer and X-Test-Vary headers
|
||||
perform set_config('response.headers', '[{"Vary": "Accept, Prefer, X-Test-Vary"}]', true);
|
||||
|
||||
In this case PostgREST will use provided value verbatim.
|
||||
@@ -31,7 +31,7 @@ The authenticator role is used for connecting to the database and should be conf
|
||||
.. _user_impersonation:
|
||||
|
||||
User Impersonation
|
||||
------------------
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The picture below shows how the server handles authentication. If auth succeeds, it switches into the user role specified by the request, otherwise it switches into the anonymous role (if it's set in :ref:`db-anon-role`).
|
||||
|
||||
@@ -43,12 +43,13 @@ This role switching mechanism is called **user impersonation**. In PostgreSQL it
|
||||
|
||||
The impersonated roles will have their settings applied. See :ref:`impersonated_settings`.
|
||||
|
||||
.. _jwt_impersonation:
|
||||
.. _jwt_auth:
|
||||
|
||||
JWT-Based User Impersonation
|
||||
----------------------------
|
||||
JWT Authentication
|
||||
------------------
|
||||
|
||||
We use `JSON Web Tokens <https://jwt.io/>`_ to authenticate API requests, this allows us to be stateless and not require database lookups for verification. As you'll recall a JWT contains a list of cryptographically signed claims. All claims are allowed but PostgREST cares specifically about a claim called role.
|
||||
We use `JSON Web Tokens <https://datatracker.ietf.org/doc/html/rfc7519/>`_ to authenticate API requests, this allows us to be stateless and not require database lookups for verification.
|
||||
As you'll recall a JWT contains a list of cryptographically signed claims. All claims are allowed but PostgREST cares specifically about a claim called role (configurable with :ref:`jwt_role_extract`).
|
||||
|
||||
.. code:: json
|
||||
|
||||
@@ -72,17 +73,10 @@ Note that the database administrator must allow the authenticator role to switch
|
||||
|
||||
If the client included no JWT (or one without a role claim) then PostgREST switches into the anonymous role. The database administrator must set the anonymous role permissions correctly to prevent anonymous users from seeing or changing things they shouldn't.
|
||||
|
||||
.. _jwt_generation:
|
||||
.. _bearer_auth:
|
||||
|
||||
JWT Generation
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
You can create a valid JWT either from inside your database (see :ref:`sql_user_management`) or via an external service (see :ref:`external_auth`).
|
||||
|
||||
.. _client_auth:
|
||||
|
||||
Client Auth
|
||||
~~~~~~~~~~~
|
||||
Bearer Authentication
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To make an authenticated request the client must include an :code:`Authorization` HTTP header with the value :code:`Bearer <jwt>`. For instance:
|
||||
|
||||
@@ -93,24 +87,29 @@ To make an authenticated request the client must include an :code:`Authorization
|
||||
|
||||
The ``Bearer`` header value can be used with or without capitalization(``bearer``).
|
||||
|
||||
.. _jwt_caching:
|
||||
.. _jwt_generation:
|
||||
|
||||
JWT Caching
|
||||
-----------
|
||||
JWT Generation
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
PostgREST validates ``JWTs`` on every request. We can cache ``JWTs`` to avoid this performance overhead.
|
||||
You can create a valid JWT either from inside your database (see :ref:`sql_user_management`) or via an external service (see :ref:`external_auth`).
|
||||
|
||||
To enable JWT caching, the config :code:`jwt-cache-max-lifetime` is to be set. It is the maximum number of seconds for which the cache stores the JWT validation results. The cache uses the :code:`exp` claim to set the cache entry lifetime. If the JWT does not have an :code:`exp` claim, it uses the config value. See :ref:`jwt-cache-max-lifetime` for more details.
|
||||
.. _jwt_signature:
|
||||
|
||||
.. note::
|
||||
JWT Signature Verification
|
||||
--------------------------
|
||||
|
||||
You can use the :ref:`server-timing_header` to see the effect of JWT caching.
|
||||
PostgREST supports both symmetric and asymmetric keys for verifying the signature of the token.
|
||||
|
||||
Symmetric Keys
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Each token is cryptographically signed with a secret key. In the case of symmetric cryptography the signer and verifier share the same secret passphrase, which can be configured with :ref:`jwt-secret`.
|
||||
If it is set to a simple string value like “reallyreallyreallyreallyverysafe” then PostgREST interprets it as an HMAC-SHA256 passphrase.
|
||||
In the case of symmetric cryptography the signer and verifier share the same secret passphrase, which can be configured with :ref:`jwt-secret`.
|
||||
If it is set to a simple string then PostgREST interprets it as an HMAC-SHA256 passphrase.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
jwt-secret = "reallyreallyreallyreallyverysafe"
|
||||
|
||||
.. _asym_keys:
|
||||
|
||||
@@ -156,39 +155,39 @@ You can specify the literal value as we saw earlier, or reference a filename to
|
||||
|
||||
jwt-secret = "@rsa.jwk.pub"
|
||||
|
||||
JWK ``kid`` validation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
``kid`` verification
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
PostgREST has built-in validation of the `key ID parameter <https://www.rfc-editor.org/rfc/rfc7517#section-4.5>`_, useful when working with a JWK Set.
|
||||
PostgREST has built-in verification of the `key ID parameter <https://www.rfc-editor.org/rfc/rfc7517#section-4.5>`_, useful when working with a JSON Web Key Set.
|
||||
It goes as follows:
|
||||
|
||||
- If the JWT contains a ``kid`` parameter, then PostgREST will look for the JWK in the :ref:`jwt-secret`.
|
||||
- If the JWT contains a ``kid`` parameter, then PostgREST will look for the JSON Web Key in the :ref:`jwt-secret`.
|
||||
|
||||
+ If no JWK matches the same ``kid`` value (or if they do not have a ``kid``), then the token will be rejected with a :ref:`401 Unauthorized <pgrst301>` error.
|
||||
+ If a JWK matches the ``kid`` value then it will validate the token against that JWK accordingly.
|
||||
+ If no key has a matching ``kid`` (or if they don't have one defined), the token will be rejected with a :ref:`401 Unauthorized <pgrst301>` error.
|
||||
+ If a key matches the ``kid`` value then it will validate the token against that key accordingly.
|
||||
|
||||
- If the JWT does not have a ``kid`` parameter, then PostgREST will validate the token against each JWK in the :ref:`jwt-secret`.
|
||||
- If the JWT doesn't have a ``kid``, PostgREST will try each key in the :ref:`jwt-secret` one by one until it finds one that works.
|
||||
|
||||
.. _jwt_claims_validation:
|
||||
|
||||
JWT Claims Validation
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
---------------------
|
||||
|
||||
PostgREST honors the following `JWT claims <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4>`_:
|
||||
Time-Based claims validation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The time-based JWT claims specified in `RFC 7519 <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4>`_ are validated:
|
||||
|
||||
- ``exp`` Expiration Time
|
||||
- ``iat`` Issued At
|
||||
- ``nbf`` Not Before
|
||||
- ``aud`` :ref:`Audience <jwt_aud_validation>`
|
||||
|
||||
.. note::
|
||||
PostgREST allows for a 30-second clock skew when validating the ``exp``, ``iat`` and ``nbf`` claims.
|
||||
In other words, it gives an extra 30 seconds before the token is rejected if there is a slight discrepancy in the timestamps.
|
||||
We allow a 30-second clock skew when validating the above claims. In other words, we give an extra 30 seconds before the JWT is rejected if there is a slight discrepancy in the timestamps.
|
||||
|
||||
.. _jwt_aud_validation:
|
||||
.. _jwt_aud:
|
||||
|
||||
JWT ``aud`` Claim Validation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
``aud`` validation
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
PostgREST has built-in validation of the `JWT audience claim <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3>`_.
|
||||
It works this way:
|
||||
@@ -201,48 +200,58 @@ It works this way:
|
||||
+ If the match fails or if the ``aud`` value is not a string or array of strings, then the token will be rejected with a :ref:`401 Unauthorized <pgrst303>` error.
|
||||
+ If the ``aud`` key **is not present** or if its value is ``null`` or ``[]``, PostgREST will interpret this token as allowed for all audiences and will complete the request.
|
||||
|
||||
.. _jwt_caching:
|
||||
|
||||
JWT Cache
|
||||
---------
|
||||
|
||||
JWT signature validation (specially :ref:`asym_keys` such as RSA) is slow, we can cache ``JWT`` validation results to avoid this performance overhead.
|
||||
|
||||
The JWT cache is bounded and uses the `SIEVE algorithm <https://cachemon.github.io/SIEVE-website>`_ for efficient eviction. The cache is enabled by default and can be configured with :ref:`jwt-cache-max-entries`.
|
||||
|
||||
It's recommended to leave the JWT cache enabled as our load tests indicate ~20% more throughput for simple GET requests when using it. This while reducing CPU utilization in exchange for a bit more memory.
|
||||
|
||||
:ref:`jwt_cache_metrics` are available.
|
||||
|
||||
.. note::
|
||||
|
||||
- If the ``jwt-secret`` is changed and the config is reloaded, the JWT cache will reset.
|
||||
- JWTs that pass :ref:`jwt_signature` are cached, regardless if they pass :ref:`jwt_claims_validation`. We do this to ensure responses stays fast under common failure cases (such as expired JWTs).
|
||||
- You can use the :ref:`server-timing_header` to see the performance benefit of JWT caching.
|
||||
|
||||
.. _jwt_role_extract:
|
||||
|
||||
JWT Role Extraction
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
-------------------
|
||||
|
||||
A JSPath DSL that specifies the location of the :code:`role` key in the JWT claims. It's configured by :ref:`jwt-role-claim-key`. This can be used to consume a JWT provided by a third party service like Auth0, Okta, Microsoft Entra or Keycloak.
|
||||
A JSON Path (`RFC 9535 <https://www.rfc-editor.org/rfc/rfc9535.html>`_) can be specified for the location of the :code:`role` key in the JWT claims. It's configured by :ref:`jwt-role-claim-key`. This can be used to consume a JWT provided by a third party service like Auth0, Okta, Microsoft Entra or Keycloak.
|
||||
|
||||
The DSL follows the `JSONPath <https://goessner.net/articles/JsonPath/>`_ expression grammar with extended string comparison operators. Supported operators are:
|
||||
|
||||
- ``==`` selects the first array element that exactly matches the right operand
|
||||
- ``!=`` selects the first array element that does not match the right operand
|
||||
- ``^==`` selects the first array element that starts with the right operand
|
||||
- ``==^`` selects the first array element that ends with the right operand
|
||||
- ``*==`` selects the first array element that contains the right operand
|
||||
You can quickly try out JSON Path by visiting https://serdejsonpath.live.
|
||||
|
||||
Usage examples:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# {"postgrest":{"roles": ["other", "author"]}}
|
||||
# the DSL accepts characters that are alphanumerical or one of "_$@" as keys
|
||||
jwt-role-claim-key = ".postgrest.roles[1]"
|
||||
jwt-role-claim-key = "$$.postgrest.roles[1]"
|
||||
|
||||
# {"https://www.example.com/role": { "key": "author" }}
|
||||
# non-alphanumerical characters can go inside quotes(escaped in the config value)
|
||||
jwt-role-claim-key = ".\"https://www.example.com/role\".key"
|
||||
# non-alphanumerical characters can go inside single quotes
|
||||
jwt-role-claim-key = "$$['https://www.example.com/role'].key"
|
||||
|
||||
# {"postgrest":{"roles": ["other", "author"]}}
|
||||
# `@` represents the current element in the array
|
||||
# all the these match the string "author"
|
||||
jwt-role-claim-key = ".postgrest.roles[?(@ == \"author\")]"
|
||||
jwt-role-claim-key = ".postgrest.roles[?(@ != \"other\")]"
|
||||
jwt-role-claim-key = ".postgrest.roles[?(@ ^== \"aut\")]"
|
||||
jwt-role-claim-key = ".postgrest.roles[?(@ ==^ \"hor\")]"
|
||||
jwt-role-claim-key = ".postgrest.roles[?(@ *== \"utho\")]"
|
||||
# filter based on equality or regular expression
|
||||
jwt-role-claim-key = "$$.postgrest.roles[?(@ == 'author')]"
|
||||
jwt-role-claim-key = "$$.postgrest.roles[?search(@, '^au')]"
|
||||
|
||||
.. note::
|
||||
|
||||
The string comparison operators are implemented as a custom extension to the JSPath and does not strictly follow the `RFC 9535 <https://www.rfc-editor.org/rfc/rfc9535.html>`_.
|
||||
- If JSON Path query returns multiple values, the first one gets selected.
|
||||
- Only when using the :ref:`file_config`, all ``$`` characters in the value must be escaped with an additional ``$`` char. For :ref:`env_variables_config` and :ref:`in_db_config`, only use a single ``$`` char.
|
||||
- In our implementation, only the `search()` function from `JSON Path Functions <https://www.rfc-editor.org/rfc/rfc9535.html#name-function-extensions>`_ is available for filtering.
|
||||
|
||||
JWT Security
|
||||
~~~~~~~~~~~~
|
||||
------------
|
||||
|
||||
There are at least three types of common critiques against using JWT: 1) against the standard itself, 2) against using libraries with known security vulnerabilities, and 3) against using JWT for web sessions. We'll briefly explain each critique, how PostgREST deals with it, and give recommendations for appropriate user action.
|
||||
|
||||
@@ -278,3 +287,23 @@ doing custom logic based on the web user info.
|
||||
END IF;
|
||||
END
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<script type="text/javascript">
|
||||
let hash = window.location.hash;
|
||||
|
||||
const redirects = {
|
||||
'#jwt-based-user-impersonation': '#jwt-authentication',
|
||||
'#client-auth': '#bearer-authentication',
|
||||
'#jwt-caching': '#jwt-cache',
|
||||
'#jwk-kid-validation': '#kid-verification',
|
||||
'#jwt-aud-claim-validation': '#aud-validation',
|
||||
};
|
||||
|
||||
let willRedirectTo = redirects[hash];
|
||||
|
||||
if (willRedirectTo) {
|
||||
window.location.href = willRedirectTo;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,23 +3,47 @@
|
||||
CLI
|
||||
===
|
||||
|
||||
PostgREST provides a CLI with the commands listed below:
|
||||
PostgREST provides a CLI with the options listed below:
|
||||
|
||||
.. code:: text
|
||||
|
||||
Usage: postgrest [-v|--version] [-e|--example] [--dump-config | --dump-schema | --ready]
|
||||
[FILENAME]
|
||||
|
||||
PostgREST / create a REST API to an existing Postgres
|
||||
database
|
||||
|
||||
Available options:
|
||||
-h,--help Show this help text
|
||||
-v,--version Show the version information
|
||||
-e,--example Show an example configuration file
|
||||
--dump-config Dump loaded configuration and exit
|
||||
--dump-schema Dump loaded schema as JSON and exit (for debugging,
|
||||
output structure is unstable)
|
||||
--ready Checks the health of PostgREST by doing a request on
|
||||
the admin server /ready endpoint
|
||||
FILENAME Path to configuration file
|
||||
|
||||
FILENAME
|
||||
--------
|
||||
|
||||
Runs PostgREST with the given :ref:`file_config`.
|
||||
|
||||
Help
|
||||
----
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ postgrest [-h|--help]
|
||||
$ postgrest --help
|
||||
|
||||
Shows all the commands available.
|
||||
Shows all the options available.
|
||||
|
||||
Version
|
||||
-------
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ postgrest [-v|--version]
|
||||
$ postgrest --version
|
||||
|
||||
Prints the PostgREST version.
|
||||
|
||||
@@ -28,16 +52,16 @@ Example
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ postgrest [-e|--example]
|
||||
$ postgrest --example
|
||||
|
||||
Shows example configuration options.
|
||||
Shows example configuration settings.
|
||||
|
||||
Dump Config
|
||||
-----------
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ postgrest [--dump-config]
|
||||
$ postgrest --dump-config
|
||||
|
||||
Dumps the loaded :ref:`configuration` values, considering the configuration file, environment variables and :ref:`in_db_config`.
|
||||
|
||||
@@ -46,6 +70,20 @@ Dump Schema
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ postgrest [--dump-schema]
|
||||
$ postgrest --dump-schema
|
||||
|
||||
Dumps the schema cache in JSON format.
|
||||
|
||||
Ready Flag
|
||||
----------
|
||||
|
||||
Makes a request to the ``/ready`` endpoint of the :ref:`admin_server`. It exits with a return code of ``0`` on success and ``1`` on failure.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ postgrest --ready
|
||||
OK: http://localhost:3001/ready
|
||||
|
||||
.. note::
|
||||
|
||||
The ``--ready`` flag cannot be used when :ref:`server-host` is configured with special hostnames. We suggest to change it to ``localhost``.
|
||||
|
||||
@@ -176,6 +176,46 @@ admin-server-port
|
||||
|
||||
Specifies the port for the :ref:`admin_server`. Cannot be equal to :ref:`server-port`.
|
||||
|
||||
.. _admin-server-unix-socket:
|
||||
|
||||
admin-server-unix-socket
|
||||
------------------------
|
||||
|
||||
=============== =================================
|
||||
**Type** String
|
||||
**Default** `n/a`
|
||||
**Reloadable** N
|
||||
**Environment** PGRST_ADMIN_SERVER_UNIX_SOCKET
|
||||
**In-Database** `n/a`
|
||||
=============== =================================
|
||||
|
||||
`Unix domain socket <https://en.wikipedia.org/wiki/Unix_domain_socket>`_ where to bind the :ref:`admin_server`.
|
||||
If specified, this takes precedence over :ref:`admin-server-port`. Example:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
admin-server-unix-socket = "/tmp/pgrst-admin.sock"
|
||||
|
||||
.. _admin-server-unix-socket-mode:
|
||||
|
||||
admin-server-unix-socket-mode
|
||||
-----------------------------
|
||||
|
||||
=============== ===================================
|
||||
**Type** String
|
||||
**Default** 660
|
||||
**Reloadable** N
|
||||
**Environment** PGRST_ADMIN_SERVER_UNIX_SOCKET_MODE
|
||||
**In-Database** `n/a`
|
||||
=============== ===================================
|
||||
|
||||
`Unix file mode <https://en.wikipedia.org/wiki/File_system_permissions>`_ to be set for the socket specified in :ref:`admin-server-unix-socket`
|
||||
Needs to be a valid octal between 600 and 777.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
admin-server-unix-socket-mode = "660"
|
||||
|
||||
.. _app.settings.*:
|
||||
|
||||
app.settings.*
|
||||
@@ -195,6 +235,33 @@ app.settings.*
|
||||
|
||||
The :code:`current_setting` function has `an optional boolean second <https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADMIN-SET>`_ argument to avoid it from raising an error if the value was not defined. Default values to :code:`app.settings` can then be given by combining this argument with :code:`coalesce` and :code:`nullif` : :code:`coalesce(nullif(current_setting('app.settings.my_custom_variable', true), ''), 'default value')`. The use of :code:`nullif` is necessary because if set in a transaction, the setting is sometimes not "rolled back" to :code:`null`. See also :ref:`this section <guc_req_headers_cookies_claims>` for more information on this behaviour.
|
||||
|
||||
.. _client-error-verbosity:
|
||||
|
||||
client-error-verbosity
|
||||
----------------------
|
||||
|
||||
=============== =======================
|
||||
**Type** String
|
||||
**Default** verbose
|
||||
**Reloadable** Y
|
||||
**Environment** PGRST_CLIENT_ERROR_VERBOSITY
|
||||
**In-Database** pgrst.client_error_verbosity
|
||||
=============== =======================
|
||||
|
||||
Specifies the verbosity of PostgREST errors. See :ref:`client_error_verbosity`.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# Return error "code", "message", "details" and "hint"
|
||||
client-error-verbosity = "verbose"
|
||||
|
||||
# Return only "code" and "message"
|
||||
client-error-verbosity = "minimal"
|
||||
|
||||
.. note::
|
||||
|
||||
This setting only affects client side error messages. Server side logs are not affected by this setting.
|
||||
|
||||
.. _db-aggregates-enabled:
|
||||
|
||||
db-aggregates-enabled
|
||||
@@ -264,7 +331,7 @@ db-channel-enabled
|
||||
|
||||
When this is set to :code:`true`, the notification channel specified in :ref:`db-channel` is enabled.
|
||||
|
||||
You should set this to ``false`` when using PostgresSQL behind an external connection pooler such as PgBouncer working in transaction pooling mode. See :ref:`this section <external_connection_poolers>` for more information.
|
||||
You should set this to ``false`` when using PostgreSQL behind an external connection pooler such as PgBouncer working in transaction pooling mode. See :ref:`this section <external_connection_poolers>` for more information.
|
||||
|
||||
.. _db-config:
|
||||
|
||||
@@ -409,7 +476,7 @@ db-pool-max-idletime
|
||||
**In-Database** `n/a`
|
||||
=============== =================================
|
||||
|
||||
*For backwards compatibility, this config parameter is also available as “db-pool-timeout”.*
|
||||
*For backwards compatibility, this config parameter is also available as "db-pool-timeout".*
|
||||
|
||||
Time in seconds to close idle pool connections.
|
||||
|
||||
@@ -479,7 +546,7 @@ db-prepared-statements
|
||||
|
||||
When disabled, the generated queries will be parameterized (invulnerable to SQL injection) but they will not be prepared (cached in the database session). Not using prepared statements will noticeably decrease performance, so it's recommended to always have this setting enabled.
|
||||
|
||||
You should only set this to ``false`` when using PostgresSQL behind an external connection pooler such as PgBouncer working in transaction pooling mode. See :ref:`this section <external_connection_poolers>` for more information.
|
||||
You should only set this to ``false`` when using PostgreSQL behind an external connection pooler such as PgBouncer working in transaction pooling mode. See :ref:`this section <external_connection_poolers>` for more information.
|
||||
|
||||
.. _db-root-spec:
|
||||
|
||||
@@ -526,7 +593,7 @@ db-tx-end
|
||||
**In-Database** pgrst.db_tx_end
|
||||
=============== =================================
|
||||
|
||||
Specifies how to terminate the database transactions.
|
||||
Specifies how to terminate the database transactions. See :ref:`prefer_tx`.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
@@ -603,7 +670,7 @@ jwt-aud
|
||||
**In-Database** pgrst.jwt_aud
|
||||
=============== =================================
|
||||
|
||||
Specifies an audience for the JWT ``aud`` claim. See :ref:`jwt_aud_validation`.
|
||||
Specifies an audience for the JWT ``aud`` claim. See :ref:`jwt_aud`.
|
||||
|
||||
.. _jwt-role-claim-key:
|
||||
|
||||
@@ -612,7 +679,7 @@ jwt-role-claim-key
|
||||
|
||||
=============== =================================
|
||||
**Type** String
|
||||
**Default** .role
|
||||
**Default** $.role
|
||||
**Reloadable** Y
|
||||
**Environment** PGRST_JWT_ROLE_CLAIM_KEY
|
||||
**In-Database** pgrst.jwt_role_claim_key
|
||||
@@ -622,6 +689,10 @@ jwt-role-claim-key
|
||||
|
||||
See :ref:`jwt_role_extract` on how to specify key paths and usage examples.
|
||||
|
||||
.. warning::
|
||||
|
||||
Only when using :ref:`file_config`, the ``$`` char needs to be escaped, so use ``$$`` and PostgREST will interpret it as a single ``$`` character.
|
||||
|
||||
.. _jwt-secret:
|
||||
|
||||
jwt-secret
|
||||
@@ -658,20 +729,20 @@ jwt-secret-is-base64
|
||||
|
||||
When this is set to :code:`true`, the value derived from :code:`jwt-secret` will be treated as a base64 encoded secret.
|
||||
|
||||
.. _jwt-cache-max-lifetime:
|
||||
.. _jwt-cache-max-entries:
|
||||
|
||||
jwt-cache-max-lifetime
|
||||
jwt-cache-max-entries
|
||||
----------------------
|
||||
|
||||
=============== =================================
|
||||
**Type** Int
|
||||
**Default** 0
|
||||
**Default** 1000
|
||||
**Reloadable** Y
|
||||
**Environment** PGRST_JWT_CACHE_MAX_LIFETIME
|
||||
**In-Database** pgrst.jwt_cache_max_lifetime
|
||||
**Environment** PGRST_JWT_CACHE_MAX_ENTRIES
|
||||
**In-Database** pgrst.jwt_cache_max_entries
|
||||
=============== =================================
|
||||
|
||||
Maximum number of seconds of lifetime for cached entries. The default :code:`0` disables caching. See :ref:`jwt_caching`.
|
||||
Maximum number of entries in JWT cache. The value :code:`0` disables JWT caching. See :ref:`jwt_caching`.
|
||||
|
||||
.. _log-level:
|
||||
|
||||
@@ -681,7 +752,7 @@ log-level
|
||||
=============== =================================
|
||||
**Type** String
|
||||
**Default** error
|
||||
**Reloadable** N
|
||||
**Reloadable** Y
|
||||
**Environment** PGRST_LOG_LEVEL
|
||||
**In-Database** `n/a`
|
||||
=============== =================================
|
||||
@@ -714,23 +785,14 @@ log-query
|
||||
---------
|
||||
|
||||
=============== =================================
|
||||
**Type** String
|
||||
**Default** "disabled"
|
||||
**Type** Boolean
|
||||
**Default** False
|
||||
**Reloadable** Y
|
||||
**Environment** PGRST_LOG_QUERY
|
||||
**In-Database** `n/a`
|
||||
=============== =================================
|
||||
|
||||
Logs the SQL query for the corresponding request at the current :ref:`log-level`.
|
||||
See :ref:`sql_query_logs`.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# Logs the main SQL query
|
||||
log-query = "main-query"
|
||||
|
||||
# Disables logging the SQL query
|
||||
log-query = "disabled"
|
||||
Logs the SQL query for the corresponding request at the current :ref:`log-level`. See :ref:`sql_query_logs`.
|
||||
|
||||
.. _openapi-mode:
|
||||
|
||||
@@ -866,6 +928,50 @@ server-port
|
||||
|
||||
The TCP port to bind the web server. Use ``0`` to automatically assign a port.
|
||||
|
||||
.. _server-reuseport:
|
||||
|
||||
server-reuseport
|
||||
----------------
|
||||
|
||||
=============== =================================
|
||||
**Type** Bool
|
||||
**Default** false
|
||||
**Reloadable** N
|
||||
**Environment** PGRST_SERVER_REUSEPORT
|
||||
**In-Database** `n/a`
|
||||
=============== =================================
|
||||
|
||||
Enables ``SO_REUSEPORT`` on the TCP server socket. This allows multiple
|
||||
PostgREST processes to bind to the same :ref:`server-host` and
|
||||
:ref:`server-port` when the operating system supports it.
|
||||
|
||||
For example, two PostgREST processes can use the same configuration:
|
||||
|
||||
.. code:: ini
|
||||
|
||||
server-host = "127.0.0.1"
|
||||
server-port = 3000
|
||||
server-reuseport = true
|
||||
|
||||
New connections are then distributed by the operating system between the
|
||||
running PostgREST processes. This can be used to start a replacement process
|
||||
before stopping the old one, or to run several PostgREST processes behind one
|
||||
port.
|
||||
|
||||
If ``server-reuseport`` is disabled, starting another PostgREST process on
|
||||
the same host and port will fail with the usual address-in-use error.
|
||||
|
||||
Enabling this setting on an operating system that does not support
|
||||
``SO_REUSEPORT`` is a configuration error. PostgREST will fail to start
|
||||
instead of falling back to a normal TCP socket.
|
||||
|
||||
When running multiple PostgREST instances on the same :ref:`server-port`, use
|
||||
a different ``admin-server-port`` for each instance. Admin ports are not shared
|
||||
between instances, so readiness checks always target one specific PostgREST
|
||||
instance.
|
||||
|
||||
This setting does not apply when :ref:`server-unix-socket` is used.
|
||||
|
||||
.. _server-trace-header:
|
||||
|
||||
server-trace-header
|
||||
@@ -936,3 +1042,37 @@ server-unix-socket-mode
|
||||
.. code:: bash
|
||||
|
||||
server-unix-socket-mode = "660"
|
||||
|
||||
.. _url-use-legacy-target-names:
|
||||
|
||||
url-use-legacy-target-names
|
||||
---------------------------
|
||||
|
||||
=============== =================================
|
||||
**Type** Boolean
|
||||
**Default** True
|
||||
**Reloadable** Y
|
||||
**Environment** PGRST_URL_USE_LEGACY_TARGET_NAMES
|
||||
**In-Database** pgrst.url_use_legacy_target_names
|
||||
=============== =================================
|
||||
|
||||
When active, it allows using the the name of an embedded table in filters, orders or limits even if it has an alias:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
curl "http://localhost:3000/table?select=alias:target(*)&target.order=id" -i
|
||||
|
||||
.. code:: text
|
||||
|
||||
Warning: 299 PostgRESTv16 "Embedded resource was referenced by relation name even though it has an alias. This is deprecated and will stop working in a future release. Update `target` to `alias` in query string filters, orders or limits."
|
||||
[...]
|
||||
|
||||
Note that the response includes a deprecation message in the ``Warning`` header.
|
||||
This will also show in the PostgREST logs:
|
||||
|
||||
.. code::
|
||||
|
||||
28/May/2026:20:33:22 -0500: WARNING: Embedded resource was referenced by relation name even though it has an alias. This is deprecated and will stop working in a future release.
|
||||
28/May/2026:20:33:22 -0500: Update filters, orders or limits that use `target` to `alias` in `GET /table?select=alias:target(*)&target.order=id`
|
||||
|
||||
This feature will be removed in a future release, so you should start using the ``alias`` in these cases.
|
||||
|
||||
@@ -15,7 +15,7 @@ Dynamic Connection Pool
|
||||
|
||||
To conserve system resources, PostgREST uses a dynamic connection pool. This enables the number of connections in the pool to increase and decrease depending on request traffic.
|
||||
|
||||
- If all the connections are being used, a new connection is added. The pool can grow until it reaches the :ref:`db-pool` size. Note that it’s pointless to set this higher than the ``max_connections`` setting in your database.
|
||||
- If all the connections are being used, a new connection is added. The pool can grow until it reaches the :ref:`db-pool` size. Note that it's pointless to set this higher than the ``max_connections`` setting in your database.
|
||||
- If a connection is unused for a period of time (:ref:`db-pool-max-idletime`), it will be released.
|
||||
- For connecting to the database, the :ref:`authenticator <roles>` role is used. You can configure this using :ref:`db-uri`.
|
||||
|
||||
@@ -47,6 +47,8 @@ Under a busy system, the :ref:`db-pool-max-idletime` won't be reached and the co
|
||||
To avoid this problem and save resources, a connection max lifetime (:ref:`db-pool-max-lifetime`) is enforced.
|
||||
After the max lifetime is reached, connections from the pool will be released and new ones will be created. This doesn't affect running requests, only unused connections will be released.
|
||||
|
||||
.. _pool_timeout:
|
||||
|
||||
Acquisition Timeout
|
||||
-------------------
|
||||
|
||||
@@ -106,4 +108,4 @@ Also set :ref:`db-channel-enabled` to ``false`` since ``LISTEN`` is not compatib
|
||||
|
||||
.. note::
|
||||
|
||||
It’s not recommended to use an external connection pooler. `Our benchmarks <https://github.com/PostgREST/postgrest/issues/2294#issuecomment-1139148672>`_ indicate it provides much lower performance than PostgREST built-in pool.
|
||||
It's not recommended to use an external connection pooler. `Our benchmarks <https://github.com/PostgREST/postgrest/issues/2294#issuecomment-1139148672>`_ indicate it provides much lower performance than PostgREST built-in pool.
|
||||
|
||||
@@ -199,7 +199,7 @@ Related to the HTTP request elements.
|
||||
| | | :ref:`switching schemas <multiple-schemas>` is not present |
|
||||
| PGRST106 | | in the :ref:`db-schemas` configuration variable. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst107: | 415 | The ``Content-Type`` sent in the request is invalid. |
|
||||
| .. _pgrst107: | 406 | The ``Accept`` media type sent in the request is invalid. |
|
||||
| | | |
|
||||
| PGRST107 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
@@ -329,7 +329,7 @@ Related to the authentication process using JWT. You can follow the :ref:`tut1`
|
||||
| PGRST301 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst302: | 401 | Attempted to do a request without |
|
||||
| | | :ref:`authentication <client_auth>` when the anonymous role |
|
||||
| | | :ref:`bearer_auth` when the anonymous role |
|
||||
| PGRST302 | | is disabled by not setting it in :ref:`db-anon-role`. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst303: | 401 | :ref:`JWT claims validation <jwt_claims_validation>` |
|
||||
@@ -473,3 +473,38 @@ For example, doing a request on a table with high count (say 30_000_000), we get
|
||||
Proxy-Status: PostgREST; error=57014
|
||||
|
||||
The PostgreSQL error code ``57014`` (`ref <https://www.postgresql.org/docs/current/errcodes-appendix.html>`_) reveals that the error is due to a short ``statement_timeout`` value.
|
||||
|
||||
.. _client_error_verbosity:
|
||||
|
||||
Client Error Verbosity
|
||||
======================
|
||||
|
||||
For HTTP clients, the error verbosity can be set via :ref:`client-error-verbosity` config.
|
||||
|
||||
With ``verbose``, it returns ``code``, ``message``, ``details`` and ``hint``.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
curl "localhost:3000/itemsxx"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"code": "PGRST205",
|
||||
"message": "Could not find the table 'public.itemsxx' in the schema cache",
|
||||
"details": "Perhaps you meant the table 'public.items'",
|
||||
"hint": null
|
||||
}
|
||||
|
||||
With ``minimal``, just ``code`` and ``message`` is returned.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
curl "localhost:3000/itemsxx"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"code": "PGRST205",
|
||||
"message": "Could not find the table 'public.itemsxx' in the schema cache"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
.. _http_server:
|
||||
|
||||
HTTP Server
|
||||
###########
|
||||
|
||||
The HTTP server is provided by `Warp <https://aosabook.org/en/posa/warp.html>`_.
|
||||
|
||||
Graceful shutdown
|
||||
-----------------
|
||||
|
||||
PostgREST uses Warp's graceful shutdown, when a ``SIGTERM`` is received:
|
||||
|
||||
- It stops accepting new requests.
|
||||
- Allows requests that are already in progress to finish.
|
||||
- Closes idle ``Keep-Alive`` connections instead of waiting for them to expire.
|
||||
- Responses sent during shutdown indicate that the connection should not be reused (e.g. for HTTP/1.x, it sends ``Connection: close``).
|
||||
|
||||
This allows PostgREST to shut down promptly without interrupting in-flight requests. Useful for zero-downtime upgrades and autoscaling/load-balancing under cloud environments (AWS ECS, Kubernetes).
|
||||
@@ -4,7 +4,7 @@ Listener
|
||||
########
|
||||
|
||||
PostgREST uses `LISTEN <https://www.postgresql.org/docs/current/sql-listen.html>`_ to reload its :ref:`Schema Cache <schema_reloading_notify>` and :ref:`Configuration <config_reloading_notify>` via `NOTIFY <https://www.postgresql.org/docs/current/sql-notify.html>`_.
|
||||
This is useful in environments where you can’t send SIGUSR1 or SIGUSR2 Unix Signals.
|
||||
This is useful in environments where you can't send SIGUSR1 or SIGUSR2 Unix Signals.
|
||||
Like on cloud managed containers or on Windows systems.
|
||||
|
||||
.. code:: postgresql
|
||||
@@ -46,7 +46,9 @@ This will cause the :ref:`connection_pool` to connect to the read replica host a
|
||||
|
||||
.. note::
|
||||
|
||||
Under the hood, PostgREST forces `target_session_attrs=read-write <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-TARGET-SESSION-ATTRS>`_ for the ``LISTEN`` session.
|
||||
- Under the hood, PostgREST forces `target_session_attrs=read-write <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-TARGET-SESSION-ATTRS>`_ for the ``LISTEN`` session.
|
||||
So if you specify ``target_session_attrs=read-only`` as mentioned above, PostgREST will override it for the ``LISTEN``.
|
||||
- ``read-only`` is only available on libpq >= 14, if you use a lower version you will get an error like ``invalid target_session_attrs value: \"read-only\"``.
|
||||
|
||||
.. _listener_automatic_recovery:
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ For diagnostic information about the server itself, PostgREST logs to ``stderr``
|
||||
06/May/2024:08:16:11 -0500: Listening for database notifications on the "pgrst" channel
|
||||
06/May/2024:08:16:11 -0500: Config reloaded
|
||||
06/May/2024:08:16:11 -0500: Schema cache queried in 3.8 milliseconds
|
||||
06/May/2024:08:16:11 -0500: Schema cache loaded 15 Relations, 8 Relationships, 8 Functions, 0 Domain Representations, 4 Media Type Handlers
|
||||
06/May/2024:08:16:11 -0500: Schema cache loaded 15 Relations, 8 Relationships, 8 RPCs, 0 Domain Representations, 4 Media Type Handlers
|
||||
06/May/2024:14:11:27 -0500: Received a config reload message on the "pgrst" channel
|
||||
06/May/2024:14:11:27 -0500: Config reloaded
|
||||
|
||||
@@ -52,14 +52,12 @@ For diagnostic information about the server itself, PostgREST logs to ``stderr``
|
||||
SQL Query Logs
|
||||
--------------
|
||||
|
||||
To log the :ref:`main SQL query <main_query>` executed for a request, set the :ref:`log-query` to ``main-query``.
|
||||
It will be logged based on the current :ref:`log-level` setting.
|
||||
For example, with this configuration:
|
||||
To log the SQL queries executed for a request, set the :ref:`log-query` to ``true``. It will be logged based on the current :ref:`log-level` setting.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
log-level = "warn"
|
||||
log-query = "main-query"
|
||||
log-query = "true"
|
||||
|
||||
The SQL queries will only be logged on ``400`` HTTP errors and up.
|
||||
So, if the user requests a resource without sufficient privileges:
|
||||
@@ -206,6 +204,79 @@ pgrst_db_pool_max
|
||||
|
||||
Max pool connections.
|
||||
|
||||
.. _jwt_cache_metrics:
|
||||
|
||||
JWT Cache Metrics
|
||||
-----------------
|
||||
|
||||
Metrics related to the :ref:`jwt_caching`.
|
||||
|
||||
pgrst_jwt_cache_requests_total
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
======== =======
|
||||
**Type** Counter
|
||||
======== =======
|
||||
|
||||
The total number of JWT cache lookups.
|
||||
|
||||
pgrst_jwt_cache_hits_total
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
======== =======
|
||||
**Type** Counter
|
||||
======== =======
|
||||
|
||||
The total number of JWT cache hits.
|
||||
|
||||
pgrst_jwt_cache_evictions_total
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
======== =======
|
||||
**Type** Counter
|
||||
======== =======
|
||||
|
||||
The total number of JWT cache evictions.
|
||||
|
||||
GHC Runtime Metrics
|
||||
-------------------
|
||||
|
||||
PostgREST can also expose GHC runtime system metrics. These use the ``ghc_*``
|
||||
prefix and include
|
||||
`GHC RTS statistics <https://ghc.gitlab.haskell.org/ghc/doc/libraries/base-4.22.0.0-inplace/GHC-Stats.html#g:1>`_
|
||||
for runtime allocation, garbage collection, memory, and CPU/elapsed time.
|
||||
|
||||
These are useful for monitoring PostgREST process health and diagnosing memory
|
||||
pressure or GC behavior.
|
||||
|
||||
To expose these metrics, enable GHC RTS statistics when starting PostgREST:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
postgrest +RTS -T -RTS
|
||||
|
||||
When enabled, the admin ``/metrics`` endpoint includes samples such as:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
# HELP ghc_gcs_total Total number of GCs
|
||||
# TYPE ghc_gcs_total counter
|
||||
ghc_gcs_total 1
|
||||
# HELP ghc_allocated_bytes_total Total bytes allocated
|
||||
# TYPE ghc_allocated_bytes_total counter
|
||||
ghc_allocated_bytes_total 12345678
|
||||
|
||||
Other available GHC runtime metrics include:
|
||||
|
||||
- ``ghc_gcs_total``
|
||||
- ``ghc_major_gcs_total``
|
||||
- ``ghc_allocated_bytes_total``
|
||||
- ``ghc_max_live_bytes``
|
||||
- ``ghc_max_mem_in_use_bytes``
|
||||
- ``ghc_mutator_cpu_seconds_total``
|
||||
- ``ghc_gc_cpu_seconds_total``
|
||||
- ``ghc_elapsed_seconds_total``
|
||||
|
||||
Traces
|
||||
======
|
||||
|
||||
@@ -265,7 +336,7 @@ This header communicates metrics of the different phases in the request-response
|
||||
Server-Timing: jwt;dur=14.9, parse;dur=71.1, plan;dur=109.0, transaction;dur=353.2, response;dur=4.4
|
||||
|
||||
- All the durations (``dur``) are in milliseconds.
|
||||
- The ``jwt`` stage is when :ref:`jwt_impersonation` is done. This duration can be lowered with :ref:`jwt_caching`.
|
||||
- The ``jwt`` stage is when :ref:`jwt_auth` is done. This duration can be lowered with :ref:`jwt_caching`.
|
||||
- On the ``parse`` stage, the :ref:`url_grammar` is parsed.
|
||||
- On the ``plan`` stage, the :ref:`schema_cache` is used to generate the :ref:`main_query` of the transaction.
|
||||
- The ``transaction`` stage corresponds to the database transaction. See :ref:`transactions`.
|
||||
@@ -359,6 +430,8 @@ By default the plan is assumed to generate the JSON representation of a resource
|
||||
|
||||
The other available parameters are ``analyze``, ``verbose``, ``settings``, ``buffers`` and ``wal``, which correspond to the `EXPLAIN command options <https://www.postgresql.org/docs/current/sql-explain.html>`_. To use the ``analyze`` and ``wal`` parameters for example, you would add them like ``Accept: application/vnd.pgrst.plan; options=analyze|wal``.
|
||||
|
||||
For a workflow that takes the ``Query Identifier`` from a verbose PostgREST plan and uses it to inspect the same query in ``pg_stat_statements``, see :ref:`debugging_performance_pg_stat_statements`.
|
||||
|
||||
Note that akin to the EXPLAIN command, the changes will be committed when using the ``analyze`` option. To avoid this, you can use the :ref:`db-tx-end` and the ``Prefer: tx=rollback`` header.
|
||||
|
||||
Securing the Execution Plan
|
||||
|
||||
@@ -3,10 +3,16 @@
|
||||
Schema Cache
|
||||
============
|
||||
|
||||
PostgREST requires metadata from the database schema to provide a REST API that abstracts SQL details. One example of this is the interface for :ref:`resource_embedding`.
|
||||
PostgREST requires metadata from the database to provide a REST API that abstracts SQL details. One example of this is the interface for :ref:`resource_embedding`.
|
||||
|
||||
Getting this metadata requires expensive queries. To avoid repeating this work, PostgREST uses a schema cache.
|
||||
|
||||
.. note::
|
||||
|
||||
- Schema cache queries have been optimized over time to stay fast, even on complex databases. You can see a summary of their execution time in :ref:`pgrst_logging` and :ref:`metrics`.
|
||||
- If the schema cache queries are slow, the most likely cause is *system catalog bloat*, see `issue#3212 <https://github.com/PostgREST/postgrest/issues/3212>`_ for more details.
|
||||
- You can turn the :ref:`log-level` to ``debug`` to see the time of each schema cache query.
|
||||
|
||||
.. _schema_reloading:
|
||||
|
||||
Schema Cache Reloading
|
||||
@@ -18,7 +24,7 @@ You can do this with UNIX signals or with PostgreSQL notifications. It's also po
|
||||
|
||||
.. note::
|
||||
|
||||
- Requests will wait until the schema cache reload is done. This to prevent client errors due to an stale schema cache.
|
||||
- If the schema cache fails to reload (e.g. due to a ``statement_timeout`` or :ref:`pool timeout <pool_timeout>`), PostgREST will continue serving requests in a "best effort" basis.
|
||||
- If you are using the :ref:`in_db_config`, a schema cache reload will :ref:`reload the configuration<config_reloading>` as well.
|
||||
|
||||
.. _schema_reloading_signals:
|
||||
@@ -53,6 +59,19 @@ To reload the schema cache from within the database, you can use the ``NOTIFY``
|
||||
|
||||
NOTIFY pgrst, 'reload schema'
|
||||
|
||||
Debouncing
|
||||
~~~~~~~~~~
|
||||
|
||||
PostgREST does not reload the schema cache for each notification when several ``NOTIFY pgrst`` events are generated quickly after one another.
|
||||
|
||||
There are two cases to consider: when notifications are sent within a single transaction and when they are sent across multiple transactions.
|
||||
|
||||
In the first case, PostgreSQL deduplicates identical ``NOTIFY`` events within the same transaction. This means that even if multiple ``NOTIFY pgrst`` statements are executed before a ``COMMIT``, only a single notification is delivered to PostgREST.
|
||||
|
||||
In the second case, when notifications are sent from separate transactions in a short time span, PostgREST applies a debouncing mechanism to avoid excessive schema cache reloads.
|
||||
|
||||
Instead of reloading the schema cache for each notification, events are grouped within a small time window of 100 milliseconds. The reload function is executed once immediately when the first notification is received and once more after the burst of events settles, resulting in at most two executions within that time window.
|
||||
|
||||
.. _auto_schema_reloading:
|
||||
|
||||
Automatic Schema Cache Reloading
|
||||
|
||||
@@ -221,7 +221,7 @@ Notice that the ``response.headers`` should be set to an *array* of single-key o
|
||||
|
||||
.. note::
|
||||
|
||||
PostgREST provided headers such as ``Content-Type``, ``Location``, etc. can be overriden this way. Note that irrespective of overridden ``Content-Type`` response header, the content will still be converted to JSON, unless you use :ref:`custom_media`.
|
||||
PostgREST provided headers such as ``Content-Type``, ``Location``, etc. can be overridden this way. Note that irrespective of overridden ``Content-Type`` response header, the content will still be converted to JSON, unless you use :ref:`custom_media`.
|
||||
|
||||
.. _guc_resp_status:
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# This file is auto-generated by postgrest-nixpkgs-upgrade
|
||||
sphinx==7.4.7
|
||||
sphinx==9.1.0
|
||||
sphinx-copybutton==0.5.2
|
||||
sphinx-rtd-dark-mode==1.3.0
|
||||
sphinx-rtd-theme==3.0.2
|
||||
sphinx-tabs==3.4.7
|
||||
sphinxext-opengraph==0.9.1
|
||||
sphinx-rtd-theme==3.1.0
|
||||
sphinx-tabs==3.5.0
|
||||
sphinxext-opengraph==0.13.0
|
||||
@@ -22,7 +22,7 @@ Step 1. Install PostgreSQL
|
||||
|
||||
If you're already familiar with using PostgreSQL and have it installed on your system you can use the existing installation (see :ref:`pg-dependency` for minimum requirements). For this tutorial we'll describe how to use the database in Docker because database configuration is otherwise too complicated for a simple tutorial.
|
||||
|
||||
If Docker is not installed, you can get it `here <https://www.docker.com/get-started>`_. Next, let's pull and start the database image:
|
||||
If Docker is not installed, you can get it `here <https://www.docker.com/get-started>`_. Make sure that Docker service is `started <https://docs.docker.com/engine/daemon/start/#start-the-daemon-using-operating-system-utilities>`_. Next, let's pull and start the database image:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1731165248,
|
||||
"narHash": "sha256-DiHFKIdBmMx5/DUARhVqaxvEIiy4EE6Eqs9Qs4oxme8=",
|
||||
"lastModified": 1784115452,
|
||||
"narHash": "sha256-BoYPdqk6jlKXy+DyUzyGV/CtRGfAhk2MmIgBhsemTGI=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a90280100f41a10914edfe729a4053e60c92b8e3",
|
||||
"rev": "35d3407a3816f3b341d8cf1d60abaf2b7b8166ac",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
in
|
||||
{
|
||||
packages = genSystems (attrs: {
|
||||
default = attrs.postgrestPackage;
|
||||
profiled = attrs.postgrestProfiled;
|
||||
default = attrs.postgrestPackage.bin;
|
||||
profiled = attrs.postgrestProfiled.bin;
|
||||
} // nixpkgs.lib.optionalAttrs (attrs ? postgrestStatic) {
|
||||
static = attrs.postgrestStatic;
|
||||
});
|
||||
@@ -42,9 +42,13 @@
|
||||
apps = genSystems (attrs: {
|
||||
default = {
|
||||
type = "app";
|
||||
program = "${attrs.postgrestStatic or attrs.postgrestPackage}/bin/postgrest";
|
||||
program = "${attrs.postgrestStatic or attrs.postgrestPackage.bin}/bin/postgrest";
|
||||
meta.description = "REST API for any Postgres database";
|
||||
};
|
||||
});
|
||||
|
||||
devShells = genSystems (postgrest: {
|
||||
default = import ./shell.nix { inherit postgrest; };
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,55 +70,16 @@ The PostgREST utilities available in `nix-shell` all have names that begin with
|
||||
`<tab>`) in `nix-shell` to see all that are available:
|
||||
|
||||
```bash
|
||||
# Note: The utilities listed here might not be up to date.
|
||||
[nix-shell]$ postgrest-<tab>
|
||||
postgrest-build postgrest-profiled-run
|
||||
postgrest-check postgrest-push-cachix
|
||||
postgrest-clean postgrest-release
|
||||
postgrest-coverage postgrest-repl
|
||||
postgrest-coverage-draft-overlay postgrest-run
|
||||
postgrest-docs-build postgrest-style
|
||||
postgrest-docs-check postgrest-style-check
|
||||
postgrest-docs-dictcheck postgrest-test-big-schema
|
||||
postgrest-docs-linkcheck postgrest-test-doctests
|
||||
postgrest-docs-render postgrest-test-io
|
||||
postgrest-docs-serve postgrest-test-memory
|
||||
postgrest-docs-spellcheck postgrest-test-replica
|
||||
postgrest-dump-minimal-imports postgrest-test-spec
|
||||
postgrest-dump-schema postgrest-test-spec-idempotence
|
||||
postgrest-gen-ctags postgrest-watch
|
||||
postgrest-gen-jwt postgrest-with-all
|
||||
postgrest-gen-secret postgrest-with-git
|
||||
postgrest-git-hooks postgrest-with-pgrst
|
||||
postgrest-hsie-graph-modules postgrest-with-postgresql-12
|
||||
postgrest-hsie-graph-symbols postgrest-with-postgresql-13
|
||||
postgrest-hsie-minimal-imports postgrest-with-postgresql-14
|
||||
postgrest-lint postgrest-with-postgresql-15
|
||||
postgrest-loadtest postgrest-with-postgresql-16
|
||||
postgrest-loadtest-against postgrest-with-postgresql-17
|
||||
postgrest-loadtest-report postgrest-with-slow-pg
|
||||
postgrest-nixpkgs-upgrade postgrest-with-slow-postgrest
|
||||
postgrest-parallel-curl
|
||||
postgrest-build
|
||||
postgrest-cabal-update
|
||||
postgrest-check
|
||||
postgrest-clean
|
||||
postgrest-commitlint
|
||||
...
|
||||
|
||||
[nix-shell]$
|
||||
|
||||
```
|
||||
|
||||
The `docker` module has large dependencies to be build before the shell becomes
|
||||
available, which could take an especially long time if the cachix binary cache
|
||||
is not used. You can activate it by passing a flag to `nix-shell` with
|
||||
`nix-shell --arg docker true`. This will make the respective utilities available:
|
||||
|
||||
```bash
|
||||
$ nix-shell --arg docker true
|
||||
[nix-shell]$ postgrest-docker-<tab>
|
||||
postgrest-docker-load
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
Note that `postgrest-docker-load` is now also available.
|
||||
Most of these commands provide a `--help` output, make sure to check it out.
|
||||
|
||||
To run one-off commands, you can also use `nix-shell --run <command>`, which
|
||||
will launch the Nix shell, run that one command and exit. Note that the tab
|
||||
@@ -134,16 +95,6 @@ $ nix-shell --run "postgrest-foo --bar"
|
||||
|
||||
```
|
||||
|
||||
A third option is to install utilities that you use very often locally:
|
||||
|
||||
```bash
|
||||
$ nix-env -f default.nix -iA devTools
|
||||
|
||||
# `postgrest-style` can now be run directly:
|
||||
$ postgrest-style
|
||||
|
||||
```
|
||||
|
||||
If you use `nix-shell` very often, you might like to use
|
||||
https://github.com/xzfc/cached-nix-shell, which skips evaluating all our Nix
|
||||
expressions if nothing changed, reducing startup time for the shell
|
||||
@@ -174,7 +125,7 @@ $ nix-shell --run "postgrest-with-all postgrest-test-spec"
|
||||
|
||||
# Run the tests against a specific version of PostgreSQL (use tab-completion in
|
||||
# nix-shell to see all available versions):
|
||||
$ nix-shell --run "postgrest-with-postgresql-13 postgrest-test-spec"
|
||||
$ nix-shell --run "postgrest-with-pg-17 postgrest-test-spec"
|
||||
|
||||
```
|
||||
|
||||
@@ -209,13 +160,7 @@ The loadtests ensure that performance doesn't drop on a change. Underlyingly the
|
||||
[nix-shell]$ postgrest-loadtest
|
||||
|
||||
# You can loadtest comparing to a different branch
|
||||
[nix-shell]$ postgrest-loadtest-against master
|
||||
|
||||
# You can simulate latency client/postgrest and postgrest/database
|
||||
[nix-shell]$ PGRST_DELAY=5ms PGDELAY=5ms postgrest-loadtest
|
||||
|
||||
# You can build postgrest directly with cabal for faster iteration
|
||||
[nix-shell]$ PGRST_BUILD_CABAL=1 postgrest-loadtest
|
||||
[nix-shell]$ postgrest-loadtest-against main
|
||||
|
||||
# Produce a markdown report to be used on CI
|
||||
[nix-shell]$ postgrest-loadtest-report
|
||||
@@ -284,7 +229,7 @@ Tools like `postgrest-build`, `postgrest-run`, `postgrest-repl` etc. are simple
|
||||
also run in CI, with the exception of the IO and Memory checks that need to be run
|
||||
separately.
|
||||
|
||||
`postgrest-with-postgresql-*` take a command as an argument and will run it
|
||||
`postgrest-with-pg-*` take a command as an argument and will run it
|
||||
with a temporary database. `postgrest-with-all` will run the command against
|
||||
all supported PostgreSQL versions. Tests run without `postgrest-with-*` are
|
||||
run against the latest PostgreSQL version by default.
|
||||
@@ -380,7 +325,7 @@ that).
|
||||
We also use `default.nix` to load our pinned version of the `nixpkgs`
|
||||
repository. This set of packages will always be the same, independently from
|
||||
where or when you use it. The pinned version is taken from `flake.lock` and
|
||||
can be updated with `nix flake update`.
|
||||
can be updated with `postgrest-nixpkgs-upgrade`.
|
||||
|
||||
### `shell.nix`
|
||||
|
||||
|
||||
@@ -16,11 +16,8 @@ The following checklist guides you through the complete process in more detail.
|
||||
## Upgrade the pinned version of `nixpkgs`
|
||||
|
||||
The pinned version of [`nixpkgs`](https://github.com/NixOS/nixpkgs) is defined
|
||||
in [`nix/nixpkgs-version.nix`](nixpkgs-version.nix). The pin refers directly to
|
||||
a GitHub tarball for the given revision, which is more efficient than pulling
|
||||
the complete Git repository. To upgrade it to the current `main` of
|
||||
`nixpkgs`, you can use a small utility script defined in
|
||||
[`nix/nixpkgs-update.nix`](nixpkgs-update.nix):
|
||||
in [`flake.nix`](../flake.nix). To upgrade it, you can use a small utility
|
||||
script defined in [`nix/tools/nixpkgsTools.nix`](tools/nixpkgsTools.nix):
|
||||
|
||||
```bash
|
||||
# From the root of the repository, enter nix-shell
|
||||
@@ -30,21 +27,12 @@ nix-shell
|
||||
postgrest-nixpkgs-upgrade
|
||||
|
||||
# Exit the nix-shell with Ctrl-d
|
||||
|
||||
```
|
||||
|
||||
## Review overlays
|
||||
|
||||
Check whether the individual [overlays](overlays) are still required.
|
||||
|
||||
## Check if patches are still required and update them as needed
|
||||
|
||||
We track a number of PostgREST-specific patches in [`nix/patches`](patches).
|
||||
Check whether the pull-requests/issues linked in the
|
||||
[`default.nix`](patches/default.nix) have progressed and remove/modify the
|
||||
patches if they did. If conflicting changes occurred, you might have to rebase
|
||||
the respective patches.
|
||||
|
||||
## Build everything
|
||||
|
||||
Using the PostgREST binary Nix cache is recommended. Install
|
||||
@@ -58,25 +46,19 @@ errors, this is probably due to one of our patches. Try to fix them and re-run
|
||||
|
||||
## Update the PostgREST binary cache
|
||||
|
||||
If you have access to the PostgREST cachix signing key, you can push the
|
||||
If you have access to the PostgREST cachix project, you can push the
|
||||
artifacts that you built locally to the binary cache. This will accelerate the
|
||||
CI builds and tests, sometimes dramatically. This might sometimes even be
|
||||
required to avoid build timeouts in CI.
|
||||
|
||||
You'll need to set the `CACHIX_SIGNING_KEY` before proceeding, e.g. by creating
|
||||
a file containing `export CACHIX_SIGNING_KEY=...` and sourcing that file, which
|
||||
avoids having the secret in your shell history.
|
||||
You'll need to login with your token with `cachix authtoken <token>`.
|
||||
|
||||
To push all new artifacts to Cachix, run:
|
||||
|
||||
```
|
||||
nix-store -qR --include-outputs $$(nix-instantiate) | cachix push postgrest
|
||||
|
||||
# Or, equivalently
|
||||
nix-shell --run postgrest-push-cachix
|
||||
|
||||
```
|
||||
|
||||
The `nix-store` command will query the nix-store to list all dependencies and
|
||||
build artifacts of PostgREST. The `cachix` command will efficiently push
|
||||
The `postgrest-push-cachix` command will query the nix-store to list all
|
||||
dependencies and build artifacts of PostgREST. It will then push
|
||||
everything that is not yet cached to the binary cache.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
{-# LANGUAGE TupleSections #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
|
||||
-- | Haskell Imports and Exports tool
|
||||
@@ -33,13 +34,15 @@ import Data.Function ((&))
|
||||
import Data.List (intercalate)
|
||||
import Data.Maybe (catMaybes, mapMaybe)
|
||||
import Data.Text (Text)
|
||||
import GHC.Driver.Errors.Types (GhcMessage)
|
||||
import GHC.Generics (Generic)
|
||||
import GHC.Hs.Extension (GhcPs)
|
||||
import GHC.Types.Error (getMessages)
|
||||
import GHC.Types.Error (Messages, defaultDiagnosticOpts, getMessages)
|
||||
import GHC.Types.Name.Occurrence (occNameString)
|
||||
import GHC.Types.Name.Reader (rdrNameOcc)
|
||||
import GHC.Unit.Module.Name (moduleNameString)
|
||||
import GHC.Unit.Module (moduleNameString)
|
||||
import GHC.Utils.Error (pprMsgEnvelopeBagWithLoc)
|
||||
import GHC.Utils.Outputable (showSDocUnsafe)
|
||||
import System.Directory.Recursive (getFilesRecursive)
|
||||
import System.Exit (exitFailure)
|
||||
|
||||
@@ -198,7 +201,7 @@ sourceSymbols source = do
|
||||
return $ concatMap (importSymbols source filepath . GHC.unLoc) hsmodImports
|
||||
|
||||
-- | Parse a Haskell module
|
||||
parseModule :: FilePath -> IO GHC.HsModule
|
||||
parseModule :: FilePath -> IO (GHC.HsModule GhcPs)
|
||||
parseModule filepath = do
|
||||
result <- ExactPrint.parseModule GHC.Paths.libdir filepath
|
||||
case result of
|
||||
@@ -206,7 +209,13 @@ parseModule filepath = do
|
||||
return $ GHC.unLoc hsmod
|
||||
Left errs ->
|
||||
fail $ "Errors with " <> show filepath <> ":\n "
|
||||
<> show (pprMsgEnvelopeBagWithLoc $ getMessages errs)
|
||||
<> formatParseErrors errs
|
||||
|
||||
formatParseErrors :: Messages GhcMessage -> String
|
||||
formatParseErrors errs =
|
||||
intercalate "\n "
|
||||
. fmap showSDocUnsafe
|
||||
$ pprMsgEnvelopeBagWithLoc (defaultDiagnosticOpts @GhcMessage) (getMessages errs)
|
||||
|
||||
-- | Symbols imported in an import declaration.
|
||||
--
|
||||
@@ -214,9 +223,12 @@ parseModule filepath = do
|
||||
-- only one item is returned.
|
||||
importSymbols :: FilePath -> FilePath -> GHC.ImportDecl GhcPs -> [ImportedSymbol]
|
||||
importSymbols source filepath GHC.ImportDecl{..} =
|
||||
case ideclHiding of
|
||||
Just (hiding, syms) ->
|
||||
symbol (if hiding then Hiding else Explicit) . Just . GHC.unLoc <$> GHC.unLoc syms
|
||||
case ideclImportList of
|
||||
Just (importListInterpretation, syms) ->
|
||||
symbol (if importListInterpretation == GHC.EverythingBut then Hiding else Explicit)
|
||||
. Just
|
||||
. GHC.unLoc
|
||||
<$> GHC.unLoc syms
|
||||
Nothing ->
|
||||
[ symbol Wildcard Nothing ]
|
||||
where
|
||||
|
||||
@@ -5,10 +5,10 @@ project. It's available in PostgREST's `nix-shell` by default.
|
||||
|
||||
## Dumping imports
|
||||
|
||||
Given source code in the directories `src` and `main`, for example, you can run:
|
||||
Given source code in the directories `src/library` and `src/executable`, for example, you can run:
|
||||
|
||||
```
|
||||
hsie dump-imports src main
|
||||
hsie dump-imports src/library src/executable
|
||||
```
|
||||
|
||||
This dumps all imports of the modules in the given directory to a CSV file,
|
||||
@@ -18,7 +18,7 @@ To dump to a JSON file (e.g., to further process with `jq`), add the `--json`
|
||||
flag:
|
||||
|
||||
```
|
||||
hsie dump-imports --json src main
|
||||
hsie dump-imports --json src/library src/executable
|
||||
```
|
||||
|
||||
## Graphing imports
|
||||
@@ -27,7 +27,7 @@ The tool can generate `graphviz` graphs of module and symbol imports by printing
|
||||
a file to `stdout` that can directly be rendered with `dot`:
|
||||
|
||||
```
|
||||
hsie graph-modules src main | dot -Tpng -o modules.png
|
||||
hsie graph-modules src/library src/executable | dot -Tpng -o modules.png
|
||||
```
|
||||
|
||||
The command `graph-modules` prints a graph of which modules insert which other
|
||||
@@ -39,7 +39,7 @@ To check whether modules are imported under consistent aliases in your project,
|
||||
run:
|
||||
|
||||
```
|
||||
hsie check-aliases main src
|
||||
hsie check-aliases src/library src/executable
|
||||
```
|
||||
|
||||
This will exit with a non-zero exit code if any inconsistent aliases are found.
|
||||
@@ -48,13 +48,13 @@ The following command checks whether any modules are imported as wildcards, i.e.
|
||||
not qualified and without specifying symbols.
|
||||
|
||||
```
|
||||
hsie check-wildcards main src
|
||||
hsie check-wildcards src/library src/executable
|
||||
```
|
||||
|
||||
To whitelist certain modules to be imported as wildcards, use `--ok`:
|
||||
|
||||
```
|
||||
hsie check-wildcards main src --ok Protolude --ok Test.Module
|
||||
hsie check-wildcards src/library src/executable --ok Protolude --ok Test.Module
|
||||
```
|
||||
|
||||
## Current limitations
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
# Creating a separate libpq package is is discussed in
|
||||
# https://github.com/NixOS/nixpkgs/issues/61580, but nixpkgs has not moved
|
||||
# forward, yet.
|
||||
# This package is passed to postgresql-libpq (haskell) which needs to be
|
||||
# cross-compiled to the static build and possibly other architectures as
|
||||
# as well. To reduce the number of dependencies that need to be built with
|
||||
# it, this derivation focuses on building the client libraries only. No
|
||||
# server, no tests.
|
||||
{ stdenv
|
||||
, lib
|
||||
, openssl
|
||||
, zlib
|
||||
, postgresql
|
||||
, pkg-config
|
||||
, tzdata
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "libpq";
|
||||
inherit (postgresql) src version patches;
|
||||
|
||||
__structuredAttrs = true;
|
||||
env.CFLAGS = "-fdata-sections -ffunction-sections"
|
||||
+ (if stdenv.cc.isClang then " -flto" else " -fmerge-constants -Wl,--gc-sections");
|
||||
|
||||
configureFlags = [
|
||||
"--without-gssapi"
|
||||
"--without-icu"
|
||||
"--without-readline"
|
||||
"--with-openssl"
|
||||
"--with-system-tzdata=${tzdata}/share/zoneinfo"
|
||||
"--sysconfdir=/etc/postgresql"
|
||||
];
|
||||
|
||||
nativeBuildInputs = [ pkg-config tzdata ];
|
||||
buildInputs = [ openssl zlib ];
|
||||
|
||||
buildFlags = [ "submake-libpq" "submake-libpgport" ];
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
make -C src/bin/pg_config install
|
||||
make -C src/common install
|
||||
make -C src/include install
|
||||
make -C src/interfaces/libpq install
|
||||
make -C src/port install
|
||||
|
||||
rm -rfv $out/share
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
outputs = [ "out" ];
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://www.postgresql.org";
|
||||
description = "Client API library for PostgreSQL";
|
||||
license = licenses.postgresql;
|
||||
};
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
, coreutils
|
||||
, git
|
||||
, lib
|
||||
, moreutils
|
||||
, runCommand
|
||||
, shellcheck
|
||||
, stdenv
|
||||
@@ -56,7 +57,7 @@ let
|
||||
# Example: This way `postgrest-watch -h` will return the help output for watch, while
|
||||
# `postgrest-watch postgrest-test-spec -h` will return the help output for test-spec.
|
||||
# Taken from: https://github.com/matejak/argbash/issues/114#issuecomment-557108274
|
||||
sed '/_positionals_count + 1/a\\t\t\t\tset -- "''${@:1:1}" "--" "''${@:2}"' -i $out
|
||||
sed '/_positionals_count + 1/a\\t\t\t\tset -- "''${@:1:1}" "--" "''${@:2}"' $out | ${moreutils}/bin/sponge $out
|
||||
'';
|
||||
|
||||
bash-completion =
|
||||
@@ -66,7 +67,7 @@ let
|
||||
''
|
||||
|
||||
+ lib.optionalString (positionalCompletion != "") ''
|
||||
sed 's#COMPREPLY.*compgen -o bashdefault .*$#${escape positionalCompletion}#' -i $out
|
||||
sed 's#COMPREPLY.*compgen -o bashdefault .*$#${escape positionalCompletion}#' $out | ${moreutils}/bin/sponge $out
|
||||
''
|
||||
);
|
||||
|
||||
@@ -103,8 +104,7 @@ let
|
||||
''
|
||||
|
||||
+ lib.optionalString withTmpDir ''
|
||||
mkdir -p "''${TMPDIR:-/tmp}/postgrest"
|
||||
tmpdir="$(${coreutils}/bin/mktemp -d --tmpdir postgrest/${name}-XXX)"
|
||||
tmpdir="$(${coreutils}/bin/mktemp -d --tmpdir=/tmp ${name}-XXX)"
|
||||
|
||||
# we keep the tmpdir when an error occurs for debugging
|
||||
trap 'echo Temporary directory kept at: $tmpdir' ERR
|
||||
|
||||
@@ -3,6 +3,4 @@
|
||||
checked-shell-script = import ./checked-shell-script;
|
||||
gitignore = import ./gitignore.nix;
|
||||
haskell-packages = import ./haskell-packages.nix;
|
||||
postgresql-libpq = import ./postgresql-libpq.nix;
|
||||
slocat = import ./slocat.nix;
|
||||
}
|
||||
|
||||
@@ -47,47 +47,64 @@ let
|
||||
# - To modify and try packages locally, see "Working with locally modified Haskell packages" in the Nix README.
|
||||
|
||||
# Before upgrading fuzzyset to 0.3, check: https://github.com/PostgREST/postgrest/issues/3329
|
||||
# jailbreak, because hspec limit for tests
|
||||
fuzzyset = prev.fuzzyset_0_2_4;
|
||||
|
||||
# TODO: Remove once available in nixpkgs haskellPackages
|
||||
configurator-pg =
|
||||
# TODO: Remove once available in nixpkgs
|
||||
auto-update =
|
||||
prev.callHackageDirect
|
||||
{
|
||||
pkg = "configurator-pg";
|
||||
ver = "0.2.11";
|
||||
sha256 = "sha256-mtGtNawDJgz2ZIEVca+IYXVu4oNw9xsfJiYWAqAbbgc=";
|
||||
pkg = "auto-update";
|
||||
ver = "0.2.7";
|
||||
sha256 = "sha256-fHX/OqF/cB9rbpGpLUtA29bcEJS43HUWHcK55yUxKoo=";
|
||||
}
|
||||
{ };
|
||||
|
||||
hasql-pool = lib.dontCheck (prev.callHackageDirect
|
||||
{
|
||||
pkg = "hasql-pool";
|
||||
ver = "1.0.1";
|
||||
sha256 = "sha256-Hf1f7lX0LWkjrb25SDBovCYPRdmUP1H6pAxzi7kT4Gg=";
|
||||
}
|
||||
{ });
|
||||
# TODO: Remove once available in nixpkgs
|
||||
aeson-jsonpath =
|
||||
prev.callHackageDirect
|
||||
{
|
||||
pkg = "aeson-jsonpath";
|
||||
ver = "0.4.2.0";
|
||||
sha256 = "sha256-K+3brf1zjSSjojtSCXFrip5rrP7AO/S4zndAxAnvEfc=";
|
||||
}
|
||||
{ };
|
||||
|
||||
hasql-notifications = lib.dontCheck (prev.callHackageDirect
|
||||
{
|
||||
pkg = "hasql-notifications";
|
||||
ver = "0.2.2.2";
|
||||
sha256 = "sha256-myKwlug7OgTa/qP6mHfCD+5Q8IhM17JvpJBfSo+M01k=";
|
||||
}
|
||||
{ });
|
||||
http2 =
|
||||
prev.callHackageDirect
|
||||
{
|
||||
pkg = "http2";
|
||||
ver = "5.4.0";
|
||||
sha256 = "sha256-PeEWVd61bQ8G7LvfLeXklzXqNJFaAjE2ecRMWJZESPE=";
|
||||
}
|
||||
{ };
|
||||
|
||||
# newer nixpkgs already has 0.10., so we fallback to default for forward compat
|
||||
jose-jwt = prev.jose-jwt_0_10_0 or prev.jose-jwt;
|
||||
http-semantics =
|
||||
prev.callHackageDirect
|
||||
{
|
||||
pkg = "http-semantics";
|
||||
ver = "0.4.0";
|
||||
sha256 = "sha256-rh0z51EKvsu5rQd5n2z3fSRjjEObouNZSBPO9NFYOF0=";
|
||||
}
|
||||
{ };
|
||||
|
||||
postgresql-libpq = lib.dontCheck (prev.callHackageDirect
|
||||
{
|
||||
pkg = "postgresql-libpq";
|
||||
ver = "0.10.1.0";
|
||||
sha256 = "sha256-tXOMqCO8opMilI9rx0D+njqjIjbZsH168Bzb8Aq8Ff4=";
|
||||
}
|
||||
{
|
||||
postgresql = super.libpq;
|
||||
});
|
||||
network-run =
|
||||
prev.callHackageDirect
|
||||
{
|
||||
pkg = "network-run";
|
||||
ver = "0.5.0";
|
||||
sha256 = "sha256-vbXh+CzxDsGApjqHxCYf/ijpZtUCApFbkcF5gyN0THU=";
|
||||
}
|
||||
{ };
|
||||
|
||||
warp =
|
||||
lib.dontCheck
|
||||
(prev.callHackageDirect
|
||||
{
|
||||
pkg = "warp";
|
||||
ver = "3.4.14";
|
||||
sha256 = "sha256-RnoOUlC6dOP0sK/tYAJCX1oLzVFG1GILUY+yVbmvW8Y=";
|
||||
}
|
||||
{ });
|
||||
};
|
||||
in
|
||||
{
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
_: super:
|
||||
{
|
||||
# Depending on which nixpkgs version is pinned, libpq might either be available already - or not.
|
||||
libpq = super.libpq or (super.callPackage ../libpq.nix {
|
||||
postgresql = super.postgresql_16;
|
||||
});
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
_: prev:
|
||||
{
|
||||
slocat = prev.buildGoModule {
|
||||
name = "slocat";
|
||||
src = prev.fetchFromGitHub {
|
||||
owner = "robx";
|
||||
repo = "slocat";
|
||||
rev = "52e7512c6029fd00483e41ccce260a3b4b9b3b64";
|
||||
sha256 = "sha256-qn6luuh5wqREu3s8RfuMCP5PKdS2WdwPrujRYTpfzQ8=";
|
||||
};
|
||||
vendorHash = null;
|
||||
};
|
||||
}
|
||||
@@ -8,65 +8,25 @@ let
|
||||
inherit (pkgs) pkgsStatic;
|
||||
inherit (pkgsStatic.haskell) lib;
|
||||
|
||||
packagesStatic =
|
||||
pkgsStatic.haskell.packages."${compiler}".override (old: {
|
||||
ghc = pkgsStatic.pkgsBuildHost.haskell.compiler."${compiler}".override {
|
||||
# Using the bundled libffi generally works better for cross-compiling
|
||||
libffi = null;
|
||||
# Building sphinx fails on some platforms
|
||||
enableDocs = false;
|
||||
# Cross compiling with native bignum works better than with gmp
|
||||
enableNativeBignum = true;
|
||||
};
|
||||
|
||||
overrides = pkgs.lib.composeExtensions old.overrides (_: prev: {
|
||||
postgresql-libpq = (lib.overrideCabal prev.postgresql-libpq {
|
||||
# TODO: This section can be simplified when this PR has made it's way to us:
|
||||
# https://github.com/NixOS/nixpkgs/pull/286370
|
||||
# Additionally, we need to use the default version in nixpkgs, otherwise the
|
||||
# override will not be active as well.
|
||||
# Using use-pkg-config flag, because pg_config won't work when cross-compiling
|
||||
configureFlags = [ "-fuse-pkg-config" ];
|
||||
# postgresql doesn't build in the fully static overlay - but the default
|
||||
# derivation is built with static libraries anyway.
|
||||
libraryPkgconfigDepends = [ pkgsStatic.libpq ];
|
||||
librarySystemDepends = [ ];
|
||||
}).overrideAttrs (_: prevAttrs: {
|
||||
buildInputs = prevAttrs.buildInputs ++ [ pkgsStatic.openssl ];
|
||||
});
|
||||
});
|
||||
});
|
||||
packagesStatic = pkgsStatic.haskell.packages.native-bignum."${compiler}";
|
||||
|
||||
makeExecutableStatic = drv: pkgs.lib.pipe drv [
|
||||
lib.compose.justStaticExecutables
|
||||
|
||||
# To successfully compile a redistributable, fully static executable we need to:
|
||||
# 1. make executable really statically linked.
|
||||
# 2. avoid any references to /nix/store to prevent blowing up the closure size.
|
||||
# 3. be able to run the executable.
|
||||
# When checking for references, we ignore the following:
|
||||
# - eeee... are removed references which don't actually exist
|
||||
# - openssl-etc references are purposely designed to be very small
|
||||
(lib.compose.overrideCabal (drv: {
|
||||
postFixup = drv.postFixup + ''
|
||||
exe="$out/bin/postgrest"
|
||||
# 1. avoid any references to /nix/store to prevent blowing up the closure size.
|
||||
(drv: drv.overrideAttrs {
|
||||
allowedReferences = [
|
||||
pkgsStatic.openssl.etc
|
||||
];
|
||||
})
|
||||
|
||||
if ! (file "$exe" | grep 'statically linked') then
|
||||
echo "not a static executable, ldd output:"
|
||||
ldd "$exe"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Checking for references to /nix/store..."
|
||||
(${pkgsStatic.binutils}/bin/strings "$exe" \
|
||||
| grep -v /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee \
|
||||
| grep -v -etc/etc/ssl \
|
||||
| grep /nix/store || exit 0 && exit 1)
|
||||
echo "No references to /nix/store found"
|
||||
|
||||
"$exe" --help
|
||||
'';
|
||||
}))
|
||||
# 2. be able to run the executable.
|
||||
(drv: drv.overrideAttrs {
|
||||
passthru.tests.version = pkgsStatic.testers.testVersion {
|
||||
package = drv;
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
in
|
||||
|
||||
@@ -15,7 +15,6 @@ let
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
exec ${cabal-install}/bin/cabal v2-build ${devCabalOptions} "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
|
||||
@@ -34,6 +33,17 @@ let
|
||||
exec ${cabal-install}/bin/cabal v2-clean
|
||||
'';
|
||||
|
||||
update =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-cabal-update";
|
||||
docs = "Update cabal's package list from hackage.haskell.org";
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
exec ${cabal-install}/bin/cabal v2-update
|
||||
'';
|
||||
|
||||
run =
|
||||
checkedShellScript
|
||||
{
|
||||
@@ -41,20 +51,26 @@ let
|
||||
docs = "Run PostgREST after building it interactively with cabal-install";
|
||||
args =
|
||||
[
|
||||
"ARG_USE_ENV([PGRST_DB_ANON_ROLE], [postgrest_test_anonymous], [PostgREST anonymous role])"
|
||||
"ARG_USE_ENV([PGRST_DB_ANON_ROLE], [], [PostgREST anonymous role. (default: 'postgrest_test_anonymous')])"
|
||||
"ARG_USE_ENV([PGRST_DB_POOL], [1], [PostgREST pool size])"
|
||||
"ARG_USE_ENV([PGRST_DB_POOL_ACQUISITION_TIMEOUT], [1], [PostgREST pool timeout])"
|
||||
"ARG_USE_ENV([PGRST_JWT_SECRET], [reallyreallyreallyreallyverysafe], [PostgREST JWT secret])"
|
||||
"ARG_USE_ENV([PGRST_ADMIN_SERVER_PORT], [3001], [PostgREST admin server port])"
|
||||
"ARG_LEFTOVERS([PostgREST arguments])"
|
||||
];
|
||||
workingDir = "/";
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
# when there's a default, argbash conflates empty string with unset, so we do this workaround to be able to do `PGRST_DB_ANON_ROLE="" <command>` for manual testing
|
||||
if [[ ! ''${PGRST_DB_ANON_ROLE+x} ]]; then
|
||||
PGRST_DB_ANON_ROLE="postgrest_test_anonymous"
|
||||
fi
|
||||
export PGRST_DB_ANON_ROLE
|
||||
export PGRST_DB_POOL
|
||||
export PGRST_DB_POOL_ACQUISITION_TIMEOUT
|
||||
export PGRST_JWT_SECRET
|
||||
export PGRST_ADMIN_SERVER_PORT
|
||||
|
||||
exec ${cabal-install}/bin/cabal v2-run ${devCabalOptions} --verbose=0 -- \
|
||||
postgrest "''${_arg_leftovers[@]}"
|
||||
@@ -68,7 +84,7 @@ let
|
||||
docs = "Run a profiled build of postgREST. This will generate a postgrest.prof file that can be used to do optimization.";
|
||||
args =
|
||||
[
|
||||
"ARG_USE_ENV([PGRST_DB_ANON_ROLE], [postgrest_test_anonymous], [PostgREST anonymous role])"
|
||||
"ARG_USE_ENV([PGRST_DB_ANON_ROLE], [], [PostgREST anonymous role. (default: 'postgrest_test_anonymous')])"
|
||||
"ARG_USE_ENV([PGRST_DB_POOL], [1], [PostgREST pool size])"
|
||||
"ARG_USE_ENV([PGRST_DB_POOL_ACQUISITION_TIMEOUT], [1], [PostgREST pool timeout])"
|
||||
"ARG_USE_ENV([PGRST_JWT_SECRET], [reallyreallyreallyreallyverysafe], [PostgREST JWT secret])"
|
||||
@@ -78,15 +94,17 @@ let
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
# when there's a default, argbash conflates empty string with unset, so we do this workaround to be able to do `PGRST_DB_ANON_ROLE="" <command>` for manual testing
|
||||
if [[ ! ''${PGRST_DB_ANON_ROLE+x} ]]; then
|
||||
PGRST_DB_ANON_ROLE="postgrest_test_anonymous"
|
||||
fi
|
||||
export PGRST_DB_ANON_ROLE
|
||||
export PGRST_DB_POOL
|
||||
export PGRST_DB_POOL_ACQUISITION_TIMEOUT
|
||||
export PGRST_JWT_SECRET
|
||||
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${cabal-install}/bin/cabal --builddir="dist-prof" v2-build --enable-profiling --disable-shared exe:postgrest
|
||||
${cabal-install}/bin/cabal --builddir="dist-prof" v2-run -- \
|
||||
postgrest +RTS -p -h -RTS "''${_arg_leftovers[@]}"
|
||||
exec ${cabal-install}/bin/cabal --builddir="dist-prof" v2-run --enable-profiling --disable-shared exe:postgrest -- \
|
||||
+RTS -p -h -RTS "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
|
||||
repl =
|
||||
@@ -109,6 +127,7 @@ buildToolbox
|
||||
inherit
|
||||
build
|
||||
clean
|
||||
update
|
||||
run
|
||||
runProfiled
|
||||
repl;
|
||||
|
||||
@@ -5,14 +5,13 @@
|
||||
, curl
|
||||
, devCabalOptions
|
||||
, entr
|
||||
, git
|
||||
, fd
|
||||
, graphviz
|
||||
, hsie
|
||||
, nix
|
||||
, silver-searcher
|
||||
, stdenv
|
||||
, style
|
||||
, tests
|
||||
, withTools
|
||||
, haskellPackages
|
||||
, ctags
|
||||
, openssl
|
||||
@@ -40,7 +39,7 @@ let
|
||||
}
|
||||
''
|
||||
while true; do
|
||||
(! ${silver-searcher}/bin/ag -l . | ${entr}/bin/entr -dr "$_arg_command" "''${_arg_leftovers[@]}")
|
||||
(! ${fd}/bin/fd -H -E .git | ${entr}/bin/entr -dr "$_arg_command" "''${_arg_leftovers[@]}")
|
||||
done
|
||||
'';
|
||||
|
||||
@@ -53,10 +52,14 @@ let
|
||||
|
||||
Requires authentication with `cachix authtoken ...`.
|
||||
'';
|
||||
args =
|
||||
[
|
||||
"ARG_OPTIONAL_SINGLE([system], , [System], [${stdenv.system}])"
|
||||
];
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
${nix}/bin/nix-instantiate \
|
||||
${nix}/bin/nix-instantiate --argstr system "$_arg_system" \
|
||||
| xargs ${nix}/bin/nix-store -qR --include-outputs \
|
||||
| ${cachix}/bin/cachix push postgrest
|
||||
'';
|
||||
@@ -77,6 +80,7 @@ let
|
||||
}
|
||||
''
|
||||
${tests}/bin/postgrest-test-spec
|
||||
${tests}/bin/postgrest-test-observability
|
||||
${tests}/bin/postgrest-test-doctests
|
||||
${tests}/bin/postgrest-test-io
|
||||
${tests}/bin/postgrest-test-big-schema
|
||||
@@ -85,156 +89,6 @@ let
|
||||
${style}/bin/postgrest-style-check
|
||||
'';
|
||||
|
||||
gitHooks =
|
||||
let
|
||||
name = "postgrest-git-hooks";
|
||||
in
|
||||
checkedShellScript
|
||||
{
|
||||
inherit name;
|
||||
docs =
|
||||
''
|
||||
Enable or disable git pre-commit and pre-push hooks.
|
||||
|
||||
Basic is faster and will only run:
|
||||
- pre-commit: postgrest-style
|
||||
- pre-push: postgrest-lint
|
||||
|
||||
Full takes a lot more time and will run:
|
||||
- pre-commit: postgrest-style && postgrest-lint
|
||||
- pre-push: postgrest-check
|
||||
|
||||
Changes made by postgrest-style will be staged automatically.
|
||||
|
||||
Example usage:
|
||||
postgrest-git-hooks disable
|
||||
postgrest-git-hooks enable basic
|
||||
postgrest-git-hooks enable full
|
||||
|
||||
The "run" operation and "--hook" argument are only used internally.
|
||||
'';
|
||||
args =
|
||||
[
|
||||
"ARG_POSITIONAL_SINGLE([operation], [Operation])"
|
||||
"ARG_TYPE_GROUP_SET([OPERATION], [OPERATION], [operation], [disable,enable,run])"
|
||||
"ARG_POSITIONAL_SINGLE([mode], [Mode], [basic])"
|
||||
"ARG_TYPE_GROUP_SET([MODE], [MODE], [mode], [basic,full])"
|
||||
"ARG_OPTIONAL_SINGLE([hook], , [Hook], [pre-commit])"
|
||||
"ARG_TYPE_GROUP_SET([HOOK], [HOOK], [hook], [pre-commit,pre-push])"
|
||||
];
|
||||
positionalCompletion =
|
||||
''
|
||||
if test "$prev" == "${name}"; then
|
||||
COMPREPLY=( $(compgen -W "enable disable" -- "$cur") )
|
||||
elif test "$prev" == "enable" || test "$prev" == "disable"; then
|
||||
COMPREPLY=( $(compgen -W "basic full" -- "$cur") )
|
||||
fi
|
||||
'';
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
if [ run != "$_arg_operation" ]; then
|
||||
# Remove all hooks first and ignore failures because the file might be missing.
|
||||
# This assumes that we're only adding lines that include "postgrest-git-hooks"
|
||||
# to the hook file.
|
||||
sed -i -e '/postgrest-git-hooks/d' .git/hooks/pre-{commit,push} 2> /dev/null || true
|
||||
|
||||
if [ disable != "$_arg_operation" ]; then
|
||||
# The nix-shell && + nix-shell || pattern makes sure we can run the hook
|
||||
# in a pure nix-shell, where nix-shell itself is not available, too.
|
||||
|
||||
# The $(nix-shell --run "command -v ...") pattern ensures we only need to enable
|
||||
# the hooks once and still run the latest of our hook scripts, even when we
|
||||
# update them in the repo.
|
||||
|
||||
echo 'command -v nix-shell > /dev/null || postgrest-git-hooks --hook=pre-commit run' "$_arg_mode" \
|
||||
>> .git/hooks/pre-commit
|
||||
# shellcheck disable=SC2016
|
||||
echo 'command -v nix-shell > /dev/null && $(nix-shell --quiet -Q --run "command -v postgrest-git-hooks") --hook=pre-commit run' "$_arg_mode" \
|
||||
>> .git/hooks/pre-commit
|
||||
chmod +x .git/hooks/pre-commit
|
||||
|
||||
echo 'command -v nix-shell > /dev/null || postgrest-git-hooks --hook=pre-push run' "$_arg_mode" \
|
||||
>> .git/hooks/pre-push
|
||||
# shellcheck disable=SC2016
|
||||
echo 'command -v nix-shell > /dev/null && $(nix-shell --quiet -Q --run "command -v postgrest-git-hooks") --hook=pre-push run' "$_arg_mode" \
|
||||
>> .git/hooks/pre-push
|
||||
chmod +x .git/hooks/pre-push
|
||||
fi
|
||||
else
|
||||
# When run from a git hook, the GIT_ environment variables conflict with our withGit helper.
|
||||
# The following unsets all GIT_ variables.
|
||||
unset "''${!GIT_@}"
|
||||
|
||||
# shellcheck disable=SC2317
|
||||
function restore () {
|
||||
ref="$(git stash list --format=format:%gD --grep "$1" -n1)"
|
||||
# this will avoid merge conflicts when applying the stash
|
||||
${git}/bin/git restore --source="$ref" .
|
||||
# restore untracked files, too. could fail with no files
|
||||
if [ "$(git show --numstat --format=oneline "$ref^3" | wc -l)" -gt 1 ]; then
|
||||
${git}/bin/git restore --overlay --source="$ref^3" .
|
||||
fi
|
||||
${git}/bin/git stash drop "$ref"
|
||||
}
|
||||
|
||||
case "$_arg_mode" in
|
||||
basic)
|
||||
case "$_arg_hook" in
|
||||
pre-commit)
|
||||
# To be able to automatically add only changes from postgrest-style to the staging area,
|
||||
# we need to run postgrest-style twice. Otherwise we'd risk merge conflicts when popping
|
||||
# the stash afterwards.
|
||||
${style}/bin/postgrest-style
|
||||
|
||||
stash="postgrest-git-hooks-$RANDOM"
|
||||
${git}/bin/git stash push --include-untracked --keep-index -m "$stash"
|
||||
if [ "$(git stash list --grep $stash)" ]; then
|
||||
# Only create the stash pop trap, if we actually created a stash.
|
||||
# Otherwise stash pop will cause havoc.
|
||||
trap 'restore "$stash"' EXIT
|
||||
fi
|
||||
|
||||
${style}/bin/postgrest-style
|
||||
${git}/bin/git add .
|
||||
;;
|
||||
pre-push)
|
||||
# Create a clean working tree without any uncomitted changes.
|
||||
${withTools.withGit} HEAD ${style}/bin/postgrest-lint
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
full)
|
||||
case "$_arg_hook" in
|
||||
pre-commit)
|
||||
# To be able to automatically add only changes from postgrest-style to the staging area,
|
||||
# we need to run postgrest-style twice. Otherwise we'd risk merge conflicts when popping
|
||||
# the stash afterwards.
|
||||
${style}/bin/postgrest-style
|
||||
|
||||
stash="postgrest-git-hooks-$RANDOM"
|
||||
${git}/bin/git stash push --include-untracked --keep-index -m "$stash"
|
||||
if [ "$(git stash list --grep $stash)" ]; then
|
||||
# Only create the stash pop trap, if we actually created a stash.
|
||||
# Otherwise stash pop will cause havoc.
|
||||
trap 'restore "$stash"' EXIT
|
||||
fi
|
||||
|
||||
${style}/bin/postgrest-style
|
||||
${git}/bin/git add .
|
||||
|
||||
${style}/bin/postgrest-lint
|
||||
;;
|
||||
pre-push)
|
||||
# Create a clean working tree without any uncomitted changes.
|
||||
${withTools.withGit} HEAD ${check}
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
'';
|
||||
|
||||
dumpMinimalImports =
|
||||
checkedShellScript
|
||||
{
|
||||
@@ -246,7 +100,6 @@ let
|
||||
}
|
||||
''
|
||||
mkdir -p "$_arg_dumpdir"
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${cabal-install}/bin/cabal v2-build ${devCabalOptions} \
|
||||
--builddir="$tmpdir" \
|
||||
--ghc-option=-ddump-minimal-imports \
|
||||
@@ -276,10 +129,10 @@ let
|
||||
{
|
||||
name = "postgrest-hsie-graph-modules";
|
||||
docs = "Create a PNG graph of modules imported within the codebase.";
|
||||
args = [ "ARG_POSITIONAL_SINGLE([outfile], [Output filename])" ];
|
||||
args = [ "ARG_OPTIONAL_SINGLE([outfile], [o], [Output filename], [postgrest-module-graph.png])" ];
|
||||
}
|
||||
''
|
||||
${hsie} graph-modules main src | ${graphviz}/bin/dot -Tpng -o "$_arg_outfile"
|
||||
${hsie} graph-modules src/library src/executable | ${graphviz}/bin/dot -Tpng -o "$_arg_outfile"
|
||||
'';
|
||||
|
||||
hsieGraphSymbols =
|
||||
@@ -390,7 +243,6 @@ buildToolbox
|
||||
inherit
|
||||
check
|
||||
dumpMinimalImports
|
||||
gitHooks
|
||||
hsieGraphModules
|
||||
hsieGraphSymbols
|
||||
hsieMinimalImports
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
, aspellDicts
|
||||
, buildToolbox
|
||||
, checkedShellScript
|
||||
, fetchPypi
|
||||
, lib
|
||||
, plantuml
|
||||
, python3
|
||||
@@ -11,26 +10,14 @@
|
||||
, writers
|
||||
}:
|
||||
let
|
||||
selectPythonPackages = ps:
|
||||
let
|
||||
# TODO: Remove with next nixpkgs update
|
||||
sphinx-rtd-theme = assert ps.sphinx-rtd-theme.version == "2.0.0"; ps.sphinx-rtd-theme.overrideAttrs rec {
|
||||
version = "3.0.2";
|
||||
src = fetchPypi {
|
||||
pname = "sphinx_rtd_theme";
|
||||
inherit version;
|
||||
hash = "sha256-t0V7wl3acjsgsIamcLmVPIWeq2CioD7o6yuyPhduX4U=";
|
||||
};
|
||||
};
|
||||
in
|
||||
[
|
||||
ps.sphinx
|
||||
ps.sphinx-copybutton
|
||||
(ps.sphinx-rtd-dark-mode.override { inherit sphinx-rtd-theme; })
|
||||
sphinx-rtd-theme
|
||||
ps.sphinx-tabs
|
||||
ps.sphinxext-opengraph
|
||||
];
|
||||
selectPythonPackages = ps: [
|
||||
ps.sphinx
|
||||
ps.sphinx-copybutton
|
||||
ps.sphinx-rtd-dark-mode
|
||||
ps.sphinx-rtd-theme
|
||||
ps.sphinx-tabs
|
||||
ps.sphinxext-opengraph
|
||||
];
|
||||
|
||||
requirements = writeTextFile {
|
||||
name = "requirements.txt";
|
||||
@@ -48,12 +35,15 @@ let
|
||||
workingDir = "/docs";
|
||||
}
|
||||
''
|
||||
# https://github.com/sphinx-doc/sphinx/issues/11739
|
||||
export LC_ALL=C
|
||||
|
||||
function build() {
|
||||
${python}/bin/sphinx-build --color -W -a -n . -b "$@"
|
||||
}
|
||||
|
||||
if [ "$_arg_language" == "" ]; then
|
||||
# clean previous build, otherwise some errors might be supressed
|
||||
# clean previous build, otherwise some errors might be suppressed
|
||||
rm -rf "../.docs-build/html/default"
|
||||
|
||||
if [ -d languages ]; then
|
||||
@@ -64,7 +54,7 @@ let
|
||||
|
||||
build html "../.docs-build/html/default"
|
||||
else
|
||||
# clean previous build, otherwise some errors might be supressed
|
||||
# clean previous build, otherwise some errors might be suppressed
|
||||
rm -rf "../.docs-build/html/$_arg_language"
|
||||
|
||||
# update and build specific locale, can be used to create new locale
|
||||
@@ -132,6 +122,10 @@ let
|
||||
workingDir = "/docs";
|
||||
}
|
||||
''
|
||||
echo "Checking spelling mistakes..."
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
FILES=$(find . -type f -iname '*.rst' | tr '\n' ' ')
|
||||
|
||||
# shellcheck disable=SC2086 disable=SC2016
|
||||
@@ -152,6 +146,10 @@ let
|
||||
workingDir = "/docs";
|
||||
}
|
||||
''
|
||||
echo "Detecting obsolete dictionary entries..."
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
FILES=$(find . -type f -iname '*.rst' | tr '\n' ' ')
|
||||
|
||||
tail -n+2 postgrest.dict \
|
||||
@@ -170,6 +168,8 @@ let
|
||||
workingDir = "/docs";
|
||||
}
|
||||
''
|
||||
export LC_ALL=C
|
||||
|
||||
${python}/bin/sphinx-build --color -b linkcheck . ../.docs-build
|
||||
'';
|
||||
|
||||
|
||||
@@ -1,63 +1,50 @@
|
||||
# generates a file to be used by the vegeta load testing tool
|
||||
|
||||
# It includes a worst case scenario for the JWT cache:
|
||||
# - all requests will have a unique JWT so no cache hits
|
||||
# - all jwts have an expiration that will be long enough to be
|
||||
# valid at time of request but short enough that already
|
||||
# validated jwts will expire later during the loadtest run
|
||||
# - the above guarantees JWT cache purging will happen
|
||||
# - we want this to track resource consumption in the worst case
|
||||
|
||||
# And a more normal scenario where non-expiring JWTs are picked
|
||||
# from an array
|
||||
import time
|
||||
import hmac
|
||||
import hashlib
|
||||
import base64
|
||||
import json
|
||||
import argparse
|
||||
import sys
|
||||
import random
|
||||
import jwcrypto.jwt as jwt
|
||||
from pathlib import Path
|
||||
|
||||
SECRET = b"reallyreallyreallyreallyverysafe"
|
||||
URL = "http://postgrest"
|
||||
JWT_DURATION = 120
|
||||
TOTAL_TARGETS = 50000 # tuned by hand to reduce result variance
|
||||
|
||||
secret_key = "reallyreallyreallyreallyverysafe"
|
||||
|
||||
|
||||
def base64url_encode(data: bytes) -> str:
|
||||
"""URL-safe Base64 encode without padding."""
|
||||
return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii")
|
||||
|
||||
|
||||
def generate_jwt(exp_inc: int) -> str:
|
||||
"""Generate an HS256 JWT"""
|
||||
# Header & payload
|
||||
header = {"alg": "HS256", "typ": "JWT"}
|
||||
now = int(time.time())
|
||||
payload = {
|
||||
def generate_target(
|
||||
now: int,
|
||||
key: jwt.JWK,
|
||||
) -> list[str]:
|
||||
"""Generate a target using an HS256 or RS256 JWT"""
|
||||
headers = {
|
||||
"sub": f"user_{random.getrandbits(32)}",
|
||||
"iat": now,
|
||||
"exp": now + exp_inc,
|
||||
}
|
||||
|
||||
claims = {
|
||||
"role": "postgrest_test_author",
|
||||
}
|
||||
|
||||
# Encode to JSON and then to Base64URL
|
||||
header_b = json.dumps(header, separators=(",", ":")).encode()
|
||||
payload_b = json.dumps(payload, separators=(",", ":")).encode()
|
||||
header_b64 = base64url_encode(header_b)
|
||||
payload_b64 = base64url_encode(payload_b)
|
||||
headers["alg"] = "RS256" if key.get("kty") == "RSA" else "HS256"
|
||||
|
||||
# Sign (HMAC‑SHA256) the "<header>.<payload>" string
|
||||
signing_input = f"{header_b64}.{payload_b64}".encode()
|
||||
signature = hmac.new(SECRET, signing_input, hashlib.sha256).digest()
|
||||
signature_b64 = base64url_encode(signature)
|
||||
token = jwt.JWT(headers, claims)
|
||||
token.make_signed_token(key)
|
||||
|
||||
return f"{header_b64}.{payload_b64}.{signature_b64}"
|
||||
|
||||
|
||||
# We want to ensure 401 Unauthorized responses don't happen during
|
||||
# JWT validation, this can happen when the jwt `exp` is too short.
|
||||
# At the same time, we want to ensure the `exp` is not too big,
|
||||
# so expires will occur and postgREST will have to clean cached expired JWTs.
|
||||
def estimate_adequate_jwt_exp_increase(iteration: int) -> int:
|
||||
# estimated time takes to build and run postgrest itself
|
||||
build_run_postgrest_time = 2
|
||||
# estimated time it takes to generate the targets file
|
||||
file_generation_time = TOTAL_TARGETS // (10**-5)
|
||||
# estimated exp time so some JWTs will expire
|
||||
dynamic_exp_inc = iteration // 1000
|
||||
|
||||
return build_run_postgrest_time + file_generation_time + dynamic_exp_inc
|
||||
return [
|
||||
f"OPTIONS {URL}/authors_only?{headers["alg"]}",
|
||||
f"Authorization: Bearer {token.serialize()}",
|
||||
"", # blank line to separate requests
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
@@ -65,29 +52,47 @@ def main():
|
||||
description="Generate Vegeta targets with unique JWTs"
|
||||
)
|
||||
parser.add_argument(
|
||||
"output",
|
||||
help="Path to write the generated targets file",
|
||||
"generated_path",
|
||||
metavar="GENERATED_PATH",
|
||||
help="Path to write the generated files",
|
||||
type=Path,
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
targets_path = args.generated_path / "gen_targets.http"
|
||||
|
||||
hs = jwt.JWK.from_password(secret_key)
|
||||
rsa = jwt.JWK.generate(kty="RSA", size=4096)
|
||||
|
||||
jwks = jwt.JWKSet()
|
||||
jwks.add(hs)
|
||||
jwks.add(rsa)
|
||||
|
||||
jwks_path = args.generated_path / "gen_jwks.json"
|
||||
|
||||
# Technically, this exports the private keys, because HS does not have the concept
|
||||
# of a public key. This is not a problem for tests, though, PostgREST can verify
|
||||
# tokens with the private key just as well.
|
||||
jwks_path.write_text(jwks.export())
|
||||
print(f"Created JWKSet on {jwks_path}")
|
||||
|
||||
ntargets = 1000
|
||||
|
||||
print(f"Generating {ntargets} targets...")
|
||||
|
||||
now = int(time.time())
|
||||
|
||||
lines = []
|
||||
start_time = time.time()
|
||||
|
||||
for i in range(TOTAL_TARGETS):
|
||||
token = generate_jwt(estimate_adequate_jwt_exp_increase(i))
|
||||
lines.append(f"OPTIONS {URL}/authors_only")
|
||||
lines.append(f"Authorization: Bearer {token}")
|
||||
lines.append("") # blank line to separate requests
|
||||
for i in range(ntargets):
|
||||
target = generate_target(now, hs if i % 2 == 0 else rsa)
|
||||
lines.extend(target)
|
||||
|
||||
try:
|
||||
with open(args.output, "w") as f:
|
||||
f.write("\n".join(lines))
|
||||
except IOError as e:
|
||||
print(f"Error writing to {args.output}: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
with open(targets_path, "w") as f:
|
||||
f.write("\n".join(lines))
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
print(f"Created {TOTAL_TARGETS} targets in {args.output} ({elapsed:.2f}s)")
|
||||
print(f"Created {ntargets} targets", end=" ")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
{ buildToolbox
|
||||
, checkedShellScript
|
||||
, commitlint
|
||||
, writeText
|
||||
}:
|
||||
let
|
||||
# Rules format: [<severity>, <"always"/"never">, <value>]
|
||||
commitlintConfig = writeText "commitlint.config.mjs" ''
|
||||
export default {
|
||||
rules: {
|
||||
"type-enum": [2, "always", [
|
||||
'add', // Add a new feature
|
||||
'amend', // To amend an unrealease commit
|
||||
'change', // Breaking changes
|
||||
'chore', // Update sponsors, changelog, readme etc
|
||||
'ci', // CI configuration files and scripts
|
||||
'docs', // Documentation
|
||||
'fix', // Bug fix
|
||||
'nix', // Related to Nix
|
||||
'perf', // Performance improvements
|
||||
'refactor', // Refactoring code
|
||||
'remove', // Remove a feature or fix
|
||||
'test', // Adding tests
|
||||
]],
|
||||
|
||||
'subject-case': [2, 'never', ['pascal-case', 'start-case']],
|
||||
'subject-empty': [2, 'never'],
|
||||
'subject-full-stop': [2, 'never', '.'],
|
||||
'subject-max-length': [2, 'always', 80],
|
||||
'subject-min-length': [2, 'always', 5],
|
||||
|
||||
'scope-case': [2, 'always', 'lower-case'],
|
||||
|
||||
'body-leading-blank': [2, 'always'],
|
||||
},
|
||||
};
|
||||
'';
|
||||
|
||||
commitCheck =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-commitlint";
|
||||
docs = "Script to validate commit messages";
|
||||
workingDir = "/";
|
||||
args = [
|
||||
"ARG_OPTIONAL_SINGLE([from],, [commit ref start from], [main])"
|
||||
"ARG_OPTIONAL_SINGLE([to],, [commit ref end at], [HEAD])"
|
||||
];
|
||||
}
|
||||
''
|
||||
# Run commitlint with the given configuration
|
||||
|
||||
${commitlint}/bin/commitlint --config ${commitlintConfig} --from "$_arg_from" --to "$_arg_to"
|
||||
'';
|
||||
in
|
||||
buildToolbox
|
||||
{
|
||||
name = "postgrest-commitlint";
|
||||
tools = { inherit commitCheck; };
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
{ buildToolbox
|
||||
, checkedShellScript
|
||||
, git
|
||||
, jq
|
||||
, libfaketime
|
||||
, python3
|
||||
, python3Packages
|
||||
, runCommand
|
||||
, vegeta
|
||||
, withTools
|
||||
, writers
|
||||
@@ -18,6 +22,8 @@ let
|
||||
];
|
||||
}
|
||||
''
|
||||
echo "Starting vegeta loadtest..."
|
||||
|
||||
# ARG_USE_ENV only adds defaults or docs for environment variables
|
||||
# We manually implement a required check here
|
||||
# See also: https://github.com/matejak/argbash/issues/80
|
||||
@@ -40,53 +46,75 @@ let
|
||||
docs = "Run the vegeta loadtests with PostgREST.";
|
||||
args = [
|
||||
"ARG_OPTIONAL_SINGLE([output], [o], [Filename to dump json output to], [./loadtest/result.bin])"
|
||||
"ARG_OPTIONAL_SINGLE([testdir], [t], [Directory to load tests and fixtures from], [./test/load])"
|
||||
"ARG_OPTIONAL_SINGLE([kind], [k], [Kind of loadtest (mixed: repeat mixed requests, jwt: run once over many requests with unique jwts)], [mixed])"
|
||||
"ARG_TYPE_GROUP_SET([KIND], [KIND], [kind], [mixed,jwt])"
|
||||
"ARG_OPTIONAL_SINGLE([kind], [k], [Kind of loadtest], [mixed])"
|
||||
"ARG_TYPE_GROUP_SET([KIND], [KIND], [kind], [mixed,jwt-cache,jwt-cache-worst])"
|
||||
"ARG_OPTIONAL_SINGLE([monitor], [m], [Monitoring file], [./loadtest/result.csv])"
|
||||
"ARG_LEFTOVERS([additional vegeta arguments])"
|
||||
];
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
# previously required settings to make this work with older branches
|
||||
export PGRST_DB_ANON_ROLE="postgrest_test_anonymous"
|
||||
export PGRST_DB_URI="postgresql://"
|
||||
export PGRST_DB_SCHEMAS="test"
|
||||
|
||||
export PGRST_DB_CONFIG="false"
|
||||
export PGRST_DB_POOL="1"
|
||||
export PGRST_DB_SCHEMAS="test"
|
||||
export PGRST_DB_TX_END="rollback-allow-override"
|
||||
export PGRST_LOG_LEVEL="crit"
|
||||
export PGRST_JWT_SECRET="reallyreallyreallyreallyverysafe"
|
||||
export PGRST_JWT_CACHE_MAX_LIFETIME="86400"
|
||||
|
||||
mkdir -p "$(dirname "$_arg_output")"
|
||||
abs_output="$(realpath "$_arg_output")"
|
||||
|
||||
case "$_arg_kind" in
|
||||
jwt)
|
||||
|
||||
${genTargets} "$_arg_testdir"/gen_targets.http
|
||||
jwt-cache)
|
||||
export PGRST_JWT_SECRET="@${generatedTargets}/gen_jwks.json"
|
||||
|
||||
# shellcheck disable=SC2145
|
||||
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
||||
${withTools.withPgrst} \
|
||||
sh -c "cd \"$_arg_testdir\" && ${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||
${vegeta}/bin/vegeta report -type=text "$_arg_output"
|
||||
${withTools.withPg} -f test/load/fixtures.sql \
|
||||
${withTools.withPgrst} --faketime '2000-01-01 00:00:00' -m "$_arg_monitor" \
|
||||
sh -c "cd test/load && \
|
||||
${runner} -targets ${generatedTargets}/gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||
;;
|
||||
|
||||
*)
|
||||
|
||||
# here we sleep purposefully to check how much memory does the schema cache consume in the final report
|
||||
mixed)
|
||||
# shellcheck disable=SC2145
|
||||
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
||||
${withTools.withSlowPg} \
|
||||
${withTools.withPgrst} \
|
||||
${withTools.withSlowPgrst} \
|
||||
sh -c "cd \"$_arg_testdir\" && ${runner} -targets targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||
${vegeta}/bin/vegeta report -type=text "$_arg_output"
|
||||
${withTools.withPg} -f test/load/fixtures.sql \
|
||||
${withTools.withPgrst} --timeout 2 --sleep 5 -m "$_arg_monitor" \
|
||||
sh -c "cd test/load && \
|
||||
${runner} -targets targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
${vegeta}/bin/vegeta report -type=text "$_arg_output"
|
||||
|
||||
if [ "$_arg_kind" != "mixed" ]; then
|
||||
# fail in case 401 happened on jwt loadtests
|
||||
unauthorized_count="$(${vegeta}/bin/vegeta report -type=json "$_arg_output" \
|
||||
| ${jq}/bin/jq -r '.status_codes["401"] // 0')"
|
||||
|
||||
if [ "$unauthorized_count" -gt 0 ]; then
|
||||
last_unauthorized_body="$(${vegeta}/bin/vegeta encode "$_arg_output" \
|
||||
| ${jq}/bin/jq -rn '
|
||||
reduce inputs as $item (null;
|
||||
if $item.code == 401 then $item else . end
|
||||
)
|
||||
| if . == null then
|
||||
empty
|
||||
else
|
||||
(.body | @base64d)
|
||||
end
|
||||
')"
|
||||
|
||||
echo "loadtest failed: found $unauthorized_count 401 Unauthorized responses" >&2
|
||||
if [ -n "$last_unauthorized_body" ]; then
|
||||
printf '%s\n' "Last 401 response body:" >&2
|
||||
printf '%s\n' "$last_unauthorized_body" >&2
|
||||
fi
|
||||
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
'';
|
||||
|
||||
loadtestAgainst =
|
||||
@@ -101,6 +129,9 @@ let
|
||||
Run the vegeta loadtest against every target branch and HEAD:
|
||||
- once on the every <target-#> branch
|
||||
- once in the current worktree
|
||||
|
||||
Note that the Nix tooling is always taken from the HEAD branch, while the PostgREST binary is taken from the target branch.
|
||||
For a discussion on why this is set up like this, see https://github.com/PostgREST/postgrest/pull/5013#discussion_r3431508441.
|
||||
'';
|
||||
args = [
|
||||
"ARG_POSITIONAL_INF([target], [Commit-ish reference to compare with], 1)"
|
||||
@@ -115,20 +146,48 @@ let
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
for tgt in "''${_arg_target[@]}"; do
|
||||
# Build postgrest for every target and HEAD.
|
||||
# Keeps a reference to the postgrest binary and faketime lib for every branch to run later.
|
||||
declare -A pgrst faketime
|
||||
for tgt in "''${_arg_target[@]}" HEAD; do
|
||||
# not using withTmpDir here, because we don't want to keep the directory on error
|
||||
tmpdir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
|
||||
${git}/bin/git worktree add -f "$tmpdir" "$tgt" > /dev/null
|
||||
pushd "$tmpdir" > /dev/null
|
||||
|
||||
build_start=$SECONDS
|
||||
echo -n "${name}: Building postgrest (nix) on $tgt... "
|
||||
# Using lib.getBin to also make this work with older checkouts, where .bin was not a thing, yet.
|
||||
nix-build --no-out-link -E 'with import ./. {}; pkgs.lib.getBin postgrestPackage' > build.log 2>&1 || {
|
||||
echo "failed, output:"
|
||||
cat build.log
|
||||
exit 1
|
||||
}
|
||||
pgrst[$tgt]="$(nix-build --no-out-link -E 'with import ./. {}; pkgs.lib.getBin postgrestPackage')/bin/postgrest"
|
||||
# To avoid glibc mismatches with back-branches, we need to take libfaketime from the target branch.
|
||||
faketime[$tgt]="$(nix-build --no-out-link -A pkgs.libfaketime)/lib/libfaketime.so.1"
|
||||
build_end=$((SECONDS - build_start))
|
||||
printf "done in %ss.\n" "$build_end"
|
||||
|
||||
popd > /dev/null
|
||||
${git}/bin/git worktree remove -f "$tmpdir" > /dev/null
|
||||
rm -rf "$tmpdir"
|
||||
done
|
||||
|
||||
# Run loadtest for every target and HEAD.
|
||||
# Running the tests is separated from building them to reduce the chances of
|
||||
# other processes skewing the results between two runs.
|
||||
for tgt in "''${_arg_target[@]}" HEAD; do
|
||||
|
||||
cat << EOF
|
||||
|
||||
Running loadtest on "$tgt"...
|
||||
Running "$_arg_kind" loadtest on "$tgt"...
|
||||
|
||||
EOF
|
||||
|
||||
# Runs the test files from the current working tree
|
||||
# to make sure both tests are run with the same files.
|
||||
# Save the results in the current working tree, too,
|
||||
# otherwise they'd be lost in the temporary working tree
|
||||
# created by withTools.withGit.
|
||||
${withTools.withGit} "$tgt" ${loadtest} -k "$_arg_kind" --output "$PWD/loadtest/$tgt.bin" --testdir "$PWD/test/load"
|
||||
FAKETIME_LIB="''${faketime[$tgt]}" PGRST_CMD="''${pgrst[$tgt]}" ${loadtest} -k "$_arg_kind" -m "loadtest/$tgt.csv" --output "loadtest/$tgt.bin"
|
||||
|
||||
cat << EOF
|
||||
|
||||
@@ -137,20 +196,6 @@ let
|
||||
EOF
|
||||
|
||||
done
|
||||
|
||||
cat << EOF
|
||||
|
||||
Running loadtest on HEAD...
|
||||
|
||||
EOF
|
||||
|
||||
${loadtest} -k "$_arg_kind" --output "$PWD/loadtest/head.bin" --testdir "$PWD/test/load"
|
||||
|
||||
cat << EOF
|
||||
|
||||
Done running on HEAD.
|
||||
|
||||
EOF
|
||||
'';
|
||||
|
||||
reporter =
|
||||
@@ -160,12 +205,14 @@ let
|
||||
docs = "Create a named json report for a single result file.";
|
||||
args = [
|
||||
"ARG_POSITIONAL_SINGLE([file], [Filename of result to create report for])"
|
||||
"ARG_OPTIONAL_SINGLE([percentile], [p], [Percentile to report latency for], 50)"
|
||||
"ARG_LEFTOVERS([additional vegeta arguments])"
|
||||
];
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
${vegeta}/bin/vegeta report -type=json "$_arg_file" \
|
||||
${vegeta}/bin/vegeta encode "$_arg_file" \
|
||||
| ${jq}/bin/jq --arg percentile "$_arg_percentile" --slurp 'map(select(.url != "")) | group_by("\(.code) \(.method) \(.url)") | map({("\(.[0].code) \(.[0].method) \(.[0].url)" | sub("http://postgrest";"")): map(.latency) | sort | .[(length-1) * ($percentile | tonumber) / 100 | floor] / 10e3 }) | .[]' \
|
||||
| ${jq}/bin/jq --arg branch "$(basename "$_arg_file" .bin)" '. + {branch: $branch}'
|
||||
'';
|
||||
|
||||
@@ -178,11 +225,31 @@ let
|
||||
import sys
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def evaluate_change(df):
|
||||
try:
|
||||
return ((df['HEAD'] / df['main'] - 1) * 100) \
|
||||
.map(lambda r: "{icon} {ratio:.1f} %".format(
|
||||
ratio=r,
|
||||
# Hardcoded failure threshold for CI is 5% here.
|
||||
icon="" if r < 5 else ":x:"
|
||||
))
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
pd.read_json(sys.stdin) \
|
||||
.set_index('param') \
|
||||
.drop(['branch', 'earliest', 'end', 'latest']) \
|
||||
.rename(columns={'latency': sys.argv[1]}) \
|
||||
.set_index(sys.argv[1]) \
|
||||
.drop(['branch']) \
|
||||
.convert_dtypes() \
|
||||
.to_markdown(sys.stdout, floatfmt='.0f')
|
||||
.assign(change=evaluate_change) \
|
||||
.to_markdown(
|
||||
sys.stdout,
|
||||
floatfmt='.1f',
|
||||
colglobalalign='right',
|
||||
colalign=('left',)
|
||||
)
|
||||
'';
|
||||
|
||||
|
||||
@@ -191,18 +258,57 @@ let
|
||||
{
|
||||
name = "postgrest-loadtest-report";
|
||||
docs = "Create a report of all loadtest reports as markdown.";
|
||||
args = [
|
||||
"ARG_OPTIONAL_SINGLE([group], [g], [Marker to group results])"
|
||||
"ARG_OPTIONAL_SINGLE([percentile], [p], [Percentile to report latency for], 50)"
|
||||
];
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
find loadtest -type f -iname '*.bin' -exec ${reporter} {} \; \
|
||||
| ${jq}/bin/jq '[paths(scalars) as $path | {param: $path | join("."), (.branch): getpath($path)}]' \
|
||||
| ${jq}/bin/jq --slurp 'flatten | group_by(.param) | map(add)' \
|
||||
| ${toMarkdown}
|
||||
echo -e "## Loadtest results $_arg_group (P$_arg_percentile)\n"
|
||||
|
||||
find loadtest -type f -iname '*.bin' -exec ${reporter} -p "$_arg_percentile" {} \; \
|
||||
| ${jq}/bin/jq '[paths(scalars) as $path | {latency: $path | join("."), (.branch): getpath($path)}]' \
|
||||
| ${jq}/bin/jq --slurp 'flatten | group_by(.latency) | map(add)' \
|
||||
| ${toMarkdown} "P$_arg_percentile latency [μs]"
|
||||
'';
|
||||
|
||||
genTargets = writers.writePython3 "postgrest-gen-loadtest-targets" { } (builtins.readFile ./generate_targets.py);
|
||||
report-load =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-loadtest-report-load";
|
||||
docs = "Create a report of all CPU/MEM usage as markdown.";
|
||||
args = [
|
||||
"ARG_OPTIONAL_SINGLE([group], [g], [Marker to group results])"
|
||||
];
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
echo -e "\n\n## Loadtest elapsed seconds vs CPU/MEM usage $_arg_group\n"
|
||||
|
||||
find loadtest -type f -iname '*.csv' \
|
||||
| sort -m \
|
||||
| ${mergeMonitorResults}
|
||||
'';
|
||||
|
||||
generatedTargets =
|
||||
runCommand "postgrest-loadtest-targets"
|
||||
{
|
||||
nativeBuildInputs = [ (python3.withPackages (pyps: [ pyps.jwcrypto ])) ];
|
||||
}
|
||||
''
|
||||
mkdir -p "$out"
|
||||
${libfaketime}/bin/faketime '2000-01-01 00:00:00' python3 ${./generate_targets.py} "$out"
|
||||
'';
|
||||
|
||||
mergeMonitorResults =
|
||||
writers.writePython3 "postgrest-merge-monitor-results"
|
||||
{
|
||||
libraries = [ python3Packages.pandas python3Packages.tabulate ];
|
||||
}
|
||||
(builtins.readFile ./merge_monitor_result.py);
|
||||
in
|
||||
buildToolbox {
|
||||
name = "postgrest-loadtest";
|
||||
tools = { inherit loadtest loadtestAgainst report; };
|
||||
tools = { inherit loadtest loadtestAgainst report report-load; };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import os
|
||||
import sys
|
||||
import pandas as pd
|
||||
|
||||
KEY = "Elapsed seconds"
|
||||
BASE_METRICS = ["CPU (%)", "Real (MB)"]
|
||||
branch_order = []
|
||||
merged = None
|
||||
|
||||
paths = [p.strip() for p in sys.stdin.read().split() if p.strip()]
|
||||
|
||||
for csv_path in paths:
|
||||
# br is branch (variable shortened to pass linter)
|
||||
br = os.path.splitext(os.path.basename(csv_path))[0]
|
||||
branch_order.append(br)
|
||||
|
||||
df = pd.read_csv(csv_path)
|
||||
|
||||
if KEY not in df.columns:
|
||||
sys.exit(f"{csv_path} is missing the {KEY} column")
|
||||
|
||||
for m in BASE_METRICS:
|
||||
if m not in df.columns:
|
||||
sys.exit(f"Error: '{csv_path}' missing required column '{m}'.")
|
||||
|
||||
# add branch marker to every metric column
|
||||
df = df.rename(columns={c: f"{c} [{br}]" for c in df.columns if c != KEY})
|
||||
|
||||
# outer join so missing rows appear
|
||||
merged = df if merged is None else merged.merge(df, on=KEY, how="outer")
|
||||
|
||||
# Re-order columns so related metrics are adjacent
|
||||
ordered_cols = [KEY]
|
||||
for metric in BASE_METRICS:
|
||||
for br in branch_order:
|
||||
col_name = f"{metric} [{br}]"
|
||||
if col_name in merged.columns:
|
||||
ordered_cols.append(col_name)
|
||||
|
||||
merged = merged[ordered_cols]
|
||||
|
||||
# replace nan with empty string
|
||||
merged = merged.fillna("")
|
||||
merged.to_markdown(sys.stdout, index=False, tablefmt="github")
|
||||
@@ -0,0 +1,57 @@
|
||||
# Monitor a process pid with psutil and emits a CSV.
|
||||
import sys
|
||||
import time
|
||||
import psutil
|
||||
import pandas as pd
|
||||
|
||||
KEY = "Elapsed seconds"
|
||||
BASE_METRICS = ["CPU (%)", "Real (MB)"]
|
||||
SAMPLE_INTERVAL_SECS = 1
|
||||
|
||||
if len(sys.argv) != 2 or not sys.argv[1].isdigit():
|
||||
sys.exit(f"Usage: {sys.argv[0]} <PID>")
|
||||
|
||||
pid = int(sys.argv[1])
|
||||
try:
|
||||
proc = psutil.Process(pid)
|
||||
except psutil.NoSuchProcess:
|
||||
sys.exit(f"Error: process {pid} not found.")
|
||||
|
||||
print(f"Starting monitoring of {pid} pid", file=sys.stderr)
|
||||
|
||||
records = []
|
||||
start = time.time()
|
||||
# ignore first result as per docs recommendation
|
||||
# https://psutil.readthedocs.io/en/latest/#psutil.cpu_percent
|
||||
proc.cpu_percent(None)
|
||||
|
||||
while True:
|
||||
try:
|
||||
if not proc.is_running():
|
||||
break
|
||||
time.sleep(SAMPLE_INTERVAL_SECS)
|
||||
|
||||
elapsed_secs = int(time.time() - start)
|
||||
cpu = proc.cpu_percent(None)
|
||||
meminfo = proc.memory_info()
|
||||
bytes_in_MB = 1024**2
|
||||
rss_mb = meminfo.rss / bytes_in_MB
|
||||
|
||||
records.append(
|
||||
[
|
||||
str(elapsed_secs),
|
||||
f"{cpu:.3f}",
|
||||
f"{rss_mb:.3f}",
|
||||
]
|
||||
)
|
||||
|
||||
except psutil.NoSuchProcess:
|
||||
break
|
||||
|
||||
end = time.time()
|
||||
total_time = end - start
|
||||
print(f"Finished {pid} pid monitoring in {total_time:.3f}", file=sys.stderr)
|
||||
|
||||
cols = [KEY] + BASE_METRICS
|
||||
df = pd.DataFrame(records, columns=cols, dtype=str)
|
||||
df.to_csv(sys.stdout, index=False)
|
||||
@@ -0,0 +1,28 @@
|
||||
{ buildToolbox
|
||||
, checkedShellScript
|
||||
}:
|
||||
# Utility script for pinning the latest stable version of Nixpkgs.
|
||||
|
||||
# Instead of running `nix flake update` manually, we run this script
|
||||
# to also pin readthedocs dependencies at the same time.
|
||||
let
|
||||
upgrade =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-nixpkgs-upgrade";
|
||||
docs = "Pin the newest version of Nixpkgs.";
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
nix flake update
|
||||
|
||||
echo "# This file is auto-generated by postgrest-nixpkgs-upgrade" > docs/requirements.txt
|
||||
cat "$(nix-build --no-out-link -A docs.requirements)" >> docs/requirements.txt
|
||||
'';
|
||||
|
||||
in
|
||||
buildToolbox
|
||||
{
|
||||
name = "postgrest-nixpkgs";
|
||||
tools = { inherit upgrade; };
|
||||
}
|
||||
@@ -7,7 +7,6 @@ let
|
||||
{
|
||||
name = "postgrest-release";
|
||||
docs = "Patch postgrest.cabal, CHANGELOG.md, commit and push all in one go.";
|
||||
args = [ "ARG_OPTIONAL_BOOLEAN([major], [m], [Bump to new major version (only applies on main branch).])" ];
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
@@ -23,24 +22,20 @@ let
|
||||
bump () {
|
||||
current_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
|
||||
# shellcheck disable=SC2034
|
||||
IFS=. read -r major minor patch <<< "$current_version"
|
||||
IFS=. read -r A B C D <<< "$current_version"
|
||||
echo "Current version is $current_version"
|
||||
|
||||
case "$1" in
|
||||
major)
|
||||
new_version="$((major+1)).0.0"
|
||||
new_docs_version="$((major+1)).0"
|
||||
A)
|
||||
new_version="$((A+1)).0"
|
||||
new_docs_version="$((A+1))"
|
||||
;;
|
||||
minor)
|
||||
new_version="$major.$((minor+1)).0"
|
||||
new_docs_version="$major.$((minor+1))"
|
||||
;;
|
||||
patch)
|
||||
new_version="$major.$minor.$((patch+1))"
|
||||
new_docs_version="$major.$minor"
|
||||
B)
|
||||
new_version="$A.$((B+1))"
|
||||
new_docs_version="$A"
|
||||
;;
|
||||
devel)
|
||||
new_version="$major.$((minor+1))"
|
||||
new_version="$((A+1))"
|
||||
new_docs_version="devel"
|
||||
;;
|
||||
esac
|
||||
@@ -50,18 +45,17 @@ let
|
||||
echo "Updating docs/conf.py ..."
|
||||
sed -i -E "s/^(version = ).*$/\1\"$new_docs_version\"/" docs/conf.py > /dev/null
|
||||
|
||||
git add postgrest.cabal docs/conf.py > /dev/null
|
||||
echo "Updating Haskell source file links ..."
|
||||
sed -i -E "s#(github\.com/PostgREST/postgrest/blob)/main/#\1/$new_version/#g" docs/explanations/architecture.rst
|
||||
|
||||
git add postgrest.cabal docs/conf.py docs/explanations/architecture.rst > /dev/null
|
||||
}
|
||||
|
||||
today_date_for_changelog="$(date '+%Y-%m-%d')"
|
||||
if [[ "$current_branch" == "main" ]]; then
|
||||
if [[ "$_arg_major" == "on" ]]; then
|
||||
bump major
|
||||
else
|
||||
bump minor
|
||||
fi
|
||||
bump A
|
||||
else
|
||||
bump patch
|
||||
bump B
|
||||
fi
|
||||
|
||||
echo "Updating CHANGELOG.md ..."
|
||||
@@ -69,19 +63,19 @@ let
|
||||
git add CHANGELOG.md > /dev/null
|
||||
|
||||
echo "Committing ..."
|
||||
git commit -m "bump version to $new_version" > /dev/null
|
||||
git commit -m "chore: bump version to $new_version" > /dev/null
|
||||
|
||||
if [[ "$current_branch" == "main" ]]; then
|
||||
bump devel
|
||||
|
||||
# The order of operations is important here:
|
||||
# - bump devel is run and $major is upated to the new version
|
||||
# - the branch is created with the new major, but the commit before the devel bump
|
||||
# - bump devel is run and $A is updated to the new version
|
||||
# - the branch is created with the new A, but the commit before the devel bump
|
||||
# - the devel bump is committed
|
||||
git branch -f "v$major"
|
||||
git branch "v$A"
|
||||
|
||||
echo "Committing (devel bump)..."
|
||||
git commit -m "bump version to $new_version" > /dev/null
|
||||
git commit -m "chore: bump version to $new_version" > /dev/null
|
||||
fi
|
||||
|
||||
trap "echo Remote not found. Please push manually ..." ERR
|
||||
@@ -90,7 +84,7 @@ let
|
||||
|
||||
if [[ "$current_branch" == "main" ]]; then
|
||||
push1="git push $remote $current_branch"
|
||||
push2="git push $remote v$major --force"
|
||||
push2="git push $remote v$A"
|
||||
else
|
||||
push1="git push $remote $current_branch"
|
||||
push2=""
|
||||
|
||||
@@ -3,13 +3,16 @@
|
||||
, buildToolbox
|
||||
, checkedShellScript
|
||||
, deadnix
|
||||
, fd
|
||||
, git
|
||||
, hlint
|
||||
, hsie
|
||||
, nixpkgs-fmt
|
||||
, silver-searcher
|
||||
, python3Packages
|
||||
, ruff
|
||||
, statix
|
||||
, stylish-haskell
|
||||
, writeText
|
||||
}:
|
||||
let
|
||||
style =
|
||||
@@ -18,27 +21,27 @@ let
|
||||
name = "postgrest-style";
|
||||
docs = "Automatically format Haskell, Nix and Python files.";
|
||||
workingDir = "/";
|
||||
withTmpDir = true;
|
||||
}
|
||||
''
|
||||
# Format Nix files
|
||||
${statix}/bin/statix fix
|
||||
${nixpkgs-fmt}/bin/nixpkgs-fmt . > /dev/null 2> /dev/null
|
||||
${nixpkgs-fmt}/bin/nixpkgs-fmt .
|
||||
|
||||
# Format Haskell files
|
||||
# --vimgrep fixes a bug in ag: https://github.com/ggreer/the_silver_searcher/issues/753
|
||||
${silver-searcher}/bin/ag -l --vimgrep -g '\.l?hs$' . \
|
||||
${fd}/bin/fd '\.l?hs$' \
|
||||
| xargs ${stylish-haskell}/bin/stylish-haskell -i
|
||||
|
||||
# Format Python files
|
||||
${black}/bin/black . 2> /dev/null
|
||||
TMPDIR="$tmpdir" ${black}/bin/black .
|
||||
'';
|
||||
|
||||
# Script to check whether any uncommited changes result from postgrest-style
|
||||
# Script to check whether any uncommitted changes result from postgrest-style
|
||||
styleCheck =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-style-check";
|
||||
docs = "Check whether postgrest-style results in any uncommited changes.";
|
||||
docs = "Check whether postgrest-style results in any uncommitted changes.";
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
@@ -49,6 +52,20 @@ let
|
||||
${git}/bin/git diff-index --exit-code HEAD -- '*.hs' '*.lhs' '*.nix' '*.py'
|
||||
'';
|
||||
|
||||
hlintConfig = writeText "hlintConfig.yml" ''
|
||||
|
||||
# Arguments passed to hlint
|
||||
- arguments: [-j, -XQuasiQuotes, -XNoPatternSynonyms]
|
||||
|
||||
# Warnings
|
||||
- warn: { lhs: "a == a", rhs: "True", note: "This comparison always evaluates to True" }
|
||||
- warn: { lhs: "a /= a", rhs: "False", note: "This comparison always evaluates to False" }
|
||||
- warn: { lhs: "a < a", rhs: "False", note: "This comparison always evaluates to False" }
|
||||
- warn: { lhs: "a > a", rhs: "False", note: "This comparison always evaluates to False" }
|
||||
- warn: { lhs: "a <= a", rhs: "True", note: "This comparison always evaluates to True" }
|
||||
- warn: { lhs: "a >= a", rhs: "True", note: "This comparison always evaluates to True" }
|
||||
'';
|
||||
|
||||
lint =
|
||||
checkedShellScript
|
||||
{
|
||||
@@ -63,13 +80,20 @@ let
|
||||
echo "Scanning nix files for unused code..."
|
||||
${deadnix}/bin/deadnix -f
|
||||
|
||||
# ruff has gaps in scanning for unused code, so we use vulture
|
||||
echo "Scanning python files for unused code..."
|
||||
${fd}/bin/fd '\.l?py$' \
|
||||
| xargs ${python3Packages.vulture}/bin/vulture --exclude docs/conf.py --min-confidence 80
|
||||
|
||||
echo "Linting python files..."
|
||||
${ruff}/bin/ruff check .
|
||||
|
||||
echo "Checking consistency of import aliases in Haskell code..."
|
||||
${hsie} check-aliases main src
|
||||
${hsie} check-aliases src/library src/executable
|
||||
|
||||
echo "Linting Haskell files..."
|
||||
# --vimgrep fixes a bug in ag: https://github.com/ggreer/the_silver_searcher/issues/753
|
||||
${silver-searcher}/bin/ag -l --vimgrep -g '\.l?hs$' . \
|
||||
| xargs ${hlint}/bin/hlint -X QuasiQuotes -X NoPatternSynonyms
|
||||
${fd}/bin/fd '\.l?hs$' \
|
||||
| xargs ${hlint}/bin/hlint --hint=${hlintConfig}
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
, glibcLocales ? null
|
||||
, gnugrep
|
||||
, hpc-codecov
|
||||
, hostPlatform
|
||||
, jq
|
||||
, lib
|
||||
, nginx
|
||||
, postgrest
|
||||
, python3
|
||||
, runtimeShell
|
||||
@@ -28,11 +28,24 @@ let
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${withTools.withPg} -f test/spec/fixtures/load.sql \
|
||||
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec -- "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
|
||||
testObservability =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-test-observability";
|
||||
docs = "Run the Haskell observability test suite.";
|
||||
args = [ "ARG_LEFTOVERS([hspec arguments])" ];
|
||||
workingDir = "/";
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
${withTools.withPg} -f test/observability/fixtures/load.sql \
|
||||
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:observability -- "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
|
||||
testDoctests =
|
||||
checkedShellScript
|
||||
{
|
||||
@@ -42,9 +55,6 @@ let
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
# This makes nix-env -iA tests.doctests.bin work.
|
||||
export NIX_GHC=${postgrest.env.NIX_GHC}
|
||||
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:doctests
|
||||
'';
|
||||
|
||||
@@ -57,7 +67,6 @@ let
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${withTools.withPg} -f test/spec/fixtures/load.sql \
|
||||
${runtimeShell} -c " \
|
||||
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec && \
|
||||
@@ -83,11 +92,11 @@ let
|
||||
args = [ "ARG_LEFTOVERS([pytest arguments])" ];
|
||||
workingDir = "/";
|
||||
withEnv = postgrest.env;
|
||||
withPath = [ nginx ];
|
||||
}
|
||||
''
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest
|
||||
${cabal-install}/bin/cabal v2-exec -- ${withTools.withPg} -f test/io/fixtures.sql \
|
||||
${cabal-install}/bin/cabal v2-exec -- ${withTools.withPg} -f test/io/fixtures/load.sql \
|
||||
${ioTestPython}/bin/pytest --ignore=test/io/test_big_schema.py --ignore=test/io/test_replica.py -v test/io "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
|
||||
@@ -101,9 +110,8 @@ let
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest
|
||||
${cabal-install}/bin/cabal v2-exec -- ${withTools.withPg} -f test/io/big_schema.sql \
|
||||
${cabal-install}/bin/cabal v2-exec -- ${withTools.withPg} -f test/io/fixtures/big_schema.sql \
|
||||
${ioTestPython}/bin/pytest -v test/io/test_big_schema.py "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
|
||||
@@ -117,9 +125,8 @@ let
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest
|
||||
${cabal-install}/bin/cabal v2-exec -- ${withTools.withPg} --replica -f test/io/replica.sql \
|
||||
${cabal-install}/bin/cabal v2-exec -- ${withTools.withPg} --replica -f test/io/fixtures/replica.sql \
|
||||
${ioTestPython}/bin/pytest -v test/io/test_replica.py "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
|
||||
@@ -133,7 +140,6 @@ let
|
||||
withPath = [ jq ];
|
||||
}
|
||||
''
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${withTools.withPg} -f test/spec/fixtures/load.sql \
|
||||
${cabal-install}/bin/cabal v2-run ${devCabalOptions} --verbose=0 -- \
|
||||
postgrest --dump-schema
|
||||
@@ -149,10 +155,11 @@ let
|
||||
redirectTixFiles = false;
|
||||
withEnv = postgrest.env;
|
||||
withTmpDir = true;
|
||||
withPath = [ nginx ];
|
||||
}
|
||||
(
|
||||
# required for `hpc markup` in CI; glibcLocales is not available e.g. on Darwin
|
||||
lib.optionalString (stdenv.isLinux && hostPlatform.libc == "glibc") ''
|
||||
lib.optionalString (stdenv.isLinux && stdenv.hostPlatform.libc == "glibc") ''
|
||||
export LOCALE_ARCHIVE="${glibcLocales}/lib/locale/locale-archive"
|
||||
'' +
|
||||
|
||||
@@ -162,8 +169,7 @@ let
|
||||
rm -rf coverage/*
|
||||
|
||||
# build once before running all the tests
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest lib:postgrest test:spec
|
||||
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest lib:postgrest test:spec test:observability
|
||||
|
||||
(
|
||||
trap 'echo Found dead code: Check file list above.' ERR ;
|
||||
@@ -172,26 +178,31 @@ let
|
||||
|
||||
# collect all tests
|
||||
HPCTIXFILE="$tmpdir"/io.tix \
|
||||
${withTools.withPg} -f test/io/fixtures.sql \
|
||||
${withTools.withPg} -f test/io/fixtures/load.sql \
|
||||
${cabal-install}/bin/cabal v2-exec ${devCabalOptions} -- ${ioTestPython}/bin/pytest --ignore=test/io/test_big_schema.py --ignore=test/io/test_replica.py -v test/io
|
||||
|
||||
HPCTIXFILE="$tmpdir"/big_schema.tix \
|
||||
${withTools.withPg} -f test/io/big_schema.sql \
|
||||
${withTools.withPg} -f test/io/fixtures/big_schema.sql \
|
||||
${cabal-install}/bin/cabal v2-exec ${devCabalOptions} -- ${ioTestPython}/bin/pytest -v test/io/test_big_schema.py
|
||||
|
||||
HPCTIXFILE="$tmpdir"/replica.tix \
|
||||
${withTools.withPg} --replica -f test/io/replica.sql \
|
||||
${withTools.withPg} --replica -f test/io/fixtures/replica.sql \
|
||||
${cabal-install}/bin/cabal v2-exec ${devCabalOptions} -- ${ioTestPython}/bin/pytest -v test/io/test_replica.py
|
||||
|
||||
HPCTIXFILE="$tmpdir"/spec.tix \
|
||||
${withTools.withPg} -f test/spec/fixtures/load.sql \
|
||||
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec
|
||||
|
||||
HPCTIXFILE="$tmpdir"/observability.tix \
|
||||
${withTools.withPg} -f test/observability/fixtures/load.sql \
|
||||
${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:observability
|
||||
|
||||
# Note: No coverage for doctests, as doctests leverage GHCi and GHCi does not support hpc
|
||||
|
||||
# collect all the tix files
|
||||
${ghc}/bin/hpc sum --union --exclude=Paths_postgrest --output="$tmpdir"/tests.tix \
|
||||
"$tmpdir"/io*.tix "$tmpdir"/big_schema*.tix "$tmpdir"/replica*.tix "$tmpdir"/spec.tix
|
||||
"$tmpdir"/io*.tix "$tmpdir"/big_schema*.tix "$tmpdir"/replica*.tix "$tmpdir"/spec.tix \
|
||||
"$tmpdir"/observability.tix
|
||||
|
||||
# prepare the overlay
|
||||
${ghc}/bin/hpc overlay --output="$tmpdir"/overlay.tix test/coverage.overlay
|
||||
@@ -246,7 +257,6 @@ let
|
||||
withPath = [ curl ];
|
||||
}
|
||||
''
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${cabal-install}/bin/cabal --builddir="dist-prof" v2-build --enable-profiling --disable-shared exe:postgrest
|
||||
${cabal-install}/bin/cabal --builddir="dist-prof" v2-exec -- ${withTools.withPg} -f test/spec/fixtures/load.sql \
|
||||
test/memory/memory-tests.sh
|
||||
@@ -259,6 +269,7 @@ buildToolbox
|
||||
tools = {
|
||||
inherit
|
||||
testSpec
|
||||
testObservability
|
||||
testDoctests
|
||||
testSpecIdempotence
|
||||
testIO
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
{ buildToolbox
|
||||
, checkedShellScript
|
||||
, curl
|
||||
, git
|
||||
, lib
|
||||
, libfaketime
|
||||
, postgresqlVersions
|
||||
, postgrest
|
||||
, slocat
|
||||
, python3Packages
|
||||
, writeText
|
||||
, writers
|
||||
}:
|
||||
let
|
||||
withTmpDb =
|
||||
{ name, postgresql }:
|
||||
{ name, postgresql, config ? "" }:
|
||||
let
|
||||
commandName = "postgrest-with-${name}";
|
||||
postgresqlConf = writeText "postgresql.conf" ("
|
||||
autovacuum = false
|
||||
listen_addresses = ''
|
||||
log_statement = all
|
||||
shared_preload_libraries=pg_stat_statements
|
||||
" + config);
|
||||
in
|
||||
checkedShellScript
|
||||
{
|
||||
@@ -23,7 +30,7 @@ let
|
||||
"ARG_OPTIONAL_SINGLE([fixtures], [f], [SQL file to load fixtures from])"
|
||||
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
||||
"ARG_LEFTOVERS([command arguments])"
|
||||
"ARG_USE_ENV([PGUSER], [postgrest_test_authenticator], [Authenticator PG role])"
|
||||
"ARG_USE_ENV([PGUSER], [Postgrest_Test_Authenticator], [Authenticator PG role])" # user is written in mixed case to implicitly test that it is being properly quoted in schema cache queries
|
||||
"ARG_USE_ENV([PGDATABASE], [postgres], [PG database name])"
|
||||
"ARG_USE_ENV([PGRST_DB_SCHEMAS], [test], [Schema to expose])"
|
||||
"ARG_USE_ENV([PGTZ], [utc], [Timezone to use])"
|
||||
@@ -44,7 +51,7 @@ let
|
||||
}
|
||||
|
||||
# Avoid starting multiple layers of withTmpDb, but make sure to have the last invocation
|
||||
# load fixtures. Otherwise postgrest-with-postgresql-xx postgrest-test-io would not be possible.
|
||||
# load fixtures. Otherwise postgrest-with-pg-xx postgrest-test-io would not be possible.
|
||||
if ! test -v PGHOST; then
|
||||
|
||||
mkdir -p "$tmpdir"/{db,socket}
|
||||
@@ -71,9 +78,19 @@ let
|
||||
TZ=$PGTZ initdb --no-locale --encoding=UTF8 --nosync -U postgres --auth=trust \
|
||||
>> "$setuplog"
|
||||
|
||||
# Append our own config to the one initdb created to avoid replacing
|
||||
# default values created by the latter.
|
||||
cat ${postgresqlConf} >> "$tmpdir/db/postgresql.conf"
|
||||
|
||||
log "Starting the database cluster..."
|
||||
|
||||
# Instead of listening on a local port, we will listen on a unix domain socket.
|
||||
pg_ctl -l "$tmpdir/db.log" -w start -o "-F -c listen_addresses=\"\" -c hba_file=$HBA_FILE -k $PGHOST -c log_statement=\"all\" " \
|
||||
# NOTE: unix domain socket filename name must remain under max limit.
|
||||
# On Linux, it's 108 chars (including '\0' terminator)
|
||||
# On MacOS, it's 104 chars
|
||||
# See: https://serverfault.com/questions/641347/check-if-a-path-exceeds-maximum-for-unix-domain-socket
|
||||
|
||||
pg_ctl -l "$tmpdir/db.log" -w start -o "-F -c hba_file=$HBA_FILE -k $PGHOST " \
|
||||
>> "$setuplog"
|
||||
|
||||
log "Creating a minimally privileged $PGUSER connection role..."
|
||||
@@ -98,7 +115,8 @@ let
|
||||
|
||||
log "Starting replica on $replica_host"
|
||||
|
||||
pg_ctl -D "$replica_dir" -l "$replica_dblog" -w start -o "-F -c listen_addresses=\"\" -c hba_file=$HBA_FILE -k $replica_host -c log_statement=\"all\" " \
|
||||
# We set a low max_standby_streaming_delay to make the replication conflict fail faster in tests (otherwise it waits for the default 30s)
|
||||
pg_ctl -D "$replica_dir" -l "$replica_dblog" -w start -o "-F -c hba_file=$HBA_FILE -k $replica_host -c max_standby_streaming_delay=\"3s\" " \
|
||||
>> "$setuplog"
|
||||
|
||||
>&2 echo "${commandName}: Replica enabled. You can connect to it with: psql 'postgres:///$PGDATABASE?host=$replica_host' -U postgres"
|
||||
@@ -109,7 +127,7 @@ let
|
||||
export PGRST_DB_URI="postgres:///$PGDATABASE?host=$PGREPLICAHOST,$PGHOST"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2317
|
||||
# shellcheck disable=SC2329
|
||||
stop () {
|
||||
log "Stopping the database cluster..."
|
||||
pg_ctl stop --mode=immediate >> "$setuplog"
|
||||
@@ -124,9 +142,12 @@ let
|
||||
fi
|
||||
|
||||
if test "$_arg_fixtures"; then
|
||||
log "Loading fixtures under the postgres role..."
|
||||
load_start=$SECONDS
|
||||
>&2 printf "${commandName}: Loading fixtures under the postgres role..."
|
||||
psql -U postgres -v PGUSER="$PGUSER" -v ON_ERROR_STOP=1 -f "$_arg_fixtures" >> "$setuplog"
|
||||
log "Done. Running command..."
|
||||
psql -U postgres -v ON_ERROR_STOP=1 -c "VACUUM ANALYZE;" >> "$setuplog"
|
||||
load_end=$((SECONDS - load_start))
|
||||
>&2 printf " done in %ss. Running command...\n" "$load_end"
|
||||
fi
|
||||
|
||||
("$_arg_command" "''${_arg_leftovers[@]}")
|
||||
@@ -175,134 +196,6 @@ let
|
||||
|
||||
withPg = withTmpDb (builtins.head postgresqlVersions);
|
||||
|
||||
withSlowPg =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-with-slow-pg";
|
||||
docs = "Run the given command with simulated high latency postgresql";
|
||||
args =
|
||||
[
|
||||
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
||||
"ARG_LEFTOVERS([command arguments])"
|
||||
"ARG_USE_ENV([PGHOST], [], [PG host (socket name)])"
|
||||
"ARG_USE_ENV([PGDELAY], [0ms], [extra PG latency (duration)])"
|
||||
];
|
||||
positionalCompletion = "_command";
|
||||
workingDir = "/";
|
||||
redirectTixFiles = false;
|
||||
withTmpDir = true;
|
||||
}
|
||||
''
|
||||
delay="''${PGDELAY:-0ms}"
|
||||
echo "delaying data to/from postgres by $delay"
|
||||
|
||||
REALPGHOST="$PGHOST"
|
||||
export PGHOST="$tmpdir/socket"
|
||||
mkdir -p "$PGHOST"
|
||||
|
||||
${slocat}/bin/slocat -delay "$delay" -src "$PGHOST/.s.PGSQL.5432" -dst "$REALPGHOST/.s.PGSQL.5432" &
|
||||
SLOCAT_PID=$!
|
||||
# shellcheck disable=SC2317
|
||||
stop_slocat() {
|
||||
kill "$SLOCAT_PID" || true
|
||||
wait "$SLOCAT_PID" || true
|
||||
}
|
||||
trap stop_slocat EXIT
|
||||
sleep 1 # should wait for socket file to appear instead
|
||||
|
||||
("$_arg_command" "''${_arg_leftovers[@]}")
|
||||
'';
|
||||
|
||||
withSlowPgrst =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-with-slow-postgrest";
|
||||
docs = "Run the given command with simulated high latency postgrest";
|
||||
args =
|
||||
[
|
||||
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
||||
"ARG_LEFTOVERS([command arguments])"
|
||||
"ARG_USE_ENV([PGRST_SERVER_UNIX_SOCKET], [], [PostgREST host (socket name)])"
|
||||
"ARG_USE_ENV([PGRST_DELAY], [0ms], [extra PostgREST latency (duration)])"
|
||||
];
|
||||
positionalCompletion = "_command";
|
||||
workingDir = "/";
|
||||
redirectTixFiles = false;
|
||||
withTmpDir = true;
|
||||
}
|
||||
''
|
||||
delay="''${PGRST_DELAY:-0ms}"
|
||||
echo "delaying data to/from PostgREST by $delay"
|
||||
|
||||
REAL_PGRST_SERVER_UNIX_SOCKET="$PGRST_SERVER_UNIX_SOCKET"
|
||||
export PGRST_SERVER_UNIX_SOCKET="$tmpdir/postgrest.socket"
|
||||
|
||||
${slocat}/bin/slocat -delay "$delay" -src "$PGRST_SERVER_UNIX_SOCKET" -dst "$REAL_PGRST_SERVER_UNIX_SOCKET" &
|
||||
SLOCAT_PID=$!
|
||||
# shellcheck disable=SC2317
|
||||
stop_slocat() {
|
||||
kill "$SLOCAT_PID" || true
|
||||
wait "$SLOCAT_PID" || true
|
||||
}
|
||||
trap stop_slocat EXIT
|
||||
sleep 1 # should wait for socket file to appear instead
|
||||
|
||||
("$_arg_command" "''${_arg_leftovers[@]}")
|
||||
'';
|
||||
|
||||
withGit =
|
||||
let
|
||||
name = "postgrest-with-git";
|
||||
in
|
||||
checkedShellScript
|
||||
{
|
||||
inherit name;
|
||||
docs =
|
||||
''
|
||||
Create a new worktree of the postgrest repo in a temporary directory and
|
||||
check out <commit>, then run <command> with arguments inside the temporary folder.
|
||||
'';
|
||||
args =
|
||||
[
|
||||
"ARG_POSITIONAL_SINGLE([commit], [Commit-ish reference to run command with])"
|
||||
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
||||
"ARG_LEFTOVERS([command arguments])"
|
||||
];
|
||||
positionalCompletion =
|
||||
''
|
||||
if test "$prev" == "${name}"; then
|
||||
__gitcomp_nl "$(__git_refs)"
|
||||
else
|
||||
_command_offset 2
|
||||
fi
|
||||
'';
|
||||
workingDir = "/";
|
||||
}
|
||||
''
|
||||
# not using withTmpDir here, because we don't want to keep the directory on error
|
||||
tmpdir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
|
||||
${git}/bin/git worktree add -f "$tmpdir" "$_arg_commit" > /dev/null
|
||||
|
||||
cd "$tmpdir"
|
||||
("$_arg_command" "''${_arg_leftovers[@]}")
|
||||
|
||||
${git}/bin/git worktree remove -f "$tmpdir" > /dev/null
|
||||
'';
|
||||
|
||||
legacyConfig =
|
||||
writeText "legacy.conf"
|
||||
''
|
||||
# Using this config file to support older postgrest versions for `postgrest-loadtest-against`
|
||||
db-uri="$(PGRST_DB_URI)"
|
||||
db-schema="$(PGRST_DB_SCHEMAS)"
|
||||
db-anon-role="$(PGRST_DB_ANON_ROLE)"
|
||||
db-pool="$(PGRST_DB_POOL)"
|
||||
server-unix-socket="$(PGRST_SERVER_UNIX_SOCKET)"
|
||||
log-level="$(PGRST_LOG_LEVEL)"
|
||||
'';
|
||||
|
||||
waitForPgrstReady =
|
||||
checkedShellScript
|
||||
{
|
||||
@@ -327,15 +220,40 @@ let
|
||||
done
|
||||
'';
|
||||
|
||||
# Broadcast SIGINT to any running postgrest instances on the host. Uses python for cross-platform compatibility.
|
||||
signalPostgrest =
|
||||
writers.writePython3 "postgrest-signal-int"
|
||||
{ libraries = [ python3Packages.psutil ]; }
|
||||
''
|
||||
import psutil
|
||||
import signal
|
||||
|
||||
for proc in psutil.process_iter(["name"]):
|
||||
try:
|
||||
if proc.info["name"] == "postgrest":
|
||||
proc.send_signal(signal.SIGINT)
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
continue
|
||||
'';
|
||||
|
||||
withPgrst =
|
||||
let
|
||||
commandName = "postgrest-with-pgrst";
|
||||
in
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-with-pgrst";
|
||||
name = commandName;
|
||||
docs = "Build and run PostgREST and run <command> with PGRST_SERVER_UNIX_SOCKET set.";
|
||||
args =
|
||||
[
|
||||
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
||||
"ARG_LEFTOVERS([command arguments])"
|
||||
"ARG_OPTIONAL_SINGLE([faketime], [f], [Fake the system time when starting PostgREST. This is useful to test expiry of JWT, for example in loadtests])"
|
||||
"ARG_OPTIONAL_SINGLE([monitor], [m], [Enable CPU and memory monitoring of the PostgREST process and output to the designated file as markdown])"
|
||||
"ARG_OPTIONAL_SINGLE([timeout], [t], [Maximum time to wait for PostgREST to be ready], [5])"
|
||||
"ARG_OPTIONAL_SINGLE([sleep], [s], [Sleep time after PostgREST is ready, this is useful for monitoring])"
|
||||
"ARG_USE_ENV([FAKETIME_LIB], [${libfaketime}/lib/libfaketime.so.1], [Faketime Library to preload])"
|
||||
"ARG_USE_ENV([PGRST_CMD], [postgrest-run], [PostgREST executable to run])"
|
||||
];
|
||||
positionalCompletion = "_command";
|
||||
workingDir = "/";
|
||||
@@ -345,54 +263,76 @@ let
|
||||
''
|
||||
export PGRST_SERVER_UNIX_SOCKET="$tmpdir"/postgrest.socket
|
||||
|
||||
rm -f result
|
||||
if [ -z "''${PGRST_BUILD_CABAL:-}" ]; then
|
||||
echo -n "Building postgrest (nix)... "
|
||||
nix-build -A postgrestPackage > "$tmpdir"/build.log 2>&1 || {
|
||||
echo "failed, output:"
|
||||
cat "$tmpdir"/build.log
|
||||
exit 1
|
||||
}
|
||||
PGRST_CMD=./result/bin/postgrest
|
||||
else
|
||||
echo -n "Building postgrest (cabal)... "
|
||||
if [ "''${PGRST_CMD}" == "postgrest-run" ]; then
|
||||
build_start=$SECONDS
|
||||
echo -n "${commandName}: Building postgrest (cabal)... "
|
||||
postgrest-build
|
||||
PGRST_CMD=postgrest-run
|
||||
build_end=$((SECONDS - build_start))
|
||||
printf "done in %ss.\n" "$build_end"
|
||||
fi
|
||||
echo "done."
|
||||
|
||||
echo -n "Starting postgrest... "
|
||||
$PGRST_CMD ${legacyConfig} > "$tmpdir"/run.log 2>&1 &
|
||||
ver=$($PGRST_CMD --version)
|
||||
|
||||
echo -n "${commandName}: Starting $ver... "
|
||||
|
||||
if [[ -n "$_arg_faketime" ]]; then
|
||||
LD_PRELOAD="$FAKETIME_LIB" FAKETIME="$_arg_faketime" "$PGRST_CMD" > "$tmpdir"/run.log 2>&1 &
|
||||
else
|
||||
$PGRST_CMD > "$tmpdir"/run.log 2>&1 &
|
||||
fi
|
||||
pid=$!
|
||||
# shellcheck disable=SC2317
|
||||
# shellcheck disable=SC2329
|
||||
cleanup() {
|
||||
# Send INT to all postgrest processes.
|
||||
# Workaround to trigger dumping postgrest.prof for postgrest-profiled-run
|
||||
# Caveat: we cannot realistically limit this to the current process' tree,
|
||||
# since pkill's --parent supports only direct children; therefore this
|
||||
# would reap neighbor postgrest instances as well, because INT is asking
|
||||
# the process to terminate too.
|
||||
# TODO: consider cgroups to make this cleaner
|
||||
${signalPostgrest}
|
||||
kill "$pid" || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
timeout -s TERM 5 ${waitForPgrstReady} || {
|
||||
wait_start=$SECONDS
|
||||
timeout -s TERM "$_arg_timeout" ${waitForPgrstReady} || {
|
||||
echo "timed out, output:"
|
||||
cat "$tmpdir"/run.log
|
||||
exit 1
|
||||
}
|
||||
echo "done."
|
||||
wait_duration=$((SECONDS - wait_start))
|
||||
printf "done in %ss.\n" "$wait_duration"
|
||||
|
||||
echo "${commandName}: You can tail the server logs with: tail -f $tmpdir/run.log"
|
||||
|
||||
if [[ -n "$_arg_monitor" ]]; then
|
||||
${monitorPid} "$pid" > "$_arg_monitor" &
|
||||
fi
|
||||
|
||||
if [[ -n "$_arg_sleep" ]]; then
|
||||
sleep "$_arg_sleep"
|
||||
fi
|
||||
|
||||
("$_arg_command" "''${_arg_leftovers[@]}")
|
||||
'';
|
||||
|
||||
monitorPid =
|
||||
writers.writePython3 "postgrest-monitor-pid"
|
||||
{
|
||||
libraries = [ python3Packages.pandas python3Packages.tabulate python3Packages.psutil ];
|
||||
}
|
||||
(builtins.readFile ./monitor_pid.py);
|
||||
in
|
||||
buildToolbox
|
||||
{
|
||||
name = "postgrest-with";
|
||||
tools = {
|
||||
inherit
|
||||
withGit
|
||||
withPgAll
|
||||
withPgrst
|
||||
withSlowPg
|
||||
withSlowPgrst;
|
||||
withPgrst;
|
||||
} // builtins.listToAttrs (
|
||||
# Create a `postgrest-with-postgresql-` for each PostgreSQL version
|
||||
# Create a `postgrest-with-pg-` for each PostgreSQL version
|
||||
builtins.map (pg: { inherit (pg) name; value = withTmpDb pg; }) postgresqlVersions
|
||||
);
|
||||
# make latest withPg available for other nix files
|
||||
|
||||
@@ -1,32 +1,31 @@
|
||||
cabal-version: 3.0
|
||||
name: postgrest
|
||||
version: 13.0.8
|
||||
version: 17
|
||||
synopsis: REST API for any Postgres database
|
||||
description: Reads the schema of a PostgreSQL database and creates RESTful routes
|
||||
for tables, views, and functions, supporting all HTTP methods that security
|
||||
permits.
|
||||
license: MIT
|
||||
license-file: LICENSE
|
||||
author: Joe Nelson, Adam Baker, Steve Chavez
|
||||
author: Joe Nelson, Adam Baker, Steve Chavez, Wolfgang Walther
|
||||
maintainer: Steve Chavez <stevechavezast@gmail.com>
|
||||
category: Executable, PostgreSQL, Network APIs
|
||||
homepage: https://postgrest.org
|
||||
bug-reports: https://github.com/PostgREST/postgrest/issues
|
||||
build-type: Simple
|
||||
extra-source-files: CHANGELOG.md
|
||||
cabal-version: >= 1.10
|
||||
|
||||
tested-with:
|
||||
-- nix
|
||||
GHC == 9.4.8
|
||||
-- cabal on Ubuntu
|
||||
-- stack on FreeBSD, MacOS, Ubuntu, Windows
|
||||
, GHC == 9.6.7
|
||||
, GHC == 9.10.3
|
||||
-- cabal on Ubuntu
|
||||
, GHC == 9.8.4
|
||||
-- nix
|
||||
, GHC == 9.12.3
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: git://github.com/PostgREST/postgrest.git
|
||||
location: https://github.com/PostgREST/postgrest.git
|
||||
|
||||
flag dev
|
||||
default: False
|
||||
@@ -39,19 +38,26 @@ flag hpc
|
||||
description: Enable HPC (dev only)
|
||||
|
||||
library
|
||||
default-language: Haskell2010
|
||||
default-language: GHC2021
|
||||
default-extensions: OverloadedStrings
|
||||
NoImplicitPrelude
|
||||
hs-source-dirs: src
|
||||
hs-source-dirs: src/library
|
||||
exposed-modules: PostgREST.Admin
|
||||
PostgREST.App
|
||||
PostgREST.AppState
|
||||
PostgREST.AppState.Pool
|
||||
PostgREST.AppState.Reload
|
||||
PostgREST.AppState.Types
|
||||
PostgREST.Auth
|
||||
PostgREST.Auth.Jwt
|
||||
PostgREST.Auth.JwtCache
|
||||
PostgREST.Auth.Types
|
||||
PostgREST.Cache.Sieve
|
||||
PostgREST.CLI
|
||||
PostgREST.Client
|
||||
PostgREST.Config
|
||||
PostgREST.Config.Database
|
||||
PostgREST.Debounce
|
||||
PostgREST.Config.JSPath
|
||||
PostgREST.Config.PgVersion
|
||||
PostgREST.Config.Proxy
|
||||
@@ -63,19 +69,23 @@ library
|
||||
PostgREST.SchemaCache.Representations
|
||||
PostgREST.SchemaCache.Table
|
||||
PostgREST.Error
|
||||
PostgREST.Listener
|
||||
PostgREST.Error.Types
|
||||
PostgREST.Logger
|
||||
PostgREST.MainTx
|
||||
PostgREST.Logger.Apache
|
||||
PostgREST.MediaType
|
||||
PostgREST.Metrics
|
||||
PostgREST.Network
|
||||
PostgREST.Observation
|
||||
PostgREST.Query
|
||||
PostgREST.Query.PreQuery
|
||||
PostgREST.Query.QueryBuilder
|
||||
PostgREST.Query.SqlFragment
|
||||
PostgREST.Query.Statements
|
||||
PostgREST.Plan
|
||||
PostgREST.Plan.CallPlan
|
||||
PostgREST.Plan.MutatePlan
|
||||
PostgREST.Plan.Negotiate
|
||||
PostgREST.Plan.ReadPlan
|
||||
PostgREST.Plan.Types
|
||||
PostgREST.RangeQuery
|
||||
@@ -83,39 +93,43 @@ library
|
||||
PostgREST.ApiRequest
|
||||
PostgREST.ApiRequest.Preferences
|
||||
PostgREST.ApiRequest.QueryParams
|
||||
PostgREST.ApiRequest.Payload
|
||||
PostgREST.ApiRequest.Types
|
||||
PostgREST.Response
|
||||
PostgREST.Response.OpenAPI
|
||||
PostgREST.Response.GucHeader
|
||||
PostgREST.Response.Performance
|
||||
PostgREST.TimeIt
|
||||
PostgREST.Version
|
||||
build-depends: base >= 4.9 && < 4.20
|
||||
build-depends: base >= 4.9 && < 4.22
|
||||
, HTTP >= 4000.3.7 && < 4000.5
|
||||
, Ranged-sets >= 0.3 && < 0.5
|
||||
, Ranged-sets >= 0.3 && < 0.6
|
||||
, aeson >= 2.0.3 && < 2.3
|
||||
, auto-update >= 0.1.4 && < 0.3
|
||||
, aeson-jsonpath >= 0.4.2 && < 0.5
|
||||
, auto-update >= 0.2.7 && < 0.3
|
||||
, base64-bytestring >= 1 && < 1.3
|
||||
, bytestring >= 0.10.8 && < 0.13
|
||||
, cache >= 0.1.3 && < 0.2.0
|
||||
, case-insensitive >= 1.2 && < 1.3
|
||||
, cassava >= 0.4.5 && < 0.6
|
||||
, clock >= 0.8.3 && < 0.9.0
|
||||
, configurator-pg >= 0.2.11 && < 0.3
|
||||
, containers >= 0.5.7 && < 0.7
|
||||
, containers >= 0.5.7 && < 0.8
|
||||
, cookie >= 0.4.2 && < 0.6
|
||||
-- crypton 1.1.0 moved from `memory` to `ram`, which jose-jwt fails to build with right now.
|
||||
-- should be possible to remove this once jose-jwt had a new release.
|
||||
, crypton < 1.1.0
|
||||
, directory >= 1.2.6 && < 1.4
|
||||
, either >= 4.4.1 && < 5.1
|
||||
, extra >= 1.7.0 && < 2.0
|
||||
, fast-logger >= 3.2.0 && < 3.3
|
||||
, fuzzyset >= 0.2.4 && < 0.3
|
||||
, hasql >= 1.6.1.1 && < 1.7
|
||||
, hasql-dynamic-statements >= 0.3.1 && < 0.4
|
||||
, hasql-notifications >= 0.2.2.2 && < 0.2.3
|
||||
, hasql-pool >= 1.0.1 && < 1.1
|
||||
, hasql-transaction >= 1.0.1 && < 1.2
|
||||
, heredoc >= 0.2 && < 0.3
|
||||
, hasql >= 1.9 && <= 1.9.3.1
|
||||
, hasql-dynamic-statements >= 0.3.1 && <= 0.3.1.8
|
||||
, hasql-notifications >= 0.2.4.0 && < 0.3
|
||||
, hasql-pool >= 1.1 && <= 1.3.0.4
|
||||
, hasql-transaction >= 1.0.1 && <= 1.2.1
|
||||
, http-client >= 0.7.19 && < 0.8
|
||||
, http-types >= 0.12.2 && < 0.13
|
||||
, insert-ordered-containers >= 0.2.2 && < 0.3
|
||||
, iproute >= 1.7.0 && < 1.8
|
||||
, jose-jwt >= 0.9.6 && < 0.11
|
||||
, lens >= 4.14 && < 5.4
|
||||
, lens-aeson >= 1.0.1 && < 1.3
|
||||
@@ -125,37 +139,40 @@ library
|
||||
, network-uri >= 2.6.1 && < 2.8
|
||||
, optparse-applicative >= 0.13 && < 0.19
|
||||
, parsec >= 3.1.11 && < 3.2
|
||||
-- Technically unused, can be removed after updating to hasql >= 1.7
|
||||
, postgresql-libpq >= 0.10
|
||||
, prometheus-client >= 1.1.1 && < 1.2.0
|
||||
, prometheus-metrics-ghc >= 1.0.1.2 && < 1.2
|
||||
, protolude >= 0.3.1 && < 0.4
|
||||
, regex-tdfa >= 1.2.2 && < 1.4
|
||||
, retry >= 0.7.4 && < 0.10
|
||||
, scientific >= 0.3.4 && < 0.4
|
||||
, streaming-commons >= 0.1.1 && < 0.3
|
||||
, streaming-commons >= 0.2.3.1 && < 0.3
|
||||
, swagger2 >= 2.4 && < 2.9
|
||||
, text >= 1.2.2 && < 2.2
|
||||
, time >= 1.6 && < 1.13
|
||||
, timeit >= 2.0 && < 2.1
|
||||
, time >= 1.6 && < 1.15
|
||||
, unordered-containers >= 0.2.8 && < 0.3
|
||||
, unix-compat >= 0.5.4 && < 0.8
|
||||
, vault >= 0.3.1.5 && < 0.4
|
||||
, vector >= 0.11 && < 0.14
|
||||
, wai >= 3.2.1 && < 3.3
|
||||
, wai-cors >= 0.2.5 && < 0.3
|
||||
, wai-extra >= 3.1.8 && < 3.2
|
||||
-- We already depend on wai-logger >= 2.3.7 indirectly via wai-extra,
|
||||
-- but we want to depend on 2.4.0 which fixes 'unknownSocket' log output
|
||||
-- for unix sockets; this is tested in test/io/test_io.py. See
|
||||
-- for unix sockets; this is tested in test/io/test_log.py. See
|
||||
-- https://github.com/kazu-yamamoto/logger/commit/3a71ca70afdbb93d4ecf0083eeba1fbbbcab3fc3
|
||||
, wai-logger >= 2.4.0
|
||||
, warp >= 3.3.19 && < 3.5
|
||||
, warp >= 3.4.14 && < 3.5
|
||||
, stm >= 2.5 && < 3
|
||||
, stm-hamt >= 1.2 && < 2
|
||||
, focus >= 1.0 && < 2
|
||||
, some >= 1.0.4.1 && < 2
|
||||
, uuid >= 1.3 && < 2
|
||||
-- -fno-spec-constr may help keep compile time memory use in check,
|
||||
-- see https://gitlab.haskell.org/ghc/ghc/issues/16017#note_219304
|
||||
-- -optP-Wno-nonportable-include-path
|
||||
-- prevents build failures on case-insensitive filesystems (macos),
|
||||
-- see https://github.com/commercialhaskell/stack/issues/3918
|
||||
ghc-options: -Werror -Wall -fwarn-identities
|
||||
ghc-options: -j -Werror -Wall -fwarn-identities
|
||||
-fno-spec-constr -optP-Wno-nonportable-include-path
|
||||
|
||||
if flag(dev)
|
||||
@@ -164,22 +181,27 @@ library
|
||||
ghc-options: -fhpc -hpcdir .hpc
|
||||
else
|
||||
ghc-options: -O2
|
||||
if impl(ghc >= 9.12)
|
||||
-- Makes GHC consider cross-module specialization for polymorphic functions
|
||||
-- without explicitly needing to add INLINE, INLINABLE or SPECIALIZE pragmas.
|
||||
-- Slightly increases the binary size but improves performance considerably.
|
||||
ghc-options: -fexpose-overloaded-unfoldings -fspecialise-aggressively
|
||||
|
||||
if !os(windows)
|
||||
build-depends:
|
||||
unix
|
||||
|
||||
executable postgrest
|
||||
default-language: Haskell2010
|
||||
default-language: GHC2021
|
||||
default-extensions: OverloadedStrings
|
||||
NoImplicitPrelude
|
||||
hs-source-dirs: main
|
||||
hs-source-dirs: src/executable
|
||||
main-is: Main.hs
|
||||
build-depends: base >= 4.9 && < 4.20
|
||||
, containers >= 0.5.7 && < 0.7
|
||||
build-depends: base >= 4.9 && < 4.22
|
||||
, containers >= 0.5.7 && < 0.8
|
||||
, postgrest
|
||||
, protolude >= 0.3.1 && < 0.4
|
||||
ghc-options: -threaded -rtsopts "-with-rtsopts=-N -I0 -qg"
|
||||
ghc-options: -j -threaded -rtsopts "-with-rtsopts=-N -I0 -qg"
|
||||
-O2 -Werror -Wall -fwarn-identities
|
||||
-fno-spec-constr -optP-Wno-nonportable-include-path
|
||||
|
||||
@@ -194,7 +216,7 @@ executable postgrest
|
||||
|
||||
test-suite spec
|
||||
type: exitcode-stdio-1.0
|
||||
default-language: Haskell2010
|
||||
default-language: GHC2021
|
||||
default-extensions: OverloadedStrings
|
||||
QuasiQuotes
|
||||
NoImplicitPrelude
|
||||
@@ -204,10 +226,12 @@ test-suite spec
|
||||
Feature.Auth.AudienceJwtSecretSpec
|
||||
Feature.Auth.AuthSpec
|
||||
Feature.Auth.BinaryJwtSecretSpec
|
||||
Feature.Auth.JwtCacheSpec
|
||||
Feature.Auth.NoAnonSpec
|
||||
Feature.Auth.NoJwtSecretSpec
|
||||
Feature.ConcurrentSpec
|
||||
Feature.CorsSpec
|
||||
Feature.HttpHeaderSpec
|
||||
Feature.ExtraSearchPathSpec
|
||||
Feature.NoSuperuserSpec
|
||||
Feature.ObservabilitySpec
|
||||
@@ -233,7 +257,10 @@ test-suite spec
|
||||
Feature.Query.PgSafeUpdateSpec
|
||||
Feature.Query.PlanSpec
|
||||
Feature.Query.PostGISSpec
|
||||
Feature.Query.PreferencesSpec
|
||||
Feature.Query.Preferences.HandlingSpec
|
||||
Feature.Query.Preferences.MaxAffectedSpec
|
||||
Feature.Query.Preferences.TimezoneSpec
|
||||
Feature.Query.PreparedStatementsSpec
|
||||
Feature.Query.QueryLimitedSpec
|
||||
Feature.Query.QuerySpec
|
||||
Feature.Query.RangeSpec
|
||||
@@ -249,18 +276,19 @@ test-suite spec
|
||||
Feature.RollbackSpec
|
||||
Feature.RpcPreRequestGucsSpec
|
||||
SpecHelper
|
||||
build-depends: base >= 4.9 && < 4.20
|
||||
build-depends: base >= 4.9 && < 4.22
|
||||
, aeson >= 2.0.3 && < 2.3
|
||||
, aeson-qq >= 0.8.1 && < 0.9
|
||||
, async >= 2.1.1 && < 2.3
|
||||
, base64-bytestring >= 1 && < 1.3
|
||||
, bytestring >= 0.10.8 && < 0.13
|
||||
, case-insensitive >= 1.2 && < 1.3
|
||||
, containers >= 0.5.7 && < 0.7
|
||||
, hasql-pool >= 1.0.1 && < 1.1
|
||||
, hasql-transaction >= 1.0.1 && < 1.2
|
||||
, containers >= 0.5.7 && < 0.8
|
||||
, hasql-pool >= 1.0.1 && <= 1.3.0.4
|
||||
, hasql-transaction >= 1.0.1 && <= 1.2.1
|
||||
, heredoc >= 0.2 && < 0.3
|
||||
, hspec >= 2.3 && < 2.12
|
||||
, hspec-expectations >= 0.8.4 && < 0.9
|
||||
, hspec-wai >= 0.10 && < 0.12
|
||||
, hspec-wai-json >= 0.10 && < 0.12
|
||||
, http-types >= 0.12.3 && < 0.13
|
||||
@@ -270,31 +298,64 @@ test-suite spec
|
||||
, monad-control >= 1.0.1 && < 1.1
|
||||
, postgrest
|
||||
, process >= 1.4.2 && < 1.7
|
||||
, prometheus-client >= 1.1.1 && < 1.2.0
|
||||
, protolude >= 0.3.1 && < 0.4
|
||||
, regex-tdfa >= 1.2.2 && < 1.4
|
||||
, scientific >= 0.3.4 && < 0.4
|
||||
, text >= 1.2.2 && < 2.2
|
||||
, time >= 1.6 && < 1.15
|
||||
, transformers-base >= 0.4.4 && < 0.5
|
||||
, wai >= 3.2.1 && < 3.3
|
||||
, wai-extra >= 3.0.19 && < 3.2
|
||||
ghc-options: -threaded -O0 -Werror -Wall -fwarn-identities
|
||||
ghc-options: -j -threaded -O0 -Werror -Wall -fwarn-identities
|
||||
-fno-spec-constr -optP-Wno-nonportable-include-path
|
||||
-fno-warn-missing-signatures
|
||||
-fwrite-ide-info
|
||||
-- https://github.com/PostgREST/postgrest/issues/387
|
||||
-with-rtsopts=-K33K
|
||||
|
||||
test-suite observability
|
||||
type: exitcode-stdio-1.0
|
||||
default-language: GHC2021
|
||||
default-extensions: OverloadedStrings
|
||||
QuasiQuotes
|
||||
NoImplicitPrelude
|
||||
hs-source-dirs: test/observability
|
||||
main-is: Main.hs
|
||||
other-modules: ObsHelper
|
||||
Observation.JwtCache
|
||||
Observation.MetricsSpec
|
||||
Observation.SchemaCacheSpec
|
||||
build-depends: base >= 4.9 && < 4.22
|
||||
, base64-bytestring >= 1 && < 1.3
|
||||
, bytestring >= 0.10.8 && < 0.13
|
||||
, hasql-pool >= 1.0.1 && <= 1.3.0.4
|
||||
, hasql-transaction >= 1.0.1 && <= 1.2.1
|
||||
, hspec >= 2.3 && < 2.12
|
||||
, hspec-expectations >= 0.8.4 && < 0.9
|
||||
, hspec-wai >= 0.10 && < 0.12
|
||||
, hspec-wai-json >= 0.10 && < 0.12
|
||||
, http-types >= 0.12.3 && < 0.13
|
||||
, jose-jwt >= 0.9.6 && < 0.11
|
||||
, postgrest
|
||||
, prometheus-client >= 1.1.1 && < 1.2.0
|
||||
, protolude >= 0.3.1 && < 0.4
|
||||
, text >= 1.2.2 && < 2.2
|
||||
, wai >= 3.2.1 && < 3.3
|
||||
ghc-options: -j -threaded -O0 -Werror -Wall -fwarn-identities
|
||||
-fno-spec-constr -optP-Wno-nonportable-include-path
|
||||
-fwrite-ide-info
|
||||
-- https://github.com/PostgREST/postgrest/issues/387
|
||||
-with-rtsopts=-K33K
|
||||
|
||||
test-suite doctests
|
||||
type: exitcode-stdio-1.0
|
||||
default-language: Haskell2010
|
||||
default-extensions: OverloadedStrings
|
||||
NoImplicitPrelude
|
||||
default-language: GHC2021
|
||||
hs-source-dirs: test/doc
|
||||
main-is: Main.hs
|
||||
build-depends: base >= 4.9 && < 4.20
|
||||
, doctest >= 0.8
|
||||
build-depends: base >= 4.9 && < 4.22
|
||||
, doctest-parallel >= 0.4
|
||||
, postgrest
|
||||
, pretty-simple
|
||||
, protolude >= 0.3.1 && < 0.4
|
||||
ghc-options: -threaded -O0 -Werror -Wall -fwarn-identities
|
||||
ghc-options: -j -threaded -O0 -Werror -Wall -fwarn-identities
|
||||
-fno-spec-constr -optP-Wno-nonportable-include-path
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
# We highly recommend that use the PostgREST binary cache by installing cachix
|
||||
# (https://app.cachix.org/) and running `cachix use postgrest`.
|
||||
{ docker ? false
|
||||
, postgrest ? import ./default.nix { }
|
||||
}:
|
||||
let
|
||||
postgrest =
|
||||
import ./default.nix { };
|
||||
|
||||
inherit (postgrest) pkgs;
|
||||
|
||||
inherit (pkgs) lib;
|
||||
@@ -21,7 +19,9 @@ let
|
||||
postgrest.cabalTools
|
||||
postgrest.devTools
|
||||
postgrest.docs
|
||||
postgrest.gitTools
|
||||
postgrest.loadtest
|
||||
postgrest.nixpkgsTools
|
||||
postgrest.release
|
||||
postgrest.style
|
||||
postgrest.tests
|
||||
@@ -35,10 +35,7 @@ lib.overrideDerivation postgrest.env (
|
||||
buildInputs =
|
||||
base.buildInputs ++ [
|
||||
pkgs.cabal-install
|
||||
pkgs.cabal2nix
|
||||
pkgs.git
|
||||
pkgs.postgresql
|
||||
pkgs.update-nix-fetchgit
|
||||
postgrest.hsie.bin
|
||||
]
|
||||
++ toolboxes;
|
||||
@@ -47,6 +44,10 @@ lib.overrideDerivation postgrest.env (
|
||||
''
|
||||
export HISTFILE=.history
|
||||
|
||||
# Bypass proxy for all hosts, it prevents HTTP client failures used in test
|
||||
# suites. See: https://github.com/PostgREST/postgrest/issues/4633 for more info
|
||||
export NO_PROXY=*
|
||||
|
||||
source ${pkgs.bash-completion}/etc/profile.d/bash_completion.sh
|
||||
source ${pkgs.git}/share/git/contrib/completion/git-completion.bash
|
||||
source ${postgrest.hsie.bash-completion}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
|
||||
module PostgREST.Admin
|
||||
( runAdmin
|
||||
) where
|
||||
|
||||
import qualified Data.Aeson as JSON
|
||||
import qualified Network.HTTP.Types.Status as HTTP
|
||||
import qualified Network.Wai as Wai
|
||||
import qualified Network.Wai.Handler.Warp as Warp
|
||||
|
||||
import Control.Monad.Extra (whenJust)
|
||||
|
||||
import Network.Socket
|
||||
import Network.Socket.ByteString
|
||||
|
||||
import PostgREST.AppState (AppState)
|
||||
import PostgREST.Config (AppConfig (..))
|
||||
import PostgREST.MediaType (MediaType (..), toContentType)
|
||||
import PostgREST.Metrics (metricsToText)
|
||||
import PostgREST.Network (resolveHost)
|
||||
import PostgREST.Observation (Observation (..))
|
||||
|
||||
import qualified PostgREST.AppState as AppState
|
||||
|
||||
import Protolude
|
||||
|
||||
runAdmin :: AppState -> Warp.Settings -> IO ()
|
||||
runAdmin appState settings = do
|
||||
AppConfig{configAdminServerPort} <- AppState.getConfig appState
|
||||
whenJust (AppState.getSocketAdmin appState) $ \adminSocket -> do
|
||||
host <- resolveHost adminSocket
|
||||
observer $ AdminStartObs host configAdminServerPort
|
||||
void . forkIO $ Warp.runSettingsSocket settings adminSocket adminApp
|
||||
where
|
||||
adminApp = admin appState
|
||||
observer = AppState.getObserver appState
|
||||
|
||||
-- | PostgREST admin application
|
||||
admin :: AppState.AppState -> Wai.Application
|
||||
admin appState req respond = do
|
||||
isMainAppReachable <- isRight <$> reachMainApp (AppState.getSocketREST appState)
|
||||
isLoaded <- AppState.isLoaded appState
|
||||
isPending <- AppState.isPending appState
|
||||
|
||||
case Wai.pathInfo req of
|
||||
["live"] ->
|
||||
respond $ Wai.responseLBS (if isMainAppReachable then HTTP.status200 else HTTP.status500) [] mempty
|
||||
["ready"] ->
|
||||
let
|
||||
status | not isMainAppReachable = HTTP.status500
|
||||
| isPending = HTTP.status503
|
||||
| isLoaded = HTTP.status200
|
||||
| otherwise = HTTP.status500
|
||||
in
|
||||
respond $ Wai.responseLBS status [] mempty
|
||||
["schema_cache"] -> do
|
||||
sCache <- AppState.getSchemaCache appState
|
||||
respond $ Wai.responseLBS HTTP.status200 [] (maybe mempty JSON.encode sCache)
|
||||
["metrics"] -> do
|
||||
mets <- metricsToText
|
||||
respond $ Wai.responseLBS HTTP.status200 [toContentType MTTextPlain] mets -- Content-Type is required for prometheus compliance
|
||||
_ ->
|
||||
respond $ Wai.responseLBS HTTP.status404 [] mempty
|
||||
|
||||
-- Try to connect to the main app socket
|
||||
-- Note that it doesn't even send a valid HTTP request, we just want to check that the main app is accepting connections
|
||||
reachMainApp :: Socket -> IO (Either IOException ())
|
||||
reachMainApp appSock = do
|
||||
sockAddr <- getSocketName appSock
|
||||
sock <- socket (addrFamily sockAddr) Stream defaultProtocol
|
||||
try $ do
|
||||
connect sock sockAddr
|
||||
withSocketsDo $ bracket (pure sock) close sendEmpty
|
||||
where
|
||||
sendEmpty sock = void $ send sock mempty
|
||||
addrFamily (SockAddrInet _ _) = AF_INET
|
||||
addrFamily (SockAddrInet6 {}) = AF_INET6
|
||||
addrFamily (SockAddrUnix _) = AF_UNIX
|
||||
@@ -1,201 +0,0 @@
|
||||
{-|
|
||||
Module : PostgREST.App
|
||||
Description : PostgREST main application
|
||||
|
||||
This module is in charge of mapping HTTP requests to PostgreSQL queries.
|
||||
Some of its functionality includes:
|
||||
|
||||
- Mapping HTTP request methods to proper SQL statements. For example, a GET request is translated to executing a SELECT query in a read-only TRANSACTION.
|
||||
- Producing HTTP Headers according to RFCs.
|
||||
- Content Negotiation
|
||||
-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
module PostgREST.App
|
||||
( postgrest
|
||||
, run
|
||||
) where
|
||||
|
||||
|
||||
import Control.Monad.Except (liftEither)
|
||||
import Data.Either.Combinators (mapLeft, whenLeft)
|
||||
import Data.Maybe (fromJust)
|
||||
import Data.String (IsString (..))
|
||||
import Network.Wai.Handler.Warp (defaultSettings, setHost, setPort,
|
||||
setServerName)
|
||||
|
||||
import qualified Data.Text.Encoding as T
|
||||
import qualified Network.Wai as Wai
|
||||
import qualified Network.Wai.Handler.Warp as Warp
|
||||
|
||||
import qualified PostgREST.Admin as Admin
|
||||
import qualified PostgREST.ApiRequest as ApiRequest
|
||||
import qualified PostgREST.AppState as AppState
|
||||
import qualified PostgREST.Auth as Auth
|
||||
import qualified PostgREST.Cors as Cors
|
||||
import qualified PostgREST.Error as Error
|
||||
import qualified PostgREST.Listener as Listener
|
||||
import qualified PostgREST.Logger as Logger
|
||||
import qualified PostgREST.Plan as Plan
|
||||
import qualified PostgREST.Query as Query
|
||||
import qualified PostgREST.Response as Response
|
||||
import qualified PostgREST.Unix as Unix (installSignalHandlers)
|
||||
|
||||
import PostgREST.ApiRequest (ApiRequest (..))
|
||||
import PostgREST.AppState (AppState)
|
||||
import PostgREST.Auth.Types (AuthResult (..))
|
||||
import PostgREST.Config (AppConfig (..), LogLevel (..),
|
||||
LogQuery (..))
|
||||
import PostgREST.Config.PgVersion (PgVersion (..))
|
||||
import PostgREST.Error (Error)
|
||||
import PostgREST.Network (resolveHost)
|
||||
import PostgREST.Observation (Observation (..))
|
||||
import PostgREST.Response.Performance (ServerTiming (..),
|
||||
serverTimingHeader)
|
||||
import PostgREST.SchemaCache (SchemaCache (..))
|
||||
import PostgREST.Version (docsVersion, prettyVersion)
|
||||
|
||||
import qualified Data.ByteString.Char8 as BS
|
||||
import qualified Data.List as L
|
||||
import qualified Network.HTTP.Types as HTTP
|
||||
import qualified Network.Socket as NS
|
||||
import Protolude hiding (Handler)
|
||||
import System.TimeIt (timeItT)
|
||||
|
||||
type Handler = ExceptT Error
|
||||
|
||||
run :: AppState -> IO ()
|
||||
run appState = do
|
||||
let observer = AppState.getObserver appState
|
||||
conf@AppConfig{..} <- AppState.getConfig appState
|
||||
|
||||
AppState.schemaCacheLoader appState -- Loads the initial SchemaCache
|
||||
Unix.installSignalHandlers (AppState.getMainThreadId appState) (AppState.schemaCacheLoader appState) (AppState.readInDbConfig False appState)
|
||||
|
||||
Listener.runListener appState
|
||||
|
||||
Admin.runAdmin appState (serverSettings conf)
|
||||
|
||||
let app = postgrest configLogLevel appState (AppState.schemaCacheLoader appState)
|
||||
|
||||
case configServerUnixSocket of
|
||||
Just path -> do
|
||||
observer $ AppServerUnixObs path
|
||||
Nothing -> do
|
||||
port <- NS.socketPort $ AppState.getSocketREST appState
|
||||
host <- resolveHost $ AppState.getSocketREST appState
|
||||
observer $ AppServerPortObs (fromJust host) port
|
||||
|
||||
Warp.runSettingsSocket (serverSettings conf) (AppState.getSocketREST appState) app
|
||||
|
||||
serverSettings :: AppConfig -> Warp.Settings
|
||||
serverSettings AppConfig{..} =
|
||||
defaultSettings
|
||||
& setHost (fromString $ toS configServerHost)
|
||||
& setPort configServerPort
|
||||
& setServerName ("postgrest/" <> prettyVersion)
|
||||
|
||||
-- | PostgREST application
|
||||
postgrest :: LogLevel -> AppState.AppState -> IO () -> Wai.Application
|
||||
postgrest logLevel appState connWorker =
|
||||
traceHeaderMiddleware appState .
|
||||
Cors.middleware appState .
|
||||
Auth.middleware appState .
|
||||
Logger.middleware logLevel Auth.getRole $
|
||||
-- fromJust can be used, because the auth middleware will **always** add
|
||||
-- some AuthResult to the vault.
|
||||
\req respond -> case fromJust $ Auth.getResult req of
|
||||
Left err -> respond $ Error.errorResponseFor err
|
||||
Right authResult -> do
|
||||
appConf <- AppState.getConfig appState -- the config must be read again because it can reload
|
||||
maybeSchemaCache <- AppState.getSchemaCache appState
|
||||
pgVer <- AppState.getPgVersion appState
|
||||
|
||||
let
|
||||
eitherResponse :: IO (Either Error Wai.Response)
|
||||
eitherResponse =
|
||||
runExceptT $ postgrestResponse appState appConf maybeSchemaCache pgVer authResult req
|
||||
|
||||
response <- either Error.errorResponseFor identity <$> eitherResponse
|
||||
-- Launch the connWorker when the connection is down. The postgrest
|
||||
-- function can respond successfully (with a stale schema cache) before
|
||||
-- the connWorker is done.
|
||||
when (isServiceUnavailable response) connWorker
|
||||
resp <- do
|
||||
delay <- AppState.getNextDelay appState
|
||||
return $ addRetryHint delay response
|
||||
respond resp
|
||||
|
||||
postgrestResponse
|
||||
:: AppState.AppState
|
||||
-> AppConfig
|
||||
-> Maybe SchemaCache
|
||||
-> PgVersion
|
||||
-> AuthResult
|
||||
-> Wai.Request
|
||||
-> Handler IO Wai.Response
|
||||
postgrestResponse appState conf@AppConfig{..} maybeSchemaCache pgVer authResult@AuthResult{..} req = do
|
||||
sCache <-
|
||||
case maybeSchemaCache of
|
||||
Just sCache ->
|
||||
return sCache
|
||||
Nothing ->
|
||||
throwError Error.NoSchemaCacheError
|
||||
|
||||
body <- lift $ Wai.strictRequestBody req
|
||||
|
||||
let jwtTime = if configServerTimingEnabled then Auth.getJwtDur req else Nothing
|
||||
timezones = dbTimezones sCache
|
||||
prefs = ApiRequest.userPreferences conf req timezones
|
||||
|
||||
(parseTime, apiReq@ApiRequest{..}) <- withTiming $ liftEither . mapLeft Error.ApiRequestError $ ApiRequest.userApiRequest conf prefs req body
|
||||
(planTime, plan) <- withTiming $ liftEither $ Plan.actionPlan iAction conf apiReq sCache
|
||||
|
||||
let query = Query.query conf authResult apiReq plan sCache pgVer
|
||||
logSQL = lift . AppState.getObserver appState . DBQuery (Query.getSQLQuery query)
|
||||
|
||||
(queryTime, queryResult) <- withTiming $ do
|
||||
case query of
|
||||
Query.NoDbQuery r -> pure r
|
||||
Query.DbQuery{..} -> do
|
||||
dbRes <- lift $ AppState.usePool appState (dqTransaction dqIsoLevel dqTxMode $ runExceptT dqDbHandler)
|
||||
let eitherResp = mapLeft Error.PgErr . mapLeft (Error.PgError (Just authRole /= configDbAnonRole)) $ dbRes
|
||||
when (configLogQuery /= LogQueryDisabled) $ whenLeft eitherResp $ logSQL . Error.status
|
||||
liftEither eitherResp >>= liftEither
|
||||
|
||||
(respTime, resp) <- withTiming $ do
|
||||
let response = Response.actionResponse queryResult apiReq (T.decodeUtf8 prettyVersion, docsVersion) conf sCache iSchema iNegotiatedByProfile
|
||||
when (configLogQuery /= LogQueryDisabled) $ logSQL $ either Error.status Response.pgrstStatus response
|
||||
liftEither response
|
||||
|
||||
return $ toWaiResponse (ServerTiming jwtTime parseTime planTime queryTime respTime) resp
|
||||
|
||||
where
|
||||
toWaiResponse :: ServerTiming -> Response.PgrstResponse -> Wai.Response
|
||||
toWaiResponse timing (Response.PgrstResponse st hdrs bod) = Wai.responseLBS st (hdrs ++ ([serverTimingHeader timing | configServerTimingEnabled])) bod
|
||||
|
||||
withTiming :: Handler IO a -> Handler IO (Maybe Double, a)
|
||||
withTiming f = if configServerTimingEnabled
|
||||
then do
|
||||
(t, r) <- timeItT f
|
||||
pure (Just t, r)
|
||||
else do
|
||||
r <- f
|
||||
pure (Nothing, r)
|
||||
|
||||
traceHeaderMiddleware :: AppState -> Wai.Middleware
|
||||
traceHeaderMiddleware appState app req respond = do
|
||||
conf <- AppState.getConfig appState
|
||||
|
||||
case configServerTraceHeader conf of
|
||||
Nothing -> app req respond
|
||||
Just hdr ->
|
||||
let hdrVal = L.lookup hdr $ Wai.requestHeaders req in
|
||||
app req (respond . Wai.mapResponseHeaders ([(hdr, fromMaybe mempty hdrVal)] ++))
|
||||
|
||||
addRetryHint :: Int -> Wai.Response -> Wai.Response
|
||||
addRetryHint delay response = do
|
||||
let h = ("Retry-After", BS.pack $ show delay)
|
||||
Wai.mapResponseHeaders (\hs -> if isServiceUnavailable response then h:hs else hs) response
|
||||
|
||||
isServiceUnavailable :: Wai.Response -> Bool
|
||||
isServiceUnavailable response = Wai.responseStatus response == HTTP.status503
|
||||