Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6df6725f45 | ||
|
|
8df108907f | ||
|
|
2b056c8fb8 | ||
|
|
bc409b350e | ||
|
|
bba1ab065a | ||
|
|
c250942709 | ||
|
|
1772c035d9 | ||
|
|
6065405bd9 | ||
|
|
0cabe3a675 | ||
|
|
df32bbfeae | ||
|
|
a307ab9297 | ||
|
|
9f63518117 | ||
|
|
c8772b54fa | ||
|
|
edcf6d7644 | ||
|
|
e0891cd457 | ||
|
|
bcab873a8f | ||
|
|
1308f1bf60 | ||
|
|
3ddf506df2 | ||
|
|
6df8db6485 | ||
|
|
6cd519cb2b | ||
|
|
cb712a8706 | ||
|
|
57b4cd0ade | ||
|
|
457edcedfd | ||
|
|
c1418ef910 | ||
|
|
af08f3a181 | ||
|
|
fa55469ae8 | ||
|
|
7ffbbc6a74 | ||
|
|
7ebeb3ce7f | ||
|
|
8cf0e5b2f3 | ||
|
|
eaf8c1b4bf | ||
|
|
6d79090e4a | ||
|
|
0152afd796 | ||
|
|
0c3b4c0bba | ||
|
|
87bfcedae0 | ||
|
|
4caea2f75d | ||
|
|
3cb6529eb7 | ||
|
|
bc594f4fbd | ||
|
|
5a8f8c1d9c | ||
|
|
ad1a998709 | ||
|
|
8c326f810f | ||
|
|
c9b6c9f66f | ||
|
|
1396077dd7 | ||
|
|
05878e78d0 | ||
|
|
075b0c309f | ||
|
|
16a486710f | ||
|
|
8a809044a9 | ||
|
|
861d387c67 | ||
|
|
576be00536 | ||
|
|
a47bfe66e7 | ||
|
|
25a4250bb3 | ||
|
|
9af4a4f599 | ||
|
|
b91aa4ead6 | ||
|
|
a6c851aa3d | ||
|
|
c09eeeefab | ||
|
|
0834bc2ca5 | ||
|
|
51017c2702 | ||
|
|
a8079f5a41 | ||
|
|
bcbd61f770 | ||
|
|
865e73eeed | ||
|
|
6633badfc6 | ||
|
|
e3695cb22d | ||
|
|
e314e73ba7 | ||
|
|
7d1fa8570e | ||
|
|
96a2ac3799 | ||
|
|
ce7aa885fd | ||
|
|
43949211f7 | ||
|
|
8a9b7030cd | ||
|
|
d3a27beadc | ||
|
|
6734855b9f | ||
|
|
0a0d3943c2 | ||
|
|
4b5bdbc496 | ||
|
|
7359be8dd5 | ||
|
|
52f7ae6dc8 | ||
|
|
7b43d8a139 | ||
|
|
53123bdbb4 | ||
|
|
216ded00c3 | ||
|
|
02ec17a990 | ||
|
|
8df3dfeba9 | ||
|
|
a41b48135f | ||
|
|
8a6c8cde6d | ||
|
|
a14559647b | ||
|
|
33c57506b4 | ||
|
|
e2f3a862be | ||
|
|
291da1a41f | ||
|
|
d5a9e5d41f | ||
|
|
1b218a97f4 | ||
|
|
0a3a20347b | ||
|
|
49cd1f3447 | ||
|
|
593b8bf385 | ||
|
|
1139031858 | ||
|
|
a74552a1c4 | ||
|
|
bd11334df7 | ||
|
|
1e353dc4d2 | ||
|
|
1a03ece441 | ||
|
|
5fb9e81969 | ||
|
|
865dd12836 | ||
|
|
165e0acf24 | ||
|
|
4d256ce5db | ||
|
|
9c075e5e52 | ||
|
|
e07807deab | ||
|
|
bd50b1e4d3 | ||
|
|
f26cdd5151 | ||
|
|
b869dd7be9 | ||
|
|
97a4402911 | ||
|
|
a101d27c9c | ||
|
|
519dbc75f3 | ||
|
|
ae3c784921 | ||
|
|
df2cde81ca | ||
|
|
1e09b9f06d | ||
|
|
08228fa2fc | ||
|
|
aecb7910de | ||
|
|
0a5ae531a4 | ||
|
|
45ba35657b | ||
|
|
a65d387a31 | ||
|
|
e7413f3aea | ||
|
|
9128c8602a | ||
|
|
a0c463b998 | ||
|
|
eb100cc746 | ||
|
|
2592639d1b | ||
|
|
bba960d4c6 | ||
|
|
b37bacb19a | ||
|
|
06ece64cb0 | ||
|
|
65c2754719 | ||
|
|
360cfa3fbd | ||
|
|
13ce0f3354 | ||
|
|
5305998a95 | ||
|
|
a9baa5bc9f | ||
|
|
580184532b | ||
|
|
48783087de | ||
|
|
96c169e593 | ||
|
|
209b70cbfc | ||
|
|
06956c1994 | ||
|
|
06ab9fbd67 | ||
|
|
340c96883e | ||
|
|
496a1734ec | ||
|
|
6e24e23b4b | ||
|
|
9c23ac2f39 | ||
|
|
5f3284eed0 | ||
|
|
72bffddbf1 | ||
|
|
f6c68ddafb | ||
|
|
6f79b41bc1 | ||
|
|
06156eb797 | ||
|
|
e1c987dc5f | ||
|
|
eb4c428da6 | ||
|
|
5688bc1521 | ||
|
|
a8034df2bf | ||
|
|
f56373313e | ||
|
|
8f98948078 | ||
|
|
1489fc84b8 | ||
|
|
ea58095e92 | ||
|
|
98976e4d57 | ||
|
|
e9ef876433 | ||
|
|
4aff0caa59 | ||
|
|
fbe317b244 | ||
|
|
abe0daa3dc | ||
|
|
b209731ff3 | ||
|
|
9049ed1f71 | ||
|
|
6aeb5065f8 | ||
|
|
4d6909a08f | ||
|
|
9c0db849ce | ||
|
|
71dd891d2f | ||
|
|
43a92d4e6b | ||
|
|
e7e102bc42 | ||
|
|
91f1580f95 | ||
|
|
a4db05aeba | ||
|
|
a85dfe9558 | ||
|
|
0ae86b7e74 | ||
|
|
49ca91cc3c | ||
|
|
0824139069 | ||
|
|
5f938dbe1a | ||
|
|
5187eadd0b | ||
|
|
9d23cfde9d | ||
|
|
737ad76dfd | ||
|
|
4bd090ba13 | ||
|
|
540ef4ad9f | ||
|
|
62726ddcfd | ||
|
|
6c382c5985 | ||
|
|
0187410882 | ||
|
|
cfde95b0aa | ||
|
|
04e7020ae3 | ||
|
|
66e49f0ea2 | ||
|
|
1b24eb34ee | ||
|
|
d5fb73bf9b | ||
|
|
c46c3b7a86 | ||
|
|
9eac683e51 | ||
|
|
35d9b98bff | ||
|
|
c946f02607 | ||
|
|
d12974339a | ||
|
|
06c0180e4f | ||
|
|
4720cfc1d3 | ||
|
|
4e4458eb95 | ||
|
|
8b74e14c4f | ||
|
|
7cc3d319bc | ||
|
|
a0bdcb706c | ||
|
|
23ad30a20a | ||
|
|
b15dd8783e | ||
|
|
8e11b6f5df | ||
|
|
7e7d4df93b | ||
|
|
c6ef4305b1 | ||
|
|
ee1ec780d2 | ||
|
|
ec7ace2e73 | ||
|
|
ad13e00f64 | ||
|
|
a6a088e9f0 | ||
|
|
09ca21d032 | ||
|
|
5fb11e37a3 | ||
|
|
aea3b24b31 | ||
|
|
34958e723a | ||
|
|
91152ebad0 | ||
|
|
f7108e8612 | ||
|
|
0d23be7731 | ||
|
|
fcabf4012b | ||
|
|
ff30d4d417 | ||
|
|
98350e2941 | ||
|
|
42656edfd9 | ||
|
|
37c2a8aa7b | ||
|
|
21e54aacda | ||
|
|
12f8852183 | ||
|
|
254a2f7f36 | ||
|
|
698db87590 | ||
|
|
8d4bc76aab | ||
|
|
52cdd50561 | ||
|
|
57d725bab6 | ||
|
|
9543e746ec | ||
|
|
33f0da0509 | ||
|
|
20bee2ec34 | ||
|
|
c4387ec7dd | ||
|
|
6909e3435e | ||
|
|
2e50a22e64 | ||
|
|
5cbabe4a21 | ||
|
|
77faf9e9bc | ||
|
|
41325fd9ba | ||
|
|
f5c5094d7d | ||
|
|
a7cddd8ece | ||
|
|
204b0ed259 | ||
|
|
8562f48a37 | ||
|
|
3f2a58ab76 | ||
|
|
f7f3aadab8 | ||
|
|
326019cca2 | ||
|
|
a9dbd9e14f | ||
|
|
8e0b67ed09 | ||
|
|
7447b03295 | ||
|
|
574e57d01a | ||
|
|
07cc6547ee | ||
|
|
92c5066a79 | ||
|
|
2768019710 | ||
|
|
038fcc02b2 | ||
|
|
535ebba219 | ||
|
|
7e4810f0a6 | ||
|
|
8273e270f2 | ||
|
|
d5b9f6cc88 | ||
|
|
aa38221fc1 | ||
|
|
92395eae8a | ||
|
|
a1178a8016 | ||
|
|
2a2a4fdd92 | ||
|
|
9f50e1d2d7 | ||
|
|
d77da6bd49 | ||
|
|
82e9895e1f | ||
|
|
5728bbc4b9 | ||
|
|
89942601e4 | ||
|
|
c3ad07fcc0 | ||
|
|
a4fe838308 | ||
|
|
b0ba2f709c | ||
|
|
d6caa5dae9 | ||
|
|
881170b9ff | ||
|
|
634a0c70b7 | ||
|
|
84e5597091 | ||
|
|
b83ee94fae | ||
|
|
ea2e166aa1 | ||
|
|
9f15044e63 | ||
|
|
fbef0078d5 | ||
|
|
06dba3953f | ||
|
|
15d5b8a292 | ||
|
|
a05aed9b3e | ||
|
|
32173bed4d | ||
|
|
57200320c3 | ||
|
|
089895ac27 | ||
|
|
5b59e79dc9 | ||
|
|
92f76722cc | ||
|
|
47bd27badb | ||
|
|
49bc82bfca | ||
|
|
c7e623549d | ||
|
|
7f67cf78e7 | ||
|
|
5811682025 | ||
|
|
4c92fc7bca | ||
|
|
c518585259 | ||
|
|
7e3c14a71a | ||
|
|
864a1440be | ||
|
|
702df3218d | ||
|
|
78bd10acc9 | ||
|
|
94441c70c0 | ||
|
|
a869734140 | ||
|
|
ff8fd648bb | ||
|
|
f03be6d2d0 | ||
|
|
b965f32137 | ||
|
|
0b998e91d6 | ||
|
|
5e44c8160f | ||
|
|
516809f2de | ||
|
|
f8991b0794 | ||
|
|
20bb295237 | ||
|
|
40bffc6950 | ||
|
|
8dc16eef27 | ||
|
|
622ff7b708 | ||
|
|
1315584de5 | ||
|
|
b6c03f3658 | ||
|
|
469e01f77b | ||
|
|
3af6df743b | ||
|
|
ef36ec10a1 | ||
|
|
3328b6059f | ||
|
|
79f2af08e3 | ||
|
|
7714bd9623 | ||
|
|
055b7820b3 | ||
|
|
5eca88fd87 | ||
|
|
6b72da38e6 | ||
|
|
c2ff7ffb62 | ||
|
|
750c22cd1c | ||
|
|
4d623986ea | ||
|
|
a5af8dc68f | ||
|
|
467b22cb29 | ||
|
|
87f883b793 | ||
|
|
3292fce732 | ||
|
|
33768c509c | ||
|
|
dd7e182b12 | ||
|
|
9f5ab4a8ba | ||
|
|
b7ee8f1cf0 | ||
|
|
36c453d6de | ||
|
|
e23b7afa3c | ||
|
|
334dda611c | ||
|
|
a343536539 | ||
|
|
0d3dc8509c | ||
|
|
66e0e88539 | ||
|
|
53f35f638c | ||
|
|
f34a480a99 | ||
|
|
1d54b8b07d | ||
|
|
2aae1a3894 | ||
|
|
87528412c0 | ||
|
|
5fd86e682b | ||
|
|
1f40492d64 | ||
|
|
0ed1d31c72 | ||
|
|
c4005ad8f2 | ||
|
|
878752faee | ||
|
|
f6c67ffdc1 | ||
|
|
95fccbb00f | ||
|
|
1caf114e5a | ||
|
|
da1cf9f76d | ||
|
|
4cd05681dd | ||
|
|
e47d19d4dc | ||
|
|
4eefe14706 | ||
|
|
8dba0dcc11 | ||
|
|
1b166ce048 | ||
|
|
458960f7c3 | ||
|
|
30d241fcff | ||
|
|
a0091f61a1 | ||
|
|
e1c9b8fe76 | ||
|
|
a0179cfd4b | ||
|
|
dbbba4e51a | ||
|
|
2fc0e2f8cb | ||
|
|
22b6ff764d | ||
|
|
3325a1bbc6 | ||
|
|
6afed0b30d | ||
|
|
4f1f80d4f2 | ||
|
|
5ef1db42ef | ||
|
|
476af62326 | ||
|
|
2186a7e1a8 | ||
|
|
1a18ef5a20 | ||
|
|
bc6e181d03 | ||
|
|
126236c3d0 | ||
|
|
e81ab0d0bb | ||
|
|
7dd37c0bc4 | ||
|
|
0d4b1d8f92 | ||
|
|
b9c9aee645 | ||
|
|
5942db7fa7 | ||
|
|
7805b99d57 | ||
|
|
8271885bb2 | ||
|
|
4fe34079d8 | ||
|
|
2d6c16c2a6 | ||
|
|
b38e29c3d1 | ||
|
|
afff7a00a4 | ||
|
|
51c0ad3e0c | ||
|
|
2414c7f7f0 | ||
|
|
a8b2ed313e | ||
|
|
eca325c11b | ||
|
|
f5868bc277 | ||
|
|
abdc2ac2a9 | ||
|
|
bed1013ecd | ||
|
|
cb2bf809a7 | ||
|
|
396b0468ce | ||
|
|
5c22846e12 | ||
|
|
aba2f43c10 | ||
|
|
664604e6ca | ||
|
|
a1509dc731 | ||
|
|
e9712b0aff | ||
|
|
91e44f12ec | ||
|
|
7ccc8c4610 | ||
|
|
3babff99b9 | ||
|
|
1fd06ff65d | ||
|
|
03905a584e | ||
|
|
b07d07f17d | ||
|
|
f983e0af34 | ||
|
|
0b5e168c8d | ||
|
|
f759b8bfc6 | ||
|
|
ca456ecc51 | ||
|
|
5ad1afbf73 | ||
|
|
59816e6576 | ||
|
|
ecb4f15de0 | ||
|
|
c21e98c9ae | ||
|
|
2cc48151dd | ||
|
|
93e457cd89 | ||
|
|
3dde972d31 | ||
|
|
7d8bceea10 | ||
|
|
3b8ec49fba | ||
|
|
b77149d48e | ||
|
|
3d734c44cf | ||
|
|
f32c9c7967 | ||
|
|
c0fe0a5ce1 | ||
|
|
a8d209cd1c | ||
|
|
1c357179fd | ||
|
|
44b825b939 | ||
|
|
a106edc5d0 | ||
|
|
367b259bd4 | ||
|
|
4119538ab7 | ||
|
|
2dcb5ee746 | ||
|
|
474b8e6426 | ||
|
|
9e9f913c9e | ||
|
|
24a5a27977 | ||
|
|
14cb86d147 | ||
|
|
ff74567473 | ||
|
|
a163828c6c | ||
|
|
702e055d43 | ||
|
|
93c3c59134 | ||
|
|
3220373803 | ||
|
|
fed4c8c9af | ||
|
|
eb81bbc464 | ||
|
|
6d1adaaa54 | ||
|
|
9475f53fd7 | ||
|
|
215bc80ff1 | ||
|
|
89c62aa7e3 | ||
|
|
f2e81062a0 | ||
|
|
8752069934 | ||
|
|
dc9a285825 | ||
|
|
73e59a1643 | ||
|
|
518b465ffa | ||
|
|
a1d4b3ed34 | ||
|
|
4b37af8f73 | ||
|
|
7cff51c002 | ||
|
|
d5365b6ead | ||
|
|
d0c0312e35 | ||
|
|
efb39e5e12 | ||
|
|
7052446abf | ||
|
|
cf220c5a91 | ||
|
|
83183ec8c6 | ||
|
|
0ec963b295 | ||
|
|
8670ce6ffe | ||
|
|
9337f63823 | ||
|
|
d78862996f | ||
|
|
5e2f3b8d59 | ||
|
|
bca1ddf0d6 | ||
|
|
a924c4b12f | ||
|
|
c9816f1661 | ||
|
|
38610e0349 | ||
|
|
99d878b13e | ||
|
|
48ab4e91be | ||
|
|
24f796a329 | ||
|
|
c4e926af2d | ||
|
|
54436e2dc6 | ||
|
|
bba1d35ade | ||
|
|
512862918f | ||
|
|
02f7cba3d2 | ||
|
|
7845bdce8c | ||
|
|
a7fd694f19 | ||
|
|
cfaf588bba | ||
|
|
be039152f2 | ||
|
|
c7c6ee40e4 | ||
|
|
0e16f34eef | ||
|
|
92cc815e9d | ||
|
|
f84b79ce41 | ||
|
|
d03770d352 | ||
|
|
c71bbba979 | ||
|
|
b11f9ef386 | ||
|
|
625a0b2393 | ||
|
|
338e8a1ae3 | ||
|
|
036cc1c5d7 | ||
|
|
ed028234cc | ||
|
|
121f192b93 | ||
|
|
7255f02610 | ||
|
|
6e7a2ca26c | ||
|
|
5e856a22a8 | ||
|
|
7948730770 | ||
|
|
1108aede5b | ||
|
|
c0f8e64bbb | ||
|
|
dbec5e08aa | ||
|
|
2f699fa115 | ||
|
|
6f0a25520a | ||
|
|
e56313196b | ||
|
|
736a5020b0 | ||
|
|
b32ba7a409 | ||
|
|
e0e4d68185 | ||
|
|
28c39e976c | ||
|
|
88e3c8c4b6 | ||
|
|
1fb9e34c9c | ||
|
|
0bae76f05e | ||
|
|
7b2ab948be | ||
|
|
16cf1ef45c | ||
|
|
7151e62499 | ||
|
|
911417471e | ||
|
|
7dbd5f799e | ||
|
|
362ee5a4ed | ||
|
|
e10599d065 | ||
|
|
f0bf51efc2 | ||
|
|
d9e7620160 | ||
|
|
f93e2723e3 | ||
|
|
e48a206075 | ||
|
|
82d2d781be | ||
|
|
59ab74b861 | ||
|
|
a3d87edda3 | ||
|
|
9598e3948d | ||
|
|
e7ddea0acf | ||
|
|
922ae0e0c9 | ||
|
|
edefd3040c | ||
|
|
a93ddbee5a | ||
|
|
45ddafc2a8 | ||
|
|
5185a92798 | ||
|
|
0da8cb230f | ||
|
|
e4741d43e4 | ||
|
|
5fc48057f8 | ||
|
|
973e7cb90e | ||
|
|
4b1e594270 | ||
|
|
3fdf6acc45 | ||
|
|
66ce8e8b18 | ||
|
|
aedaca1290 | ||
|
|
84bfa2d9ca | ||
|
|
4be05649f4 | ||
|
|
cdf5dece65 | ||
|
|
49a62772c0 | ||
|
|
aa6e3afc0e | ||
|
|
cb4c54074a | ||
|
|
71da89fdcc | ||
|
|
6382b597fa | ||
|
|
cee92cac05 | ||
|
|
e8cdd4463b | ||
|
|
127fbb41d5 | ||
|
|
83e4ad41fe | ||
|
|
f4c6a99adb | ||
|
|
174ccadb15 | ||
|
|
cb90e3e557 | ||
|
|
49af7dcbc2 | ||
|
|
e35f2d28c7 | ||
|
|
6bb81b5d4b | ||
|
|
3cb2c0f297 | ||
|
|
1a5cadb855 | ||
|
|
fc65694c0a | ||
|
|
f2f392b0f4 | ||
|
|
8f5a3f50c2 | ||
|
|
2ce053b69e | ||
|
|
bfe6bd1165 | ||
|
|
f52829d165 | ||
|
|
625f2ce8d7 | ||
|
|
42dd4ab607 | ||
|
|
8a6b429c16 | ||
|
|
1cab8cf82d | ||
|
|
27bcd8074f | ||
|
|
0f87137387 | ||
|
|
e54fb2a264 | ||
|
|
8e6410e55d | ||
|
|
708e0cd0b6 | ||
|
|
8b170afe49 | ||
|
|
b8f2ffc0a3 | ||
|
|
f1181cce73 | ||
|
|
ecbf49ad7f | ||
|
|
d461e3387f | ||
|
|
022e1101e7 | ||
|
|
6e1e8f02de | ||
|
|
01def80937 | ||
|
|
7742891c26 | ||
|
|
b237a4943c | ||
|
|
07777ea76b | ||
|
|
588b0bc7e1 | ||
|
|
e508f071b4 | ||
|
|
892f4a8491 | ||
|
|
404fcb6b27 | ||
|
|
3262607b5b | ||
|
|
e56ffe7b5e | ||
|
|
19768064dd | ||
|
|
71816e64f8 | ||
|
|
e1e1ee2d31 | ||
|
|
e37a2a569c | ||
|
|
58503af432 | ||
|
|
e0e604aa9f | ||
|
|
b3dfde5ecf | ||
|
|
360c90afe3 | ||
|
|
8706779b42 | ||
|
|
f52fb70e3d | ||
|
|
b3fb9ff215 | ||
|
|
4d8272fa0d | ||
|
|
349726cdc0 | ||
|
|
adb8522276 | ||
|
|
e84b4cbcfe | ||
|
|
6b255e9806 | ||
|
|
564c51fe79 | ||
|
|
2c1eb9ab13 | ||
|
|
1f12dedce6 | ||
|
|
8c2a95b98e | ||
|
|
5b95d22e2a | ||
|
|
5a31320a8b | ||
|
|
6a2ba25414 | ||
|
|
b35ffd4931 | ||
|
|
455dc5c8bc | ||
|
|
dacf39404e | ||
|
|
216550e959 | ||
|
|
2a77ff5714 | ||
|
|
684a9f9566 | ||
|
|
79de60db58 | ||
|
|
d1d6001a6d | ||
|
|
954252ed16 | ||
|
|
aed5618d28 | ||
|
|
8258631b03 | ||
|
|
0ae281f15f | ||
|
|
e5d4697507 | ||
|
|
d38bfc8cf3 | ||
|
|
5919a37197 | ||
|
|
2c53cb4fae | ||
|
|
8a606f7223 | ||
|
|
267c482cfe | ||
|
|
fda3e58619 | ||
|
|
d152689038 | ||
|
|
f882954019 | ||
|
|
6e7349d76c | ||
|
|
be869d3f54 | ||
|
|
d00bb026b6 | ||
|
|
98a776f2dc | ||
|
|
3025e18b3a | ||
|
|
5cd719bdff | ||
|
|
c0791a5f21 | ||
|
|
6e82a55d68 | ||
|
|
0060e8906d | ||
|
|
577cf29f64 | ||
|
|
debf4dc715 | ||
|
|
7611308b0e |
@@ -1,15 +0,0 @@
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-1
|
||||
|
||||
build_task:
|
||||
name: Build FreeBSD (Stack)
|
||||
install_script: pkg install -y postgresql13-client hs-stack git
|
||||
|
||||
stack_cache:
|
||||
folders: /.stack
|
||||
fingerprint_script: cat postgrest.cabal stack.yaml.lock
|
||||
reupload_on_changes: false
|
||||
|
||||
build_script: stack build -j 1 --local-bin-path . --copy-bins
|
||||
bin_artifacts:
|
||||
path: postgrest
|
||||
@@ -2,5 +2,5 @@
|
||||
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 - https://github.com/PostgREST/postgrest-docs
|
||||
- If relevant, update the docs
|
||||
-->
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
name: Setup Nix
|
||||
|
||||
description: Installs nix, sets up cachix and installs a subset of tooling.
|
||||
|
||||
inputs:
|
||||
authToken:
|
||||
description: Token to pass to cachix
|
||||
tools:
|
||||
description: Tools to install with nix-env -iA <tools>
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: cachix/install-nix-action@v18
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: postgrest
|
||||
authToken: ${{ inputs.authToken }}
|
||||
- if: ${{ inputs.tools }}
|
||||
run: nix-env -f default.nix -iA ${{ inputs.tools }}
|
||||
shell: bash
|
||||
@@ -1,18 +0,0 @@
|
||||
codecov:
|
||||
branch: main
|
||||
require_ci_to_pass: false
|
||||
|
||||
comment: false
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
only_pulls: false
|
||||
patch:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
only_pulls: true
|
||||
@@ -1,6 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Get the FreeBSD PostgREST executable built by CirrusCI for the given GITHUB_COMMIT in GITHUB_REPOSITORY
|
||||
|
||||
# We use the GitHub API for 'check suites' to find the corresponding CirrusCI job, see:
|
||||
# https://docs.github.com/en/rest/reference/checks#list-check-suites-for-a-git-reference
|
||||
|
||||
cirrus_artifact_name=bin
|
||||
gh_accept_header="Accept: application/vnd.github.v3+json"
|
||||
|
||||
get_gh_check_runs_url() {
|
||||
gh_checks_list_url="https://api.github.com/repos/$GITHUB_REPOSITORY/commits/$GITHUB_COMMIT/check-suites"
|
||||
>&2 echo "Getting list of check-suites from $gh_checks_list_url ..."
|
||||
curl --fail -H "$gh_accept_header" "$gh_checks_list_url" \
|
||||
| jq -r '.check_suites[] | select(.app.slug == "cirrus-ci") | .check_runs_url'
|
||||
}
|
||||
|
||||
wait_for_cirrusci() {
|
||||
gh_check_runs_url="$(get_gh_check_runs_url)"
|
||||
>&2 echo "Waiting to CirrusCI run to complete (two hours maximum)..."
|
||||
for _ in $(seq 1 120); do
|
||||
echo "Checking for CirrusCI task status at $gh_check_runs_url ..."
|
||||
status=$(curl --fail "$gh_check_runs_url" | jq -r '.check_runs[] | .status')
|
||||
if [ "$status" == "completed" ]; then
|
||||
break
|
||||
else
|
||||
echo "CirrusCI task is still $status, waiting..."
|
||||
sleep 60
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# The CirrusCI taskid can change if a new check run is started for the same commit,
|
||||
# e.g. when pushing both a branch and tag. We make sure that we have the very
|
||||
# latest taskid by re-loading the 'gh_check_runs_url' and the 'check run' itself.
|
||||
get_cirrus_taskid() {
|
||||
gh_check_runs_url="$(get_gh_check_runs_url)"
|
||||
>&2 echo "Getting the CirrusCI task id from $gh_check_runs_url ..."
|
||||
curl --fail -H "$gh_accept_header" "$gh_check_runs_url" \
|
||||
| jq -r '.check_runs[] | .external_id'
|
||||
}
|
||||
|
||||
download_artifact() {
|
||||
cirrus_task_id="$(get_cirrus_taskid)"
|
||||
cirrus_artifact_url="https://api.cirrus-ci.com/v1/artifact/task/$cirrus_task_id/$cirrus_artifact_name.zip"
|
||||
>&2 echo "Attemping to download the CirrusCI artifact from $cirrus_artifact_url ..."
|
||||
curl --fail "$cirrus_artifact_url" -o freebsd.zip
|
||||
}
|
||||
|
||||
wait_for_cirrusci
|
||||
download_artifact
|
||||
|
||||
echo "Unpacking executable..."
|
||||
unzip freebsd.zip -d .
|
||||
rm -rf freebsd.zip
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Tag a release that will be built with Github Actions. The version is
|
||||
# read from 'postgrest.cabal'.
|
||||
|
||||
version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
|
||||
|
||||
echo "Tagging version v$version"
|
||||
git tag -f "v$version"
|
||||
|
||||
echo "Pushing tag..."
|
||||
git push -f origin "refs/tags/v$version"
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script builds PostgREST in a remote ARM server
|
||||
|
||||
[ -z "$1" ] && { echo "Missing 1st argument: PostgREST github commit SHA"; exit 1; }
|
||||
[ -z "$2" ] && { echo "Missing 2nd argument: Build environment directory name"; exit 1; }
|
||||
|
||||
PGRST_GITHUB_COMMIT="$1"
|
||||
SCRIPT_DIR="$2"
|
||||
|
||||
DOCKER_BUILD_DIR="$SCRIPT_DIR/docker-env"
|
||||
|
||||
install_packages() {
|
||||
sudo apt-get update -y
|
||||
sudo apt-get upgrade -y
|
||||
sudo apt-get install -y git build-essential curl libffi-dev libffi7 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5 llvm libnuma-dev zlib1g-dev libpq-dev jq gcc
|
||||
sudo apt-get clean
|
||||
}
|
||||
|
||||
install_ghcup() {
|
||||
export BOOTSTRAP_HASKELL_NONINTERACTIVE=1
|
||||
export BOOTSTRAP_HASKELL_MINIMAL=1
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
|
||||
source ~/.ghcup/env
|
||||
}
|
||||
|
||||
install_cabal() {
|
||||
ghcup upgrade
|
||||
ghcup install cabal 3.6.0.0
|
||||
ghcup set cabal 3.6.0.0
|
||||
}
|
||||
|
||||
install_ghc() {
|
||||
ghcup install ghc 8.10.7
|
||||
ghcup set ghc 8.10.7
|
||||
}
|
||||
|
||||
install_packages
|
||||
|
||||
# Add ghcup to the PATH for this session
|
||||
[ -f ~/.ghcup/env ] && source ~/.ghcup/env
|
||||
|
||||
ghcup --version || install_ghcup
|
||||
cabal --version || install_cabal
|
||||
ghc --version || install_ghc
|
||||
|
||||
cd ~/$SCRIPT_DIR
|
||||
|
||||
# Clone the repository and build the project
|
||||
git clone https://github.com/PostgREST/postgrest.git
|
||||
cd postgrest
|
||||
git checkout $PGRST_GITHUB_COMMIT
|
||||
cabal v2-update && cabal v2-build
|
||||
|
||||
# Copy the built binary to the Dockerfile directory
|
||||
PGRST_BIN=$(cabal exec which postgrest | tail -1)
|
||||
cp $PGRST_BIN ~/$DOCKER_BUILD_DIR
|
||||
|
||||
# Move and compress the built binary
|
||||
mkdir -p ~/$SCRIPT_DIR/result
|
||||
mv $PGRST_BIN ~/$SCRIPT_DIR/result
|
||||
cd ~/$SCRIPT_DIR
|
||||
tar -cJf result.tar.xz result
|
||||
@@ -1,16 +0,0 @@
|
||||
# PostgREST docker hub image
|
||||
|
||||
FROM ubuntu:focal AS postgrest
|
||||
|
||||
RUN apt-get update -y \
|
||||
&& apt install -y --no-install-recommends libpq-dev zlib1g-dev jq gcc libnuma-dev \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY postgrest /usr/bin/postgrest
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
USER 1000
|
||||
|
||||
CMD postgrest
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script publishes the Docker ARM images to Docker Hub.
|
||||
|
||||
[ -z "$1" ] && { echo "Missing 1st argument: PostgREST github commit SHA"; exit 1; }
|
||||
[ -z "$2" ] && { echo "Missing 2nd argument: Docker repo"; exit 1; }
|
||||
[ -z "$3" ] && { echo "Missing 3rd argument: Docker username"; exit 1; }
|
||||
[ -z "$4" ] && { echo "Missing 4th argument: Docker password"; exit 1; }
|
||||
[ -z "$5" ] && { echo "Missing 5th argument: Build environment directory name"; exit 1; }
|
||||
[ -z "$6" ] && { echo "Missing 6th argument: PostgREST version"; exit 1; }
|
||||
|
||||
PGRST_GITHUB_COMMIT="$1"
|
||||
DOCKER_REPO="$2"
|
||||
DOCKER_USER="$3"
|
||||
DOCKER_PASS="$4"
|
||||
SCRIPT_DIR="$5"
|
||||
PGRST_VERSION="v$6"
|
||||
IS_PRERELEASE="$7"
|
||||
|
||||
DOCKER_BUILD_DIR="$SCRIPT_DIR/docker-env"
|
||||
|
||||
clean_env()
|
||||
{
|
||||
sudo docker logout
|
||||
}
|
||||
|
||||
# Login to Docker
|
||||
sudo docker logout
|
||||
{ echo $DOCKER_PASS | sudo docker login -u $DOCKER_USER --password-stdin; } || { echo "Couldn't login to docker"; exit 1; }
|
||||
|
||||
trap clean_env sigint sigterm exit
|
||||
|
||||
# Move to the docker build environment
|
||||
cd ~/$DOCKER_BUILD_DIR
|
||||
|
||||
# Push final images to Docker hub
|
||||
# NOTE: This command publishes a separate ARM image because the builds cannot
|
||||
# be added to the manifest if they are not in the registry beforehand.
|
||||
# This image must be manually deleted from Docker Hub at the end of the process.
|
||||
sudo docker buildx build --build-arg PGRST_GITHUB_COMMIT=$PGRST_GITHUB_COMMIT \
|
||||
-t $DOCKER_REPO/postgrest:$PGRST_VERSION-arm \
|
||||
--push .
|
||||
|
||||
# Add the arm images to the manifest
|
||||
# NOTE: This assumes that there already is a `postgrest:<version>` image
|
||||
# for the amd64 architecture pushed to Docker Hub
|
||||
sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:$PGRST_VERSION $DOCKER_REPO/postgrest:$PGRST_VERSION-arm
|
||||
[ -z $IS_PRERELEASE ] && sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:latest $DOCKER_REPO/postgrest:$PGRST_VERSION-arm
|
||||
|
||||
sudo docker logout
|
||||
@@ -1,506 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- rel-*
|
||||
tags:
|
||||
- v*
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- rel-*
|
||||
|
||||
jobs:
|
||||
Lint-Style:
|
||||
name: Lint & check code style
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
tools: style
|
||||
- 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
|
||||
|
||||
|
||||
Test-Nix:
|
||||
name: Test (Nix)
|
||||
runs-on: ubuntu-latest
|
||||
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@v3
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
tools: tests
|
||||
|
||||
- name: Run coverage (IO tests and Spec tests against PostgreSQL 15)
|
||||
run: postgrest-coverage
|
||||
- name: Upload coverage to codecov
|
||||
uses: codecov/codecov-action@v3.1.1
|
||||
with:
|
||||
files: ./coverage/codecov.json
|
||||
|
||||
- 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
|
||||
|
||||
|
||||
Test-Pg-Nix:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pgVersion: [9.6, 10, 11, 12, 13, 14, 15]
|
||||
name: Test PG ${{ matrix.pgVersion }} (Nix)
|
||||
runs-on: ubuntu-latest
|
||||
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@v3
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
tools: tests withTools
|
||||
|
||||
- name: Run spec tests
|
||||
if: always()
|
||||
run: postgrest-with-postgresql-${{ matrix.pgVersion }} postgrest-test-spec
|
||||
|
||||
- name: Run IO tests
|
||||
if: always()
|
||||
run: postgrest-with-postgresql-${{ matrix.pgVersion }} -f test/io/fixtures.sql postgrest-test-io
|
||||
|
||||
- name: Run query cost tests
|
||||
if: always()
|
||||
run: postgrest-with-postgresql-${{ matrix.pgVersion }} postgrest-test-querycost
|
||||
|
||||
|
||||
Test-Memory-Nix:
|
||||
name: Test memory (Nix)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
tools: memory
|
||||
- name: Run memory tests
|
||||
run: postgrest-test-memory
|
||||
|
||||
|
||||
Build-Static-Nix:
|
||||
name: Build Linux static (Nix)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: tests
|
||||
|
||||
- name: Build static executable
|
||||
run: nix-build -A postgrestStatic
|
||||
- name: Check static executable
|
||||
run: postgrest-check-static result/bin/postgrest
|
||||
- name: Save built executable as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: postgrest-linux-static-x64
|
||||
path: result/bin/postgrest
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build Docker image
|
||||
run: nix-build -A docker.image --out-link postgrest-docker.tar.gz
|
||||
- name: Save built Docker image as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: postgrest-docker-x64
|
||||
path: postgrest-docker.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build and push everything to Cachix (main branch only)
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
run: |
|
||||
nix-build
|
||||
nix-env -f default.nix -iA devTools
|
||||
postgrest-push-cachix
|
||||
|
||||
|
||||
Build-Stack:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
cache: |
|
||||
~/.stack
|
||||
.stack-work
|
||||
artifact: postgrest-ubuntu-x64
|
||||
|
||||
- name: MacOS
|
||||
runs-on: macos-latest
|
||||
cache: |
|
||||
~/.stack
|
||||
.stack-work
|
||||
artifact: postgrest-macos-x64
|
||||
|
||||
- name: Windows
|
||||
runs-on: windows-latest
|
||||
cache: |
|
||||
~\AppData\Roaming\stack
|
||||
~\AppData\Local\Programs\stack
|
||||
.stack-work
|
||||
deps: Add-Content $env:GITHUB_PATH $env:PGBIN
|
||||
artifact: postgrest-windows-x64
|
||||
|
||||
name: Build ${{ matrix.name }} (Stack)
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Stack working files cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ matrix.cache }}
|
||||
key: ${{ runner.os }}-${{ hashFiles('stack.yaml.lock') }}
|
||||
- name: Install dependencies
|
||||
if: ${{ matrix.deps }}
|
||||
run: ${{ matrix.deps }}
|
||||
- name: Build with Stack
|
||||
run: stack build --local-bin-path result --copy-bins
|
||||
- name: Save built executable as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.artifact }}
|
||||
path: |
|
||||
result/postgrest
|
||||
result/postgrest.exe
|
||||
if-no-files-found: error
|
||||
|
||||
Get-FreeBSD-CirrusCI:
|
||||
name: Get FreeBSD build from CirrusCI
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Get FreeBSD executable from CirrusCI
|
||||
env:
|
||||
# GITHUB_SHA does weird things for pull request, so we roll our own:
|
||||
GITHUB_COMMIT: ${{github.event.pull_request.head.sha || github.sha}}
|
||||
run: .github/get_cirrusci_freebsd
|
||||
- name: Save executable as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: postgrest-freebsd-x64
|
||||
path: postgrest
|
||||
if-no-files-found: error
|
||||
|
||||
Build-Cabal:
|
||||
strategy:
|
||||
matrix:
|
||||
ghc: ['8.10.7', '9.2.4']
|
||||
fail-fast: false
|
||||
name: Build Linux (Cabal, GHC ${{ matrix.ghc }})
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: ghcup
|
||||
run: |
|
||||
ghcup install ghc ${{ matrix.ghc }}
|
||||
ghcup set ghc ${{ matrix.ghc }}
|
||||
- name: Copy cabal.project
|
||||
run: |
|
||||
cp cabal.project.non-nix cabal.project
|
||||
- name: Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cabal
|
||||
key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.ghc }}-
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cabal update
|
||||
cabal build --only-dependencies --enable-tests --enable-benchmarks
|
||||
- name: Build
|
||||
run: cabal build --enable-tests --enable-benchmarks all
|
||||
|
||||
Build-Cabal-Arm:
|
||||
name: Build aarch64 (Cabal)
|
||||
if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || startsWith(github.ref, 'refs/heads/rel-') }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
remotepath: ${{ steps.Remote-Dir.outputs.remotepath }}
|
||||
env:
|
||||
GITHUB_COMMIT: ${{ github.sha }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- id: Remote-Dir
|
||||
name: Unique directory name for the remote build
|
||||
run: echo "::set-output name=remotepath::postgrest-build-$(uuidgen)"
|
||||
- name: Copy script files to the remote server
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: ${{ secrets.SSH_ARM_HOST }}
|
||||
username: ubuntu
|
||||
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
||||
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
||||
source: ".github/scripts/arm/*"
|
||||
target: ${{ steps.Remote-Dir.outputs.remotepath }}
|
||||
strip_components: 3
|
||||
- name: Build ARM
|
||||
uses: appleboy/ssh-action@master
|
||||
env:
|
||||
REMOTE_DIR: ${{ steps.Remote-Dir.outputs.remotepath }}
|
||||
with:
|
||||
host: ${{ secrets.SSH_ARM_HOST }}
|
||||
username: ubuntu
|
||||
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
||||
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
||||
command_timeout: 120m
|
||||
script_stop: true
|
||||
envs: GITHUB_COMMIT,REMOTE_DIR
|
||||
script: bash ~/$REMOTE_DIR/build.sh "$GITHUB_COMMIT" "$REMOTE_DIR"
|
||||
- name: Download binaries from remote server
|
||||
uses: nicklasfrahm/scp-action@main
|
||||
with:
|
||||
direction: download
|
||||
host: ${{ secrets.SSH_ARM_HOST }}
|
||||
username: ubuntu
|
||||
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
||||
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
||||
source: "${{ steps.Remote-Dir.outputs.remotepath }}/result.tar.xz"
|
||||
target: "result.tar.xz"
|
||||
- name: Extract downloaded binaries
|
||||
run: tar -xvf result.tar.xz && rm result.tar.xz
|
||||
- name: Save aarch64 executable as artifact
|
||||
uses: actions/upload-artifact@v2.3.1
|
||||
with:
|
||||
name: postgrest-ubuntu-aarch64
|
||||
path: result/postgrest
|
||||
if-no-files-found: error
|
||||
|
||||
|
||||
Prepare-Release:
|
||||
name: Prepare release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- Lint-Style
|
||||
- Test-Nix
|
||||
- Test-Pg-Nix
|
||||
- Test-Memory-Nix
|
||||
- Build-Static-Nix
|
||||
- Build-Stack
|
||||
#- Get-FreeBSD-CirrusCI
|
||||
- Build-Cabal-Arm
|
||||
outputs:
|
||||
version: ${{ steps.Identify-Version.outputs.version }}
|
||||
isprerelease: ${{ steps.Identify-Version.outputs.isprerelease }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- id: Identify-Version
|
||||
name: Identify the version to be released
|
||||
run: |
|
||||
tag_version="${GITHUB_REF##*/}"
|
||||
cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
|
||||
|
||||
if [ "$tag_version" != "v$cabal_version" ]; then
|
||||
echo "Tagged version ($tag_version) does not match the one in postgrest.cabal (v$cabal_version). Aborting release..."
|
||||
exit 1
|
||||
else
|
||||
echo "Version to be released is $cabal_version"
|
||||
echo "::set-output name=version::$cabal_version"
|
||||
fi
|
||||
|
||||
if [[ "$cabal_version" != *.*.*.* ]]; then
|
||||
echo "Version is for a full release (version does not have four components)"
|
||||
else
|
||||
echo "Version is for a pre-release (version has four components, e.g., 1.1.1.1)"
|
||||
echo "::set-output name=isprerelease::1"
|
||||
fi
|
||||
- name: Identify changes from CHANGELOG.md
|
||||
run: |
|
||||
version="${{ steps.Identify-Version.outputs.version }}"
|
||||
isprerelease="${{ steps.Identify-Version.outputs.isprerelease }}"
|
||||
|
||||
if [ -n "$isprerelease" ]; then
|
||||
echo "Getting unreleased changes..."
|
||||
sed -n "1,/## Unreleased/d;/## \[/q;p" CHANGELOG.md > CHANGES.md
|
||||
else
|
||||
echo "Full release (no '-' in version), 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: Save CHANGES.md as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: release-changes
|
||||
path: CHANGES.md
|
||||
if-no-files-found: error
|
||||
|
||||
|
||||
Release-GitHub:
|
||||
name: Release on GitHub
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
needs: Prepare-Release
|
||||
env:
|
||||
VERSION: ${{ needs.Prepare-Release.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
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-v$VERSION-linux-static-x64.tar.xz" \
|
||||
-C artifacts/postgrest-linux-static-x64 postgrest
|
||||
|
||||
# No need to release Ubuntu, as the static Linux binary built with Nix
|
||||
# covers all Linux use-cases
|
||||
#tar cfJv "release-bundle/postgrest-v$VERSION-ubuntu-x64.tar.xz" \
|
||||
# -C artifacts/postgrest-ubuntu-x64 postgrest
|
||||
|
||||
tar cJvf "release-bundle/postgrest-v$VERSION-macos-x64.tar.xz" \
|
||||
-C artifacts/postgrest-macos-x64 postgrest
|
||||
|
||||
# TODO: Fix timeouts for FreeBSD builds in Cirrus
|
||||
#tar cJvf "release-bundle/postgrest-v$VERSION-freebsd-x64.tar.xz" \
|
||||
# -C artifacts/postgrest-freebsd-x64 postgrest
|
||||
|
||||
tar cJvf "release-bundle/postgrest-v$VERSION-ubuntu-aarch64.tar.xz" \
|
||||
-C artifacts/postgrest-ubuntu-aarch64 postgrest
|
||||
|
||||
zip "release-bundle/postgrest-v$VERSION-windows-x64.zip" \
|
||||
artifacts/postgrest-windows-x64/postgrest.exe
|
||||
|
||||
- name: Save release bundle
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: release-bundle
|
||||
path: release-bundle
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Publish release on GitHub
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
isprerelease="${{ needs.Prepare-Release.outputs.isprerelease }}"
|
||||
echo "Releasing version v$VERSION on GitHub (isprerelease=$isprerelease)..."
|
||||
|
||||
gh release delete "v$VERSION" || true
|
||||
gh release create "v$VERSION" \
|
||||
-F artifacts/release-changes/CHANGES.md \
|
||||
${isprerelease:+"--prerelease"} \
|
||||
release-bundle/*
|
||||
|
||||
|
||||
Release-Docker:
|
||||
name: Release on Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- Build-Cabal-Arm
|
||||
- Prepare-Release
|
||||
env:
|
||||
GITHUB_COMMIT: ${{ github.sha }}
|
||||
DOCKER_REPO: postgrest
|
||||
DOCKER_USER: stevechavez
|
||||
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
|
||||
VERSION: ${{ needs.Prepare-Release.outputs.version }}
|
||||
ISPRERELEASE: ${{ needs.Prepare-Release.outputs.isprerelease }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
tools: release
|
||||
- name: Download Docker image
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: postgrest-docker-x64
|
||||
- name: Publish images on Docker Hub
|
||||
run: |
|
||||
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
|
||||
docker load -i postgrest-docker.tar.gz
|
||||
|
||||
docker tag postgrest:latest "$DOCKER_REPO/postgrest:v$VERSION"
|
||||
docker push "$DOCKER_REPO/postgrest:v$VERSION"
|
||||
|
||||
# Only tag 'latest' for full releases
|
||||
if [[ -z "$ISPRERELEASE" ]]; then
|
||||
echo "Pushing to 'latest' tag for full release of v$VERSION ..."
|
||||
docker tag postgrest:latest "$DOCKER_REPO"/postgrest:latest
|
||||
docker push "$DOCKER_REPO"/postgrest:latest
|
||||
else
|
||||
echo "Skipping pushing to 'latest' tag for v$VERSION pre-release..."
|
||||
fi
|
||||
- name: Publish images for ARM builds on Docker Hub
|
||||
uses: appleboy/ssh-action@master
|
||||
env:
|
||||
REMOTE_DIR: ${{ needs.Build-Cabal-Arm.outputs.remotepath }}
|
||||
with:
|
||||
host: ${{ secrets.SSH_ARM_HOST }}
|
||||
username: ubuntu
|
||||
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
||||
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
||||
script_stop: true
|
||||
envs: GITHUB_COMMIT,DOCKER_REPO,DOCKER_USER,DOCKER_PASS,REMOTE_DIR,VERSION,ISPRERELEASE
|
||||
script: bash ~/$REMOTE_DIR/docker-publish.sh "$GITHUB_COMMIT" "$DOCKER_REPO" "$DOCKER_USER" "$DOCKER_PASS" "$REMOTE_DIR" "$VERSION" "$ISPRERELEASE"
|
||||
# TODO: Enable dockerhub description update again, once a solution for the permission problem is found:
|
||||
# https://github.com/docker/hub-feedback/issues/1927
|
||||
# - name: Update descriptions on Docker Hub
|
||||
# env:
|
||||
# DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
|
||||
# run: |
|
||||
# if [[ -z "$ISPRERELEASE" ]]; then
|
||||
# echo "Updating description on Docker Hub..."
|
||||
# postgrest-release-dockerhub-description
|
||||
# else
|
||||
# echo "Skipping updating description for pre-release..."
|
||||
# fi
|
||||
|
||||
Clean-Arm-Server:
|
||||
name: Remove copied files from server
|
||||
needs:
|
||||
- Build-Cabal-Arm
|
||||
- Release-Docker
|
||||
if: ${{ always() && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || startsWith(github.ref, 'refs/heads/rel-')) }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
REMOTE_DIR: ${{ needs.Build-Cabal-Arm.outputs.remotepath }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- name: Remove uploaded files from server
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.SSH_ARM_HOST }}
|
||||
username: ubuntu
|
||||
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
||||
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
||||
envs: REMOTE_DIR
|
||||
script: rm -rf $REMOTE_DIR
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v[0-9]+
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- v[0-9]+
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build docs
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31
|
||||
- run: nix-env -f docs/default.nix -iA build
|
||||
- run: postgrest-docs-build
|
||||
|
||||
spellcheck:
|
||||
name: Run spellcheck
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31
|
||||
- run: nix-env -f docs/default.nix -iA spellcheck
|
||||
- run: postgrest-docs-spellcheck
|
||||
|
||||
dictcheck:
|
||||
name: Run dictcheck
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31
|
||||
- run: nix-env -f docs/default.nix -iA dictcheck
|
||||
- run: postgrest-docs-dictcheck
|
||||
|
||||
linkcheck:
|
||||
name: Run linkcheck
|
||||
if: github.base_ref == 'main'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31
|
||||
- run: nix-env -f docs/default.nix -iA linkcheck
|
||||
- run: postgrest-docs-linkcheck
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
name: Loadtest
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- v*
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
Loadtest-Nix:
|
||||
name: Loadtest (Nix)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
tools: loadtest
|
||||
- name: Run loadtest
|
||||
run: |
|
||||
postgrest-loadtest-against main
|
||||
postgrest-loadtest-report > loadtest/loadtest.md
|
||||
- name: Upload report
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: loadtest.md
|
||||
path: loadtest/loadtest.md
|
||||
if-no-files-found: error
|
||||
@@ -1,33 +0,0 @@
|
||||
name: Upload Reports
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Loadtest"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
name: Loadtest
|
||||
permissions:
|
||||
checks: write
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- name: Download from Artifacts
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: ${{ github.event.workflow.name }}
|
||||
run_id: ${{github.event.workflow_run.id }}
|
||||
name: loadtest.md
|
||||
path: artifacts
|
||||
- name: Upload to GitHub Checks
|
||||
uses: LouisBrunner/checks-action@v1.5.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sha: ${{ github.event.workflow_run.head_sha }}
|
||||
name: Loadtest Results
|
||||
conclusion: neutral
|
||||
output: |
|
||||
{"summary":""}
|
||||
output_text_description_file: artifacts/loadtest.md
|
||||
@@ -23,3 +23,4 @@ coverage
|
||||
.hpc
|
||||
loadtest
|
||||
.history
|
||||
.docs-build
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
@@ -3,6 +3,19 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [10.2.0] - 2023-04-12
|
||||
|
||||
### Added
|
||||
|
||||
- #2663, Limit maximal postgresql connection lifetime - @robx
|
||||
+ New option `db-pool-max-lifetime` (default 30m)
|
||||
+ `db-pool-acquisition-timeout` is no longer optional and defaults to 10s
|
||||
+ Fixes postgresql resource leak with long-lived connections (#2638)
|
||||
|
||||
### Fixed
|
||||
|
||||
- #2667, Fix `db-pool-acquisition-timeout` not logging to stderr when the timeout is reached - @steve-chavez
|
||||
|
||||
## [10.1.2] - 2023-02-01
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
This repository follows the same contribution guidelines as the main PostgREST repository contribution guidelines:
|
||||
|
||||
https://github.com/PostgREST/postgrest/blob/main/.github/CONTRIBUTING.md
|
||||
@@ -1,8 +1,6 @@
|
||||

|
||||
|
||||
[](https://www.patreon.com/postgrest)
|
||||
[](https://www.paypal.me/postgrest)
|
||||
[](https://gitter.im/begriffs/postgrest)
|
||||
[](http://postgrest.org)
|
||||
[](https://hub.docker.com/r/postgrest/postgrest/)
|
||||
[](https://github.com/PostgREST/postgrest/actions?query=branch%3Amain)
|
||||
@@ -145,11 +143,7 @@ and the [API guide](http://postgrest.org/en/stable/api.html).
|
||||
|
||||
## Supporting development
|
||||
|
||||
You can help PostgREST ongoing maintenance and development by:
|
||||
|
||||
- Making a regular donation through Patreon https://www.patreon.com/postgrest
|
||||
|
||||
- Alternatively, you can make a one-time donation via Paypal https://www.paypal.me/postgrest
|
||||
You can help PostgREST ongoing maintenance and development by making a regular donation through Patreon https://www.patreon.com/postgrest
|
||||
|
||||
Every donation will be spent on making PostgREST better for the whole community.
|
||||
|
||||
|
||||
@@ -17,4 +17,4 @@ packages: .
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/PostgREST/postgresql-libpq.git
|
||||
tag: 33ff97db570b5b432255f5f24a68db51453f6eb8
|
||||
tag: 890a0a16cf57dd401420fdc6c7d576fb696003bc
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
_build
|
||||
Pipfile.lock
|
||||
*.aux
|
||||
*.log
|
||||
_diagrams/db.pdf
|
||||
misspellings
|
||||
unuseddict
|
||||
*.mo
|
||||
@@ -0,0 +1,20 @@
|
||||
# PostgREST documentation https://postgrest.org/
|
||||
|
||||
PostgREST docs use the reStructuredText format, check this [cheatsheet](https://github.com/ralsina/rst-cheatsheet/blob/master/rst-cheatsheet.rst) to get acquainted with it.
|
||||
|
||||
To build the docs locally, use [nix](https://nixos.org/nix/):
|
||||
|
||||
```bash
|
||||
nix-shell
|
||||
```
|
||||
|
||||
Once in the nix-shell you have the following commands available:
|
||||
|
||||
- `postgrest-docs-build`: Build the docs.
|
||||
- `postgrest-docs-serve`: Build the docs and start a livereload server on `http://localhost:5500`.
|
||||
- `postgrest-docs-spellcheck`: Run aspell.
|
||||
|
||||
## Documentation structure
|
||||
|
||||
This documentation is structured according to tutorials-howtos-topics-references. For more details on the rationale of this structure,
|
||||
see https://www.divio.com/blog/documentation.
|
||||
@@ -0,0 +1,40 @@
|
||||
## ERD
|
||||
|
||||
The ER diagrams were created with https://github.com/BurntSushi/erd/.
|
||||
|
||||
You can go download erd from https://github.com/BurntSushi/erd/releases and then do:
|
||||
|
||||
```bash
|
||||
./erd_static-x86-64 -i film.er -o ../_static/film.png
|
||||
```
|
||||
|
||||
## LaTeX
|
||||
|
||||
The schema structure diagram is done with LaTeX. You can use a GUI like https://www.mathcha.io/editor to create the .tex file.
|
||||
|
||||
Then use this command to generate the png file.
|
||||
|
||||
```bash
|
||||
pdflatex --shell-escape -halt-on-error db.tex
|
||||
|
||||
## and move it to the static folder(it's not easy to do it in one go with the pdflatex)
|
||||
mv db.png ../_static/
|
||||
```
|
||||
|
||||
LaTeX is used because it's a tweakable plain text format.
|
||||
|
||||
You can install the full latex suite with `nix`:
|
||||
|
||||
```
|
||||
nix-env -iA texlive.combined.scheme-full
|
||||
```
|
||||
|
||||
To tweak the file with a live reload environment use:
|
||||
|
||||
```bash
|
||||
# open the pdf(zathura used as an example)
|
||||
zathura db.pdf &
|
||||
|
||||
# live reload with entr
|
||||
echo db.tex | entr pdflatex --shell-escape -halt-on-error db.tex
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
\documentclass[convert]{standalone}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{tikz}
|
||||
\usepackage{mathdots}
|
||||
\usepackage{yhmath}
|
||||
\usepackage{cancel}
|
||||
\usepackage{color}
|
||||
\usepackage{siunitx}
|
||||
\usepackage{array}
|
||||
\usepackage{multirow}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{gensymb}
|
||||
\usepackage{tabularx}
|
||||
\usepackage{booktabs}
|
||||
\usetikzlibrary{fadings}
|
||||
\usetikzlibrary{patterns}
|
||||
\usetikzlibrary{shadows.blur}
|
||||
\usetikzlibrary{shapes}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\newcommand\customScale{0.35}
|
||||
|
||||
\begin{tikzpicture}[x=0.75pt,y=0.75pt,yscale=-1,xscale=1, scale=\customScale, every node/.style={scale=\customScale}]
|
||||
|
||||
%Shape: Can [id:dp7234864758664346]
|
||||
\draw [fill={rgb, 255:red, 47; green, 97; blue, 144 } ,fill opacity=1 ] (497.5,51.5) -- (497.5,255.5) .. controls (497.5,275.66) and (423.18,292) .. (331.5,292) .. controls (239.82,292) and (165.5,275.66) .. (165.5,255.5) -- (165.5,51.5) .. controls (165.5,31.34) and (239.82,15) .. (331.5,15) .. controls (423.18,15) and (497.5,31.34) .. (497.5,51.5) .. controls (497.5,71.66) and (423.18,88) .. (331.5,88) .. controls (239.82,88) and (165.5,71.66) .. (165.5,51.5) ;
|
||||
%Shape: Rectangle [id:dp7384065579958246]
|
||||
\draw [fill={rgb, 255:red, 236; green, 227; blue, 227 } ,fill opacity=1 ] (189,115) -- (252.5,115) -- (252.5,155) -- (189,155) -- cycle ;
|
||||
%Shape: Rectangle [id:dp24763906430298177]
|
||||
\draw [fill={rgb, 255:red, 236; green, 227; blue, 227 } ,fill opacity=1 ] (292,118) -- (362,118) -- (362,158) -- (292,158) -- cycle ;
|
||||
%Shape: Rectangle [id:dp3775601612537265]
|
||||
\draw [fill={rgb, 255:red, 236; green, 227; blue, 227 } ,fill opacity=1 ] (397,114) -- (467,114) -- (467,154) -- (397,154) -- cycle ;
|
||||
%Shape: Rectangle [id:dp7071457022893852]
|
||||
\draw [fill={rgb, 255:red, 248; green, 231; blue, 28 } ,fill opacity=1 ] (269,199) -- (397.5,199) -- (397.5,273) -- (269,273) -- cycle ;
|
||||
%Straight Lines [id:da8846759047437789]
|
||||
\draw (268,234) -- (226.44,155.77) ;
|
||||
\draw [shift={(225.5,154)}, rotate = 422.02] [color={rgb, 255:red, 0; green, 0; blue, 0 } ][line width=0.75] (10.93,-3.29) .. controls (6.95,-1.4) and (3.31,-0.3) .. (0,0) .. controls (3.31,0.3) and (6.95,1.4) .. (10.93,3.29) ;
|
||||
%Straight Lines [id:da6908444738113828]
|
||||
\draw (309.5,198) -- (307.6,161) ;
|
||||
\draw [shift={(307.5,159)}, rotate = 447.06] [color={rgb, 255:red, 0; green, 0; blue, 0 } ][line width=0.75] (10.93,-3.29) .. controls (6.95,-1.4) and (3.31,-0.3) .. (0,0) .. controls (3.31,0.3) and (6.95,1.4) .. (10.93,3.29) ;
|
||||
%Straight Lines [id:da7168757864413169]
|
||||
\draw (398.5,233) -- (431.72,154.84) ;
|
||||
\draw [shift={(432.5,153)}, rotate = 473.03] [color={rgb, 255:red, 0; green, 0; blue, 0 } ][line width=0.75] (10.93,-3.29) .. controls (6.95,-1.4) and (3.31,-0.3) .. (0,0) .. controls (3.31,0.3) and (6.95,1.4) .. (10.93,3.29) ;
|
||||
%Up Down Arrow [id:dp14059754167108496]
|
||||
\draw [fill={rgb, 255:red, 126; green, 211; blue, 33 } ,fill opacity=1 ] (312.5,288.5) -- (330,273) -- (347.5,288.5) -- (338.75,288.5) -- (338.75,319.5) -- (347.5,319.5) -- (330,335) -- (312.5,319.5) -- (321.25,319.5) -- (321.25,288.5) -- cycle ;
|
||||
|
||||
% Text Node
|
||||
\draw (201,129) node [anchor=north west][inner sep=0.75pt] [align=left] {tables};
|
||||
% Text Node
|
||||
\draw (307,130) node [anchor=north west][inner sep=0.75pt] [align=left ] {tables};
|
||||
% Text Node
|
||||
\draw (414,127) node [anchor=north west][inner sep=0.75pt] [align=left] {tables};
|
||||
% Text Node
|
||||
\draw (272,203) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 0; green, 0; blue, 0 } ,opacity=1 ] [align=center] { \\ views \\ + \\ \ \ stored procedures};
|
||||
|
||||
% Text Node
|
||||
\draw (322,178) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 255; green, 255; blue, 255 } ,opacity=1 ] [align=left] {\large\textbf{api}};
|
||||
% Text Node
|
||||
\draw (190,97) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 255; green, 255; blue, 255 } ,opacity=1 ] [align=left] {\large\textbf{internal}};
|
||||
% Text Node
|
||||
\draw (300,99) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 255; green, 255; blue, 255 } ,opacity=1 ] [align=left] {\large\textbf{private}};
|
||||
% Text Node
|
||||
\draw (417,101) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 255; green, 255; blue, 255 } ,opacity=1 ] [align=left] {\large\textbf{core}};
|
||||
% Text Node
|
||||
\draw (358,306) node [anchor=north west][inner sep=0.75pt] [align=left] {REST};
|
||||
|
||||
\end{tikzpicture}
|
||||
|
||||
|
||||
\end{document}
|
||||
@@ -0,0 +1,40 @@
|
||||
[Films]
|
||||
*id
|
||||
+director_id
|
||||
title
|
||||
year
|
||||
rating
|
||||
language
|
||||
|
||||
[Directors]
|
||||
*id
|
||||
first_name
|
||||
last_name
|
||||
|
||||
[Actors]
|
||||
*id
|
||||
first_name
|
||||
last_name
|
||||
|
||||
[Roles]
|
||||
*+film_id
|
||||
*+actor_id
|
||||
character
|
||||
|
||||
[Competitions]
|
||||
*id
|
||||
name
|
||||
year
|
||||
|
||||
[Nominations]
|
||||
*+competition_id
|
||||
*+film_id
|
||||
rank
|
||||
|
||||
Roles *--1 Actors
|
||||
Roles *--1 Films
|
||||
|
||||
Nominations *--1 Competitions
|
||||
Nominations *--1 Films
|
||||
|
||||
Films *--1 Directors
|
||||
@@ -0,0 +1,15 @@
|
||||
[Addresses]
|
||||
*id
|
||||
name
|
||||
city
|
||||
state
|
||||
postal_code
|
||||
|
||||
[Orders]
|
||||
*id
|
||||
name
|
||||
+billing_address_id
|
||||
+shipping_address_id
|
||||
|
||||
Orders *--1 Addresses
|
||||
Orders *--1 Addresses
|
||||
|
After Width: | Height: | Size: 88 KiB |
@@ -0,0 +1,67 @@
|
||||
.wy-nav-content {
|
||||
max-width: initial;
|
||||
}
|
||||
|
||||
#postgrest-documentation > h1 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.wy-menu.rst-pro {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
div.highlight {
|
||||
background: #fff !important;
|
||||
}
|
||||
|
||||
div.line-block {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
#sponsors {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#sponsors h2 {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#sponsors img{
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#thanks{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#thanks img{
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#thanks h2{
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#thanks p{
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#thanks ul{
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.image-container {
|
||||
max-width: 800px;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.wy-table-responsive table td {
|
||||
white-space: normal !important;
|
||||
}
|
||||
|
||||
.wy-table-responsive {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
After Width: | Height: | Size: 345 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 468 B |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,385 @@
|
||||
.. _admin:
|
||||
|
||||
Hardening PostgREST
|
||||
===================
|
||||
|
||||
PostgREST is a fast way to construct a RESTful API. Its default behavior is great for scaffolding in development. When it's time to go to production it works great too, as long as you take precautions. PostgREST is a small sharp tool that focuses on performing the API-to-database mapping. We rely on a reverse proxy like Nginx for additional safeguards.
|
||||
|
||||
The first step is to create an Nginx configuration file that proxies requests to an underlying PostgREST server.
|
||||
|
||||
.. code-block:: nginx
|
||||
|
||||
http {
|
||||
# ...
|
||||
# upstream configuration
|
||||
upstream postgrest {
|
||||
server localhost:3000;
|
||||
}
|
||||
# ...
|
||||
server {
|
||||
# ...
|
||||
# expose to the outside world
|
||||
location /api/ {
|
||||
default_type application/json;
|
||||
proxy_hide_header Content-Location;
|
||||
add_header Content-Location /api/$upstream_http_content_location;
|
||||
proxy_set_header Connection "";
|
||||
proxy_http_version 1.1;
|
||||
proxy_pass http://postgrest/;
|
||||
}
|
||||
# ...
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
For ubuntu, if you already installed nginx through :code:`apt` you can add this to the config file in
|
||||
:code:`/etc/nginx/sites-enabled/default`.
|
||||
|
||||
.. _block_fulltable:
|
||||
|
||||
Block Full-Table Operations
|
||||
---------------------------
|
||||
|
||||
Each table in the admin-selected schema gets exposed as a top level route. Client requests are executed by certain database roles depending on their authentication. All HTTP verbs are supported that correspond to actions permitted to the role. For instance if the active role can drop rows of the table then the DELETE verb is allowed for clients. Here's an API request to delete old rows from a hypothetical logs table:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
DELETE /logs?time=lt.1991-08-06 HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/logs?time=lt.1991-08-06" -X DELETE
|
||||
|
||||
However it's very easy to delete the **entire table** by omitting the query parameter!
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
DELETE /logs HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/logs" -X DELETE
|
||||
|
||||
This can happen accidentally such as by switching a request from a GET to a DELETE. To protect against accidental operations use the `pg-safeupdate <https://github.com/eradman/pg-safeupdate>`_ PostgreSQL extension. It raises an error if UPDATE or DELETE are executed without specifying conditions. To install it you can use the `PGXN <https://pgxn.org/>`_ network:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo -E pgxn install safeupdate
|
||||
|
||||
# then add this to postgresql.conf:
|
||||
# shared_preload_libraries='safeupdate';
|
||||
|
||||
This does not protect against malicious actions, since someone can add a url parameter that does not affect the result set. To prevent this you must turn to database permissions, forbidding the wrong people from deleting rows, and using `row-level security <https://www.postgresql.org/docs/current/ddl-rowsecurity.html>`_ if finer access control is required.
|
||||
|
||||
Count-Header DoS
|
||||
----------------
|
||||
|
||||
For convenience to client-side pagination controls PostgREST supports counting and reporting total table size in its response. As described in :ref:`limits`, responses ordinarily include a range but leave the total unspecified like
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Range-Unit: items
|
||||
Content-Range: 0-14/*
|
||||
|
||||
However including the request header :code:`Prefer: count=exact` calculates and includes the full count:
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 206 Partial Content
|
||||
Range-Unit: items
|
||||
Content-Range: 0-14/3573458
|
||||
|
||||
This is fine in small tables, but count performance degrades in big tables due to the MVCC architecture of PostgreSQL. For very large tables it can take a very long time to retrieve the results which allows a denial of service attack. The solution is to strip this header from all requests:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- Pending nginx config: Remove any prefer header which contains the word count
|
||||
|
||||
.. _https:
|
||||
|
||||
HTTPS
|
||||
-----
|
||||
|
||||
PostgREST aims to do one thing well: add an HTTP interface to a PostgreSQL database. To keep the code small and focused we do not implement HTTPS. Use a reverse proxy such as NGINX to add this, `here's how <https://nginx.org/en/docs/http/configuring_https_servers.html>`_. Note that some Platforms as a Service like Heroku also add SSL automatically in their load balancer.
|
||||
|
||||
Rate Limiting
|
||||
-------------
|
||||
|
||||
Nginx supports "leaky bucket" rate limiting (see `official docs <https://nginx.org/en/docs/http/ngx_http_limit_req_module.html>`_). Using standard Nginx configuration, routes can be grouped into *request zones* for rate limiting. For instance we can define a zone for login attempts:
|
||||
|
||||
.. code-block:: nginx
|
||||
|
||||
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
|
||||
|
||||
This creates a shared memory zone called "login" to store a log of IP addresses that access the rate limited urls. The space reserved, 10 MB (:code:`10m`) will give us enough space to store a history of 160k requests. We have chosen to allow only allow one request per second (:code:`1r/s`).
|
||||
|
||||
Next we apply the zone to certain routes, like a hypothetical stored procedure called :code:`login`.
|
||||
|
||||
.. code-block:: nginx
|
||||
|
||||
location /rpc/login/ {
|
||||
# apply rate limiting
|
||||
limit_req zone=login burst=5;
|
||||
}
|
||||
|
||||
The burst argument tells Nginx to start dropping requests if more than five queue up from a specific IP.
|
||||
|
||||
Nginx rate limiting is general and indiscriminate. To rate limit each authenticated request individually you will need to add logic in a :ref:`Custom Validation <custom_validation>` function.
|
||||
|
||||
.. _external_connection_poolers:
|
||||
|
||||
Using External Connection Poolers
|
||||
---------------------------------
|
||||
|
||||
PostgREST manages its :ref:`own pool of connections <db-pool>` and uses prepared statements by default in order to increase performance. However, this setting is incompatible with external connection poolers such as PgBouncer working in transaction pooling mode. In this case, you need to set the :ref:`db-prepared-statements` config option to ``false``. On the other hand, session pooling is fully compatible with PostgREST, while statement pooling is not compatible at all.
|
||||
|
||||
.. note::
|
||||
|
||||
If prepared statements are enabled, PostgREST will quit after detecting that transaction or statement pooling is being used.
|
||||
|
||||
You should also set the :ref:`db-channel-enabled` config option to ``false``, due to the ``LISTEN`` command not being compatible with transaction pooling, although it should not give any errors if it's left enabled by default.
|
||||
|
||||
Debugging
|
||||
=========
|
||||
|
||||
Server Version
|
||||
--------------
|
||||
|
||||
When debugging a problem it's important to verify the PostgREST version. At any time you can make a request to the running server and determine exactly which version is deployed. Look for the :code:`Server` HTTP response header, which contains the version number.
|
||||
|
||||
Errors
|
||||
------
|
||||
|
||||
See the :doc:`Errors <errors>` reference page for detailed information on the errors that PostgREST returns.
|
||||
|
||||
.. _pgrst_logging:
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
PostgREST logs basic request information to ``stdout``, including the authenticated user if available, the requesting IP address and user agent, the URL requested, and HTTP response status.
|
||||
|
||||
.. code::
|
||||
|
||||
127.0.0.1 - user [26/Jul/2021:01:56:38 -0500] "GET /clients HTTP/1.1" 200 - "" "curl/7.64.0"
|
||||
127.0.0.1 - anonymous [26/Jul/2021:01:56:48 -0500] "GET /unexistent HTTP/1.1" 404 - "" "curl/7.64.0"
|
||||
|
||||
For diagnostic information about the server itself, PostgREST logs to ``stderr``.
|
||||
|
||||
.. code::
|
||||
|
||||
12/Jun/2021:17:47:39 -0500: Attempting to connect to the database...
|
||||
12/Jun/2021:17:47:39 -0500: Listening on port 3000
|
||||
12/Jun/2021:17:47:39 -0500: Connection successful
|
||||
12/Jun/2021:17:47:39 -0500: Config re-loaded
|
||||
12/Jun/2021:17:47:40 -0500: Schema cache loaded
|
||||
|
||||
.. note::
|
||||
|
||||
When running it in an SSH session you must detach it from stdout or it will be terminated when the session closes. The easiest technique is redirecting the output to a log file or to the syslog:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
ssh foo@example.com \
|
||||
'postgrest foo.conf </dev/null >/var/log/postgrest.log 2>&1 &'
|
||||
|
||||
# another option is to pipe the output into "logger -t postgrest"
|
||||
|
||||
PostgREST logging provides limited information for debugging server errors. It's helpful to get full information about both client requests and the corresponding SQL commands executed against the underlying database.
|
||||
|
||||
HTTP Requests
|
||||
-------------
|
||||
|
||||
A great way to inspect incoming HTTP requests including headers and query parameters is to sniff the network traffic on the port where PostgREST is running. For instance on a development server bound to port 3000 on localhost, run this:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# sudo access is necessary for watching the network
|
||||
sudo ngrep -d lo0 port 3000
|
||||
|
||||
The options to ngrep vary depending on the address and host on which you've bound the server. The binding is described in the :ref:`configuration` section. The ngrep output isn't particularly pretty, but it's legible.
|
||||
|
||||
.. _automatic_recovery:
|
||||
|
||||
Automatic Connection Recovery
|
||||
-----------------------------
|
||||
|
||||
When PostgREST loses the connection to the database, it retries the connection using capped exponential backoff, with 32 seconds being the maximum backoff time.
|
||||
|
||||
This retry behavior is triggered immediately after the connection is lost if :ref:`db-channel-enabled` is set to true(the default), otherwise it will be activated once a request is made.
|
||||
|
||||
To notify the client when the next reconnection attempt will be, PostgREST responds with ``503 Service Unavailable`` and the ``Retry-After: x`` header, where ``x`` is the number of seconds programmed for the next retry.
|
||||
|
||||
Database Logs
|
||||
-------------
|
||||
|
||||
Once you've verified that requests are as you expect, you can get more information about the server operations by watching the database logs. By default PostgreSQL does not keep these logs, so you'll need to make the configuration changes below. Find :code:`postgresql.conf` inside your PostgreSQL data directory (to find that, issue the command :code:`show data_directory;`). Either find the settings scattered throughout the file and change them to the following values, or append this block of code to the end of the configuration file.
|
||||
|
||||
.. code:: sql
|
||||
|
||||
# send logs where the collector can access them
|
||||
log_destination = "stderr"
|
||||
|
||||
# collect stderr output to log files
|
||||
logging_collector = on
|
||||
|
||||
# save logs in pg_log/ under the pg data directory
|
||||
log_directory = "pg_log"
|
||||
|
||||
# (optional) new log file per day
|
||||
log_filename = "postgresql-%Y-%m-%d.log"
|
||||
|
||||
# log every kind of SQL statement
|
||||
log_statement = "all"
|
||||
|
||||
Restart the database and watch the log file in real-time to understand how HTTP requests are being translated into SQL commands.
|
||||
|
||||
.. note::
|
||||
|
||||
On Docker you can enable the logs by using a custom ``init.sh``:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
#!/bin/sh
|
||||
echo "log_statement = 'all'" >> /var/lib/postgresql/data/postgresql.conf
|
||||
|
||||
After that you can start the container and check the logs with ``docker logs``.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
docker run -v "$(pwd)/init.sh":"/docker-entrypoint-initdb.d/init.sh" -d postgres
|
||||
docker logs -f <container-id>
|
||||
|
||||
Schema Reloading
|
||||
----------------
|
||||
|
||||
Changing the schema while the server is running can lead to errors due to a stale schema cache. To learn how to refresh the cache see :ref:`schema_reloading`.
|
||||
|
||||
.. _health_check:
|
||||
|
||||
Health Check
|
||||
------------
|
||||
|
||||
You can enable a minimal health check to verify if PostgREST is available for client requests and to check the status of its internal state.
|
||||
|
||||
To do this, set the configuration variable :ref:`admin-server-port` to the port number of your preference. Two endpoints ``live`` and ``ready`` will then be available.
|
||||
|
||||
The ``live`` endpoint verifies if PostgREST is running on its configured port. A request will return ``200 OK`` if PostgREST is alive or ``503`` otherwise.
|
||||
|
||||
The ``ready`` endpoint also checks the state of both the Database Connection and the :ref:`schema_cache`. A request will return ``200 OK`` if it is ready or ``503`` if not.
|
||||
|
||||
For instance, to verify if PostgREST is running at ``localhost:3000`` while the ``admin-server-port`` is set to ``3001``:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET localhost:3001/live HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl -I "http://localhost:3001/live"
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
If you have a machine with multiple network interfaces and multiple PostgREST instances in the same port, you need to specify a unique :ref:`hostname <server-host>` in the configuration of each PostgREST instance for the health check to work correctly. Don't use the special values(``!4``, ``*``, etc) in this case because the health check could report a false positive.
|
||||
|
||||
Daemonizing
|
||||
===========
|
||||
|
||||
For Linux distributions that use **systemd** (Ubuntu, Debian, Archlinux) you can create a daemon in the following way.
|
||||
|
||||
First, create postgrest configuration in ``/etc/postgrest/config``
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
db-uri = "postgres://<your_user>:<your_password>@localhost:5432/<your_db>"
|
||||
db-schemas = "<your_exposed_schema>"
|
||||
db-anon-role = "<your_anon_role>"
|
||||
jwt-secret = "<your_secret>"
|
||||
|
||||
Then create the systemd service file in ``/etc/systemd/system/postgrest.service``
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[Unit]
|
||||
Description=REST API for any PostgreSQL database
|
||||
After=postgresql.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/postgrest /etc/postgrest/config
|
||||
ExecReload=/bin/kill -SIGUSR1 $MAINPID
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
After that, you can enable the service at boot time and start it with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
systemctl enable postgrest
|
||||
systemctl start postgrest
|
||||
|
||||
## For reloading the service
|
||||
## systemctl restart postgrest
|
||||
|
||||
.. _file_descriptors:
|
||||
|
||||
File Descriptors
|
||||
----------------
|
||||
|
||||
File descriptors are kernel resources that are used by HTTP connections (among others). File descriptors are limited per process. The kernel default limit is 1024, which is increased in some Linux distributions.
|
||||
When under heavy traffic, PostgREST can reach this limit and start showing ``No file descriptors available`` errors. To clear these errors, you can increase the process' file descriptor limit.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[Service]
|
||||
LimitNOFILE=10000
|
||||
|
||||
Alternate URL Structure
|
||||
=======================
|
||||
|
||||
As discussed in :ref:`singular_plural`, there are no special URL forms for singular resources in PostgREST, only operators for filtering. Thus there are no URLs like :code:`/people/1`. It would be specified instead as
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /people?id=eq.1 HTTP/1.1
|
||||
Accept: application/vnd.pgrst.object+json
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/people?id=eq.1" \
|
||||
-H "Accept: application/vnd.pgrst.object+json"
|
||||
|
||||
This allows compound primary keys and makes the intent for singular response independent of a URL convention.
|
||||
|
||||
Nginx rewrite rules allow you to simulate the familiar URL convention. The following example adds a rewrite rule for all table endpoints, but you'll want to restrict it to those tables that have a numeric simple primary key named "id."
|
||||
|
||||
.. code-block:: nginx
|
||||
|
||||
# support /endpoint/:id url style
|
||||
location ~ ^/([a-z_]+)/([0-9]+) {
|
||||
|
||||
# make the response singular
|
||||
proxy_set_header Accept 'application/vnd.pgrst.object+json';
|
||||
|
||||
# assuming an upstream named "postgrest"
|
||||
proxy_pass http://postgrest/$1?id=eq.$2;
|
||||
|
||||
}
|
||||
|
||||
.. TODO
|
||||
.. Administration
|
||||
.. API Versioning
|
||||
.. HTTP Caching
|
||||
.. Upgrading
|
||||
@@ -0,0 +1,496 @@
|
||||
.. _roles:
|
||||
|
||||
Overview of Role System
|
||||
=======================
|
||||
|
||||
PostgREST is designed to keep the database at the center of API security. All authorization happens through database roles and permissions. It is PostgREST's job to **authenticate** requests -- i.e. verify that a client is who they say they are -- and then let the database **authorize** client actions.
|
||||
|
||||
Authentication Sequence
|
||||
-----------------------
|
||||
|
||||
There are three types of roles used by PostgREST, the **authenticator**, **anonymous** and **user** roles. The database administrator creates these roles and configures PostgREST to use them.
|
||||
|
||||
.. image:: _static/security-roles.png
|
||||
|
||||
The authenticator should be created :code:`NOINHERIT` and configured in the database to have very limited access. It is a chameleon whose job is to "become" other users to service authenticated HTTP requests. 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`).
|
||||
|
||||
.. image:: _static/security-anon-choice.png
|
||||
|
||||
Here are the technical details. We use `JSON Web Tokens <https://jwt.io/>`_ to authenticate API requests. 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.
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"role": "user123"
|
||||
}
|
||||
|
||||
When a request contains a valid JWT with a role claim PostgREST will switch to the database role with that name for the duration of the HTTP request.
|
||||
|
||||
.. code:: sql
|
||||
|
||||
SET LOCAL ROLE user123;
|
||||
|
||||
Note that the database administrator must allow the authenticator role to switch into this user by previously executing
|
||||
|
||||
.. code:: sql
|
||||
|
||||
GRANT user123 TO authenticator;
|
||||
|
||||
If the client included no JWT (or one without a role claim) then PostgREST switches into the anonymous role whose actual database-specific name, like that of with the authenticator role, is specified in the PostgREST server configuration file. The database administrator must set anonymous role permissions correctly to prevent anonymous users from seeing or changing things they shouldn't.
|
||||
|
||||
Users and Groups
|
||||
----------------
|
||||
|
||||
PostgreSQL manages database access permissions using the concept of roles. A role can be thought of as either a database user, or a group of database users, depending on how the role is set up.
|
||||
|
||||
Roles for Each Web User
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
PostgREST can accommodate either viewpoint. If you treat a role as a single user then the JWT-based role switching described above does most of what you need. When an authenticated user makes a request PostgREST will switch into the 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.2ndquadrant.com/en/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.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE TABLE chat (
|
||||
message_uuid UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
message_time TIMESTAMP NOT NULL DEFAULT now(),
|
||||
message_from NAME NOT NULL DEFAULT current_user,
|
||||
message_to NAME NOT NULL,
|
||||
message_subject VARCHAR(64) NOT NULL,
|
||||
message_body TEXT
|
||||
);
|
||||
|
||||
ALTER TABLE chat ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
We want to enforce a policy that ensures a user can see only those messages sent by them or intended for them. Also we want to prevent a user from forging the message_from column with another person's name.
|
||||
|
||||
PostgreSQL allows us to set this policy with row-level security:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE POLICY chat_policy ON chat
|
||||
USING ((message_to = current_user) OR (message_from = current_user))
|
||||
WITH CHECK (message_from = current_user)
|
||||
|
||||
Anyone accessing the generated API endpoint for the chat table will see exactly the rows they should, without our needing custom imperative server-side coding.
|
||||
|
||||
.. warning::
|
||||
|
||||
Roles are namespaced per-cluster rather than per-database so they may be prone to collision.
|
||||
|
||||
Web Users Sharing Role
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Alternately database roles can represent groups instead of (or in addition to) individual users. You may choose that all signed-in users for a web app share the role webuser. You can distinguish individual users by including extra claims in the JWT such as email.
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"role": "webuser",
|
||||
"email": "john@doe.com"
|
||||
}
|
||||
|
||||
SQL code can access claims through GUC variables set by PostgREST per request. For instance to get the email claim, call this function:
|
||||
|
||||
For PostgreSQL server version >= 14
|
||||
|
||||
.. code:: sql
|
||||
|
||||
current_setting('request.jwt.claims', true)::json->>'email';
|
||||
|
||||
|
||||
For PostgreSQL server version < 14
|
||||
|
||||
.. code:: sql
|
||||
|
||||
current_setting('request.jwt.claim.email', true);
|
||||
|
||||
This allows JWT generation services to include extra information and your database code to react to it. For instance the RLS example could be modified to use this current_setting rather than current_user. The second 'true' argument tells current_setting to return NULL if the setting is missing from the current configuration.
|
||||
|
||||
Hybrid User-Group Roles
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can mix the group and individual role policies. For instance we could still have a webuser role and individual users which inherit from it:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE ROLE webuser NOLOGIN;
|
||||
-- grant this role access to certain tables etc
|
||||
|
||||
CREATE ROLE user000 NOLOGIN;
|
||||
GRANT webuser TO user000;
|
||||
-- now user000 can do whatever webuser can
|
||||
|
||||
GRANT user000 TO authenticator;
|
||||
-- allow authenticator to switch into user000 role
|
||||
-- (the role itself has nologin)
|
||||
|
||||
.. _custom_validation:
|
||||
|
||||
Custom Validation
|
||||
-----------------
|
||||
|
||||
PostgREST honors the :code:`exp` claim for token expiration, rejecting expired tokens. However it does not enforce any extra constraints. An example of an extra constraint would be to immediately revoke access for a certain user. The configuration file parameter :code:`db-pre-request` specifies a stored procedure to call immediately after the authenticator switches into a new role and before the main query itself runs.
|
||||
|
||||
Here's an example. In the config file specify a stored procedure:
|
||||
|
||||
.. code:: ini
|
||||
|
||||
db-pre-request = "public.check_user"
|
||||
|
||||
In the function you can run arbitrary code to check the request and raise an exception to block it if desired.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE OR REPLACE FUNCTION check_user() RETURNS void AS $$
|
||||
BEGIN
|
||||
IF current_user = 'evil_user' THEN
|
||||
RAISE EXCEPTION 'No, you are evil'
|
||||
USING HINT = 'Stop being so evil and maybe you can log in';
|
||||
END IF;
|
||||
END
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
.. _client_auth:
|
||||
|
||||
Client Auth
|
||||
===========
|
||||
|
||||
To make an authenticated request the client must include an :code:`Authorization` HTTP header with the value :code:`Bearer <jwt>`. For instance:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /foo HTTP/1.1
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiamRvZSIsImV4cCI6MTQ3NTUxNjI1MH0.GYDZV3yM0gqvuEtJmfpplLBXSGYnke_Pvnl0tbKAjB4
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/foo" \
|
||||
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiamRvZSIsImV4cCI6MTQ3NTUxNjI1MH0.GYDZV3yM0gqvuEtJmfpplLBXSGYnke_Pvnl0tbKAjB4"
|
||||
|
||||
The ``Bearer`` header value can be used with or without capitalization(``bearer``).
|
||||
|
||||
JWT Generation
|
||||
--------------
|
||||
|
||||
You can create a valid JWT either from inside your database or via an external service. Each token is cryptographically signed with a secret key. In the case of symmetric cryptography the signer and verifier share the same secret passphrase. In asymmetric cryptography the signer uses the private key and the verifier the public key. PostgREST supports both symmetric and asymmetric cryptography.
|
||||
|
||||
JWT from SQL
|
||||
~~~~~~~~~~~~
|
||||
|
||||
You can create JWT tokens in SQL using the `pgjwt extension <https://github.com/michelp/pgjwt>`_. It's simple and requires only pgcrypto. If you're on an environment like Amazon RDS which doesn't support installing new extensions, you can still manually run the `SQL inside pgjwt <https://github.com/michelp/pgjwt/blob/master/pgjwt--0.1.1.sql>`_ (you'll need to replace ``@extschema@`` with another schema or just delete it) which creates the functions you will need.
|
||||
|
||||
Next write a stored procedure that returns the token. The one below returns a token with a hard-coded role, which expires five minutes after it was issued. Note this function has a hard-coded secret as well.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE TYPE jwt_token AS (
|
||||
token text
|
||||
);
|
||||
|
||||
CREATE FUNCTION jwt_test() RETURNS public.jwt_token AS $$
|
||||
SELECT public.sign(
|
||||
row_to_json(r), 'reallyreallyreallyreallyverysafe'
|
||||
) AS token
|
||||
FROM (
|
||||
SELECT
|
||||
'my_role'::text as role,
|
||||
extract(epoch from now())::integer + 300 AS exp
|
||||
) r;
|
||||
$$ LANGUAGE sql;
|
||||
|
||||
PostgREST exposes this function to clients via a POST request to ``/rpc/jwt_test``.
|
||||
|
||||
.. note::
|
||||
|
||||
To avoid hard-coding the secret in stored procedures, save it as a property of the database.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- run this once
|
||||
ALTER DATABASE mydb SET "app.jwt_secret" TO 'reallyreallyreallyreallyverysafe';
|
||||
|
||||
-- then all functions can refer to app.jwt_secret
|
||||
SELECT sign(
|
||||
row_to_json(r), current_setting('app.jwt_secret')
|
||||
) AS token
|
||||
FROM ...
|
||||
|
||||
JWT from Auth0
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
An external service like `Auth0 <https://auth0.com/>`_ can do the hard work transforming OAuth from Github, Twitter, Google etc into a JWT suitable for PostgREST. Auth0 can also handle email signup and password reset flows.
|
||||
|
||||
To use Auth0, create `an application <https://auth0.com/docs/get-started/applications>`_ for your app and `an API <https://auth0.com/docs/get-started/apis>`_ for your PostgREST server. Auth0 supports both HS256 and RS256 scheme for the issued tokens for APIs. For simplicity, you may first try HS256 scheme while creating your API on Auth0. Your application should use your PostgREST API's `API identifier <https://auth0.com/docs/get-started/apis/api-settings>`_ by setting it with the `audience parameter <https://auth0.com/docs/secure/tokens/access-tokens/get-access-tokens#control-access-token-audience>`_ during the authorization request. This will ensure that Auth0 will issue an access token for your PostgREST API. For PostgREST to verify the access token, you will need to set ``jwt-secret`` on PostgREST config file with your API's signing secret.
|
||||
|
||||
.. note::
|
||||
|
||||
Our code requires a database role in the JWT. To add it you need to save the database role in Auth0 `app metadata <https://auth0.com/docs/manage-users/user-accounts/metadata/manage-metadata-rules>`_. Then, you will need to write `a rule <https://auth0.com/docs/customize/rules>`_ that will extract the role from the user's app_metadata and set it as a `custom claim <https://auth0.com/docs/get-started/apis/scopes/sample-use-cases-scopes-and-claims#add-custom-claims-to-a-token>`_ in the access token. Note that, you may use Auth0's `core authorization feature <https://auth0.com/docs/manage-users/access-control/rbac>`_ for more complex use cases. Metadata solution is mentioned here for simplicity.
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
function (user, context, callback) {
|
||||
|
||||
// Follow the documentations at
|
||||
// https://postgrest.org/en/latest/configuration.html#db-role-claim-key
|
||||
// to set a custom role claim on PostgREST
|
||||
// and use it as custom claim attribute in this rule
|
||||
const myRoleClaim = 'https://myapp.com/role';
|
||||
|
||||
user.app_metadata = user.app_metadata || {};
|
||||
context.accessToken[myRoleClaim] = user.app_metadata.role;
|
||||
callback(null, user, context);
|
||||
}
|
||||
|
||||
.. _asym_keys:
|
||||
|
||||
Asymmetric Keys
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
As described in the :ref:`configuration` section, PostgREST accepts a ``jwt-secret`` config file parameter. If it is set to a simple string value like "reallyreallyreallyreallyverysafe" then PostgREST interprets it as an HMAC-SHA256 passphrase. However you can also specify a literal JSON Web Key (JWK) or set. For example, you can use an RSA-256 public key encoded as a JWK:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"alg":"RS256",
|
||||
"e":"AQAB",
|
||||
"key_ops":["verify"],
|
||||
"kty":"RSA",
|
||||
"n":"9zKNYTaYGfGm1tBMpRT6FxOYrM720GhXdettc02uyakYSEHU2IJz90G_MLlEl4-WWWYoS_QKFupw3s7aPYlaAjamG22rAnvWu-rRkP5sSSkKvud_IgKL4iE6Y2WJx2Bkl1XUFkdZ8wlEUR6O1ft3TS4uA-qKifSZ43CahzAJyUezOH9shI--tirC028lNg767ldEki3WnVr3zokSujC9YJ_9XXjw2hFBfmJUrNb0-wldvxQbFU8RPXip-GQ_JPTrCTZhrzGFeWPvhA6Rqmc3b1PhM9jY7Dur1sjYWYVyXlFNCK3c-6feo5WlRfe1aCWmwZQh6O18eTmLeT4nWYkDzQ"
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
This could also be a JSON Web Key Set (JWKS) if it was contained within an array assigned to a `keys` member, e.g. ``{ keys: [jwk1, jwk2] }``.
|
||||
|
||||
Just pass it in as a single line string, escaping the quotes:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
jwt-secret = "{ \"alg\":\"RS256\", … }"
|
||||
|
||||
To generate such a public/private key pair use a utility like `latchset/jose <https://github.com/latchset/jose>`_.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
jose jwk gen -i '{"alg": "RS256"}' -o rsa.jwk
|
||||
jose jwk pub -i rsa.jwk -o rsa.jwk.pub
|
||||
|
||||
# now rsa.jwk.pub contains the desired JSON object
|
||||
|
||||
You can specify the literal value as we saw earlier, or reference a filename to load the JWK from a file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
jwt-secret = "@rsa.jwk.pub"
|
||||
|
||||
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.
|
||||
|
||||
The critique against the `JWT standard <https://datatracker.ietf.org/doc/html/rfc7519>`_ is voiced in detail `elsewhere on the web <https://web.archive.org/web/20230123041631/https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid>`_. The most relevant part for PostgREST is the so-called :code:`alg=none` issue. Some servers implementing JWT allow clients to choose the algorithm used to sign the JWT. In this case, an attacker could set the algorithm to :code:`none`, remove the need for any signature at all and gain unauthorized access. The current implementation of PostgREST, however, does not allow clients to set the signature algorithm in the HTTP request, making this attack irrelevant. The critique against the standard is that it requires the implementation of the :code:`alg=none` at all.
|
||||
|
||||
Critiques against JWT libraries are only relevant to PostgREST via the library it uses. As mentioned above, not allowing clients to choose the signature algorithm in HTTP requests removes the greatest risk. Another more subtle attack is possible where servers use asymmetric algorithms like RSA for signatures. Once again this is not relevant to PostgREST since it is not supported. Curious readers can find more information in `this article <https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/>`_. Recommendations about high quality libraries for usage in API clients can be found on `jwt.io <https://jwt.io/>`_.
|
||||
|
||||
The last type of critique focuses on the misuse of JWT for maintaining web sessions. The basic recommendation is to `stop using JWT for sessions <http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/>`_ because most, if not all, solutions to the problems that arise when you do, `do not work <http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/>`_. The linked articles discuss the problems in depth but the essence of the problem is that JWT is not designed to be secure and stateful units for client-side storage and therefore not suited to session management.
|
||||
|
||||
PostgREST uses JWT mainly for authentication and authorization purposes and encourages users to do the same. For web sessions, using cookies over HTTPS is good enough and well catered for by standard web frameworks.
|
||||
|
||||
Schema Isolation
|
||||
================
|
||||
|
||||
You can isolate your api schema from internal implementation details, as explained in :ref:`schema_isolation`. For an example of wrapping a private table with a public view see the :ref:`public_ui` section below.
|
||||
|
||||
.. _sql_user_management:
|
||||
|
||||
SQL User Management
|
||||
===================
|
||||
|
||||
Storing Users and Passwords
|
||||
---------------------------
|
||||
|
||||
As mentioned, 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.
|
||||
|
||||
The following table, functions, and triggers will live in a :code:`basic_auth` schema that you shouldn't expose publicly in the API. The public views and functions will live in a different schema which internally references this internal information.
|
||||
|
||||
First we'll need a table to keep track of our users:
|
||||
|
||||
.. code:: sql
|
||||
|
||||
-- We put things inside the basic_auth schema to hide
|
||||
-- them from public view. Certain public procs/views will
|
||||
-- refer to helpers and tables inside.
|
||||
create schema if not exists basic_auth;
|
||||
|
||||
create table if not exists
|
||||
basic_auth.users (
|
||||
email text primary key check ( email ~* '^.+@.+\..+$' ),
|
||||
pass text not null check (length(pass) < 512),
|
||||
role name not null check (length(role) < 512)
|
||||
);
|
||||
|
||||
We would like the role to be a foreign key to actual database roles, however PostgreSQL does not support these constraints against the :code:`pg_roles` table. We'll use a trigger to manually enforce it.
|
||||
|
||||
.. code-block:: plpgsql
|
||||
|
||||
create or replace function
|
||||
basic_auth.check_role_exists() returns trigger as $$
|
||||
begin
|
||||
if not exists (select 1 from pg_roles as r where r.rolname = new.role) then
|
||||
raise foreign_key_violation using message =
|
||||
'unknown database role: ' || new.role;
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end
|
||||
$$ language plpgsql;
|
||||
|
||||
drop trigger if exists ensure_user_role_exists on basic_auth.users;
|
||||
create constraint trigger ensure_user_role_exists
|
||||
after insert or update on basic_auth.users
|
||||
for each row
|
||||
execute procedure basic_auth.check_role_exists();
|
||||
|
||||
Next we'll use the pgcrypto extension and a trigger to keep passwords safe in the :code:`users` table.
|
||||
|
||||
.. code-block:: plpgsql
|
||||
|
||||
create extension if not exists pgcrypto;
|
||||
|
||||
create or replace function
|
||||
basic_auth.encrypt_pass() returns trigger as $$
|
||||
begin
|
||||
if tg_op = 'INSERT' or new.pass <> old.pass then
|
||||
new.pass = crypt(new.pass, gen_salt('bf'));
|
||||
end if;
|
||||
return new;
|
||||
end
|
||||
$$ language plpgsql;
|
||||
|
||||
drop trigger if exists encrypt_pass on basic_auth.users;
|
||||
create trigger encrypt_pass
|
||||
before insert or update on basic_auth.users
|
||||
for each row
|
||||
execute procedure basic_auth.encrypt_pass();
|
||||
|
||||
With the table in place we can make a helper to check a password against the encrypted column. It returns the database role for a user if the email and password are correct.
|
||||
|
||||
.. code-block:: plpgsql
|
||||
|
||||
create or replace function
|
||||
basic_auth.user_role(email text, pass text) returns name
|
||||
language plpgsql
|
||||
as $$
|
||||
begin
|
||||
return (
|
||||
select role from basic_auth.users
|
||||
where users.email = user_role.email
|
||||
and users.pass = crypt(user_role.pass, users.pass)
|
||||
);
|
||||
end;
|
||||
$$;
|
||||
|
||||
.. _public_ui:
|
||||
|
||||
Public User Interface
|
||||
---------------------
|
||||
|
||||
In the previous section we created an internal table to store user information. Here we create a login function which takes an email address and password and returns JWT if the credentials match a user in the internal table.
|
||||
|
||||
Permissions
|
||||
~~~~~~~~~~~
|
||||
|
||||
Your database roles need access to the schema, tables, views and functions in order to service HTTP requests.
|
||||
Recall from the `Overview of Role System`_ that PostgREST uses special roles to process requests, namely the authenticator and
|
||||
anonymous roles. Below is an example of permissions that allow anonymous users to create accounts and attempt to log in.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- the names "anon" and "authenticator" are configurable and not
|
||||
-- sacred, we simply choose them for clarity
|
||||
create role anon noinherit;
|
||||
create role authenticator noinherit;
|
||||
grant anon to authenticator;
|
||||
|
||||
Then, add ``db-anon-role`` to the configuration file to allow anonymous requests.
|
||||
|
||||
.. code:: ini
|
||||
|
||||
db-anon-role = "anon"
|
||||
|
||||
Logins
|
||||
~~~~~~
|
||||
|
||||
As described in `JWT from SQL`_, we'll create a JWT 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
|
||||
|
||||
-- add type
|
||||
CREATE TYPE basic_auth.jwt_token AS (
|
||||
token text
|
||||
);
|
||||
|
||||
-- login should be on your exposed schema
|
||||
create or replace function
|
||||
login(email text, pass text) returns basic_auth.jwt_token as $$
|
||||
declare
|
||||
_role name;
|
||||
result basic_auth.jwt_token;
|
||||
begin
|
||||
-- check email and password
|
||||
select basic_auth.user_role(email, pass) into _role;
|
||||
if _role is null then
|
||||
raise invalid_password using message = 'invalid user or password';
|
||||
end if;
|
||||
|
||||
select sign(
|
||||
row_to_json(r), 'reallyreallyreallyreallyverysafe'
|
||||
) as token
|
||||
from (
|
||||
select _role as role, login.email as email,
|
||||
extract(epoch from now())::integer + 60*60 as exp
|
||||
) r
|
||||
into result;
|
||||
return result;
|
||||
end;
|
||||
$$ language plpgsql security definer;
|
||||
|
||||
grant execute on function login(text,text) to anon;
|
||||
|
||||
Since the above :code:`login` function is defined as `security definer <https://www.postgresql.org/docs/current/sql-createfunction.html#id-1.9.3.67.10.2>`_,
|
||||
the anonymous user :code:`anon` doesn't need permission to read the :code:`basic_auth.users` table. It doesn't even need permission to access the :code:`basic_auth` schema.
|
||||
:code:`grant execute on function` is included for clarity but it might not be needed, see :ref:`func_privs` for more details.
|
||||
|
||||
An API request to call this function would look like:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /rpc/login HTTP/1.1
|
||||
|
||||
{ "email": "foo@bar.com", "pass": "foobar" }
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/rpc/login" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d '{ "email": "foo@bar.com", "pass": "foobar" }'
|
||||
|
||||
The response would look like the snippet below. Try decoding the token at `jwt.io <https://jwt.io/>`_. (It was encoded with a secret of :code:`reallyreallyreallyreallyverysafe` as specified in the SQL code above. You'll want to change this secret in your app!)
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImZvb0BiYXIuY29tIiwicGFzcyI6ImZvb2JhciJ9.37066TTRlh-1hXhnA9oO9Pj6lgL6zFuJU0iCHhuCFno"
|
||||
}
|
||||
|
||||
|
||||
Alternatives
|
||||
~~~~~~~~~~~~
|
||||
|
||||
See the how-to :ref:`sql-user-management-using-postgres-users-and-passwords` for a similar way that completely avoids the table :code:`basic_auth.users`.
|
||||
@@ -0,0 +1,293 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# PostgREST documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sun Oct 9 16:53:00 2016.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# 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,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ["sphinx_tabs.tabs", "sphinx_copybutton"]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = ".rst"
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = "index"
|
||||
|
||||
# This is overriden by readthedocs with the version tag anyway
|
||||
version = "10.2"
|
||||
# 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"
|
||||
copyright = "2017, " + author
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
# today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
# default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
# add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
# add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = "sphinx"
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
# keep_warnings = False
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {"display_version": False}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents.
|
||||
# "<project> v<release> documentation" by default.
|
||||
# html_title = u'PostgREST v0.4.0.0'
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
# html_logo = None
|
||||
|
||||
# The name of an image file (relative to this directory) to use as a favicon of
|
||||
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
html_favicon = "_static/favicon.ico"
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ["_static"]
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
# html_extra_path = []
|
||||
|
||||
# If not None, a 'Last updated on:' timestamp is inserted at every page
|
||||
# bottom, using the given strftime format.
|
||||
# The empty string is equivalent to '%b %d, %Y'.
|
||||
# html_last_updated_fmt = None
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
# html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
# html_file_suffix = None
|
||||
|
||||
# Language to be used for generating the HTML full-text search index.
|
||||
# Sphinx supports the following languages:
|
||||
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
|
||||
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'
|
||||
# html_search_language = 'en'
|
||||
|
||||
# A dictionary with options for the search language support, empty by default.
|
||||
# 'ja' uses this config value.
|
||||
# 'zh' user can custom change `jieba` dictionary path.
|
||||
# html_search_options = {'type': 'default'}
|
||||
|
||||
# The name of a javascript file (relative to the configuration directory) that
|
||||
# implements a search results scorer. If empty, the default will be used.
|
||||
# html_search_scorer = 'scorer.js'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = "PostgRESTdoc"
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
# Latex figure (float) alignment
|
||||
#'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, "PostgREST.tex", "PostgREST Documentation", author, "manual"),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [(master_doc, "postgrest", "PostgREST Documentation", [author], 1)]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(
|
||||
master_doc,
|
||||
"PostgREST",
|
||||
"PostgREST Documentation",
|
||||
author,
|
||||
"PostgREST",
|
||||
"REST API for any PostgreSQL database",
|
||||
"Web",
|
||||
),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
# texinfo_no_detailmenu = False
|
||||
|
||||
# -- Custom setup ---------------------------------------------------------
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_css_file("css/custom.css")
|
||||
|
||||
|
||||
user_agent = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:135.0) Gecko/20100101 Firefox/135.0"
|
||||
|
||||
# sphinx-tabs configuration
|
||||
sphinx_tabs_disable_tab_closing = True
|
||||
@@ -0,0 +1,728 @@
|
||||
.. _configuration:
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Without configuration, PostgREST won't be able to serve requests. At the minimum it needs either :ref:`a role to serve anonymous requests with <db-anon-role>` - or :ref:`a secret to use for JWT authentication <jwt-secret>`. Config parameters can be provided via :ref:`file_config`, via :ref:`env_variables_config` or through :ref:`in_db_config`.
|
||||
|
||||
To connect to a database it uses a `libpq connection string <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>`_. The connection string can be set in the configuration file or via environment variable or can be read from an external file. See :ref:`db-uri` for details. Any parameter that is not set in the connection string is read from `libpq environment variables <https://www.postgresql.org/docs/current/libpq-envars.html>`_. The default connection string is ``postgresql://``, which reads **all** parameters from the environment.
|
||||
|
||||
The user with whom PostgREST connects to the database is also known as the authenticator role. For more information about the anonymous vs authenticator roles see :ref:`roles`.
|
||||
|
||||
Config parameters are read in the following order:
|
||||
|
||||
1. From the config file.
|
||||
2. From environment variables, overriding values from the config file.
|
||||
3. From the database, overriding values from both the config file and environment variables.
|
||||
|
||||
.. _file_config:
|
||||
|
||||
Config File
|
||||
-----------
|
||||
|
||||
PostgREST can read a config file. There is no predefined location for this file, you must specify the file path as the one and only argument to the server:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
./postgrest /path/to/postgrest.conf
|
||||
|
||||
.. note::
|
||||
|
||||
Configuration can be reloaded without restarting the server. See :ref:`config_reloading`.
|
||||
|
||||
The configuration file must contain a set of key value pairs:
|
||||
|
||||
.. code::
|
||||
|
||||
# postgrest.conf
|
||||
|
||||
# The standard connection URI format, documented at
|
||||
# https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
|
||||
db-uri = "postgres://user:pass@host:5432/dbname"
|
||||
|
||||
# The database role to use when no client authentication is provided.
|
||||
# Should differ from authenticator
|
||||
db-anon-role = "anon"
|
||||
|
||||
# The secret to verify the JWT for authenticated requests with.
|
||||
# Needs to be 32 characters minimum.
|
||||
jwt-secret = "reallyreallyreallyreallyverysafe"
|
||||
jwt-secret-is-base64 = False
|
||||
|
||||
# Port the postgrest process is listening on for http requests
|
||||
server-port = 80
|
||||
|
||||
You can run ``postgrest --example`` to display all possible configuration parameters and how to use them in a configuration file.
|
||||
|
||||
.. _env_variables_config:
|
||||
|
||||
Environment Variables
|
||||
---------------------
|
||||
|
||||
You can also set these :ref:`configuration parameters <config_full_list>` using environment variables. They are capitalized, have a ``PGRST_`` prefix, and use underscores. For example: ``PGRST_DB_URI`` corresponds to ``db-uri`` and ``PGRST_APP_SETTINGS_*`` to ``app.settings.*``.
|
||||
|
||||
.. _in_db_config:
|
||||
|
||||
In-Database Configuration
|
||||
-------------------------
|
||||
|
||||
By adding settings to the **authenticator** role (see :ref:`roles`), you can make the database the single source of truth for PostgREST's configuration.
|
||||
This is enabled by :ref:`db-config`.
|
||||
|
||||
For example, you can configure :ref:`db-schemas` and :ref:`jwt-secret` like this:
|
||||
|
||||
.. code:: postgresql
|
||||
|
||||
ALTER ROLE authenticator SET pgrst.db_schemas = "tenant1, tenant2, tenant3"
|
||||
ALTER ROLE authenticator IN DATABASE <your_database_name> SET pgrst.jwt_secret = "REALLYREALLYREALLYREALLYVERYSAFE"
|
||||
|
||||
You can use both database-specific settings with `IN DATABASE` and cluster-wide settings without it. Database-specific settings will override cluster-wide settings if both are used for the same parameter.
|
||||
|
||||
Note that underscores(``_``) need to be used instead of dashes(``-``) for the in-database config parameters.
|
||||
|
||||
.. important::
|
||||
|
||||
For altering a role in this way, you need a SUPERUSER. You might not be able to use this configuration mode on cloud-hosted databases.
|
||||
|
||||
When using both the configuration file and the in-database configuration, the latter takes precedence.
|
||||
|
||||
.. danger::
|
||||
|
||||
If direct connections to the database are allowed, then it's not safe to use the in-db configuration for storing the :ref:`jwt-secret`.
|
||||
The settings of every role are PUBLIC - they can be viewed by any user that queries the ``pg_catalog.pg_db_role_setting`` table.
|
||||
In this case you should keep the :ref:`jwt-secret` in the configuration file or as environment variables.
|
||||
|
||||
.. _config_reloading:
|
||||
|
||||
Configuration Reloading
|
||||
=======================
|
||||
|
||||
It's possible to reload PostgREST's configuration without restarting the server. You can do this :ref:`via signal <config_reloading_signal>` or :ref:`via notification <config_reloading_notify>`.
|
||||
|
||||
It's not possible to change :ref:`env_variables_config` for a running process and reloading a Docker container configuration will not work. In these cases, you need to restart the PostgREST server or use :ref:`in_db_config` as an alternative.
|
||||
|
||||
.. important::
|
||||
|
||||
The following settings will not be reloaded. You will need to restart PostgREST to change those.
|
||||
|
||||
* :ref:`admin-server-port`
|
||||
* :ref:`db-uri`
|
||||
* :ref:`db-pool`
|
||||
* :ref:`db-pool-acquisition-timeout`
|
||||
* :ref:`db-pool-max-lifetime`
|
||||
* :ref:`server-host`
|
||||
* :ref:`server-port`
|
||||
* :ref:`server-unix-socket`
|
||||
* :ref:`server-unix-socket-mode`
|
||||
|
||||
.. _config_reloading_signal:
|
||||
|
||||
Reload with signal
|
||||
------------------
|
||||
|
||||
To reload the configuration via signal, send a SIGUSR2 signal to the server process.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
killall -SIGUSR2 postgrest
|
||||
|
||||
.. _config_reloading_notify:
|
||||
|
||||
Reload with NOTIFY
|
||||
------------------
|
||||
|
||||
To reload the configuration from within the database, you can use a NOTIFY command.
|
||||
|
||||
.. code:: postgresql
|
||||
|
||||
NOTIFY pgrst, 'reload config'
|
||||
|
||||
The ``"pgrst"`` notification channel is enabled by default. For configuring the channel, see :ref:`db-channel` and :ref:`db-channel-enabled`.
|
||||
|
||||
.. _config_full_list:
|
||||
|
||||
List of parameters
|
||||
==================
|
||||
|
||||
=========================== ======= ================= ==========
|
||||
Name Type Default Reloadable
|
||||
=========================== ======= ================= ==========
|
||||
admin-server-port Int
|
||||
app.settings.* String Y
|
||||
db-anon-role String Y
|
||||
db-channel String pgrst Y
|
||||
db-channel-enabled Boolean True Y
|
||||
db-config Boolean True Y
|
||||
db-extra-search-path String public Y
|
||||
db-max-rows Int ∞ Y
|
||||
db-plan-enabled Boolean False Y
|
||||
db-pool Int 10
|
||||
db-pool-acquisition-timeout Int 10
|
||||
db-pool-max-lifetime Int 1800
|
||||
db-pre-request String Y
|
||||
db-prepared-statements Boolean True Y
|
||||
db-schemas String public Y
|
||||
db-tx-end String commit
|
||||
db-uri String postgresql://
|
||||
db-use-legacy-gucs Boolean True Y
|
||||
jwt-aud String Y
|
||||
jwt-role-claim-key String .role Y
|
||||
jwt-secret String Y
|
||||
jwt-secret-is-base64 Boolean False Y
|
||||
log-level String error Y
|
||||
openapi-mode String follow-privileges Y
|
||||
openapi-security-active Boolean False Y
|
||||
openapi-server-proxy-uri String Y
|
||||
raw-media-types String Y
|
||||
server-host String !4
|
||||
server-port Int 3000
|
||||
server-unix-socket String
|
||||
server-unix-socket-mode String 660
|
||||
=========================== ======= ================= ==========
|
||||
|
||||
.. _admin-server-port:
|
||||
|
||||
admin-server-port
|
||||
-----------------
|
||||
|
||||
=============== =======================
|
||||
**Environment** PGRST_ADMIN_SERVER_PORT
|
||||
**In-Database** `n/a`
|
||||
=============== =======================
|
||||
|
||||
Specifies the port for the :ref:`health_check` endpoints.
|
||||
|
||||
.. _app.settings.*:
|
||||
|
||||
app.settings.*
|
||||
--------------
|
||||
|
||||
=============== ====================
|
||||
**Environment** PGRST_APP_SETTINGS_*
|
||||
**In-Database** pgrst.app_settings_*
|
||||
=============== ====================
|
||||
|
||||
Arbitrary settings that can be used to pass in secret keys directly as strings, or via OS environment variables. For instance: :code:`app.settings.jwt_secret = "$(MYAPP_JWT_SECRET)"` will take :code:`MYAPP_JWT_SECRET` from the environment and make it available to postgresql functions as :code:`current_setting('app.settings.jwt_secret')`.
|
||||
|
||||
.. _db-anon-role:
|
||||
|
||||
db-anon-role
|
||||
------------
|
||||
|
||||
=============== ==================
|
||||
**Environment** PGRST_DB_ANON_ROLE
|
||||
**In-Database** `n/a`
|
||||
=============== ==================
|
||||
|
||||
The database role to use when executing commands on behalf of unauthenticated clients. For more information, see :ref:`roles`.
|
||||
|
||||
When unset anonymous access will be blocked.
|
||||
|
||||
.. _db-channel:
|
||||
|
||||
db-channel
|
||||
----------
|
||||
|
||||
=============== ================
|
||||
**Environment** PGRST_DB_CHANNEL
|
||||
**In-Database** `n/a`
|
||||
=============== ================
|
||||
|
||||
The name of the notification channel that PostgREST uses for :ref:`schema_reloading` and configuration reloading.
|
||||
|
||||
.. _db-channel-enabled:
|
||||
|
||||
db-channel-enabled
|
||||
------------------
|
||||
|
||||
=============== ========================
|
||||
**Environment** PGRST_DB_CHANNEL_ENABLED
|
||||
**In-Database** `n/a`
|
||||
=============== ========================
|
||||
|
||||
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.
|
||||
|
||||
.. _db-config:
|
||||
|
||||
db-config
|
||||
---------
|
||||
|
||||
=============== ===============
|
||||
**Environment** PGRST_DB_CONFIG
|
||||
**In-Database** `n/a`
|
||||
=============== ===============
|
||||
|
||||
Enables the in-database configuration.
|
||||
|
||||
.. _db-extra-search-path:
|
||||
|
||||
db-extra-search-path
|
||||
--------------------
|
||||
|
||||
=============== ==========================
|
||||
**Environment** PGRST_DB_EXTRA_SEARCH_PATH
|
||||
**In-Database** pgrst.db_extra_search_path
|
||||
=============== ==========================
|
||||
|
||||
Extra schemas to add to the `search_path <https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH>`_ of every request. These schemas tables, views and stored procedures **don't get API endpoints**, they can only be referred from the database objects inside your :ref:`db-schemas`.
|
||||
|
||||
This parameter was meant to make it easier to use **PostgreSQL extensions** (like PostGIS) that are outside of the :ref:`db-schemas`.
|
||||
|
||||
Multiple schemas can be added in a comma-separated string, e.g. ``public, extensions``.
|
||||
|
||||
.. _db-max-rows:
|
||||
|
||||
db-max-rows
|
||||
-----------
|
||||
|
||||
*For backwards compatibility, this config parameter is also available without prefix as "max-rows".*
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_DB_MAX_ROWS
|
||||
**In-Database** pgrst.db_max_rows
|
||||
=============== =================
|
||||
|
||||
A hard limit to the number of rows PostgREST will fetch from a view, table, or stored procedure. Limits payload size for accidental or malicious requests.
|
||||
|
||||
.. _db-plan-enabled:
|
||||
|
||||
db-plan-enabled
|
||||
---------------
|
||||
|
||||
=============== =====================
|
||||
**Environment** PGRST_DB_PLAN_ENABLED
|
||||
**In-Database** pgrst.db_plan_enabled
|
||||
=============== =====================
|
||||
|
||||
When this is set to :code:`true`, the execution plan of a request can be retrieved by using the :code:`Accept: application/vnd.pgrst.plan` header. See :ref:`explain_plan`.
|
||||
|
||||
It's recommended to use this in testing environments only since it reveals internal database details.
|
||||
However, if you choose to use it in production you can add a :ref:`db-pre-request` to filter the requests that can use this feature.
|
||||
|
||||
For example, to only allow requests from an IP address to get the execution plans:
|
||||
|
||||
.. code-block:: postgresql
|
||||
|
||||
-- Assuming a proxy(Nginx, Cloudflare, etc) passes an "X-Forwarded-For" header(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)
|
||||
create or replace function filter_plan_requests()
|
||||
returns void as $$
|
||||
declare
|
||||
headers json := current_setting('request.headers', true)::json;
|
||||
client_ip text := coalesce(headers->>'x-forwarded-for', '');
|
||||
accept text := coalesce(headers->>'accept', '');
|
||||
begin
|
||||
if accept like 'application/vnd.pgrst.plan%' and client_ip != '144.96.121.73' then
|
||||
raise insufficient_privilege using
|
||||
message = 'Not allowed to use application/vnd.pgrst.plan';
|
||||
end if;
|
||||
end; $$ language plpgsql;
|
||||
|
||||
-- set this function on your postgrest.conf
|
||||
-- db-pre-request = filter_plan_requests
|
||||
|
||||
.. _db-pool:
|
||||
|
||||
db-pool
|
||||
-------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_DB_POOL
|
||||
**In-Database** `n/a`
|
||||
=============== =================
|
||||
|
||||
Number of connections to keep open in PostgREST's database pool. Having enough here for the maximum expected simultaneous client connections can improve performance. Note it's pointless to set this higher than the :code:`max_connections` GUC in your database.
|
||||
|
||||
.. _db-pool-acquisition-timeout:
|
||||
|
||||
db-pool-acquisition-timeout
|
||||
---------------------------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_DB_POOL_ACQUISITION_TIMEOUT
|
||||
**In-Database** `n/a`
|
||||
=============== =================
|
||||
|
||||
Specifies the maximum time in seconds that the request will wait for the pool to free up a connection slot to the database. If it times out without acquiring a connection, then the request is aborted and a ``504`` error is returned.
|
||||
|
||||
.. _db-pool-max-lifetime:
|
||||
|
||||
db-pool-max-lifetime
|
||||
--------------------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_DB_POOL_MAX_LIFETIME
|
||||
**In-Database** `n/a`
|
||||
=============== =================
|
||||
|
||||
Specifies the maximum time in seconds of an existing connection in the pool. When this lifetime is reached, then the connection will be closed and returned to the pool.
|
||||
|
||||
.. _db-pre-request:
|
||||
|
||||
db-pre-request
|
||||
--------------
|
||||
|
||||
*For backwards compatibility, this config parameter is also available without prefix as "pre-request".*
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_DB_PRE_REQUEST
|
||||
**In-Database** pgrst.db_pre_request
|
||||
=============== =================
|
||||
|
||||
A schema-qualified stored procedure name to call right after switching roles for a client request. This provides an opportunity to modify SQL variables or raise an exception to prevent the request from completing.
|
||||
|
||||
.. _db-prepared-statements:
|
||||
|
||||
db-prepared-statements
|
||||
----------------------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_DB_PREPARED_STATEMENTS
|
||||
**In-Database** pgrst.db_prepared_statements
|
||||
=============== =================
|
||||
|
||||
Enables or disables 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.
|
||||
|
||||
.. _db-schemas:
|
||||
|
||||
db-schemas
|
||||
----------
|
||||
|
||||
*For backwards compatibility, this config parameter is also available in singular as "db-schema".*
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_DB_SCHEMAS
|
||||
**In-Database** pgrst.db_schemas
|
||||
=============== =================
|
||||
|
||||
The database schema to expose to REST clients. Tables, views and stored procedures in this schema will get API endpoints.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
db-schemas = "api"
|
||||
|
||||
This schema gets added to the `search_path <https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH>`_ of every request.
|
||||
|
||||
List of schemas
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
You can also specify a list of schemas that can be used for **schema-based multitenancy** and **api versioning** by :ref:`multiple-schemas`. Example:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
db-schemas = "tenant1, tenant2"
|
||||
|
||||
If you don't :ref:`Switch Schemas <multiple-schemas>`, the first schema in the list(``tenant1`` in this case) is chosen as the default schema.
|
||||
|
||||
*Only the chosen schema* gets added to the `search_path <https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH>`_ of every request.
|
||||
|
||||
.. warning::
|
||||
|
||||
Never expose private schemas in this way. See :ref:`schema_isolation`.
|
||||
|
||||
.. _db-tx-end:
|
||||
|
||||
db-tx-end
|
||||
---------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_DB_TX_END
|
||||
**In-Database** pgrst.db_tx_end
|
||||
=============== =================
|
||||
|
||||
Specifies how to terminate the database transactions.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# The transaction is always committed
|
||||
db-tx-end = "commit"
|
||||
|
||||
# The transaction is committed unless a "Prefer: tx=rollback" header is sent
|
||||
db-tx-end = "commit-allow-override"
|
||||
|
||||
# The transaction is always rolled back
|
||||
db-tx-end = "rollback"
|
||||
|
||||
# The transaction is rolled back unless a "Prefer: tx=commit" header is sent
|
||||
db-tx-end = "rollback-allow-override"
|
||||
|
||||
.. _db-uri:
|
||||
|
||||
db-uri
|
||||
------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_DB_URI
|
||||
**In-Database** `n/a`
|
||||
=============== =================
|
||||
|
||||
The standard connection PostgreSQL `URI format <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>`_. Symbols and unusual characters in the password or other fields should be percent encoded to avoid a parse error. If enforcing an SSL connection to the database is required you can use `sslmode <https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS>`_ in the URI, for example ``postgres://user:pass@host:5432/dbname?sslmode=require``.
|
||||
|
||||
When running PostgREST on the same machine as PostgreSQL, it is also possible to connect to the database using a `Unix socket <https://en.wikipedia.org/wiki/Unix_domain_socket>`_ and the `Peer Authentication method <https://www.postgresql.org/docs/current/auth-peer.html>`_ as an alternative to TCP/IP communication and authentication with a password, this also grants higher performance. To do this you can omit the host and the password, e.g. ``postgres://user@/dbname``, see the `libpq connection string <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>`_ documentation for more details.
|
||||
|
||||
Choosing a value for this parameter beginning with the at sign such as ``@filename`` (e.g. ``@./configs/my-config``) loads the connection string out of an external file.
|
||||
|
||||
.. _db-use-legacy-gucs:
|
||||
|
||||
db-use-legacy-gucs
|
||||
------------------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_DB_USE_LEGACY_GUCS
|
||||
**In-Database** pgrst.db_use_legacy_gucs
|
||||
=============== =================
|
||||
|
||||
Determine if GUC request settings for headers, cookies and jwt claims use the :ref:`legacy names <guc_legacy_names>` (string with dashes, invalid starting from PostgreSQL v14) with text values instead of the :ref:`new names <guc_req_headers_cookies_claims>` (string without dashes, valid on all PostgreSQL versions) with json values.
|
||||
|
||||
On PostgreSQL versions 14 and above, this parameter is ignored.
|
||||
|
||||
.. _jwt-aud:
|
||||
|
||||
jwt-aud
|
||||
-------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_JWT_AUD
|
||||
**In-Database** pgrst.jwt_aud
|
||||
=============== =================
|
||||
|
||||
Specifies the `JWT audience claim <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3>`_. If this claim is present in the client provided JWT then you must set this to the same value as in the JWT, otherwise verifying the JWT will fail.
|
||||
|
||||
.. _jwt-role-claim-key:
|
||||
|
||||
jwt-role-claim-key
|
||||
------------------
|
||||
|
||||
*For backwards compatibility, this config parameter is also available without prefix as "role-claim-key".*
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_JWT_ROLE_CLAIM_KEY
|
||||
**In-Database** pgrst.jwt_role_claim_key
|
||||
=============== =================
|
||||
|
||||
A JSPath DSL that specifies the location of the :code:`role` key in the JWT claims. This can be used to consume a JWT provided by a third party service like Auth0, Okta or Keycloak. 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]"
|
||||
|
||||
# {"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"
|
||||
|
||||
.. _jwt-secret:
|
||||
|
||||
jwt-secret
|
||||
----------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_JWT_SECRET
|
||||
**In-Database** pgrst.jwt_secret
|
||||
=============== =================
|
||||
|
||||
The secret or `JSON Web Key (JWK) (or set) <https://datatracker.ietf.org/doc/html/rfc7517>`_ used to decode JWT tokens clients provide for authentication. For security the key must be **at least 32 characters long**. If this parameter is not specified then PostgREST refuses authentication requests. Choosing a value for this parameter beginning with the at sign such as :code:`@filename` loads the secret out of an external file. This is useful for automating deployments. Note that any binary secrets must be base64 encoded. Both symmetric and asymmetric cryptography are supported. For more info see :ref:`asym_keys`.
|
||||
|
||||
Choosing a value for this parameter beginning with the at sign such as ``@filename`` (e.g. ``@./configs/my-config``) loads the secret out of an external file.
|
||||
|
||||
.. warning::
|
||||
|
||||
Only when using the :ref:`file_config`, if the ``jwt-secret`` contains a ``$`` character by itself it will give errors. In this case, use ``$$`` and PostgREST will interpret it as a single ``$`` character.
|
||||
|
||||
.. _jwt-secret-is-base64:
|
||||
|
||||
jwt-secret-is-base64
|
||||
--------------------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_JWT_SECRET_IS_BASE64
|
||||
**In-Database** pgrst.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.
|
||||
|
||||
.. _log-level:
|
||||
|
||||
log-level
|
||||
---------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_LOG_LEVEL
|
||||
**In-Database** `n/a`
|
||||
=============== =================
|
||||
|
||||
Specifies the level of information to be logged while running PostgREST.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# Only startup and db connection recovery messages are logged
|
||||
log-level = "crit"
|
||||
|
||||
# All the "crit" level events plus server errors (status 5xx) are logged
|
||||
log-level = "error"
|
||||
|
||||
# All the "error" level events plus request errors (status 4xx) are logged
|
||||
log-level = "warn"
|
||||
|
||||
# All the "warn" level events plus all requests (every status code) are logged
|
||||
log-level = "info"
|
||||
|
||||
|
||||
Because currently there's no buffering for logging, the levels with minimal logging(``crit/error``) will increase throughput.
|
||||
|
||||
.. _openapi-mode:
|
||||
|
||||
openapi-mode
|
||||
------------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_OPENAPI_MODE
|
||||
**In-Database** pgrst.openapi_mode
|
||||
=============== =================
|
||||
|
||||
Specifies how the OpenAPI output should be displayed.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# Follows the privileges of the JWT role claim (or from db-anon-role if the JWT is not sent)
|
||||
# Shows information depending on the permissions that the role making the request has
|
||||
openapi-mode = "follow-privileges"
|
||||
|
||||
# Ignores the privileges of the JWT role claim (or from db-anon-role if the JWT is not sent)
|
||||
# Shows all the exposed information, regardless of the permissions that the role making the request has
|
||||
openapi-mode = "ignore-privileges"
|
||||
|
||||
# Disables the OpenApi output altogether.
|
||||
# Throws a `404 Not Found` error when accessing the API root path
|
||||
openapi-mode = "disabled"
|
||||
|
||||
.. _openapi-security-active:
|
||||
|
||||
openapi-security-active
|
||||
-----------------------
|
||||
|
||||
=============== =============================
|
||||
**Environment** PGRST_OPENAPI_SECURITY_ACTIVE
|
||||
**In-Database** pgrst.openapi_security_active
|
||||
=============== =============================
|
||||
|
||||
When this is set to :code:`true`, security options are included in the :ref:`OpenAPI output <open-api>`.
|
||||
|
||||
.. _openapi-server-proxy-uri:
|
||||
|
||||
openapi-server-proxy-uri
|
||||
------------------------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_OPENAPI_SERVER_PROXY_URI
|
||||
**In-Database** pgrst.openapi_server_proxy_uri
|
||||
=============== =================
|
||||
|
||||
Overrides the base URL used within the OpenAPI self-documentation hosted at the API root path. Use a complete URI syntax :code:`scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]`. Ex. :code:`https://postgrest.com`
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"version": "0.4.3.0",
|
||||
"title": "PostgREST API",
|
||||
"description": "This is a dynamic API generated by PostgREST"
|
||||
},
|
||||
"host": "postgrest.com:443",
|
||||
"basePath": "/",
|
||||
"schemes": [
|
||||
"https"
|
||||
]
|
||||
}
|
||||
|
||||
.. _raw-media-types:
|
||||
|
||||
raw-media-types
|
||||
---------------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_RAW_MEDIA_TYPES
|
||||
**In-Database** pgrst.raw_media_types
|
||||
=============== =================
|
||||
|
||||
This serves to extend the `Media Types <https://en.wikipedia.org/wiki/Media_type>`_ that PostgREST currently accepts through an ``Accept`` header.
|
||||
|
||||
These media types can be requested by following the same rules as the ones defined in :ref:`scalar_return_formats`.
|
||||
|
||||
As an example, the below config would allow you to request an **image** and a **XML** file by doing a request with ``Accept: image/png``
|
||||
or ``Accept: font/woff2``, respectively.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
raw-media-types="image/png, font/woff2"
|
||||
|
||||
.. _server-host:
|
||||
|
||||
server-host
|
||||
-----------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_SERVER_HOST
|
||||
**In-Database** `n/a`
|
||||
=============== =================
|
||||
|
||||
Where to bind the PostgREST web server. In addition to the usual address options, PostgREST interprets these reserved addresses with special meanings:
|
||||
|
||||
* :code:`*` - any IPv4 or IPv6 hostname
|
||||
* :code:`*4` - any IPv4 or IPv6 hostname, IPv4 preferred
|
||||
* :code:`!4` - any IPv4 hostname
|
||||
* :code:`*6` - any IPv4 or IPv6 hostname, IPv6 preferred
|
||||
* :code:`!6` - any IPv6 hostname
|
||||
|
||||
.. _server-port:
|
||||
|
||||
server-port
|
||||
-----------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_SERVER_PORT
|
||||
**In-Database** `n/a`
|
||||
=============== =================
|
||||
|
||||
The TCP port to bind the web server.
|
||||
|
||||
.. _server-unix-socket:
|
||||
|
||||
server-unix-socket
|
||||
------------------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_SERVER_UNIX_SOCKET
|
||||
**In-Database** `n/a`
|
||||
=============== =================
|
||||
|
||||
`Unix domain socket <https://en.wikipedia.org/wiki/Unix_domain_socket>`_ where to bind the PostgREST web server.
|
||||
If specified, this takes precedence over :ref:`server-port`. Example:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
server-unix-socket = "/tmp/pgrst.sock"
|
||||
|
||||
.. _server-unix-socket-mode:
|
||||
|
||||
server-unix-socket-mode
|
||||
-----------------------
|
||||
|
||||
=============== =================
|
||||
**Environment** PGRST_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:`server-unix-socket`
|
||||
Needs to be a valid octal between 600 and 777.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
server-unix-socket-mode = "660"
|
||||
@@ -0,0 +1,91 @@
|
||||
let
|
||||
# Commit of the Nixpkgs repository that we want to use.
|
||||
nixpkgsVersion = {
|
||||
date = "2021-06-02";
|
||||
rev = "84aa23742f6c72501f9cc209f29c438766f5352d";
|
||||
tarballHash = "0h7xl6q0yjrbl9vm3h6lkxw692nm8bg3wy65gm95a2mivhrdjpxp";
|
||||
};
|
||||
|
||||
# Nix files that describe the Nixpkgs repository. We evaluate the expression
|
||||
# using `import` below.
|
||||
pkgs = import
|
||||
(fetchTarball {
|
||||
url = "https://github.com/nixos/nixpkgs/archive/${nixpkgsVersion.rev}.tar.gz";
|
||||
sha256 = nixpkgsVersion.tarballHash;
|
||||
})
|
||||
{ };
|
||||
|
||||
sphinxTabsPkg = ps: ps.callPackage ./extensions/sphinx-tabs.nix { };
|
||||
sphinxCopybuttonPkg = ps: ps.callPackage ./extensions/sphinx-copybutton.nix { };
|
||||
|
||||
python = pkgs.python3.withPackages (ps: [ ps.sphinx ps.sphinx_rtd_theme ps.livereload (sphinxTabsPkg ps) (sphinxCopybuttonPkg ps) ]);
|
||||
in
|
||||
{
|
||||
inherit pkgs;
|
||||
|
||||
build =
|
||||
pkgs.writeShellScriptBin "postgrest-docs-build"
|
||||
''
|
||||
set -euo pipefail
|
||||
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/docs"
|
||||
|
||||
# clean previous build, otherwise some errors might be supressed
|
||||
rm -rf _build
|
||||
|
||||
${python}/bin/sphinx-build --color -W -b html -a -n . _build
|
||||
'';
|
||||
|
||||
serve =
|
||||
pkgs.writeShellScriptBin "postgrest-docs-serve"
|
||||
''
|
||||
set -euo pipefail
|
||||
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/docs"
|
||||
|
||||
# livereload_docs.py needs to find "sphinx-build"
|
||||
PATH=${python}/bin:$PATH
|
||||
|
||||
${python}/bin/python livereload_docs.py
|
||||
'';
|
||||
|
||||
spellcheck =
|
||||
pkgs.writeShellScriptBin "postgrest-docs-spellcheck"
|
||||
''
|
||||
set -euo pipefail
|
||||
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/docs"
|
||||
|
||||
FILES=$(find . -type f -iname '*.rst' | tr '\n' ' ')
|
||||
|
||||
cat $FILES \
|
||||
| grep -v '^\(\.\.\| \)' \
|
||||
| sed 's/`.*`//g' \
|
||||
| ${pkgs.aspell}/bin/aspell -d ${pkgs.aspellDicts.en}/lib/aspell/en_US -p ./postgrest.dict list \
|
||||
| sort -f \
|
||||
| tee misspellings
|
||||
test ! -s misspellings
|
||||
'';
|
||||
|
||||
# dictcheck detects obsolete entries in postgrest.dict, that are not used anymore
|
||||
dictcheck =
|
||||
pkgs.writeShellScriptBin "postgrest-docs-dictcheck"
|
||||
''
|
||||
set -euo pipefail
|
||||
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/docs"
|
||||
|
||||
FILES=$(find . -type f -iname '*.rst' | tr '\n' ' ')
|
||||
|
||||
cat postgrest.dict \
|
||||
| tail -n+2 \
|
||||
| tr '\n' '\0' \
|
||||
| xargs -0 -n 1 -i \
|
||||
sh -c "grep \"{}\" $FILES > /dev/null || echo \"{}\""
|
||||
'';
|
||||
|
||||
linkcheck =
|
||||
pkgs.writeShellScriptBin "postgrest-docs-linkcheck"
|
||||
''
|
||||
set -euo pipefail
|
||||
cd "$(${pkgs.git}/bin/git rev-parse --show-toplevel)/docs"
|
||||
|
||||
${python}/bin/sphinx-build --color -b linkcheck . _build
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
.. _community_tutorials:
|
||||
|
||||
Community Tutorials
|
||||
-------------------
|
||||
|
||||
* `Building a Contacts List with PostgREST and Vue.js <https://www.youtube.com/watch?v=iHtsALtD5-U>`_ -
|
||||
In this video series, DigitalOcean shows how to build and deploy an Nginx + PostgREST(using a managed PostgreSQL database) + Vue.js webapp in an Ubuntu server droplet.
|
||||
|
||||
* `PostgREST + Auth0: Create REST API in minutes, and add social login using Auth0 <https://samkhawase.com/blog/postgrest-1-introduction/>`_ - A step-by-step tutorial to show how to dockerize and integrate Auth0 to PostgREST service.
|
||||
|
||||
* `PostgREST + PostGIS API tutorial in 5 minutes <https://gis-ops.com/postgrest-postgis-api-tutorial-geospatial-api-in-5-minutes/>`_ -
|
||||
In this tutorial, GIS • OPS shows how to perform PostGIS calculations through PostgREST :ref:`s_procs` interface.
|
||||
|
||||
* `"CodeLess" backend using postgres, postgrest and oauth2 authentication with keycloak <https://www.mathieupassenaud.fr/codeless_backend/>`_ -
|
||||
A step-by-step tutorial for using PostgREST with KeyCloak(hosted on a managed service).
|
||||
|
||||
* `How PostgreSQL triggers work when called with a PostgREST PATCH HTTP request <https://blog.fgribreau.com/2020/11/how-postgresql-triggers-works-when.html>`_ - A tutorial to see how the old and new values are set or not when doing a PATCH request to PostgREST.
|
||||
|
||||
.. _templates:
|
||||
|
||||
Templates
|
||||
---------
|
||||
|
||||
* `compose-postgrest <https://github.com/mattddowney/compose-postgrest>`_ - docker-compose setup with Nginx and HTML example
|
||||
* `svelte-postgrest-template <https://github.com/guyromm/svelte-postgrest-template>`_ - Svelte/SvelteKit, PostgREST, EveryLayout and social auth
|
||||
|
||||
.. _eco_example_apps:
|
||||
|
||||
Example Apps
|
||||
------------
|
||||
|
||||
* `chronicle <https://github.com/srid/chronicle>`_ - tracking a tree of personal memories
|
||||
* `code-du-travail-backoffice <https://github.com/SocialGouv/code-du-travail-backoffice>`_ - data administration portal for the official French Labor Code and Agreements
|
||||
* `delibrium-postgrest <https://gitlab.com/delibrium/delibrium-postgrest/>`_ - example school API and front-end in Vue.js
|
||||
* `elm-workshop <https://github.com/diogob/elm-workshop>`_ - building a simple database query UI
|
||||
* `ember-postgrest-dynamic-ui <https://github.com/benoror/ember-postgrest-dynamic-ui>`_ - generating Ember forms to edit data
|
||||
* `ETH-transactions-storage <https://github.com/Adamant-im/ETH-transactions-storage>`_ - indexer for Ethereum to get transaction list by ETH address
|
||||
* `ext-postgrest-crud <https://github.com/timwis/ext-postgrest-crud>`_ - browser-based spreadsheet
|
||||
* `general <https://github.com/PierreRochard/general>`_ - example auth back-end
|
||||
* `goodfilm <https://github.com/tyrchen/goodfilm>`_ - example film API
|
||||
* `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
|
||||
* `handsontable-postgrest <https://github.com/timwis/handsontable-postgrest>`_ - an excel-like database table editor
|
||||
* `heritage-near-me <https://github.com/CodeforAustralia/heritage-near-me>`_ - Elm and PostgREST with PostGIS
|
||||
* `ng-admin-postgrest <https://github.com/marmelab/ng-admin-postgrest>`_ - automatic database admin panel
|
||||
* `pgrst-dev-setup <https://github.com/Qu4tro/pgrst-dev-setup>`_ - docker-compose and tmuxp setup for experimentation.
|
||||
* `postgres-postgrest-cloudflared-example <https://github.com/cloudflare/postgres-postgrest-cloudflared-example>`_ - docker-compose setup exposing PostgREST using cloudfared
|
||||
* `postgrest-demo <https://github.com/SMRxT/postgrest-demo>`_ - multi-tenant logging system
|
||||
* `postgrest-example <https://github.com/begriffs/postgrest-example>`_ - sqitch versioning for API
|
||||
* `postgrest-sessions-example <https://github.com/monacoremo/postgrest-sessions-example>`_ - example for cookie-based sessions
|
||||
* `postgrest-translation-proxy <https://github.com/NikolayS/postgrest-translation-proxy>`_ - calling to external translation service
|
||||
* `postgrest-ui <https://github.com/tatut/postgrest-ui>`_ - ClojureScript UI components for PostgREST
|
||||
* `postgrest-vercel <https://github.com/seveibar/postgrest-vercel>`_ - run PostgREST on Vercel (Serverless/AWS Lambda)
|
||||
* `PostgrestSkeleton <https://github.com/Recmo/PostgrestSkeleton>`_ - Docker Compose, PostgREST, Nginx and Auth0
|
||||
* `PostGUI <https://github.com/priyank-purohit/PostGUI>`_ - React Material UI admin panel
|
||||
* `prospector <https://github.com/sfcta/prospector>`_ - data warehouse and visualization platform
|
||||
|
||||
.. _devops:
|
||||
|
||||
DevOps
|
||||
------
|
||||
|
||||
* `cloudgov-demo-postgrest <https://github.com/GSA/cloudgov-demo-postgrest>`_ - demo for a federally-compliant REST API on cloud.gov
|
||||
* `cloudstark/helm-charts <https://github.com/cloudstark/helm-charts/tree/master/postgrest>`_ - helm chart to deploy PostgREST to a Kubernetes cluster via a Deployment and Service
|
||||
* `jbkarle/postgrest <https://github.com/jbkarle/postgrest>`_ - helm chart with a demo database for development and test purposes
|
||||
* `Limezest/postgrest-cloud-run <https://github.com/Limezest/postgrest-cloud-run>`_ - expose a PostgreSQL database on Cloud SQL using Cloud Run
|
||||
|
||||
.. _eco_external_notification:
|
||||
|
||||
External Notification
|
||||
---------------------
|
||||
|
||||
These are PostgreSQL bridges that propagate LISTEN/NOTIFY to external queues for further processing. This allows stored procedures to initiate actions outside the database such as sending emails.
|
||||
|
||||
* `pg-bridge <https://github.com/matthewmueller/pg-bridge>`_ - Amazon SNS
|
||||
* `pg-kinesis-bridge <https://github.com/daurnimator/pg-kinesis-bridge>`_ - Amazon Kinesis
|
||||
* `pg-notify-webhook <https://github.com/vbalasu/pg-notify-webhook>`_ - trigger webhooks from PostgreSQL's LISTEN/NOTIFY
|
||||
* `pgsql-listen-exchange <https://github.com/gmr/pgsql-listen-exchange>`_ - RabbitMQ
|
||||
* `postgres-websockets <https://github.com/diogob/postgres-websockets>`_ - expose web sockets for PostgreSQL's LISTEN/NOTIFY
|
||||
* `postgresql-to-amqp <https://github.com/FGRibreau/postgresql-to-amqp>`_ - AMQP
|
||||
* `postgresql2websocket <https://github.com/frafra/postgresql2websocket>`_ - Websockets
|
||||
* `skeeter <https://github.com/SpiderOak/skeeter>`_ - ZeroMQ
|
||||
|
||||
|
||||
.. _eco_extensions:
|
||||
|
||||
Extensions
|
||||
----------
|
||||
|
||||
* `aiodata <https://github.com/Exahilosys/aiodata>`_ - Python, event-based proxy and caching client.
|
||||
* `pg-safeupdate <https://github.com/eradman/pg-safeupdate>`_ - prevent full-table updates or deletes
|
||||
* `postgrest-auth (criles25) <https://github.com/criles25/postgrest-auth>`_ - email based auth/signup
|
||||
* `postgrest-node <https://github.com/seveibar/postgrest-node>`_ - Run a PostgREST server in Node.js via npm module
|
||||
* `postgrest-oauth <https://github.com/nblumoe/postgrest-oauth>`_ - OAuth2 WAI middleware
|
||||
* `postgrest-oauth/api <https://github.com/postgrest-oauth/api>`_ - OAuth2 server
|
||||
* `PostgREST-writeAPI <https://github.com/ppKrauss/PostgREST-writeAPI>`_ - generate Nginx rewrite rules to fit an OpenAPI spec
|
||||
* `spas <https://github.com/srid/spas>`_ - allow file uploads and basic auth
|
||||
|
||||
.. _clientside_libraries:
|
||||
|
||||
Client-Side Libraries
|
||||
---------------------
|
||||
|
||||
* `aor-postgrest-client <https://github.com/tomberek/aor-postgrest-client>`_ - JS, admin-on-rest
|
||||
* `elm-postgrest <https://github.com/john-kelly/elm-postgrest>`_ - Elm
|
||||
* `general-angular <https://github.com/PierreRochard/general-angular>`_ - TypeScript, generate UI from API description
|
||||
* `jarvus-postgrest-apikit <https://github.com/JarvusInnovations/jarvus-postgrest-apikit>`_ - JS, Sencha framework
|
||||
* `mithril-postgrest <https://github.com/catarse/mithril-postgrest>`_ - JS, Mithril
|
||||
* `ng-postgrest <https://github.com/team142/ng-postgrest>`_ - Angular app for browsing, editing data exposed over PostgREST.
|
||||
* `postgrest-client <https://github.com/calebmer/postgrest-client>`_ - JS
|
||||
* `postgrest-csharp <https://github.com/supabase-community/postgrest-csharp>`_ - C#
|
||||
* `postgrest-dart <https://github.com/supabase-community/postgrest-dart>`_ - Dart
|
||||
* `postgrest-ex <https://github.com/J0/postgrest-ex>`_ - Elixir
|
||||
* `postgrest-go <https://github.com/supabase-community/postgrest-go>`_ - Go
|
||||
* `postgrest-js <https://github.com/supabase/postgrest-js>`_ - TypeScript/JavaScript
|
||||
* `postgrest-kt <https://github.com/supabase-community/postgrest-kt>`_ - Kotlin
|
||||
* `postgrest-py <https://github.com/supabase/postgrest-py>`_ - Python
|
||||
* `postgrest-request <https://github.com/lewisjared/postgrest-request>`_ - JS, SuperAgent
|
||||
* `postgrest-rs <https://github.com/supabase-community/postgrest-rs>`_ - Rust
|
||||
* `postgrest-sharp-client <https://github.com/thejettdurham/postgrest-sharp-client>`_ (needs maintainer) - C#, RestSharp
|
||||
* `postgrest-swift <https://github.com/supabase-community/postgrest-swift>`_ - Swift
|
||||
* `postgrest-url <https://github.com/hugomrdias/postgrest-url>`_ - JS, just for generating query URLs
|
||||
* `postgrest_python_requests_client <https://github.com/davidthewatson/postgrest_python_requests_client>`_ - Python
|
||||
* `postgrester <https://github.com/ivangabriele/postgrester>`_ - JS + Typescript
|
||||
* `postgrestR <https://github.com/clesiemo3/postgrestR>`_ - R
|
||||
* `py-postgrest <https://github.com/Kong/py-postgrest>`_ - Python
|
||||
* `redux-postgrest <https://github.com/andytango/redux-postgrest>`_ - TypeScript/JS, client integrated with (React) Redux.
|
||||
* `vue-postgrest <https://github.com/technowledgy/vue-postgrest>`_ - Vue.js
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
.. _error_source:
|
||||
|
||||
Error Source
|
||||
============
|
||||
|
||||
For the most part, error messages will come directly from the database with the same `structure that PostgreSQL uses <https://www.postgresql.org/docs/current/error-style-guide.html>`_. PostgREST will convert the ``MESSAGE``, ``DETAIL``, ``HINT`` and ``ERRCODE`` from the PostgreSQL error to JSON format and add an HTTP status code to the response (see :ref:`status_codes`). For instance, this is the error you will get when querying a nonexistent table:
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
GET /nonexistent_table?id=eq.1 HTTP/1.1
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 404 Not Found
|
||||
Content-Type: application/json; charset=utf-8
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"hint": null,
|
||||
"details": null,
|
||||
"code": "42P01",
|
||||
"message": "relation \"api.nonexistent_table\" does not exist"
|
||||
}
|
||||
|
||||
However, some errors do come from PostgREST itself (such as those related to the :ref:`schema_cache`). These have the same structure as the PostgreSQL errors but are differentiated by the ``PGRST`` prefix in the ``code`` field (see :ref:`pgrst_errors`). For instance, when querying a function that does not exist, the error will be:
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
POST /rpc/nonexistent_function HTTP/1.1
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 404 Not Found
|
||||
Content-Type: application/json; charset=utf-8
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"hint": "If a new function was created in the database with this name and parameters, try reloading the schema cache.",
|
||||
"details": null
|
||||
"code": "PGRST202",
|
||||
"message": "Could not find the api.nonexistent_function() function in the schema cache"
|
||||
}
|
||||
|
||||
.. _status_codes:
|
||||
|
||||
HTTP Status Codes
|
||||
=================
|
||||
|
||||
PostgREST translates `PostgreSQL error codes <https://www.postgresql.org/docs/current/errcodes-appendix.html>`_ into HTTP status as follows:
|
||||
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| PostgreSQL error code(s) | HTTP status | Error description |
|
||||
+==========================+=========================+=================================+
|
||||
| 08* | 503 | pg connection err |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 09* | 500 | triggered action exception |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 0L* | 403 | invalid grantor |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 0P* | 403 | invalid role specification |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 23503 | 409 | foreign key violation |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 23505 | 409 | uniqueness violation |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 25006 | 405 | read only sql transaction |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 25* | 500 | invalid transaction state |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 28* | 403 | invalid auth specification |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 2D* | 500 | invalid transaction termination |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 38* | 500 | external routine exception |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 39* | 500 | external routine invocation |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 3B* | 500 | savepoint exception |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 40* | 500 | transaction rollback |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 53* | 503 | insufficient resources |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 54* | 413 | too complex |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 55* | 500 | obj not in prerequisite state |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 57* | 500 | operator intervention |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 58* | 500 | system error |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| F0* | 500 | config file error |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| HV* | 500 | foreign data wrapper error |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| P0001 | 400 | default code for "raise" |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| P0* | 500 | PL/pgSQL error |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| XX* | 500 | internal error |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 42883 | 404 | undefined function |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 42P01 | 404 | undefined table |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| 42501 | | if authenticated 403, | insufficient privileges |
|
||||
| | | else 401 | |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
| other | 400 | |
|
||||
+--------------------------+-------------------------+---------------------------------+
|
||||
|
||||
.. _pgrst_errors:
|
||||
|
||||
PostgREST Error Codes
|
||||
=====================
|
||||
|
||||
PostgREST error codes have the form ``PGRSTgxx``, where ``PGRST`` is the prefix that differentiates the error from a PostgreSQL error, ``g`` is the group where the error belongs and ``xx`` is the number that identifies the error in the group.
|
||||
|
||||
.. _pgrst0**:
|
||||
|
||||
Group 0 - Connection
|
||||
--------------------
|
||||
|
||||
Related to the connection with the database.
|
||||
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| Code | HTTP status | Description |
|
||||
+===============+=============+=============================================================+
|
||||
| .. _pgrst000: | 503 | Could not connect with the database due to an incorrect |
|
||||
| | | :ref:`db-uri` or due to the PostgreSQL service not running. |
|
||||
| PGRST000 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst001: | 503 | Could not connect with the database due to an internal |
|
||||
| | | error. |
|
||||
| PGRST001 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst002: | 503 | Could not connect with the database when building the |
|
||||
| | | :ref:`schema_cache` due to the PostgreSQL service not |
|
||||
| PGRST002 | | running. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst003: | 504 | The request timed out waiting for a pool connection |
|
||||
| | | to be available. See :ref:`db-pool-acquisition-timeout`. |
|
||||
| PGRST003 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
|
||||
.. _pgrst1**:
|
||||
|
||||
Group 1 - Api Request
|
||||
---------------------
|
||||
|
||||
Related to the HTTP request elements.
|
||||
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| Code | HTTP status | Description |
|
||||
+===============+=============+=============================================================+
|
||||
| .. _pgrst100: | 400 | Parsing error in the query string parameter. |
|
||||
| | | See :ref:`h_filter`, :ref:`operators` and :ref:`ordering`. |
|
||||
| PGRST100 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst101: | 405 | For :ref:`functions <s_procs>`, only ``GET`` and ``POST`` |
|
||||
| | | verbs are allowed. Any other verb will throw this error. |
|
||||
| PGRST101 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst102: | 400 | An invalid request body was sent(e.g. an empty body or |
|
||||
| | | malformed JSON). |
|
||||
| PGRST102 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst103: | 416 | An invalid range was specified for :ref:`limits`. |
|
||||
| | | |
|
||||
| PGRST103 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst105: | 405 | An invalid :ref:`PUT <upsert_put>` request was done |
|
||||
| | | |
|
||||
| PGRST105 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst106: | 406 | The schema specified when |
|
||||
| | | :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 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst108: | 400 | The filter is applied to a embedded resource that is not |
|
||||
| | | specified in the ``select`` part of the query string. |
|
||||
| PGRST108 | | See :ref:`embed_filters`. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst109: | 400 | Restricting a Deletion or an Update using limits must |
|
||||
| | | include the ordering of a unique column. |
|
||||
| PGRST109 | | See :ref:`limited_update_delete`. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst110: | 400 | When restricting a Deletion or an Update using limits |
|
||||
| | | modifies more rows than the maximum specified in the limit. |
|
||||
| PGRST110 | | See :ref:`limited_update_delete`. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst111: | 500 | An invalid ``response.headers`` was set. |
|
||||
| | | See :ref:`guc_resp_hdrs`. |
|
||||
| PGRST111 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst112: | 500 | The status code must be a positive integer. |
|
||||
| | | See :ref:`guc_resp_status`. |
|
||||
| PGRST112 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst113: | 406 | More than one column was returned for a scalar result. |
|
||||
| | | See :ref:`scalar_return_formats`. |
|
||||
| PGRST113 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst114: | 400 | For an :ref:`UPSERT using PUT <upsert_put>`, when |
|
||||
| | | :ref:`limits and offsets <limits>` are used. |
|
||||
| PGRST114 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst115: | 400 | For an :ref:`UPSERT using PUT <upsert_put>`, when the |
|
||||
| | | primary key in the query string and the body are different. |
|
||||
| PGRST115 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst116: | 406 | More than 1 or no items where returned when requesting |
|
||||
| | | a singular response. See :ref:`singular_plural`. |
|
||||
| PGRST116 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst117: | 405 | The HTTP verb used in the request in not supported. |
|
||||
| | | |
|
||||
| PGRST117 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
|
||||
.. _pgrst2**:
|
||||
|
||||
Group 2 - Schema Cache
|
||||
----------------------
|
||||
|
||||
Related to a :ref:`stale schema cache <stale_schema>`. Most of the time, these errors are solved by :ref:`reloading the schema cache <schema_reloading>`.
|
||||
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| Code | HTTP status | Description |
|
||||
+===============+=============+=============================================================+
|
||||
| .. _pgrst200: | 400 | Caused by :ref:`stale_fk_relationships`, otherwise any of |
|
||||
| | | the embedding resources or the relationship itself may not |
|
||||
| PGRST200 | | exist in the database. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst201: | 300 | An ambiguous embedding request was made. |
|
||||
| | | See :ref:`embed_disamb`. |
|
||||
| PGRST201 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst202: | 404 | Caused by a :ref:`stale_function_signature`, otherwise |
|
||||
| | | the function may not exist in the database. |
|
||||
| PGRST202 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst203: | 300 | Caused by requesting overloaded functions with the same |
|
||||
| | | argument names but different types, or by using a ``POST`` |
|
||||
| PGRST203 | | verb to request overloaded functions with a ``JSON`` or |
|
||||
| | | ``JSONB`` type unnamed parameter. The solution is to rename |
|
||||
| | | the function or add/modify the names of the arguments. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst204: | 400 | Caused when the :ref:`column specified <specify_columns>` |
|
||||
| | | in the ``columns`` query parameter is not found. |
|
||||
| PGRST204 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
|
||||
.. _pgrst3**:
|
||||
|
||||
Group 3 - JWT
|
||||
-------------
|
||||
|
||||
Related to the authentication process using JWT. You can follow the :ref:`tut1` for an example on how to implement authentication and the :doc:`Authentication page <auth>` for more information on this process.
|
||||
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| Code | HTTP status | Description |
|
||||
+===============+=============+=============================================================+
|
||||
| .. _pgrst300: | 500 | A :ref:`JWT secret <jwt-secret>` is missing from the |
|
||||
| | | configuration. |
|
||||
| PGRST300 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst301: | 401 | Any error related to the verification of the JWT, |
|
||||
| | | which means that the JWT provided is invalid in some way. |
|
||||
| PGRST301 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst302: | 401 | Attempted to do a request without |
|
||||
| | | :ref:`authentication <client_auth>` when the anonymous role |
|
||||
| PGRST302 | | is disabled by not setting it in :ref:`db-anon-role`. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
|
||||
.. The Internal Errors Group X** is always at the end
|
||||
|
||||
.. _pgrst_X**:
|
||||
|
||||
Group X - Internal
|
||||
------------------
|
||||
|
||||
Internal errors. If you encounter any of these, you may have stumbled on a PostgREST bug, please `open an issue <https://github.com/PostgREST/postgrest/issues>`_ and we'll be glad to fix it.
|
||||
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| Code | HTTP status | Description |
|
||||
+===============+=============+=============================================================+
|
||||
| .. _pgrstX00: | 500 | Internal errors related to the library used for connecting |
|
||||
| | | to the database. |
|
||||
| PGRSTX00 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
@@ -0,0 +1,33 @@
|
||||
{ lib
|
||||
, buildPythonPackage
|
||||
, fetchFromGitHub
|
||||
, sphinx
|
||||
}:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "sphinx-copybutton";
|
||||
version = "0.4.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "executablebooks";
|
||||
repo = "sphinx-copybutton";
|
||||
rev = "v${version}";
|
||||
sha256 = "sha256-vrEIvQeP7AMXSme1PBp0ox5k8Q1rz+1cbHIO+o17Jqc=";
|
||||
fetchSubmodules = true;
|
||||
};
|
||||
|
||||
propagatedBuildInputs = [
|
||||
sphinx
|
||||
];
|
||||
|
||||
doCheck = false; # no tests
|
||||
|
||||
pythonImportsCheck = [ "sphinx_copybutton" ];
|
||||
|
||||
meta = with lib; {
|
||||
description = "A small sphinx extension to add a \"copy\" button to code blocks";
|
||||
homepage = "https://github.com/executablebooks/sphinx-copybutton";
|
||||
license = licenses.mit;
|
||||
maintainers = with maintainers; [ Luflosi ];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{ lib
|
||||
, buildPythonPackage
|
||||
, fetchPypi
|
||||
, sphinx
|
||||
}:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "sphinx-tabs";
|
||||
version = "3.2.0";
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "sha256:1970aahi6sa7c37cpz8nwgdb2xzf21rk6ykdd1m6w9wvxla7j4rk";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = [
|
||||
sphinx
|
||||
];
|
||||
|
||||
doCheck = false;
|
||||
|
||||
pythonImportsCheck = [ "sphinx_tabs" ];
|
||||
|
||||
meta = with lib; {
|
||||
description = "Create tabbed content in Sphinx documentation when building HTML";
|
||||
homepage = "https://sphinx-tabs.readthedocs.io";
|
||||
license = licenses.mit;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
.. _create_soap_endpoint:
|
||||
|
||||
Create a SOAP endpoint
|
||||
======================
|
||||
|
||||
:author: `fjf2002 <https://github.com/fjf2002>`_
|
||||
|
||||
PostgREST now has XML support. With a bit of work, SOAP endpoints become possible.
|
||||
|
||||
Please note that PostgREST supports just ``text/xml`` MIME type in request/response headers ``Content-Type`` and ``Accept``.
|
||||
If you have to use other MIME types such as ``application/soap+xml``, you could manipulate the headers in your reverse proxy.
|
||||
|
||||
|
||||
|
||||
Minimal Example
|
||||
---------------
|
||||
This example will simply return the request body, inside a tag ``therequestbodywas``.
|
||||
|
||||
Add the following function to your PostgreSQL database:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE OR REPLACE FUNCTION my_soap_endpoint(xml) RETURNS xml AS $$
|
||||
DECLARE
|
||||
nsarray CONSTANT text[][] := ARRAY[
|
||||
ARRAY['soapenv', 'http://schemas.xmlsoap.org/soap/envelope/']
|
||||
];
|
||||
BEGIN
|
||||
RETURN xmlelement(
|
||||
NAME "soapenv:Envelope",
|
||||
XMLATTRIBUTES('http://schemas.xmlsoap.org/soap/envelope/' AS "xmlns:soapenv"),
|
||||
xmlelement(NAME "soapenv:Header"),
|
||||
xmlelement(
|
||||
NAME "soapenv:Body",
|
||||
xmlelement(
|
||||
NAME theRequestBodyWas,
|
||||
(xpath('/soapenv:Envelope/soapenv:Body', $1, nsarray))[1]
|
||||
)
|
||||
)
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
Do not forget to refresh the :ref:`PostgREST schema cache <schema_reloading>`.
|
||||
|
||||
Use ``curl`` for a first test:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl http://localhost:3000/rpc/my_soap_endpoint \
|
||||
--header 'Content-Type: text/xml' \
|
||||
--header 'Accept: text/xml' \
|
||||
--data-binary @- <<XML
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soapenv:Header/>
|
||||
<soapenv:Body>
|
||||
<mySOAPContent>
|
||||
My SOAP Content
|
||||
</mySOAPContent>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
XML
|
||||
|
||||
The output should contain the original request body within the ``therequestbodywas`` entity,
|
||||
and should roughly look like:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soapenv:Header/>
|
||||
<soapenv:Body>
|
||||
<therequestbodywas>
|
||||
<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<mySOAPContent>
|
||||
My SOAP Content
|
||||
</mySOAPContent>
|
||||
</soapenv:Body>
|
||||
</therequestbodywas>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
|
||||
Unfortunately the ``Accept: text/xml`` header is currently mandatory concerning PostgREST, otherwise it will respond
|
||||
with a ``Content-Type: application/json`` header and enclose the response with quotes.
|
||||
(You can check the returned headers by adding ``-v`` to the curl call.)
|
||||
|
||||
If your SOAP clients do not send the ``Accept: text/xml`` header, you can fix that in your nginx reverse proxy
|
||||
by adding something like ...
|
||||
|
||||
.. code-block:: nginx
|
||||
|
||||
set $accept $http_accept;
|
||||
if ($contentType ~ "^text/xml($|;)") {
|
||||
set $accept "text/xml";
|
||||
}
|
||||
proxy_set_header Accept $accept;
|
||||
|
||||
to your ``location`` nginx configuration.
|
||||
(The given example sets the ``Accept`` header for each request of Content-Type ``text/xml``.)
|
||||
|
||||
|
||||
A more elaborate example
|
||||
------------------------
|
||||
|
||||
Here we have a SOAP service that converts a fraction to a decimal value,
|
||||
with pass-through of PostgreSQL errors to the SOAP response.
|
||||
Please note that in production you probably should not pass through plain database errors
|
||||
potentially disclosing internals to the client, but instead handle the errors directly.
|
||||
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- helper function
|
||||
CREATE OR REPLACE FUNCTION _soap_envelope(body xml)
|
||||
RETURNS xml
|
||||
LANGUAGE sql
|
||||
AS $function$
|
||||
SELECT xmlelement(
|
||||
NAME "soapenv:Envelope",
|
||||
XMLATTRIBUTES('http://schemas.xmlsoap.org/soap/envelope/' AS "xmlns:soapenv"),
|
||||
xmlelement(NAME "soapenv:Header"),
|
||||
xmlelement(NAME "soapenv:Body", body)
|
||||
);
|
||||
$function$;
|
||||
|
||||
-- helper function
|
||||
CREATE OR REPLACE FUNCTION _soap_exception(
|
||||
faultcode text,
|
||||
faultstring text
|
||||
)
|
||||
RETURNS xml
|
||||
LANGUAGE sql
|
||||
AS $function$
|
||||
SELECT _soap_envelope(
|
||||
xmlelement(NAME "soapenv:Fault",
|
||||
xmlelement(NAME "faultcode", faultcode),
|
||||
xmlelement(NAME "faultstring", faultstring)
|
||||
)
|
||||
);
|
||||
$function$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION fraction_to_decimal(xml)
|
||||
RETURNS xml
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
DECLARE
|
||||
nsarray CONSTANT text[][] := ARRAY[
|
||||
ARRAY['soapenv', 'http://schemas.xmlsoap.org/soap/envelope/']
|
||||
];
|
||||
exc_msg text;
|
||||
exc_detail text;
|
||||
exc_hint text;
|
||||
exc_sqlstate text;
|
||||
BEGIN
|
||||
-- simulating a statement that results in an exception:
|
||||
RETURN _soap_envelope(xmlelement(
|
||||
NAME "decimalValue",
|
||||
(
|
||||
(xpath('/soapenv:Envelope/soapenv:Body/fraction/numerator/text()', $1, nsarray))[1]::text::int
|
||||
/
|
||||
(xpath('/soapenv:Envelope/soapenv:Body/fraction/denominator/text()', $1, nsarray))[1]::text::int
|
||||
)::text::xml
|
||||
));
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
GET STACKED DIAGNOSTICS
|
||||
exc_msg := MESSAGE_TEXT,
|
||||
exc_detail := PG_EXCEPTION_DETAIL,
|
||||
exc_hint := PG_EXCEPTION_HINT,
|
||||
exc_sqlstate := RETURNED_SQLSTATE;
|
||||
RAISE WARNING USING
|
||||
MESSAGE = exc_msg,
|
||||
DETAIL = exc_detail,
|
||||
HINT = exc_hint;
|
||||
RETURN _soap_exception(faultcode => exc_sqlstate, faultstring => concat(exc_msg, ', DETAIL: ', exc_detail, ', HINT: ', exc_hint));
|
||||
END
|
||||
$function$;
|
||||
|
||||
Let's test the ``fraction_to_decimal`` service with illegal values:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl http://localhost:3000/rpc/fraction_to_decimal \
|
||||
--header 'Content-Type: text/xml' \
|
||||
--header 'Accept: text/xml' \
|
||||
--data-binary @- <<XML
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soapenv:Header/>
|
||||
<soapenv:Body>
|
||||
<fraction>
|
||||
<numerator>42</numerator>
|
||||
<denominator>0</denominator>
|
||||
</fraction>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
XML
|
||||
|
||||
The output should roughly look like:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soapenv:Header/>
|
||||
<soapenv:Body>
|
||||
<soapenv:Fault>
|
||||
<faultcode>22012</faultcode>
|
||||
<faultstring>division by zero, DETAIL: , HINT: </faultstring>
|
||||
</soapenv:Fault>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
|
||||
|
||||
References
|
||||
----------
|
||||
For more information concerning PostgREST, cf.
|
||||
|
||||
- :ref:`s_proc_single_unnamed`
|
||||
- :ref:`scalar_return_formats`
|
||||
- :ref:`Nginx reverse proxy <admin>`
|
||||
|
||||
For SOAP reference, visit
|
||||
|
||||
- the specification at https://www.w3.org/TR/soap/
|
||||
- shorter more practical advice is available at https://www.w3schools.com/xml/xml_soap.asp
|
||||
@@ -0,0 +1,96 @@
|
||||
.. _providing_img:
|
||||
|
||||
Providing images for ``<img>``
|
||||
==============================
|
||||
|
||||
:author: `pkel <https://github.com/pkel>`_
|
||||
|
||||
In this how-to, you will learn how to create an endpoint for providing images to HTML :code:`<img>` tags without client side JavaScript. In fact, the presented technique is suitable for providing not only images, but arbitrary files.
|
||||
|
||||
We will start with a minimal example that highlights the general concept.
|
||||
Afterwards we present a more detailed solution that fixes a few shortcomings of the first approach.
|
||||
|
||||
.. warning::
|
||||
|
||||
Be careful when saving binaries in the database, having a separate storage service for these is preferable in most cases. See `Storing Binary files in the Database <https://wiki.postgresql.org/wiki/BinaryFilesInDB>`_.
|
||||
|
||||
Minimal Example
|
||||
---------------
|
||||
|
||||
First, we need a public table for storing the files.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create table files(
|
||||
id int primary key
|
||||
, blob bytea
|
||||
);
|
||||
|
||||
Let's assume this table contains an image of two cute kittens with id 42.
|
||||
We can retrieve this image in binary format from our PostgREST API by requesting :code:`/files?select=blob&id=eq.42` with the :code:`Accept: application/octet-stream` header.
|
||||
Unfortunately, putting the URL into the :code:`src` of an :code:`<img>` tag will not work.
|
||||
That's because browsers do not send the required :code:`Accept: application/octet-stream` header.
|
||||
|
||||
Luckily we can specify the accepted media types in the :ref:`raw-media-types` configuration variable.
|
||||
In this case, the :code:`Accept: image/webp` header is sent by many web browsers by default, so let's add it to the configuration variable, like this: :code:`raw-media-types="image/webp"`.
|
||||
Now, the image will be displayed in the HTML page:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<img src="http://localhost:3000/files?select=blob&id=eq.42" alt="Cute Kittens"/>
|
||||
|
||||
Improved Version
|
||||
----------------
|
||||
|
||||
The basic solution has some shortcomings:
|
||||
|
||||
1. The response :code:`Content-Type` header is set to :code:`image/webp`.
|
||||
This might be a problem if you want to specify a different format for the file.
|
||||
2. Download requests (e.g. Right Click -> Save Image As) to :code:`/files?select=blob&id=eq.42` will propose :code:`files` as filename.
|
||||
This might confuse users.
|
||||
3. Requests to the binary endpoint are not cached.
|
||||
This will cause unnecessary load on the database.
|
||||
|
||||
The following improved version addresses these problems.
|
||||
First, in addition to the minimal example, we need to store the media types and names of our files in the database.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
alter table files
|
||||
add column type text,
|
||||
add column name text;
|
||||
|
||||
Next, we set up an RPC endpoint that sets the content type and filename.
|
||||
We use this opportunity to configure some basic, client-side caching.
|
||||
For production, you probably want to configure additional caches, e.g. on the :ref:`reverse proxy <admin>`.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create function file(id int) returns bytea as
|
||||
$$
|
||||
declare headers text;
|
||||
declare blob bytea;
|
||||
begin
|
||||
select format(
|
||||
'[{"Content-Type": "%s"},'
|
||||
'{"Content-Disposition": "inline; filename=\"%s\""},'
|
||||
'{"Cache-Control": "max-age=259200"}]'
|
||||
, files.type, files.name)
|
||||
from files where files.id = file.id into headers;
|
||||
perform set_config('response.headers', headers, true);
|
||||
select files.blob from files where files.id = file.id into blob;
|
||||
if found
|
||||
then return(blob);
|
||||
else raise sqlstate 'PT404' using
|
||||
message = 'NOT FOUND',
|
||||
detail = 'File not found',
|
||||
hint = format('%s seems to be an invalid file id', file.id);
|
||||
end if;
|
||||
end
|
||||
$$ language plpgsql;
|
||||
|
||||
With this, we can obtain the cat image from :code:`/rpc/file?id=42`. Thus, the resulting HTML will be:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<img src="http://localhost:3000/rpc/file?id=42" alt="Cute Kittens"/>
|
||||
@@ -0,0 +1,341 @@
|
||||
.. _sql-user-management-using-postgres-users-and-passwords:
|
||||
|
||||
SQL User Management using postgres' users and passwords
|
||||
=======================================================
|
||||
|
||||
:author: `fjf2002 <https://github.com/fjf2002>`_
|
||||
|
||||
|
||||
This is an alternative to chapter :ref:`sql_user_management`, solely using the PostgreSQL built-in table `pg_catalog.pg_authid <https://www.postgresql.org/docs/current/catalog-pg-authid.html>`_ for user management. This means
|
||||
|
||||
- no dedicated user table (aside from :code:`pg_authid`) is required
|
||||
|
||||
- PostgreSQL's users and passwords (i. e. the stuff in :code:`pg_authid`) are also used at the PostgREST level.
|
||||
|
||||
.. note::
|
||||
Only PostgreSQL users with SCRAM-SHA-256 password hashes (the default since PostgreSQL v14) are supported.
|
||||
|
||||
.. warning::
|
||||
|
||||
This is experimental. We can't give you any guarantees, especially concerning security. Use at your own risk.
|
||||
|
||||
|
||||
|
||||
Working with pg_authid and SCRAM-SHA-256 hashes
|
||||
-----------------------------------------------
|
||||
|
||||
As in :ref:`sql_user_management`, we create a :code:`basic_auth` schema:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- We put things inside the basic_auth schema to hide
|
||||
-- them from public view. Certain public procs/views will
|
||||
-- refer to helpers and tables inside.
|
||||
CREATE SCHEMA IF NOT EXISTS basic_auth;
|
||||
|
||||
|
||||
As in :ref:`sql_user_management`, we create the :code:`pgcrypto` and :code:`pgjwt` extensions. Here we prefer to put the extensions in its own schemas:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE SCHEMA ext_pgcrypto;
|
||||
ALTER SCHEMA ext_pgcrypto OWNER TO postgres;
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA ext_pgcrypto;
|
||||
|
||||
|
||||
Concerning the `pgjwt extension <https://github.com/michelp/pgjwt>`_, please cf. to :ref:`client_auth`.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE SCHEMA ext_pgjwt;
|
||||
ALTER SCHEMA ext_pgjwt OWNER TO postgres;
|
||||
CREATE EXTENSION IF NOT EXISTS pgjwt WITH SCHEMA ext_pgjwt;
|
||||
|
||||
|
||||
In order to be able to work with postgres' SCRAM-SHA-256 password hashes, we also need the PBKDF2 key derivation function. Luckily there is `a PL/pgSQL implementation on stackoverflow <https://stackoverflow.com/q/47162200/2337147>`_:
|
||||
|
||||
.. code-block:: plpgsql
|
||||
|
||||
CREATE FUNCTION basic_auth.pbkdf2(salt bytea, pw text, count integer, desired_length integer, algorithm text) RETURNS bytea
|
||||
LANGUAGE plpgsql IMMUTABLE
|
||||
AS $$
|
||||
DECLARE
|
||||
hash_length integer;
|
||||
block_count integer;
|
||||
output bytea;
|
||||
the_last bytea;
|
||||
xorsum bytea;
|
||||
i_as_int32 bytea;
|
||||
i integer;
|
||||
j integer;
|
||||
k integer;
|
||||
BEGIN
|
||||
algorithm := lower(algorithm);
|
||||
CASE algorithm
|
||||
WHEN 'md5' then
|
||||
hash_length := 16;
|
||||
WHEN 'sha1' then
|
||||
hash_length = 20;
|
||||
WHEN 'sha256' then
|
||||
hash_length = 32;
|
||||
WHEN 'sha512' then
|
||||
hash_length = 64;
|
||||
ELSE
|
||||
RAISE EXCEPTION 'Unknown algorithm "%"', algorithm;
|
||||
END CASE;
|
||||
--
|
||||
block_count := ceil(desired_length::real / hash_length::real);
|
||||
--
|
||||
FOR i in 1 .. block_count LOOP
|
||||
i_as_int32 := E'\\000\\000\\000'::bytea || chr(i)::bytea;
|
||||
i_as_int32 := substring(i_as_int32, length(i_as_int32) - 3);
|
||||
--
|
||||
the_last := salt::bytea || i_as_int32;
|
||||
--
|
||||
xorsum := ext_pgcrypto.HMAC(the_last, pw::bytea, algorithm);
|
||||
the_last := xorsum;
|
||||
--
|
||||
FOR j IN 2 .. count LOOP
|
||||
the_last := ext_pgcrypto.HMAC(the_last, pw::bytea, algorithm);
|
||||
|
||||
-- xor the two
|
||||
FOR k IN 1 .. length(xorsum) LOOP
|
||||
xorsum := set_byte(xorsum, k - 1, get_byte(xorsum, k - 1) # get_byte(the_last, k - 1));
|
||||
END LOOP;
|
||||
END LOOP;
|
||||
--
|
||||
IF output IS NULL THEN
|
||||
output := xorsum;
|
||||
ELSE
|
||||
output := output || xorsum;
|
||||
END IF;
|
||||
END LOOP;
|
||||
--
|
||||
RETURN substring(output FROM 1 FOR desired_length);
|
||||
END $$;
|
||||
|
||||
ALTER FUNCTION basic_auth.pbkdf2(salt bytea, pw text, count integer, desired_length integer, algorithm text) OWNER TO postgres;
|
||||
|
||||
|
||||
Analogous to :ref:`sql_user_management` creates the function :code:`basic_auth.user_role`, we create a helper function to check the user's password, here with another name and signature (since we want the username, not an email address).
|
||||
But contrary to :ref:`sql_user_management`, this function does not use a dedicated :code:`users` table with passwords, but instead utilizes the built-in table `pg_catalog.pg_authid <https://www.postgresql.org/docs/current/catalog-pg-authid.html>`_:
|
||||
|
||||
.. code-block:: plpgsql
|
||||
|
||||
CREATE FUNCTION basic_auth.check_user_pass(username text, password text) RETURNS name
|
||||
LANGUAGE sql
|
||||
AS
|
||||
$$
|
||||
SELECT rolname AS username
|
||||
FROM pg_authid
|
||||
-- regexp-split scram hash:
|
||||
CROSS JOIN LATERAL regexp_match(rolpassword, '^SCRAM-SHA-256\$(.*):(.*)\$(.*):(.*)$') AS rm
|
||||
-- identify regexp groups with sane names:
|
||||
CROSS JOIN LATERAL (SELECT rm[1]::integer AS iteration_count, decode(rm[2], 'base64') as salt, decode(rm[3], 'base64') AS stored_key, decode(rm[4], 'base64') AS server_key, 32 AS digest_length) AS stored_password_part
|
||||
-- calculate pbkdf2-digest:
|
||||
CROSS JOIN LATERAL (SELECT basic_auth.pbkdf2(salt, check_user_pass.password, iteration_count, digest_length, 'sha256')) AS digest_key(digest_key)
|
||||
-- based on that, calculate hashed passwort part:
|
||||
CROSS JOIN LATERAL (SELECT ext_pgcrypto.digest(ext_pgcrypto.hmac('Client Key', digest_key, 'sha256'), 'sha256') AS stored_key, ext_pgcrypto.hmac('Server Key', digest_key, 'sha256') AS server_key) AS check_password_part
|
||||
WHERE rolpassword IS NOT NULL
|
||||
AND pg_authid.rolname = check_user_pass.username
|
||||
-- verify password:
|
||||
AND check_password_part.stored_key = stored_password_part.stored_key
|
||||
AND check_password_part.server_key = stored_password_part.server_key;
|
||||
$$;
|
||||
|
||||
ALTER FUNCTION basic_auth.check_user_pass(username text, password text) OWNER TO postgres;
|
||||
|
||||
|
||||
|
||||
Public User Interface
|
||||
---------------------
|
||||
|
||||
Analogous to :ref:`sql_user_management`, we create a login function which takes a username and password and returns a JWT if the credentials match a user in the internal table.
|
||||
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.
|
||||
|
||||
|
||||
.. code-block:: plpgsql
|
||||
|
||||
CREATE TYPE basic_auth.jwt_token AS (
|
||||
token text
|
||||
);
|
||||
|
||||
-- if you are not using psql, you need to replace :DBNAME with the current database's name.
|
||||
ALTER DATABASE :DBNAME SET "app.jwt_secret" to 'reallyreallyreallyreallyverysafe';
|
||||
|
||||
|
||||
CREATE FUNCTION public.login(username text, password text) RETURNS basic_auth.jwt_token
|
||||
LANGUAGE plpgsql security definer
|
||||
AS $$
|
||||
DECLARE
|
||||
_role name;
|
||||
result basic_auth.jwt_token;
|
||||
BEGIN
|
||||
-- check email and password
|
||||
SELECT basic_auth.check_user_pass(username, password) INTO _role;
|
||||
IF _role IS NULL THEN
|
||||
RAISE invalid_password USING message = 'invalid user or password';
|
||||
END IF;
|
||||
--
|
||||
SELECT ext_pgjwt.sign(
|
||||
row_to_json(r), current_setting('app.jwt_secret')
|
||||
) AS token
|
||||
FROM (
|
||||
SELECT login.username as role,
|
||||
extract(epoch FROM now())::integer + 60*60 AS exp
|
||||
) r
|
||||
INTO result;
|
||||
RETURN result;
|
||||
END;
|
||||
$$;
|
||||
|
||||
ALTER FUNCTION public.login(username text, password text) OWNER TO postgres;
|
||||
|
||||
|
||||
|
||||
Permissions
|
||||
~~~~~~~~~~~
|
||||
|
||||
Analogous to :ref:`sql_user_management`:
|
||||
Your database roles need access to the schema, tables, views and functions in order to service HTTP requests.
|
||||
Recall from the :ref:`roles` that PostgREST uses special roles to process requests, namely the authenticator and
|
||||
anonymous roles. Below is an example of permissions that allow anonymous users to attempt to log in.
|
||||
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- the names "anon" and "authenticator" are configurable and not
|
||||
-- sacred, we simply choose them for clarity
|
||||
CREATE ROLE anon NOINHERIT;
|
||||
CREATE role authenticator NOINHERIT LOGIN PASSWORD 'secret';
|
||||
GRANT anon TO authenticator;
|
||||
|
||||
GRANT EXECUTE ON FUNCTION public.login(username text, password text) TO anon;
|
||||
|
||||
|
||||
Since the above :code:`login` function is defined as `security definer <https://www.postgresql.org/docs/current/sql-createfunction.html#id-1.9.3.67.10.2>`_,
|
||||
the anonymous user :code:`anon` doesn't need permission to access the table :code:`pg_catalog.pg_authid` .
|
||||
:code:`grant execute on function` is included for clarity but it might not be needed, see :ref:`func_privs` for more details.
|
||||
|
||||
Choose a secure password for role :code:`authenticator`.
|
||||
Do not forget to configure PostgREST to use the :code:`authenticator` user to connect, and to use the :code:`anon` user as anonymous user.
|
||||
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
Let us create a sample user:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE ROLE foo PASSWORD 'bar';
|
||||
|
||||
|
||||
Test at the SQL level
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Execute:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
SELECT * FROM public.login('foo', 'bar');
|
||||
|
||||
|
||||
This should return a single scalar field like:
|
||||
|
||||
::
|
||||
|
||||
token
|
||||
-----------------------------------------------------------------------------------------------------------------------------
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZm9vIiwiZXhwIjoxNjY4MTg4ODQ3fQ.idBBHuDiQuN_S7JJ2v3pBOr9QypCliYQtCgwYOzAqEk
|
||||
(1 row)
|
||||
|
||||
|
||||
Test at the REST level
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
An API request to call this function would look like:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /rpc/login HTTP/1.1
|
||||
|
||||
{ "username": "foo", "password": "bar" }
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/rpc/login" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d '{ "username": "foo", "password": "bar" }'
|
||||
|
||||
The response would look like the snippet below. Try decoding the token at `jwt.io <https://jwt.io/>`_. (It was encoded with a secret of :code:`reallyreallyreallyreallyverysafe` as specified in the SQL code above. You'll want to change this secret in your app!)
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VwcCIsImV4cCI6MTY2ODE4ODQzN30.WSytcouNMQe44ZzOQit2AQsqTKFD5mIvT3z2uHwdoYY"
|
||||
}
|
||||
|
||||
|
||||
|
||||
A more sophisticated test at the REST level
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Let's add a table, intended for the :code:`foo` user:
|
||||
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE TABLE public.foobar(foo int, bar text, baz float);
|
||||
ALTER TABLE public.foobar owner TO postgres;
|
||||
|
||||
|
||||
Now try to get the table's contents with:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /foobar HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/foobar"
|
||||
|
||||
|
||||
This should fail --- of course, we haven't specified the user, thus PostgREST falls back to the :code:`anon` user and denies access.
|
||||
Add an :code:`Authorization` header. Please use the token value from the login function call above instead of the one provided below.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /foobar HTTP/1.1
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZm9vIiwiZXhwIjoxNjY4MTkyMjAyfQ.zzdHCBjfkqDQLQ8D7CHO3cIALF6KBCsfPTWgwhCiHCY
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/foobar" \
|
||||
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZm9vIiwiZXhwIjoxNjY4MTkyMjAyfQ.zzdHCBjfkqDQLQ8D7CHO3cIALF6KBCsfPTWgwhCiHCY"
|
||||
|
||||
|
||||
This will fail again --- we get :code:`Permission denied to set role`. We forgot to allow the authenticator role to switch into this user by executing:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
GRANT foo TO authenticator;
|
||||
|
||||
|
||||
Re-execute the last REST request. We fail again --- we also forgot to grant permissions for :code:`foo` on the table. Execute:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
GRANT SELECT ON TABLE public.foobar TO foo;
|
||||
|
||||
Now the REST request should succeed. An empty JSON array :code:`[]` is returned.
|
||||
@@ -0,0 +1,763 @@
|
||||
.. _working_with_types:
|
||||
|
||||
Working with PostgreSQL data types
|
||||
==================================
|
||||
|
||||
:author: `Laurence Isla <https://github.com/laurenceisla>`_
|
||||
|
||||
PostgREST makes use of PostgreSQL string representations to work with data types. Thanks to this, you can use special values, such as ``now`` for timestamps, ``yes`` for booleans or time values including the time zones. This page describes how you can take advantage of these string representations to perform operations on different PostgreSQL data types.
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 1
|
||||
|
||||
Timestamps
|
||||
----------
|
||||
|
||||
You can use the **time zone** to filter or send data if needed.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create table reports (
|
||||
id int primary key
|
||||
, due_date timestamptz
|
||||
);
|
||||
|
||||
Suppose you are located in Sydney and want create a report with the date in the local time zone. Your request should look like this:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /reports HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
[{ "id": 1, "due_date": "2022-02-24 11:10:15 Australia/Sydney" },
|
||||
{ "id": 2, "due_date": "2022-02-27 22:00:00 Australia/Sydney" }]
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/reports" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d '[{ "id": 1, "due_date": "2022-02-24 11:10:15 Australia/Sydney" },{ "id": 2, "due_date": "2022-02-27 22:00:00 Australia/Sydney" }]'
|
||||
|
||||
Someone located in Cairo can retrieve the data using their local time, too:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /reports?due_date=eq.2022-02-24+02:10:15+Africa/Cairo HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/reports?due_date=eq.2022-02-24+02:10:15+Africa/Cairo"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"due_date": "2022-02-23T19:10:15-05:00"
|
||||
}
|
||||
]
|
||||
|
||||
The response has the date in the time zone configured by the server: ``UTC -05:00``.
|
||||
|
||||
You can use other comparative filters and also all the `PostgreSQL special date/time input values <https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-DATETIME-SPECIAL-TABLE>`_ as illustrated in this example:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /reports?or=(and(due_date.gte.today,due_date.lte.tomorrow),and(due_date.gt.-infinity,due_date.lte.epoch)) HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/reports?or=(and(due_date.gte.today,due_date.lte.tomorrow),and(due_date.gt.-infinity,due_date.lte.epoch))"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"id": 2,
|
||||
"due_date": "2022-02-27T06:00:00-05:00"
|
||||
}
|
||||
]
|
||||
|
||||
JSON
|
||||
----
|
||||
|
||||
To work with a ``json`` type column, you can handle the value as a JSON object.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create table products (
|
||||
id int primary key,
|
||||
name text unique,
|
||||
extra_info json
|
||||
);
|
||||
|
||||
You can insert a new product using a JSON object for the ``extra_info`` column:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /products HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Canned fish",
|
||||
"extra_info": {
|
||||
"expiry_date": "2025-12-31",
|
||||
"exportable": true
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/products" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d @- << EOF
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Canned fish",
|
||||
"extra_info": {
|
||||
"expiry_date": "2025-12-31",
|
||||
"exportable": true
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
To query and filter the data see :ref:`json_columns` for a complete reference.
|
||||
|
||||
Arrays
|
||||
------
|
||||
|
||||
To handle `array types <https://www.postgresql.org/docs/current/arrays.html>`_ you can use string representation or JSON array format.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create table movies (
|
||||
id int primary key,
|
||||
title text not null,
|
||||
tags text[],
|
||||
performance_times time[]
|
||||
);
|
||||
|
||||
You can insert a new value using string representation.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /movies HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Paddington",
|
||||
"tags": "{family,comedy,not streamable}",
|
||||
"performance_times": "{12:40,15:00,20:00}"
|
||||
}
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/movies" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d @- << EOF
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Paddington",
|
||||
"tags": "{family,comedy,not streamable}",
|
||||
"performance_times": "{12:40,15:00,20:00}"
|
||||
}
|
||||
EOF
|
||||
|
||||
Or you could send the same data using JSON array format:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /movies HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Paddington",
|
||||
"tags": ["family", "comedy", "not streamable"],
|
||||
"performance_times": ["12:40", "15:00", "20:00"]
|
||||
}
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/movies" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d @- << EOF
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Paddington",
|
||||
"tags": ["family", "comedy", "not streamable"],
|
||||
"performance_times": ["12:40", "15:00", "20:00"]
|
||||
}
|
||||
EOF
|
||||
|
||||
To query the data you can use arrow operators. See :ref:`composite_array_columns`.
|
||||
|
||||
Multidimensional Arrays
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Similarly to one-dimensional arrays, both the string representation and JSON array format are allowed.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- This new column stores the cinema, floor and auditorium numbers in that order
|
||||
alter table movies
|
||||
add column cinema_floor_auditorium int[][][];
|
||||
|
||||
You can now update the item using JSON array format:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
PATCH /movies?id=eq.1 HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"cinema_floor_auditorium": [ [ [1,2], [6,7] ], [ [3,5], [8,9] ] ]
|
||||
}
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/movies?id=eq.1" \
|
||||
-X PATCH -H "Content-Type: application/json" \
|
||||
-d @- << EOF
|
||||
{
|
||||
"cinema_floor_auditorium": [ [ [1,2], [6,7] ], [ [3,5], [8,9] ] ]
|
||||
}
|
||||
EOF
|
||||
|
||||
Then, for example, to query the auditoriums that are located in the first cinema (position 0 in the array) and on the second floor (position 1 in the next inner array), we can use the arrow operators this way:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /movies?select=title,auditorium:cinema_floor_auditorium->0->1&id=eq.1 HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/movies?select=title,auditorium:cinema_floor_auditorium->0->1&id=eq.1"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"title": "Paddington",
|
||||
"auditorium": [6,7]
|
||||
}
|
||||
]
|
||||
|
||||
Composite Types
|
||||
---------------
|
||||
|
||||
With PostgREST, you have two options to handle `composite type columns <https://www.postgresql.org/docs/current/rowtypes.html>`_.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create type dimension as (
|
||||
length decimal(6,2),
|
||||
width decimal (6,2),
|
||||
height decimal (6,2),
|
||||
unit text
|
||||
);
|
||||
|
||||
create table products (
|
||||
id int primary key,
|
||||
size dimension
|
||||
);
|
||||
|
||||
insert into products (id, size)
|
||||
values (1, '(5.0,5.0,10.0,"cm")');
|
||||
|
||||
On one hand you can insert values using string representation.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /products HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{ "id": 2, "size": "(0.7,0.5,1.8,\"m\")" }
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/products" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d @- << EOF
|
||||
{ "id": 2, "size": "(0.7,0.5,1.8,\"m\")" }
|
||||
EOF
|
||||
|
||||
Or you could insert the same data in JSON format.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /products HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 2,
|
||||
"size": {
|
||||
"length": 0.7,
|
||||
"width": 0.5,
|
||||
"height": 1.8,
|
||||
"unit": "m"
|
||||
}
|
||||
}
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/products" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d @- << EOF
|
||||
{
|
||||
"id": 2,
|
||||
"size": {
|
||||
"length": 0.7,
|
||||
"width": 0.5,
|
||||
"height": 1.8,
|
||||
"unit": "m"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
You can also query the data using arrow operators. See :ref:`composite_array_columns`.
|
||||
|
||||
Ranges
|
||||
------
|
||||
|
||||
PostgREST allows you to handle `ranges <https://www.postgresql.org/docs/current/rangetypes.html>`_.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create table events (
|
||||
id int primary key,
|
||||
name text unique,
|
||||
duration tsrange
|
||||
);
|
||||
|
||||
To insert a new event, specify the ``duration`` value as a string representation of the ``tsrange`` type:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /events HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"name": "New Year's Party",
|
||||
"duration": "['2022-12-31 11:00','2023-01-01 06:00']"
|
||||
}
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/events" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d @- << EOF
|
||||
{
|
||||
"id": 1,
|
||||
"name": "New Year's Party",
|
||||
"duration": "['2022-12-31 11:00','2023-01-01 06:00']"
|
||||
}
|
||||
EOF
|
||||
|
||||
You can use range :ref:`operators <operators>` to filter the data. But, in this case, requesting a filter like ``events?duration=cs.2023-01-01`` will return an error, because PostgreSQL needs an explicit cast from string to timestamp. A workaround is to use a range starting and ending in the same date:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /events?duration=cs.[2023-01-01,2023-01-01] HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/events?duration=cs.\[2023-01-01,2023-01-01\]"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "New Year's Party",
|
||||
"duration": "[\"2022-12-31 11:00:00\",\"2023-01-01 06:00:00\"]"
|
||||
}
|
||||
]
|
||||
|
||||
.. _casting_range_to_json:
|
||||
|
||||
Casting a Range to a JSON Object
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As you may have noticed, the ``tsrange`` value is returned as a string literal. To return it as a JSON value, first you need to create a function that will do the conversion from a ``tsrange`` type:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create or replace function tsrange_to_json(tsrange) returns json as $$
|
||||
select json_build_object(
|
||||
'lower', lower($1)
|
||||
, 'upper', upper($1)
|
||||
, 'lower_inc', lower_inc($1)
|
||||
, 'upper_inc', upper_inc($1)
|
||||
);
|
||||
$$ language sql;
|
||||
|
||||
Then, create the cast using this function:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create cast (tsrange as json) with function tsrange_to_json(tsrange) as assignment;
|
||||
|
||||
Finally, do the request :ref:`casting the range column <casting_columns>`:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /events?select=id,name,duration::json HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/events?select=id,name,duration::json"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "New Year's Party",
|
||||
"duration": {
|
||||
"lower": "2022-12-31T11:00:00",
|
||||
"upper": "2023-01-01T06:00:00",
|
||||
"lower_inc": true,
|
||||
"upper_inc": true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
.. note::
|
||||
|
||||
If you don't want to modify casts for built-in types, an option would be to `create a custom type <https://www.postgresql.org/docs/current/sql-createtype.html>`_
|
||||
for your own ``tsrange`` and add its own cast.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create type mytsrange as range (subtype = timestamp, subtype_diff = tsrange_subdiff);
|
||||
|
||||
-- define column types and casting function analogously to the above example
|
||||
-- ...
|
||||
|
||||
create cast (mytsrange as json) with function mytsrange_to_json(mytsrange) as assignment;
|
||||
|
||||
Bytea
|
||||
-----
|
||||
|
||||
To send raw binary to PostgREST you need a function with a single unnamed parameter of `bytea type <https://www.postgresql.org/docs/current/datatype-binary.html>`_.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create table files (
|
||||
id int primary key generated always as identity,
|
||||
file bytea
|
||||
);
|
||||
|
||||
create function upload_binary(bytea) returns void as $$
|
||||
insert into files (file) values ($1);
|
||||
$$ language sql;
|
||||
|
||||
Let's download the PostgREST logo for our test.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "https://postgrest.org/en/latest/_images/logo.png" -o postgrest-logo.png
|
||||
|
||||
Now, to send the file ``postgrest-logo.png`` we need to set the ``Content-Type: application/octet-stream`` header in the request:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /rpc/upload_binary HTTP/1.1
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
postgrest-logo.png
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/rpc/upload_binary" \
|
||||
-X POST -H "Content-Type: application/octet-stream" \
|
||||
--data-binary "@postgrest-logo.png"
|
||||
|
||||
To get the image from the database, set the ``Accept: application/octet-stream`` header and select only the
|
||||
``bytea`` type column.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /files?select=file&id=eq.1 HTTP/1.1
|
||||
Accept: application/octet-stream
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/files?select=file&id=eq.1" \
|
||||
-H "Accept: application/octet-stream"
|
||||
|
||||
Use more accurate headers according to the type of the files by using the :ref:`raw-media-types` configuration. For example, adding the ``raw-media-types="image/png"`` setting to the configuration file will allow you to use the ``Accept: image/png`` header:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /files?select=file&id=eq.1 HTTP/1.1
|
||||
Accept: image/png
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/files?select=file&id=eq.1" \
|
||||
-H "Accept: image/png"
|
||||
|
||||
See :ref:`providing_img` for a step-by-step example on how to handle images in HTML.
|
||||
|
||||
.. warning::
|
||||
|
||||
Be careful when saving binaries in the database, having a separate storage service for these is preferable in most cases. See `Storing Binary files in the Database <https://wiki.postgresql.org/wiki/BinaryFilesInDB>`_.
|
||||
|
||||
hstore
|
||||
------
|
||||
|
||||
You can work with data types belonging to additional supplied modules such as `hstore <https://www.postgresql.org/docs/current/hstore.html>`_.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- Activate the hstore module in the current database
|
||||
create extension if not exists hstore;
|
||||
|
||||
create table countries (
|
||||
id int primary key,
|
||||
name hstore unique
|
||||
);
|
||||
|
||||
The ``name`` column will have the name of the country in different formats. You can insert values using the string representation for that data type:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /countries HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{ "id": 1, "name": "common => Egypt, official => \"Arab Republic of Egypt\", native => مصر" },
|
||||
{ "id": 2, "name": "common => Germany, official => \"Federal Republic of Germany\", native => Deutschland" }
|
||||
]
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/countries" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-d @- << EOF
|
||||
[
|
||||
{ "id": 1, "name": "common => Egypt, official => \"Arab Republic of Egypt\", native => مصر" },
|
||||
{ "id": 2, "name": "common => Germany, official => \"Federal Republic of Germany\", native => Deutschland" }
|
||||
]
|
||||
EOF
|
||||
|
||||
Notice that the use of ``"`` in the value of the ``name`` column needs to be escaped using a backslash ``\``.
|
||||
|
||||
You can also query and filter the value of a ``hstore`` column using the arrow operators, as you would do for a :ref:`JSON column<json_columns>`. For example, if you want to get the native name of Egypt:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /countries?select=name->>native&name->>common=like.Egypt HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/countries?select=name->>native&name->>common=like.Egypt"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[{ "native": "مصر" }]
|
||||
|
||||
.. _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/install/>`_ 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:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
POST /coverage HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{ "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))" }
|
||||
]
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
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>`_:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /coverage HTTP/1.1
|
||||
Accept: application/geo+json
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
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:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /rpc/coverage_geo_collection HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
.. title:: PostgREST Documentation
|
||||
|
||||
PostgREST Documentation
|
||||
=======================
|
||||
|
||||
.. container:: image-container
|
||||
|
||||
.. figure:: _static/logo.png
|
||||
|
||||
.. image:: https://img.shields.io/github/stars/postgrest/postgrest.svg?style=social
|
||||
:target: https://github.com/PostgREST/postgrest
|
||||
|
||||
.. image:: https://img.shields.io/github/v/release/PostgREST/postgrest.svg
|
||||
:target: https://github.com/PostgREST/postgrest/releases
|
||||
|
||||
.. image:: https://img.shields.io/docker/pulls/postgrest/postgrest.svg
|
||||
:target: https://hub.docker.com/r/postgrest/postgrest/
|
||||
|
||||
.. image:: https://img.shields.io/badge/Donate-Patreon-orange.svg?colorB=F96854
|
||||
:target: https://www.patreon.com/postgrest
|
||||
|
||||
|
|
||||
|
||||
PostgREST is a standalone web server that turns your PostgreSQL database directly into a RESTful API. The structural constraints and permissions in the database determine the API endpoints and operations.
|
||||
|
||||
Sponsors
|
||||
--------
|
||||
|
||||
.. container:: image-container
|
||||
|
||||
.. image:: _static/cybertec-new.png
|
||||
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: _static/2ndquadrant.png
|
||||
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
|
||||
:width: 13em
|
||||
|
||||
.. image:: _static/retool.png
|
||||
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: _static/gnuhost.png
|
||||
:target: https://euronodes.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: _static/supabase.png
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
:width: 13em
|
||||
|
||||
.. image:: _static/oblivious.jpg
|
||||
:target: https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. 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.
|
||||
|
||||
.. .. image:: _static/empty.png
|
||||
:target: #sponsors
|
||||
:width: 13em
|
||||
|
||||
|
|
||||
|
||||
Motivation
|
||||
----------
|
||||
|
||||
Using PostgREST is an alternative to manual CRUD programming. Custom API servers suffer problems. Writing business logic often duplicates, ignores or hobbles database structure. Object-relational mapping is a leaky abstraction leading to slow imperative code. The PostgREST philosophy establishes a single declarative source of truth: the data itself.
|
||||
|
||||
Declarative Programming
|
||||
-----------------------
|
||||
|
||||
It's easier to ask PostgreSQL to join data for you and let its query planner figure out the details than to loop through rows yourself. It's easier to assign permissions to db objects than to add guards in controllers. (This is especially true for cascading permissions in data dependencies.) It's easier to set constraints than to litter code with sanity checks.
|
||||
|
||||
Leak-proof Abstraction
|
||||
----------------------
|
||||
|
||||
There is no ORM involved. Creating new views happens in SQL with known performance implications. A database administrator can now create an API from scratch with no custom programming.
|
||||
|
||||
One Thing Well
|
||||
--------------
|
||||
|
||||
PostgREST has a focused scope. It works well with other tools like Nginx. This forces you to cleanly separate the data-centric CRUD operations from other concerns. Use a collection of sharp tools rather than building a big ball of mud.
|
||||
|
||||
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.
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
:caption: Release Notes
|
||||
:titlesonly:
|
||||
:hidden:
|
||||
|
||||
v10.2.0 <releases/v10.2.0>
|
||||
v10.0.0 <releases/v10.0.0>
|
||||
v9.0.1 <releases/v9.0.1>
|
||||
v9.0.0 <releases/v9.0.0>
|
||||
releases/v8.0.0
|
||||
releases/v7.0.1
|
||||
releases/v7.0.0
|
||||
releases/v6.0.2
|
||||
releases/v5.2.0
|
||||
|
||||
Tutorials
|
||||
---------
|
||||
|
||||
Are you new to PostgREST? This is the place to start!
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
:caption: Tutorials
|
||||
:hidden:
|
||||
|
||||
tutorials/*
|
||||
|
||||
- :doc:`tutorials/tut0`
|
||||
- :doc:`tutorials/tut1`
|
||||
|
||||
Also have a look at :doc:`Installation <install>` and :ref:`community_tutorials`.
|
||||
|
||||
Reference guides
|
||||
----------------
|
||||
|
||||
Technical references for PostgREST's functionality.
|
||||
|
||||
.. toctree::
|
||||
:caption: API
|
||||
:hidden:
|
||||
|
||||
api.rst
|
||||
|
||||
.. toctree::
|
||||
:caption: Configuration
|
||||
:hidden:
|
||||
|
||||
configuration.rst
|
||||
|
||||
.. toctree::
|
||||
:caption: Schema Cache
|
||||
:hidden:
|
||||
|
||||
schema_cache.rst
|
||||
|
||||
.. toctree::
|
||||
:caption: Errors
|
||||
:hidden:
|
||||
|
||||
errors.rst
|
||||
|
||||
- :doc:`API <api>`
|
||||
- :doc:`configuration`
|
||||
- :doc:`Schema Cache <schema_cache>`
|
||||
- :doc:`Errors <errors>`
|
||||
|
||||
Topic guides
|
||||
------------
|
||||
|
||||
Explanations of some key concepts in PostgREST.
|
||||
|
||||
.. toctree::
|
||||
:caption: Authentication
|
||||
:hidden:
|
||||
|
||||
auth.rst
|
||||
|
||||
.. toctree::
|
||||
:caption: Schema Structure
|
||||
:hidden:
|
||||
|
||||
schema_structure.rst
|
||||
|
||||
.. toctree::
|
||||
:caption: Administration
|
||||
:hidden:
|
||||
|
||||
admin.rst
|
||||
|
||||
.. toctree::
|
||||
:caption: Installation
|
||||
:hidden:
|
||||
|
||||
install.rst
|
||||
|
||||
- :doc:`Authentication <auth>`
|
||||
- :doc:`Schema Structure <schema_structure>`
|
||||
- :doc:`Administration <admin>`
|
||||
- :doc:`Installation <install>`
|
||||
|
||||
.. _how_tos:
|
||||
|
||||
How-to guides
|
||||
-------------
|
||||
|
||||
These are recipes that'll help you address specific use-cases.
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
:caption: How-to guides
|
||||
:hidden:
|
||||
|
||||
how-tos/working-with-postgresql-data-types
|
||||
how-tos/providing-images-for-img
|
||||
how-tos/create-soap-endpoint
|
||||
how-tos/sql-user-management-using-postgres-users-and-passwords
|
||||
|
||||
- :doc:`how-tos/providing-images-for-img`
|
||||
- :doc:`how-tos/working-with-postgresql-data-types`
|
||||
- :doc:`how-tos/create-soap-endpoint`
|
||||
- :doc:`how-tos/sql-user-management-using-postgres-users-and-passwords`
|
||||
|
||||
Ecosystem
|
||||
---------
|
||||
|
||||
PostgREST has a growing ecosystem of examples, libraries, and experiments. Here is a selection.
|
||||
|
||||
.. toctree::
|
||||
:caption: Ecosystem
|
||||
:hidden:
|
||||
|
||||
ecosystem.rst
|
||||
|
||||
* :ref:`community_tutorials`
|
||||
* :ref:`templates`
|
||||
* :ref:`eco_example_apps`
|
||||
* :ref:`devops`
|
||||
* :ref:`eco_external_notification`
|
||||
* :ref:`eco_extensions`
|
||||
* :ref:`clientside_libraries`
|
||||
|
||||
|
||||
Release Notes
|
||||
-------------
|
||||
|
||||
Changes among versions.
|
||||
|
||||
- :doc:`releases/v9.0.0`
|
||||
- :doc:`releases/v8.0.0`
|
||||
|
||||
In Production
|
||||
-------------
|
||||
|
||||
Here are some companies that use PostgREST in production.
|
||||
|
||||
* `Catarse <https://www.catarse.me>`_
|
||||
* `Datrium <https://www.datrium.com>`_
|
||||
* `Drip Depot <https://www.dripdepot.com>`_
|
||||
* `Image-charts <https://www.image-charts.com>`_
|
||||
* `Netwo <https://www.netwo.io>`_
|
||||
* `Nimbus <https://www.nimbusforwork.com>`_
|
||||
- See how Nimbus uses PostgREST in `Paul Copplestone's blog post <https://paul.copplest.one/blog/nimbus-tech-2019-04.html>`_.
|
||||
* `OpenBooking <https://www.openbooking.ch>`_
|
||||
* `Supabase <https://supabase.com>`_
|
||||
|
||||
Testimonials
|
||||
------------
|
||||
|
||||
"It's so fast to develop, it feels like cheating!"
|
||||
|
||||
-- François-Guillaume Ribreau
|
||||
|
||||
"I just have to say that, the CPU/Memory usage compared to our
|
||||
Node.js/Waterline ORM based API is ridiculous. It's hard to even push
|
||||
it over 60/70 MB while our current API constantly hits 1GB running on 6
|
||||
instances (dynos)."
|
||||
|
||||
-- Louis Brauer
|
||||
|
||||
"I really enjoyed the fact that all of a sudden I was writing
|
||||
microservices in SQL DDL (and v8 JavaScript functions). I dodged so
|
||||
much boilerplate. The next thing I knew, we pulled out a full rewrite
|
||||
of a Spring+MySQL legacy app in 6 months. Literally 10x faster, and
|
||||
code was super concise. The old one took 3 years and a team of 4
|
||||
people to develop."
|
||||
|
||||
-- Simone Scarduzio
|
||||
|
||||
"I like the fact that PostgREST does one thing, and one thing well.
|
||||
While PostgREST takes care of bridging the gap between our HTTP server
|
||||
and PostgreSQL database, we can focus on the development of our API in
|
||||
a single language: SQL. This puts the database in the center of our
|
||||
architecture, and pushed us to improve our skills in SQL programming
|
||||
and database design."
|
||||
|
||||
-- Eric Bréchemier, Data Engineer, eGull SAS
|
||||
|
||||
"PostgREST is performant, stable, and transparent. It allows us to
|
||||
bootstrap projects really fast, and to focus on our data and application
|
||||
instead of building out the ORM layer. In our k8s cluster, we run a few
|
||||
pods per schema we want exposed, and we scale up/down depending on demand.
|
||||
Couldn't be happier."
|
||||
|
||||
-- Anupam Garg, Datrium, Inc.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Please see the `Contributing guidelines <https://github.com/PostgREST/postgrest/blob/main/.github/CONTRIBUTING.md>`_ in the main PostgREST repository.
|
||||
@@ -0,0 +1,381 @@
|
||||
.. _install:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
The release page has `pre-compiled binaries for Mac OS X, Windows, Linux and FreeBSD <https://github.com/PostgREST/postgrest/releases/latest>`_ .
|
||||
The Linux binary is a static executable that can be run on any Linux distribution.
|
||||
|
||||
You can also use your OS package manager.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Mac OSX
|
||||
|
||||
You can install PostgREST from the `Homebrew official repo <https://formulae.brew.sh/formula/postgrest>`_.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
brew install postgrest
|
||||
|
||||
.. group-tab:: FreeBSD
|
||||
|
||||
You can install PostgREST from the `official ports <https://www.freshports.org/www/hs-postgrest>`_.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
pkg install hs-postgrest
|
||||
|
||||
.. group-tab:: Linux
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. tab:: Arch Linux
|
||||
|
||||
You can install PostgREST from the `community repo <https://archlinux.org/packages/community/x86_64/postgrest>`_.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
pacman -S postgrest
|
||||
|
||||
.. tab:: Nix
|
||||
|
||||
You can install PostgREST from nixpkgs.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
nix-env -i haskellPackages.postgrest
|
||||
|
||||
.. group-tab:: Windows
|
||||
|
||||
You can install PostgREST using `Chocolatey <https://community.chocolatey.org/packages/postgrest>`_ or `Scoop <https://github.com/ScoopInstaller/Scoop>`_.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
choco install postgrest
|
||||
scoop install postgrest
|
||||
|
||||
Running PostgREST
|
||||
=================
|
||||
|
||||
If you downloaded PostgREST from the release page, first extract the compressed file to obtain the executable.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# For UNIX platforms
|
||||
tar Jxf postgrest-[version]-[platform].tar.xz
|
||||
|
||||
# On Windows you should unzip the file
|
||||
|
||||
Now you can run PostgREST with the :code:`--help` flag to see usage instructions:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Running postgrest binary
|
||||
./postgrest --help
|
||||
|
||||
# Running postgrest installed from a package manager
|
||||
postgrest --help
|
||||
|
||||
# You should see a usage help message
|
||||
|
||||
The PostgREST server reads a configuration file as its only argument:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
postgrest /path/to/postgrest.conf
|
||||
|
||||
# You can also generate a sample config file with
|
||||
# postgrest -e > postgrest.conf
|
||||
# You'll need to edit this file and remove the usage parts for postgrest to read it
|
||||
|
||||
For a complete reference of the configuration file, see :ref:`configuration`.
|
||||
|
||||
.. note::
|
||||
|
||||
If you see a dialog box like this on Windows, it may be that the :code:`pg_config` program is not in your system path.
|
||||
|
||||
.. image:: _static/win-err-dialog.png
|
||||
|
||||
It usually lives in :code:`C:\Program Files\PostgreSQL\<version>\bin`. See this `article <https://www.howtogeek.com/118594/how-to-edit-your-system-path-for-easy-command-line-access/>`_ about how to modify the system path.
|
||||
|
||||
To test that the system path is set correctly, run ``pg_config`` from the command line. You should see it output a list of paths.
|
||||
|
||||
.. _pg-dependency:
|
||||
|
||||
PostgreSQL dependency
|
||||
---------------------
|
||||
|
||||
To use PostgREST you will need an underlying database. We require PostgreSQL 9.6 or greater. You can use something like `Amazon RDS <https://aws.amazon.com/rds/>`_ but installing your own locally is cheaper and more convenient for development. You can also run PostgreSQL in a :ref:`docker container<pg-in-docker>`.
|
||||
|
||||
Docker
|
||||
======
|
||||
|
||||
You can get the `official PostgREST Docker image <https://hub.docker.com/r/postgrest/postgrest>`_ with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker pull postgrest/postgrest
|
||||
|
||||
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.
|
||||
|
||||
Containerized PostgREST with native PostgreSQL
|
||||
----------------------------------------------
|
||||
|
||||
The first way to run PostgREST in Docker is to connect it to an existing native database on the host.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Run the server
|
||||
docker run --rm --net=host \
|
||||
-e PGRST_DB_URI="postgres://app_user:password@localhost/postgres" \
|
||||
postgrest/postgrest
|
||||
|
||||
The database connection string above is just an example. Adjust the role and password as necessary. You may need to edit PostgreSQL's :code:`pg_hba.conf` to grant the user local login access.
|
||||
|
||||
.. note::
|
||||
|
||||
Docker on Mac does not support the :code:`--net=host` flag. Instead you'll need to create an IP address alias to the host. Requests for the IP address from inside the container are unable to resolve and fall back to resolution by the host.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo ifconfig lo0 10.0.0.10 alias
|
||||
|
||||
You should then use 10.0.0.10 as the host in your database connection string. Also remember to include the IP address in the :code:`listen_address` within postgresql.conf. For instance:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
listen_addresses = 'localhost,10.0.0.10'
|
||||
|
||||
You might also need to add a new IPv4 local connection within pg_hba.conf. For instance:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
host all all 10.0.0.10/32 trust
|
||||
|
||||
The docker command will then look like this:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Run the server
|
||||
docker run --rm -p 3000:3000 \
|
||||
-e PGRST_DB_URI="postgres://app_user:password@10.0.0.10/postgres" \
|
||||
postgrest/postgrest
|
||||
|
||||
.. _pg-in-docker:
|
||||
|
||||
Containerized PostgREST *and* db with docker-compose
|
||||
----------------------------------------------------
|
||||
|
||||
To avoid having to install the database at all, you can run both it and the server in containers and link them together with docker-compose. Use this configuration:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# docker-compose.yml
|
||||
|
||||
version: '3'
|
||||
services:
|
||||
server:
|
||||
image: postgrest/postgrest
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
PGRST_DB_URI: postgres://app_user:password@db:5432/app_db
|
||||
PGRST_OPENAPI_SERVER_PROXY_URI: http://127.0.0.1:3000
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
image: postgres
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_DB: app_db
|
||||
POSTGRES_USER: app_user
|
||||
POSTGRES_PASSWORD: password
|
||||
# Uncomment this if you want to persist the data.
|
||||
# volumes:
|
||||
# - "./pgdata:/var/lib/postgresql/data"
|
||||
|
||||
Go into the directory where you saved this file and run :code:`docker-compose up`. You will see the logs of both the database and PostgREST, and be able to access the latter on port 3000.
|
||||
|
||||
If you want to have a visual overview of your API in your browser you can add swagger-ui to your :code:`docker-compose.yml`:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
swagger:
|
||||
image: swaggerapi/swagger-ui
|
||||
ports:
|
||||
- "8080:8080"
|
||||
expose:
|
||||
- "8080"
|
||||
environment:
|
||||
API_URL: http://localhost:3000/
|
||||
|
||||
With this you can see the swagger-ui in your browser on port 8080.
|
||||
|
||||
.. _build_source:
|
||||
|
||||
Building from Source
|
||||
====================
|
||||
|
||||
When a pre-built binary does not exist for your system you can build the project from source.
|
||||
|
||||
.. note::
|
||||
|
||||
We discourage building and using PostgREST on **Alpine Linux** because of a reported GHC memory leak on that platform.
|
||||
|
||||
You can build PostgREST from source with `Stack <https://github.com/commercialhaskell/stack>`_. It will install any necessary Haskell dependencies on your system.
|
||||
|
||||
* `Install Stack <https://docs.haskellstack.org/en/stable/#how-to-install-stack>`_ for your platform
|
||||
* Install Library Dependencies
|
||||
|
||||
===================== =======================================
|
||||
Operating System Dependencies
|
||||
===================== =======================================
|
||||
Ubuntu/Debian libpq-dev, libgmp-dev, zlib1g-dev
|
||||
CentOS/Fedora/Red Hat postgresql-devel, zlib-devel, gmp-devel
|
||||
BSD postgresql12-client
|
||||
OS X libpq, gmp
|
||||
===================== =======================================
|
||||
|
||||
* Build and install binary
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://github.com/PostgREST/postgrest.git
|
||||
cd postgrest
|
||||
|
||||
# adjust local-bin-path to taste
|
||||
stack build --install-ghc --copy-bins --local-bin-path /usr/local/bin
|
||||
|
||||
.. note::
|
||||
|
||||
- If building fails and your system has less than 1GB of memory, try adding a swap file.
|
||||
- `--install-ghc` flag is only needed for the first build and can be omitted in the subsequent builds.
|
||||
|
||||
* Check that the server is installed: :code:`postgrest --help`.
|
||||
|
||||
.. _deploy_heroku:
|
||||
|
||||
Deploying to Heroku
|
||||
===================
|
||||
|
||||
1. Log into Heroku using the `Heroku CLI <https://devcenter.heroku.com/articles/heroku-cli>`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# If you have multiple Heroku accounts, use flag '--interactive' to switch between them
|
||||
heroku login --interactive
|
||||
|
||||
|
||||
2. Create a new Heroku app using the PostgREST buildpack:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir ${YOUR_APP_NAME}
|
||||
cd ${YOUR_APP_NAME}
|
||||
git init .
|
||||
|
||||
heroku apps:create ${YOUR_APP_NAME} --buildpack https://github.com/PostgREST/postgrest-heroku.git
|
||||
heroku git:remote -a ${YOUR_APP_NAME}
|
||||
|
||||
3. Create a new Heroku PostgreSQL add-on attached to the app and keep notes of the assigned add-on name (e.g. :code:`postgresql-curly-58902`) referred later as ${HEROKU_PG_DB_NAME}
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
heroku addons:create heroku-postgresql:standard-0 -a ${YOUR_APP_NAME}
|
||||
# wait until the add-on is available
|
||||
heroku pg:wait -a ${YOUR_APP_NAME}
|
||||
|
||||
4. Create the necessary user roles according to the
|
||||
`PostgREST documentation <https://postgrest.org/en/stable/auth.html>`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
heroku pg:credentials:create --name api_user -a ${YOUR_APP_NAME}
|
||||
# use the following command to ensure the new credential state is active before attaching it
|
||||
heroku pg:credentials -a ${YOUR_APP_NAME}
|
||||
|
||||
heroku addons:attach ${HEROKU_PG_DB_NAME} --credential api_user -a ${YOUR_APP_NAME}
|
||||
|
||||
5. Connect to the PostgreSQL database and create some sample data:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
heroku psql -a ${YOUR_APP_NAME}
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
# from the psql command prompt execute the following commands:
|
||||
create schema api;
|
||||
|
||||
create table api.todos (
|
||||
id serial primary key,
|
||||
done boolean not null default false,
|
||||
task text not null,
|
||||
due timestamptz
|
||||
);
|
||||
|
||||
insert into api.todos (task) values
|
||||
('finish tutorial 0'), ('pat self on back');
|
||||
|
||||
grant usage on schema api to api_user;
|
||||
grant select on api.todos to api_user;
|
||||
|
||||
6. Create the :code:`Procfile`:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
web: PGRST_SERVER_HOST=0.0.0.0 PGRST_SERVER_PORT=${PORT} PGRST_DB_URI=${PGRST_DB_URI:-${DATABASE_URL}} ./postgrest-${POSTGREST_VER}
|
||||
..
|
||||
|
||||
Set the following environment variables on Heroku:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
heroku config:set POSTGREST_VER=10.0.0
|
||||
heroku config:set PGRST_DB_SCHEMA=api
|
||||
heroku config:set PGRST_DB_ANON_ROLE=api_user
|
||||
..
|
||||
|
||||
PGRST_DB_URI can be set if an external database is used or if it's different from the default Heroku DATABASE_URL. This latter is used if nothing is provided.
|
||||
POSTGREST_VER is mandatory to select and build the required PostgREST release.
|
||||
|
||||
See https://postgrest.org/en/stable/configuration.html#environment-variables for the full list of environment variables.
|
||||
|
||||
7. Build and deploy your app:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git add Procfile
|
||||
git commit -m "PostgREST on Heroku"
|
||||
git push heroku master
|
||||
..
|
||||
|
||||
Your Heroku app should be live at :code:`${YOUR_APP_NAME}.herokuapp.com`
|
||||
|
||||
8. Test your app
|
||||
|
||||
From a terminal display the application logs:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
heroku logs -t
|
||||
..
|
||||
|
||||
From a different terminal retrieve with curl the records previously created:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl https://${YOUR_APP_NAME}.herokuapp.com/todos
|
||||
..
|
||||
|
||||
and test that any attempt to modify the table via a read-only user is not allowed:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl https://${YOUR_APP_NAME}.herokuapp.com/todos -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"task": "do bad thing"}'
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
from livereload import Server, shell
|
||||
from subprocess import call
|
||||
|
||||
## Build docs at startup
|
||||
call(["sphinx-build", "-b", "html", "-a", "-n", ".", "_build"])
|
||||
server = Server()
|
||||
server.watch("**/*.rst", shell("sphinx-build -b html -a -n . _build"))
|
||||
# For custom port and host
|
||||
# server.serve(root='_build/', host='192.168.1.2')
|
||||
server.serve(root="_build/")
|
||||
@@ -0,0 +1,209 @@
|
||||
personal_ws-1.1 en 0 utf-8
|
||||
Adossi
|
||||
AMQP
|
||||
api
|
||||
API's
|
||||
Archlinux
|
||||
aud
|
||||
Auth
|
||||
auth
|
||||
authenticator
|
||||
backoff
|
||||
balancer
|
||||
Beles
|
||||
booleans
|
||||
Bouscal
|
||||
buildpack
|
||||
Bytea
|
||||
Cardano
|
||||
cd
|
||||
centric
|
||||
changelog
|
||||
ClojureScript
|
||||
cloudfared
|
||||
config
|
||||
CORS
|
||||
CPUs
|
||||
cryptographically
|
||||
CSV
|
||||
Daemonizing
|
||||
DDL
|
||||
DevOps
|
||||
DiBiase
|
||||
dockerize
|
||||
DoS
|
||||
eq
|
||||
ETH
|
||||
Ethereum
|
||||
EveryLayout
|
||||
Fenko
|
||||
Fernandes
|
||||
filename
|
||||
FreeBSD
|
||||
fts
|
||||
GC
|
||||
GeoJSON
|
||||
GHC
|
||||
Github
|
||||
Google
|
||||
grantor
|
||||
GraphQL
|
||||
gte
|
||||
GUC
|
||||
GUCs
|
||||
gucs
|
||||
Gumbs
|
||||
Haskell
|
||||
Heroku
|
||||
HMAC
|
||||
Homebrew
|
||||
hstore
|
||||
HTTP
|
||||
HTTPS
|
||||
HV
|
||||
Ibarluzea
|
||||
ilike
|
||||
imatch
|
||||
io
|
||||
IP
|
||||
JS
|
||||
js
|
||||
JSON
|
||||
JWK
|
||||
JWT
|
||||
jwt
|
||||
JWTs
|
||||
Kinesis
|
||||
Kofi
|
||||
Kubernetes
|
||||
localhost
|
||||
login
|
||||
Logins
|
||||
logins
|
||||
lon
|
||||
lt
|
||||
lte
|
||||
middleware
|
||||
misprediction
|
||||
Mithril
|
||||
multi
|
||||
MVCC
|
||||
namespace
|
||||
namespaced
|
||||
neq
|
||||
nginx
|
||||
ngrep
|
||||
nixpkgs
|
||||
npm
|
||||
nxl
|
||||
nxr
|
||||
OAuth
|
||||
onwards
|
||||
OpenAPI
|
||||
openapi
|
||||
ORM
|
||||
ov
|
||||
passphrase
|
||||
Pawel
|
||||
PBKDF
|
||||
Pelletier
|
||||
Petr
|
||||
PgBouncer
|
||||
pgcrypto
|
||||
pgjwt
|
||||
pgrst
|
||||
pgrstX
|
||||
PGRSTX
|
||||
pgSQL
|
||||
authid
|
||||
phfts
|
||||
phraseto
|
||||
plainto
|
||||
plfts
|
||||
poolers
|
||||
POSIX
|
||||
PostGIS
|
||||
PostgreSQL
|
||||
PostgreSQL's
|
||||
PostgREST
|
||||
postgres
|
||||
postgrest
|
||||
PostgREST's
|
||||
pre
|
||||
preflight
|
||||
psql
|
||||
Qin
|
||||
RabbitMQ
|
||||
Rafaj
|
||||
RDS
|
||||
reallyreallyreallyreallyverysafe
|
||||
Rechkemmer
|
||||
reconnection
|
||||
Redux
|
||||
refactor
|
||||
Reloadable
|
||||
Remo
|
||||
requester's
|
||||
RESTful
|
||||
RestSharp
|
||||
RLS
|
||||
RPC
|
||||
RSA
|
||||
Saleeba
|
||||
savepoint
|
||||
schemas
|
||||
Sencha
|
||||
Serverless
|
||||
Severin
|
||||
SHA
|
||||
signup
|
||||
SIGUSR
|
||||
sl
|
||||
SNS
|
||||
sqitch
|
||||
SQL
|
||||
sql
|
||||
sr
|
||||
SSL
|
||||
stateful
|
||||
stdout
|
||||
Stolarz
|
||||
subselect
|
||||
SuperAgent
|
||||
SvelteKit
|
||||
SwaggerUI
|
||||
syslog
|
||||
systemd
|
||||
Tcl
|
||||
tmuxp
|
||||
todo
|
||||
todos
|
||||
Tsingson
|
||||
tsquery
|
||||
tx
|
||||
Tyll
|
||||
TypeScript
|
||||
UI
|
||||
ui
|
||||
unicode
|
||||
unix
|
||||
updatable
|
||||
UPSERT
|
||||
Upsert
|
||||
upsert
|
||||
uri
|
||||
url
|
||||
urls
|
||||
variadic
|
||||
Vercel
|
||||
verifier
|
||||
versioning
|
||||
Vondra
|
||||
Vue
|
||||
WAI
|
||||
webhooks
|
||||
websearch
|
||||
Websockets
|
||||
webuser
|
||||
wfts
|
||||
ZeroMQ
|
||||
@@ -0,0 +1,227 @@
|
||||
|
||||
PostgREST 10.0.0
|
||||
================
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
XML/SOAP support for RPC
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
RPC now understands the ``text/xml`` media type, allowing SQL functions to send XML output(``Accept: text/xml``) and receive XML input(``Content-Type: text/xml``). This makes SOAP endpoints possible, check the :ref:`create_soap_endpoint` how-to and the :ref:`scalar_return_formats` reference for more details.
|
||||
|
||||
GeoJSON support
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
GeoJSON is supported across the board(reads, writes, RPC) with the ``Accept: application/geo+json`` header, this depends on PostGIS from the versions 3.0.0 and up. The :ref:`working with PostGIS section <ww_postgis>` has an example to get you started.
|
||||
|
||||
Execution Plan
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The :ref:`execution plan <explain_plan>` of a request is now obtainable with the ``Accept: application/vnd.pgrst.plan`` header. The result can be in ``text`` or ``json`` formats and is compatible with EXPLAIN vizualizers like `explain.depesz.com <https://explain.depesz.com>`_ or `explain.dalibo.com <https://explain.dalibo.com>`_.
|
||||
|
||||
Resource Embedding
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- A :ref:`one-to-one relationship <one-to-one>` is now detected when a foreign key is unique.
|
||||
|
||||
- Using :ref:`computed_relationships`, you can add custom relationships or override automatically detected ones. This makes :ref:`resource_embedding` possible on Foreign Data Wrappers and complex SQL views.
|
||||
|
||||
Horizontal/Vertical Filtering
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- :ref:`Accessing fields of a Composite type or elements of an Array type <composite_array_columns>` is now possible with the arrow operators(``->``, ``->>``) in the same way you would access a JSON type fields.
|
||||
|
||||
- :ref:`pattern_matching` operators for `POSIX regular expressions <https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-POSIX-REGEXP>`_ are now available: ``match`` and ``imatch``, equivalent in PostgreSQL to ``~`` and ``~*`` respectively.
|
||||
|
||||
Insertions/Updates
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- ``limit`` can now affect the number of updated/deleted rows. See :ref:`limited_update_delete`.
|
||||
|
||||
OpenAPI
|
||||
~~~~~~~
|
||||
|
||||
You can now activate the "Authorize" button in SwaggerUI by enabling the :ref:`openapi-security-active` configuration. Add your JWT token prepending :code:`Bearer` to it and you'll be able to request protected resources.
|
||||
|
||||
Administration
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
- Two :ref:`health check endpoints <health_check>` are now exposed in a secondary port.
|
||||
|
||||
- :ref:`pgrst_logging` now shows the database user.
|
||||
|
||||
- It is now possible to execute PostgREST without specifying any configuration variable. The three that were mandatory on the previous versions, are no longer so.
|
||||
|
||||
- If :ref:`db-uri` is not set, PostgREST will use the `libpq environment variables <https://www.postgresql.org/docs/current/libpq-envars.html>`_ for the database connection.
|
||||
- If :ref:`db-schemas` is not set, it will use the database ``public`` schema.
|
||||
- If :ref:`db-anon-role` is not set, it will not allow anonymous requests.
|
||||
|
||||
Error messages
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
- To increase consistency, all the errors messages are now normalized. The ``hint``, ``details``, ``code`` and ``message`` fields will always be present in the body, each one defaulting to a ``null`` value. In the same way, the :ref:`errors that were raised <raise_error>` with ``SQLSTATE`` now include the ``message`` and ``code`` in the body.
|
||||
|
||||
- To further clarify the source of an error, we now add a ``PGRST`` prefix to the error code of all the errors that are PostgREST-specific and don't come from the database. These errors have unique codes that identify them and are documented in the :ref:`pgrst_errors` section.
|
||||
|
||||
Documentation improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Added a :doc:`/how-tos/working-with-postgresql-data-types` how-to, which contains explanations and examples on how to work with different PostgreSQL data types such as timestamps, ranges or PostGIS types, among others.
|
||||
|
||||
* Added in-database and environment variable settings for each :ref:`configuration variable <config_full_list>`.
|
||||
|
||||
* Added the :ref:`file_descriptors` subsection.
|
||||
|
||||
* Added a reference page for :doc:`Error documentation </errors>`.
|
||||
|
||||
* Moved the :ref:`error_source` and the :ref:`status_codes` sections to the :doc:`errors reference page </errors>`.
|
||||
|
||||
* Moved the *Casting type to custom JSON* how-to to the :ref:`casting_range_to_json` subsection.
|
||||
|
||||
* Removed direct links for PostgREST versions older than 8.0 from the versions menu.
|
||||
|
||||
* Removed the *Embedding table from another schema* how-to.
|
||||
|
||||
* Restructured the :ref:`resource_embedding` section:
|
||||
|
||||
- Added a :ref:`one-to-many` and :ref:`many-to-one` subsections.
|
||||
|
||||
- Renamed the *Embedding through join tables* subsection to :ref:`many-to-many`.
|
||||
|
||||
* Split up the *Insertions/Updates* section into :ref:`insert` and :ref:`update`.
|
||||
|
||||
Breaking changes
|
||||
----------------
|
||||
|
||||
* Many-to-many relationships now require that foreign key columns be part of the join table composite key
|
||||
|
||||
- This was needed to reduce :ref:`embed_disamb` errors in complex schemas(`#2070 <https://github.com/PostgREST/postgrest/issues/2070>`_).
|
||||
|
||||
- For migrating to this version, the less invasive method is to use :ref:`computed_relationships` to replace the previous many-to-many relationships.
|
||||
|
||||
- Otherwise you can change your join table primary key. For example with ``alter table permission_user drop constraint permission_user_pkey, add primary key (id, user_id, permission_id);``
|
||||
|
||||
* Views now are not detected when embedding using :ref:`target_disamb`.
|
||||
|
||||
- This embedding form was easily made ambiguous whenever a new view was added(`#2277 <https://github.com/PostgREST/postgrest/issues/2277>`_).
|
||||
|
||||
- For migrating to this version, you can use :ref:`computed_relationships` to replace the previous view relationships.
|
||||
|
||||
- :ref:`hint_disamb` works as usual on views.
|
||||
|
||||
* ``limit/offset`` now limits the affected rows on ``UPDATE``/``DELETE``
|
||||
|
||||
- Previously, ``limit``/``offset`` only limited the returned rows but not the actual updated rows(`#2156 <https://github.com/PostgREST/postgrest/issues/2156>`_)
|
||||
|
||||
* ``max-rows`` is no longer applied on ``POST``, ``PATCH``, ``PUT`` and ``DELETE`` returned rows
|
||||
|
||||
- This was misleading because the affected rows were not really affected by ``max-rows``, only the returned rows were limited(`#2155 <https://github.com/PostgREST/postgrest/issues/2155>`_)
|
||||
|
||||
* Return ``204 No Content`` without ``Content-Type`` for RPCs returning ``VOID``
|
||||
|
||||
- Previously, those RPCs would return ``null`` as a body with ``Content-Type: application/json`` (`#2001 <https://github.com/PostgREST/postgrest/issues/2001>`_).
|
||||
|
||||
* Using ``Prefer: return=representation`` no longer returns a ``Location`` header
|
||||
|
||||
- This reduces unnecessary computing for all insertions (`#2312 <https://github.com/PostgREST/postgrest/issues/2312>`_)
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
* Return ``204 No Content`` without ``Content-Type`` for ``PUT`` (`#2058 <https://github.com/PostgREST/postgrest/issues/2058>`_)
|
||||
|
||||
* Clarify error for failed schema cache load. (`#2107 <https://github.com/PostgREST/postgrest/issues/2107>`_)
|
||||
|
||||
- From ``Database connection lost. Retrying the connection`` to ``Could not query the database for the schema cache. Retrying.``
|
||||
|
||||
* Fix silently ignoring filter on a non-existent embedded resource (`#1771 <https://github.com/PostgREST/postgrest/issues/1771>`_)
|
||||
|
||||
* Remove functions, which are not callable due to unnamed arguments, from schema cache and OpenAPI output. (`#2152 <https://github.com/PostgREST/postgrest/issues/2152>`_)
|
||||
|
||||
* Fix accessing JSON array fields with ``->`` and ``->>`` in ``?select=`` and ``?order=``. (`#2145 <https://github.com/PostgREST/postgrest/issues/2145>`_)
|
||||
|
||||
* Ignore ``max-rows`` on ``POST``, ``PATCH``, ``PUT`` and ``DELETE`` (`#2155 <https://github.com/PostgREST/postgrest/issues/2155>`_)
|
||||
|
||||
* Fix inferring a foreign key column as a primary key column on views (`#2254 <https://github.com/PostgREST/postgrest/issues/2254>`_)
|
||||
|
||||
* Restrict generated many-to-many relationships (`#2070 <https://github.com/PostgREST/postgrest/issues/2070>`_)
|
||||
|
||||
- Only adds many-to-many relationships when a table has foreign keys to two other tables and these foreign key columns are part of the table's primary key columns.
|
||||
|
||||
* Allow casting to types with underscores and numbers (e.g. ``select=oid_array::_int4``) (`#2278 <https://github.com/PostgREST/postgrest/issues/2278>`_)
|
||||
|
||||
* Prevent views from breaking one-to-many/many-to-one embeds when using column or foreign key as target (`#2277 <https://github.com/PostgREST/postgrest/issues/2277>`_, `#2238 <https://github.com/PostgREST/postgrest/issues/2238>`_, `#1643 <https://github.com/PostgREST/postgrest/issues/1643>`_)
|
||||
|
||||
- When using a column or foreign key as target for embedding (``/tbl?select=*,col-or-fk(*)``), only tables are now detected and views are not.
|
||||
|
||||
- You can still use a column or an inferred foreign key on a view to embed a table (``/view?select=*,col-or-fk(*)``)
|
||||
|
||||
* Increase the ``db-pool-timeout`` to 1 hour to prevent frequent high connection latency (`#2317 <https://github.com/PostgREST/postgrest/issues/2317>`_)
|
||||
|
||||
* The search path now correctly identifies schemas with uppercase and special characters in their names (regression) (`#2341 <https://github.com/PostgREST/postgrest/issues/2341>`_)
|
||||
|
||||
* "404 Not Found" on nested routes and "405 Method Not Allowed" errors no longer start an empty database transaction (`#2364 <https://github.com/PostgREST/postgrest/issues/2364>`_)
|
||||
|
||||
* Fix inaccurate result count when an inner embed was selected after a normal embed in the query string (`#2342 <https://github.com/PostgREST/postgrest/issues/2342>`_)
|
||||
|
||||
* ``OPTIONS`` requests no longer start an empty database transaction (`#2376 <https://github.com/PostgREST/postgrest/issues/2376>`_)
|
||||
|
||||
* Allow using columns with dollar sign ($) without double quoting in filters and ``select`` (`#2395 <https://github.com/PostgREST/postgrest/issues/2395>`_)
|
||||
|
||||
* Fix loop crash error on startup in PostgreSQL 15 beta 3. ``Log: "UNION types \"char\" and text cannot be matched."`` (`#2410 <https://github.com/PostgREST/postgrest/issues/2410>`_)
|
||||
|
||||
* Fix race conditions managing database connection helper (`#2397 <https://github.com/PostgREST/postgrest/issues/2397>`_)
|
||||
|
||||
* Allow ``limit=0`` in the request query to return an empty array (`#2269 <https://github.com/PostgREST/postgrest/issues/2269>`_)
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
Big thanks from the `PostgREST team <https://github.com/orgs/PostgREST/people>`_ to our sponsors!
|
||||
|
||||
.. container:: image-container
|
||||
|
||||
.. image:: ../_static/cybertec-new.png
|
||||
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/2ndquadrant.png
|
||||
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/retool.png
|
||||
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/gnuhost.png
|
||||
:target: https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/supabase.png
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/oblivious.jpg
|
||||
:target: https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
* Evans Fernandes
|
||||
* `Jan Sommer <https://github.com/nerfpops>`_
|
||||
* `Franz Gusenbauer <https://www.igutech.at/>`_
|
||||
* `Daniel Babiak <https://github.com/dbabiak>`_
|
||||
* Tsingson Qin
|
||||
* Michel Pelletier
|
||||
* Jay Hannah
|
||||
* Robert Stolarz
|
||||
* Nicholas DiBiase
|
||||
* Christopher Reid
|
||||
* Nathan Bouscal
|
||||
* Daniel Rafaj
|
||||
* David Fenko
|
||||
* Remo Rechkemmer
|
||||
* Severin Ibarluzea
|
||||
* Tom Saleeba
|
||||
* Pawel Tyll
|
||||
|
||||
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
|
||||
@@ -0,0 +1,153 @@
|
||||
|
||||
PostgREST 10.2.0
|
||||
================
|
||||
|
||||
This minor version adds bug fixes and some features that provide stability to v10.0.0. These release notes include the changes added in versions `10.1.0 <https://github.com/PostgREST/postgrest/releases/tag/v10.1.0>`_, `10.1.1 <https://github.com/PostgREST/postgrest/releases/tag/v10.1.1>`_ and `10.1.2 <https://github.com/PostgREST/postgrest/releases/tag/v10.1.2>`_. You can look at the detailed changelog and download the pre-compiled binaries on the `GitHub release page <https://github.com/PostgREST/postgrest/releases/tag/v10.2.0>`_.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
Pool Connection Lifetime
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To prevent memory leaks caused by long-lived connections, PostgREST limits their lifetime in the pool through :ref:`db-pool-max-lifetime`.
|
||||
|
||||
Pool Connection Acquisition Timeout
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There is now a time limit to wait for pool connections to be acquired. If a new request cannot get a connection in the time specified in :ref:`db-pool-acquisition-timeout` then a response with a ``504`` status is returned.
|
||||
|
||||
Documentation improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Added HTTP status codes to the :ref:`pgrst_errors`.
|
||||
|
||||
* Added a how-to on :ref:`sql-user-management-using-postgres-users-and-passwords`.
|
||||
|
||||
* Updated the :ref:`Heroku installation page <deploy_heroku>`.
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
* Removed ``db-pool-timeout`` option because it was removed in the ``hasql-pool`` library that PostgREST uses for SQL connections. (`#2444 <https://github.com/PostgREST/postgrest/issues/2444>`_)
|
||||
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
* Deprecate bulk-calls when including the ``Prefer: params=multiple-objects`` in the request. It is preferable to use a function with an :ref:`array <s_procs_array>` or JSON parameter for a better performance. (`#1385 <https://github.com/PostgREST/postgrest/issues/1385>`_)
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
* Reduce allocations communication with PostgreSQL, particularly for request bodies. (`#2261 <https://github.com/PostgREST/postgrest/issues/2261>`_, `#2349 <https://github.com/PostgREST/postgrest/issues/2349>`_, `#2467 <https://github.com/PostgREST/postgrest/issues/2467>`_)
|
||||
|
||||
* Fix ``SIGUSR1`` to fully flush the connection pool. (`#2401 <https://github.com/PostgREST/postgrest/issues/2401>`_, `#2444 <https://github.com/PostgREST/postgrest/issues/2444>`_)
|
||||
|
||||
* Fix opening an empty transaction on failed resource embedding. (`#2428 <https://github.com/PostgREST/postgrest/issues/2428>`_)
|
||||
|
||||
* Fix embedding the same table multiple times. (`#2455 <https://github.com/PostgREST/postgrest/issues/2455>`_)
|
||||
|
||||
* Fix a regression when embedding views where base tables have a different column order for foreign key columns (`#2518 <https://github.com/PostgREST/postgrest/issues/2518>`_)
|
||||
|
||||
* Fix a regression with the ``Location`` header when :ref:`inserting <insert>` into views with primary keys from multiple tables (`#2458 <https://github.com/PostgREST/postgrest/issues/2458>`_)
|
||||
|
||||
* Fix a regression in OpenAPI output with mode ``follow-privileges`` (`#2356 <https://github.com/PostgREST/postgrest/issues/2356>`_)
|
||||
|
||||
* Fix infinite recursion when loading schema cache with self-referencing view (`#2283 <https://github.com/PostgREST/postgrest/issues/2283>`_)
|
||||
|
||||
* Return status code ``200`` instead of ``404`` for ``PATCH`` requests which don't affect any rows (`#2343 <https://github.com/PostgREST/postgrest/issues/2343>`_)
|
||||
|
||||
* Treat the :ref:`computed relationships <computed_relationships>` that do not return ``SETOF`` as M2O/O2O relationship (`#2481 <https://github.com/PostgREST/postgrest/issues/2481>`_)
|
||||
|
||||
* Fix embedding a computed relationship with a normal relationship (`#2534 <https://github.com/PostgREST/postgrest/issues/2534>`_)
|
||||
|
||||
* Fix error message when ``[]`` is used inside ``select`` (`#2362 <https://github.com/PostgREST/postgrest/issues/2362>`_)
|
||||
|
||||
* Disallow ``!inner`` on computed columns (`#2475 <https://github.com/PostgREST/postgrest/issues/2475>`_)
|
||||
|
||||
* Ignore leading and trailing spaces in column names when parsing the query string (`#2285 <https://github.com/PostgREST/postgrest/issues/2285>`_)
|
||||
|
||||
* Fix ``UPSERT`` with PostgreSQL 15 (`#2545 <https://github.com/PostgREST/postgrest/issues/2545>`_)
|
||||
|
||||
* Fix embedding views with multiple references to the same base column (`#2459 <https://github.com/PostgREST/postgrest/issues/2459>`_)
|
||||
|
||||
* Fix regression when embedding views with partial references to multi column foreign keys (`#2548 <https://github.com/PostgREST/postgrest/issues/2548>`_)
|
||||
|
||||
* Fix regression when requesting ``limit=0`` and ``db-max-row`` is set (`#2558 <https://github.com/PostgREST/postgrest/issues/2558>`_)
|
||||
|
||||
* Return a clear error without hitting the database when trying to update or insert an unknown column with ``?columns`` (`#2542 <https://github.com/PostgREST/postgrest/issues/2542>`_)
|
||||
|
||||
* Fix bad M2M embedding on RPC (`#2565 <https://github.com/PostgREST/postgrest/issues/2565>`_)
|
||||
|
||||
* Replace misleading error message when no function is found with a hint containing functions/parameters names suggestions (`#2575 <https://github.com/PostgREST/postgrest/issues/2575>`_)
|
||||
|
||||
* Move explanation about "single parameters" from the ``message`` to the ``details`` in the error output (`#2582 <https://github.com/PostgREST/postgrest/issues/2582>`_)
|
||||
|
||||
* Replace misleading error message when no relationship is found with a hint containing parent/child names suggestions (`#2569 <https://github.com/PostgREST/postgrest/issues/2569>`_)
|
||||
|
||||
* Add the required OpenAPI items object when the parameter is an array (`#1405 <https://github.com/PostgREST/postgrest/issues/1405>`_)
|
||||
|
||||
* Add upsert headers for ``POST`` requests to the OpenAPI output (`#2592 <https://github.com/PostgREST/postgrest/issues/2592>`_)
|
||||
|
||||
* Fix foreign keys pointing to ``VIEW`` instead of ``TABLE`` in OpenAPI output (`#2623 <https://github.com/PostgREST/postgrest/issues/2623>`_)
|
||||
|
||||
* Consider any PostgreSQL authentication failure as fatal and exit immediately (`#2622 <https://github.com/PostgREST/postgrest/issues/2622>`_)
|
||||
|
||||
* Fix ``NOTIFY pgrst`` not reloading the db connections catalog cache (`#2620 <https://github.com/PostgREST/postgrest/issues/2620>`_)
|
||||
|
||||
* Fix ``db-pool-acquisition-timeout`` not logging to stderr when the timeout is reached (`#2667 <https://github.com/PostgREST/postgrest/issues/2667>`_)
|
||||
|
||||
* Fix PostgreSQL resource leak with long-lived connections through the :ref:`db-pool-max-lifetime` configuration (`#2638 <https://github.com/PostgREST/postgrest/issues/2638>`_)
|
||||
|
||||
* There is now a stricter parsing of the query string. Instead of silently ignoring, the parser now returns a :ref:`PostgREST error <pgrst100>` on invalid syntax. (`#2537 <https://github.com/PostgREST/postgrest/issues/2537>`_)
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
Big thanks from the `PostgREST team <https://github.com/orgs/PostgREST/people>`_ to our sponsors!
|
||||
|
||||
.. container:: image-container
|
||||
|
||||
.. image:: ../_static/cybertec-new.png
|
||||
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/2ndquadrant.png
|
||||
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/retool.png
|
||||
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/gnuhost.png
|
||||
:target: https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/supabase.png
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/oblivious.jpg
|
||||
:target: https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
* Evans Fernandes
|
||||
* `Jan Sommer <https://github.com/nerfpops>`_
|
||||
* `Franz Gusenbauer <https://www.igutech.at/>`_
|
||||
* `Daniel Babiak <https://github.com/dbabiak>`_
|
||||
* Tsingson Qin
|
||||
* Michel Pelletier
|
||||
* Jay Hannah
|
||||
* Robert Stolarz
|
||||
* Nicholas DiBiase
|
||||
* Christopher Reid
|
||||
* Nathan Bouscal
|
||||
* Daniel Rafaj
|
||||
* David Fenko
|
||||
* Remo Rechkemmer
|
||||
* Severin Ibarluzea
|
||||
* Tom Saleeba
|
||||
* Pawel Tyll
|
||||
|
||||
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
|
||||
@@ -0,0 +1,26 @@
|
||||
v5.2.0
|
||||
======
|
||||
|
||||
* Explicit qualification introduced in ``v5.0`` is no longer necessary, this section will not be included from this version onwards. A :ref:`db-extra-search-path` configuration parameter was introduced to avoid the need to explictly qualify database objects. If you install PostgreSQL extensions on the ``public`` schema, they'll work normally from now on.
|
||||
|
||||
* Now you can filter :ref:`tabs-cols-w-spaces`.
|
||||
|
||||
* Included the ability to quote columns that have :ref:`reserved-chars`.
|
||||
|
||||
* Thanks to `Zhou Feng <https://github.com/zhoufeng1989>`_, now is possible to reference an external file in :ref:`db-uri`.
|
||||
|
||||
* Thanks to `Russell Davies <https://github.com/russelldavies>`_, Json Web Key Sets are now accepted by :ref:`jwt-secret`.
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
This release was made possible thanks to:
|
||||
|
||||
* `Daniel Babiak <https://github.com/dbabiak>`_
|
||||
* `Michel Pelletier <https://github.com/michelp>`_
|
||||
* Tsingson Qin
|
||||
* Jay Hannah
|
||||
* Victor Adossi
|
||||
* Petr Beles
|
||||
|
||||
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
|
||||
@@ -0,0 +1,79 @@
|
||||
.. |br| raw:: html
|
||||
|
||||
<br />
|
||||
|
||||
v6.0.2
|
||||
======
|
||||
|
||||
Full changelog is available at `PostgREST releases page <https://github.com/PostgREST/postgrest/releases>`_.
|
||||
|
||||
Added
|
||||
-----
|
||||
|
||||
* Ignoring payload keys for insert/update can be now done with the ``?columns`` query parameter. See :ref:`specify_columns`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* `websearch_to_tsquery <https://www.postgresql.org/docs/current/functions-textsearch.html#id-1.5.8.19.7.2.2.7.1.1.1>`_ can now be used
|
||||
through the ``wfts`` operator. See :ref:`fts`.
|
||||
|br| -- `@herulume <https://github.com/herulume>`_
|
||||
|
||||
* Resource Embedding on materialized views is now possible. See :ref:`embedding_views`.
|
||||
|br| -- `@vitorbaptista <https://github.com/vitorbaptista>`_
|
||||
|
||||
* Bulk calling an RPC is now allowed. See :ref:`bulk_call`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* It's now possible to request a ``text/plain`` output. See :ref:`scalar_return_formats`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Config option for specifying PostgREST database pool timeout ``db-pool-timeout``.
|
||||
|br| -- `@Qu4tro <https://github.com/Qu4tro>`_
|
||||
|
||||
* Config option for binding the PostgREST web server to an unix socket. See :ref:`server-unix-socket`.
|
||||
|br| -- `@Dansvidania <https://github.com/Dansvidania>`_
|
||||
|
||||
* Config option for extending the supported media types. See :ref:`raw-media-types`.
|
||||
|br| -- `@Dansvidania <https://github.com/Dansvidania>`_
|
||||
|
||||
* We now offer an statically linked binary for Linux. Look for **postgrest-<version>-linux-x64-static.tar.xz** on the
|
||||
`releases page <https://github.com/PostgREST/postgrest/releases>`_.
|
||||
|br| -- `@clojurians-org <https://github.com/clojurians-org>`_
|
||||
|
||||
* A :ref:`how_tos` section was added to the documentation.
|
||||
|
||||
Changed
|
||||
-------
|
||||
|
||||
* ``SIGHUP`` support was removed. You should use ``SIGUSR1`` instead. See :ref:`schema_reloading`.
|
||||
|
||||
* server-host default of ``127.0.0.1`` was changed to ``!4``. See :ref:`server-host`.
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
This release is sponsored by:
|
||||
|
||||
.. image:: ../_static/cybertec.png
|
||||
:target: https://www.cybertec-postgresql.com/en/
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/2ndquadrant.png
|
||||
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/retool.png
|
||||
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
* `Daniel Babiak <https://github.com/dbabiak>`_
|
||||
* Evans Fernandes
|
||||
* Tsingson Qin
|
||||
* Michel Pelletier
|
||||
* Jay Hannah
|
||||
* Robert Stolarz
|
||||
* Kofi Gumbs
|
||||
* Nicholas DiBiase
|
||||
* Christopher Reid
|
||||
* Nathan Bouscal
|
||||
|
||||
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
|
||||
@@ -0,0 +1,106 @@
|
||||
.. |br| raw:: html
|
||||
|
||||
<br />
|
||||
|
||||
v7.0.0
|
||||
======
|
||||
|
||||
You can download this release at the `PostgREST v7.0.0 release page <https://github.com/PostgREST/postgrest/releases/tag/v7.0.0>`_.
|
||||
|
||||
Added
|
||||
-----
|
||||
|
||||
* Support for :ref:`Switching to a schema <multiple-schemas>` defined in :ref:`db-schemas`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_, `@mahmoudkassem <https://github.com/mahmoudkassem>`_
|
||||
|
||||
* Support for :ref:`planned_count` and :ref:`estimated_count`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_, `@LorenzHenk <https://github.com/LorenzHenk>`_
|
||||
|
||||
* Support for the :ref:`on_conflict <on_conflict>` query parameter to UPSERT based on a unique constraint.
|
||||
|br| -- `@ykst <https://github.com/ykst>`_
|
||||
|
||||
* Support for :ref:`Resource Embedding Disambiguation <embed_disamb>`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Support for user defined socket permission via :ref:`server-unix-socket-mode` config option
|
||||
|br| -- `@Dansvidania <https://github.com/Dansvidania>`_
|
||||
|
||||
* HTTP logic improvements -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
+ Support for HTTP HEAD requests.
|
||||
+ GUCs for :ref:`guc_req_path_method`.
|
||||
+ Support for :ref:`pre_req_headers`.
|
||||
+ Allow overriding provided headers(Content-Type, Location, etc) by :ref:`guc_resp_hdrs`
|
||||
+ Access to the ``Authorization`` header value through ``request.header.authorization``
|
||||
|
||||
* Documentation improvements
|
||||
|
||||
+ Explanation for :doc:`Schema Structure <../schema_structure>`.
|
||||
+ Reference for :ref:`s_proc_embed`.
|
||||
+ Reference for :ref:`mutation_embed`.
|
||||
+ Reference for filters on :ref:`json_columns`.
|
||||
+ How-to for :ref:`providing_img`.
|
||||
+ Added :ref:`community_tutorials` section.
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
* Allow embedding a view when its source table foreign key is UNIQUE
|
||||
|br| -- `@bwbroersma <https://github.com/bwbroersma>`_
|
||||
|
||||
* ``Accept: application/vnd.pgrst.object+json`` behavior is now enforced for POST/PATCH/DELETE regardless of ``Prefer: return=minimal``
|
||||
|br| -- `@dwagin <https://github.com/dwagin>`_
|
||||
|
||||
* Fix self join resource embedding on PATCH
|
||||
|br| -- `@herulume <https://github.com/herulume>`_, `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Allow PATCH/DELETE without ``Prefer: return=minimal`` on tables with no SELECT privileges
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Fix many to many resource embedding for RPC/PATCH
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
Changed
|
||||
-------
|
||||
|
||||
* :ref:`bulk_call` should now be done by specifying a ``Prefer: params=multiple-objects`` header. This fixes a performance regression when calling stored procedures.
|
||||
|
||||
* Resource Embedding now outputs an error when multiple relationships between two tables are found, see :ref:`embed_disamb`.
|
||||
|
||||
* ``server-proxy-uri`` config option has been renamed to :ref:`openapi-server-proxy-uri`.
|
||||
|
||||
* Default Unix Socket file mode from 755 to 660
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
This release was made possible thanks to:
|
||||
|
||||
.. image:: ../_static/cybertec.png
|
||||
:target: https://www.cybertec-postgresql.com/en/
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/2ndquadrant.png
|
||||
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/retool.png
|
||||
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
* `Daniel Babiak <https://github.com/dbabiak>`_
|
||||
* Evans Fernandes
|
||||
* `Jan Sommer <https://github.com/nerfpops>`_
|
||||
* Tsingson Qin
|
||||
* Michel Pelletier
|
||||
* Jay Hannah
|
||||
* Robert Stolarz
|
||||
* Kofi Gumbs
|
||||
* Nicholas DiBiase
|
||||
* Christopher Reid
|
||||
* Nathan Bouscal
|
||||
* Daniel Rafaj
|
||||
* David Fenko
|
||||
|
||||
|
||||
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
|
||||
@@ -0,0 +1,69 @@
|
||||
.. |br| raw:: html
|
||||
|
||||
<br />
|
||||
|
||||
v7.0.1
|
||||
======
|
||||
|
||||
You can see the full changelog at `PostgREST v7.0.1 release page <https://github.com/PostgREST/postgrest/releases/tag/v7.0.1>`_.
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
* Fix overloaded computed columns on RPC
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Fix POST, PATCH, DELETE with ``?select=`` and ``Prefer: return=minimal`` and PATCH with empty body
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Fix missing ``openapi-server-proxy-uri`` config option
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Fix ``Content-Profile`` not working for POST RPC
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Fix PUT restriction for including all columns in payload
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Documentation improvements
|
||||
|
||||
+ Added package managers to :ref:`install`.
|
||||
|
||||
Changed
|
||||
-------
|
||||
|
||||
* From this version onwards, the release page will include a single Linux static executable that can be run on any Linux distribution.
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
This release was made possible thanks to:
|
||||
|
||||
.. image:: ../_static/cybertec.png
|
||||
:target: https://www.cybertec-postgresql.com/en/
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/2ndquadrant.png
|
||||
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/retool.png
|
||||
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
* `Daniel Babiak <https://github.com/dbabiak>`_
|
||||
* Evans Fernandes
|
||||
* `Jan Sommer <https://github.com/nerfpops>`_
|
||||
* Tsingson Qin
|
||||
* Michel Pelletier
|
||||
* Jay Hannah
|
||||
* Robert Stolarz
|
||||
* Kofi Gumbs
|
||||
* Nicholas DiBiase
|
||||
* Christopher Reid
|
||||
* Nathan Bouscal
|
||||
* Daniel Rafaj
|
||||
* David Fenko
|
||||
|
||||
|
||||
If you'd like to join them, consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
|
||||
@@ -0,0 +1,191 @@
|
||||
.. |br| raw:: html
|
||||
|
||||
<br />
|
||||
|
||||
v8.0.0
|
||||
======
|
||||
|
||||
You can download this release at the `PostgREST v8.0.0 release page <https://github.com/PostgREST/postgrest/releases/tag/v8.0.0>`_.
|
||||
|
||||
Added
|
||||
-----
|
||||
|
||||
* Allow HTTP status override through the :ref:`response.status <guc_resp_status>` GUC.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Allow :ref:`s_procs_variadic`.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Allow :ref:`embedding_view_chains` recursively to any depth.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* No downtime when reloading the schema cache. See :ref:`schema_reloading`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Allow schema cache reloading using PostgreSQL :ref:`NOTIFY <schema_reloading_notify>` command. This enables :ref:`auto_schema_reloading`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Allow sending the header ``Prefer: headers-only`` to get a response with a ``Location`` header. See :ref:`insert`.
|
||||
|br| -- `@laurenceisla <https://github.com/laurenceisla>`_
|
||||
|
||||
* Allow :ref:`external_connection_poolers` such as PgBouncer in transaction pooling mode.
|
||||
|br| -- `@laurenceisla <https://github.com/laurenceisla>`_
|
||||
|
||||
* Allow :ref:`config_reloading` by sending a SIGUSR2 signal.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Allow ``Bearer`` with and without capitalization as authentication schema. See :ref:`client_auth`.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* :ref:`in_db_config` that can be :ref:`reloaded with NOTIFY <config_reloading_notify>`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Allow OPTIONS to generate HTTP methods based on views triggers. See :ref:`OPTIONS requests <options_requests>`.
|
||||
|br| -- `@laurenceisla <https://github.com/laurenceisla>`_
|
||||
|
||||
* Show timestamps for server diagnostic information. See :ref:`pgrst_logging`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Config options for showing a full OpenAPI output regardless of the JWT role privileges and for disabling it altogether. See :ref:`openapi-mode`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Config option for logging level. See :ref:`log-level`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Config option for enabling or disabling prepared statements. See :ref:`db-prepared-statements`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Config option for specifying how to terminate the transactions (allowing rollbacks, useful for testing). See :ref:`db-tx-end`.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Documentation improvements
|
||||
|
||||
+ Added the :doc:`../schema_cache` page.
|
||||
+ Moved the :ref:`schema_reloading` reference from :doc:`../admin` to :doc:`../schema_cache`
|
||||
|
||||
Changed
|
||||
-------
|
||||
|
||||
* Docker images are now optimized to be built from the scratch image. This reduces the compressed image size from over 30 MB to about 4 MB.
|
||||
For more details, see `Docker image built with Nix <https://github.com/PostgREST/postgrest/tree/main/nix/tools/docker#user-content-docker-image-built-with-nix>`_.
|
||||
|br| -- `@monacoremo <https://github.com/monacoremo>`_
|
||||
|
||||
* The Docker image no longer has an internal ``/etc/postgrest.conf`` file, you must use :ref:`env_variables_config` to configure it.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* The ``pg_listen`` `utility <https://github.com/begriffs/pg_listen>`_ is no longer needed to automatically reload the schema cache
|
||||
and it's replaced entirely by database notifications. See :ref:`auto_schema_reloading`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* POST requests for insertions no longer include a ``Location`` header in the response by default and behave the same way as having a
|
||||
``Prefer: return=minimal`` header in the request. This prevents permissions errors when having a write-only table. See :ref:`insert`.
|
||||
|br| -- `@laurenceisla <https://github.com/laurenceisla>`_
|
||||
|
||||
* Modified the default logging level from ``info`` to ``error``. See :ref:`log-level`.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Changed the error message for a not found RPC on a stale schema (see :ref:`stale_function_signature`) and for the unsupported case of
|
||||
overloaded functions with the same argument names but different types.
|
||||
|br| -- `@laurenceisla <https://github.com/laurenceisla>`_
|
||||
|
||||
* Changed the error message for the no relationship found error. See :ref:`stale_fk_relationships`.
|
||||
|br| -- `@laurenceisla <https://github.com/laurenceisla>`_
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
* Fix showing UNKNOWN on ``postgrest --help`` invocation.
|
||||
|br| -- `@monacoremo <https://github.com/monacoremo>`_
|
||||
|
||||
* Removed single column restriction to allow composite foreign keys in join tables.
|
||||
|br| -- `@goteguru <https://github.com/goteguru>`_
|
||||
|
||||
* Fix expired JWTs starting an empty transaction on the db.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Fix location header for POST request with ``select=`` without PK.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Fix error messages on connection failure for localized PostgreSQL on Windows.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Fix ``application/octet-stream`` appending ``charset=utf-8``.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Fix overloading of functions with unnamed arguments.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Return ``405 Method not Allowed`` for GET of volatile RPC instead of 500.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Fix RPC return type handling and embedding for domains with composite base type.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Fix embedding through views that have COALESCE with subselect.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Fix parsing of boolean config values for Docker environment variables, now it accepts double quoted truth values ``("true", "false")`` and numbers ``("1", "0")``.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Fix using ``app.settings.xxx`` config options in Docker, now they can be used as ``PGRST_APP_SETTINGS_xxx``.
|
||||
|br| -- `@wolfgangwalther <https://github.com/wolfgangwalther>`_
|
||||
|
||||
* Fix panic when attempting to run with unix socket on non-unix host and properly close unix domain socket on exit.
|
||||
|br| -- `@monacoremo <https://github.com/monacoremo>`_
|
||||
|
||||
* Disregard internal junction (in non-exposed schema) when embedding.
|
||||
|br| -- `@steve-chavez <https://github.com/steve-chavez>`_
|
||||
|
||||
* Fix requests for overloaded functions from HTML forms to no longer hang.
|
||||
|br| -- `@laurenceisla <https://github.com/laurenceisla>`_
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
Big thanks from the `PostgREST team <https://github.com/orgs/PostgREST/people>`_ to our sponsors!
|
||||
|
||||
.. container:: image-container
|
||||
|
||||
.. image:: ../_static/cybertec-new.png
|
||||
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/2ndquadrant.png
|
||||
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/retool.png
|
||||
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/gnuhost.png
|
||||
:target: https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/supabase.png
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/oblivious.jpg
|
||||
:target: https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
* Evans Fernandes
|
||||
* `Jan Sommer <https://github.com/nerfpops>`_
|
||||
* `Franz Gusenbauer <https://www.igutech.at/>`_
|
||||
* `Daniel Babiak <https://github.com/dbabiak>`_
|
||||
* Tsingson Qin
|
||||
* Michel Pelletier
|
||||
* Jay Hannah
|
||||
* Robert Stolarz
|
||||
* Nicholas DiBiase
|
||||
* Christopher Reid
|
||||
* Nathan Bouscal
|
||||
* Daniel Rafaj
|
||||
* David Fenko
|
||||
* Remo Rechkemmer
|
||||
* Severin Ibarluzea
|
||||
* Tom Saleeba
|
||||
* Pawel Tyll
|
||||
|
||||
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
PostgREST 9.0.0
|
||||
===============
|
||||
|
||||
This major version is released with PostgreSQL 14 compatibility and is accompanied with new features and bug fixes. You can look at the detailed changelog and download the pre-compiled binaries on the `GitHub release page <https://github.com/PostgREST/postgrest/releases/tag/v9.0.0>`_.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
PostgreSQL 14 compatibility
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
PostgreSQL 14 Beta 1 tightened its GUC naming scheme making it impossible to use multiple dots (``.``) and dashes (``-``) on custom GUC parameters, this caused our :ref:`old HTTP Context <guc_legacy_names>` to fail across all requests. Thankfully, `@robertsosinski <https://github.com/robertsosinski>`_ got the PostgreSQL team to reconsider allowing multiple dots in the GUC name, allowing us to avoid a major breaking change. You can see the full discussion `here <https://www.postgresql.org/message-id/17045-6a4a9f0d1513f72b%40postgresql.org>`_.
|
||||
|
||||
Still, dashes cannot be used on PostgreSQL 14 custom GUC parameters, so we changed our HTTP Context :ref:`to namespace using a mix of dots and JSON <guc_req_headers_cookies_claims>`. On older PostgreSQL versions we still use the :ref:`guc_legacy_names`. If you wish to use the new JSON GUCs on these versions, set the :ref:`db-use-legacy-gucs` config option to false.
|
||||
|
||||
Resource Embedding with Top-level Filtering
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Historically, Resource Embedding was always done with a query that included the equivalent of a ``LEFT JOIN``, which meant you could not
|
||||
exclude any of the top-level resource rows. You can now use :ref:`embedding_top_level_filter` to do the equivalent of an ``INNER JOIN``, thus you can filter the top-level resource rows with any of the available operators.
|
||||
|
||||
Partitioned Tables
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Partitioned tables now integrate with all the feature set. You can :ref:`embed partitioned tables <embedding_partitioned_tables>`, UPSERT, INSERT(with a correctly generated Location header) and make OPTIONS requests on them. They're also included in the generated OpenAPI.
|
||||
|
||||
Functions(RPC)
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* Functions with a :ref:`single unnamed parameter <s_proc_single_unnamed>` can now be used to POST raw ``bytea``, ``text`` or ``json/jsonb``.
|
||||
|
||||
Horizontal Filtering
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* The ``unknown`` value for three-valued logic can now be used on the ``is`` :ref:`operator <operators>`.
|
||||
|
||||
* Escaping double quotes(``"``) in double-quoted surrounded strings is now possible by using backslashes, e.g. ``?col=in.("Double\"Quote")``. Backslashes can be escaped with a preceding backslash, e.g. ``?col=in.("Back\\slash")``. See :ref:`reserved-chars`.
|
||||
|
||||
Administration
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* A ``Retry-After`` header is now added when PostgREST is doing :ref:`automatic_recovery`.
|
||||
|
||||
Error messages
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* :ref:`embed_disamb` now shows an improved error message that includes relevant hints for clearing out the ambiguous embedding.
|
||||
|
||||
Documentation improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Added ``curl`` snippets to the :doc:`API <../api>` page.
|
||||
|
||||
* Added the :ref:`automatic_recovery` section.
|
||||
|
||||
* Added the :ref:`nested_embedding` section.
|
||||
|
||||
* Added the :ref:`logical_operators` section.
|
||||
|
||||
* Added the :ref:`templates` and :ref:`devops` sections to the :doc:`Ecosystem </ecosystem>`.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
* Correct RPC return type handling for RETURNS TABLE with a single column (`#1930 <https://github.com/PostgREST/postgrest/issues/1930>`_).
|
||||
|
||||
* Schema Cache query failing with ``standard_conforming_strings = off`` (`#1992 <https://github.com/PostgREST/postgrest/issues/1992>`_).
|
||||
|
||||
* OpenAPI missing default values for String types (`#1871 <https://github.com/PostgREST/postgrest/issues/1871>`_).
|
||||
|
||||
Breaking changes
|
||||
----------------
|
||||
|
||||
* Dropped support for PostgreSQL 9.5 as it already reached its end-of-life according to `PostgreSQL versioning policy <https://www.postgresql.org/support/versioning/>`_.
|
||||
|
||||
* Partitions of a `partitioned table <https://www.postgresql.org/docs/current/ddl-partitioning.html#DDL-PARTITIONING-DECLARATIVE>`_ are no longer included in the :doc:`../schema_cache`. This is so errors are not generated when doing resource embedding on partitioned tables.
|
||||
|
||||
* Dropped support for doing :ref:`hint_disamb` using dots instead of exclamation marks, e.g. doing ``select=*,projects.client_id(*)`` instead of ``select=*,projects!client_id(*)``). Using dots was undocumented and deprecated back in `v6.0.2 <https://github.com/PostgREST/postgrest/releases/tag/v6.0.2>`_.
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
Big thanks from the `PostgREST team <https://github.com/orgs/PostgREST/people>`_ to our sponsors!
|
||||
|
||||
.. container:: image-container
|
||||
|
||||
.. image:: ../_static/cybertec-new.png
|
||||
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/2ndquadrant.png
|
||||
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/retool.png
|
||||
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/gnuhost.png
|
||||
:target: https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/supabase.png
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/oblivious.jpg
|
||||
:target: https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
* Evans Fernandes
|
||||
* `Jan Sommer <https://github.com/nerfpops>`_
|
||||
* `Franz Gusenbauer <https://www.igutech.at/>`_
|
||||
* `Daniel Babiak <https://github.com/dbabiak>`_
|
||||
* Tsingson Qin
|
||||
* Michel Pelletier
|
||||
* Jay Hannah
|
||||
* Robert Stolarz
|
||||
* Nicholas DiBiase
|
||||
* Christopher Reid
|
||||
* Nathan Bouscal
|
||||
* Daniel Rafaj
|
||||
* David Fenko
|
||||
* Remo Rechkemmer
|
||||
* Severin Ibarluzea
|
||||
* Tom Saleeba
|
||||
* Pawel Tyll
|
||||
|
||||
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
|
||||
@@ -0,0 +1,89 @@
|
||||
|
||||
PostgREST 9.0.1
|
||||
===============
|
||||
|
||||
This version includes important fixes for production environments and other miscellaneous fixes. You can download the pre-compiled binaries on the `GitHub release page <https://github.com/PostgREST/postgrest/releases/tag/v9.0.1>`_.
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
* Keep working when ``EMFILE (Too many open files)`` is reached. (`#2042 <https://github.com/PostgREST/postgrest/issues/2042>`_)
|
||||
|
||||
* Disable parallel GC for better performance on higher core CPUs (`#2294 <https://github.com/PostgREST/postgrest/issues/2294>`_). Thanks to `NoRedInk for their blog post <https://blog.noredink.com/post/666654908557180928/tuning-haskell-rts-for-kubernetes-part-2>`_ that lead us to this fix.
|
||||
|
||||
* Fix using CPU while idle. (`#1076 <https://github.com/PostgREST/postgrest/issues/1076>`_)
|
||||
|
||||
* Fix reading database configuration properly when ``=`` is present in the value. (`#2120 <https://github.com/PostgREST/postgrest/issues/2120>`_)
|
||||
|
||||
* Fix ``is`` not working with upper or mixed case values like ``NULL``, ``TrUe``, ``FaLsE``. (`#2077 <https://github.com/PostgREST/postgrest/issues/2077>`_)
|
||||
|
||||
* Execute deferred constraint triggers when using ``Prefer: tx=rollback``. (`#2020 <https://github.com/PostgREST/postgrest/issues/2020>`_)
|
||||
|
||||
* Ignore ``Content-Type`` headers for ``GET`` requests when calling RPCs. (`#2147 <https://github.com/PostgREST/postgrest/issues/2147>`_)
|
||||
|
||||
* Previously, ``GET`` without parameters, but with ``Content-Type: text/plain`` or ``Content-Type: application/octet-stream`` would fail with ``404 Not Found``, even if a function without arguments was available.
|
||||
|
||||
* Fix wrong CORS header from ``Authentication`` to ``Authorization``. (`#1724 <https://github.com/PostgREST/postgrest/issues/1724>`_)
|
||||
|
||||
* Fix ``json`` and ``jsonb`` columns showing a type in OpenAPI spec. (`#2165 <https://github.com/PostgREST/postgrest/issues/2165>`_)
|
||||
|
||||
* Remove trigger functions from the schema cache and OpenAPI output, because they can't be called directly anyway. (`#2135 <https://github.com/PostgREST/postgrest/issues/2135>`_)
|
||||
|
||||
* Remove aggregates, procedures and window functions from the schema cache and OpenAPI output. (`#2101 <https://github.com/PostgREST/postgrest/issues/2101>`_)
|
||||
|
||||
* Fix schema cache loading when views with ``XMLTABLE`` and ``DEFAULT`` are present. (`#2024 <https://github.com/PostgREST/postgrest/issues/2024>`_)
|
||||
|
||||
* Fix ``--dump-schema`` running with a wrong PG version. (`#2153 <https://github.com/PostgREST/postgrest/issues/2153>`_)
|
||||
|
||||
* Fix misleading disambiguation error where the content of the ``relationship`` key looks like valid syntax. (`#2239 <https://github.com/PostgREST/postgrest/issues/2239>`_)
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
Big thanks from the `PostgREST team <https://github.com/orgs/PostgREST/people>`_ to our sponsors!
|
||||
|
||||
.. container:: image-container
|
||||
|
||||
.. image:: ../_static/cybertec-new.png
|
||||
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/2ndquadrant.png
|
||||
:target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/retool.png
|
||||
:target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/gnuhost.png
|
||||
:target: https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/supabase.png
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
:width: 13em
|
||||
|
||||
.. image:: ../_static/oblivious.jpg
|
||||
:target: https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:width: 13em
|
||||
|
||||
* Evans Fernandes
|
||||
* `Jan Sommer <https://github.com/nerfpops>`_
|
||||
* `Franz Gusenbauer <https://www.igutech.at/>`_
|
||||
* `Daniel Babiak <https://github.com/dbabiak>`_
|
||||
* Tsingson Qin
|
||||
* Michel Pelletier
|
||||
* Jay Hannah
|
||||
* Robert Stolarz
|
||||
* Nicholas DiBiase
|
||||
* Christopher Reid
|
||||
* Nathan Bouscal
|
||||
* Daniel Rafaj
|
||||
* David Fenko
|
||||
* Remo Rechkemmer
|
||||
* Severin Ibarluzea
|
||||
* Tom Saleeba
|
||||
* Pawel Tyll
|
||||
|
||||
If you like to join them please consider `supporting PostgREST development <https://github.com/PostgREST/postgrest#user-content-supporting-development>`_.
|
||||
@@ -0,0 +1,6 @@
|
||||
docutils==0.20.1
|
||||
sphinx>=4.3.0
|
||||
sphinx-copybutton
|
||||
sphinx-rtd-theme>=0.5.1
|
||||
sphinx-tabs
|
||||
urllib3==2.2.3
|
||||
@@ -0,0 +1,237 @@
|
||||
.. _schema_cache:
|
||||
|
||||
Schema Cache
|
||||
============
|
||||
|
||||
Certain PostgREST features require metadata from the database schema. Getting this metadata requires executing expensive queries, so
|
||||
in order to avoid repeating this work, PostgREST uses a schema cache.
|
||||
|
||||
+--------------------------------------------+-------------------------------------------------------------------------------+
|
||||
| Feature | Required Metadata |
|
||||
+============================================+===============================================================================+
|
||||
| :ref:`resource_embedding` | Foreign key constraints |
|
||||
+--------------------------------------------+-------------------------------------------------------------------------------+
|
||||
| :ref:`Stored Functions <s_procs>` | Function signature (parameters, return type, volatility and |
|
||||
| | `overloading <https://www.postgresql.org/docs/current/xfunc-overload.html>`_) |
|
||||
+--------------------------------------------+-------------------------------------------------------------------------------+
|
||||
| :ref:`Upserts <upsert>` | Primary keys |
|
||||
+--------------------------------------------+-------------------------------------------------------------------------------+
|
||||
| :ref:`Insertions <insert>` | Primary keys (optional: only if the Location header is requested) |
|
||||
+--------------------------------------------+-------------------------------------------------------------------------------+
|
||||
| :ref:`OPTIONS requests <options_requests>` | View INSTEAD OF TRIGGERS and primary keys |
|
||||
+--------------------------------------------+-------------------------------------------------------------------------------+
|
||||
| :ref:`open-api` | Table columns, primary keys and foreign keys |
|
||||
+ +-------------------------------------------------------------------------------+
|
||||
| | View columns and INSTEAD OF TRIGGERS |
|
||||
+ +-------------------------------------------------------------------------------+
|
||||
| | Function signature |
|
||||
+--------------------------------------------+-------------------------------------------------------------------------------+
|
||||
|
||||
.. _stale_schema:
|
||||
|
||||
The Stale Schema Cache
|
||||
----------------------
|
||||
|
||||
When you make changes on the metadata mentioned above, the schema cache will turn stale on a running PostgREST. Future requests that use the above features will need the :ref:`schema cache to be reloaded <schema_reloading>`; otherwise, you'll get an error instead of the expected result.
|
||||
|
||||
For instance, let's see what would happen if you have a stale schema cache for foreign key relationships and function signatures.
|
||||
|
||||
.. _stale_fk_relationships:
|
||||
|
||||
Stale Foreign Key Relationships
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Suppose you add a ``cities`` table to your database and define a foreign key that references an existing ``countries`` table. Then, you make a request to get the ``cities`` and their belonging ``countries``.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /cities?select=name,country:countries(id,name) HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/cities?select=name,country:countries(id,name)"
|
||||
|
||||
The result will be an error:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"hint": "Verify that 'cities' and 'countries' exist in the schema 'api' and that there is a foreign key relationship between them. If a new relationship was created, try reloading the schema cache.",
|
||||
"details": null,
|
||||
"code": "PGRST200",
|
||||
"message": "Could not find a relationship between 'cities' and 'countries' in the schema cache"
|
||||
}
|
||||
|
||||
As you can see, PostgREST couldn't find the newly created foreign key in the schema cache. See :ref:`schema_reloading` and :ref:`auto_schema_reloading` to solve this issue.
|
||||
|
||||
.. _stale_function_signature:
|
||||
|
||||
Stale Function Signature
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The same issue will occur on newly created functions on a running PostgREST.
|
||||
|
||||
.. code-block:: plpgsql
|
||||
|
||||
CREATE FUNCTION plus_one(num integer)
|
||||
RETURNS integer AS $$
|
||||
SELECT num + 1;
|
||||
$$ LANGUAGE SQL IMMUTABLE;
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: http
|
||||
|
||||
GET /rpc/plus_one?num=1 HTTP/1.1
|
||||
|
||||
.. code-tab:: bash Curl
|
||||
|
||||
curl "http://localhost:3000/rpc/plus_one?num=1"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"hint": "If a new function was created in the database with this name and parameters, try reloading the schema cache.",
|
||||
"details": null,
|
||||
"code": "PGRST202",
|
||||
"message": "Could not find the api.plus_one(num) function in the schema cache"
|
||||
}
|
||||
|
||||
Here, PostgREST tries to find the function on the stale schema to no avail. See :ref:`schema_reloading` and :ref:`auto_schema_reloading` to solve this issue.
|
||||
|
||||
.. _schema_reloading:
|
||||
|
||||
Schema Cache Reloading
|
||||
----------------------
|
||||
|
||||
To reload the cache without restarting the PostgREST server, send a SIGUSR1 signal to the server process.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
killall -SIGUSR1 postgrest
|
||||
|
||||
|
||||
For docker you can do:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
docker kill -s SIGUSR1 <container>
|
||||
|
||||
# or in docker-compose
|
||||
docker-compose kill -s SIGUSR1 <service>
|
||||
|
||||
There's no downtime when reloading the schema cache. The reloading will happen on a background thread while requests keep being served.
|
||||
|
||||
.. _schema_reloading_notify:
|
||||
|
||||
Reloading with NOTIFY
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are environments where you can't send the SIGUSR1 Unix Signal (like on managed containers in cloud services or on Windows systems). For this reason, PostgREST also allows you to reload its schema cache through PostgreSQL `NOTIFY <https://www.postgresql.org/docs/current/sql-notify.html>`_ as follows:
|
||||
|
||||
.. code-block:: postgresql
|
||||
|
||||
NOTIFY pgrst, 'reload schema'
|
||||
|
||||
The ``"pgrst"`` notification channel is enabled by default. For configuring the channel, see :ref:`db-channel` and :ref:`db-channel-enabled`.
|
||||
|
||||
.. _auto_schema_reloading:
|
||||
|
||||
Automatic Schema Cache Reloading
|
||||
--------------------------------
|
||||
|
||||
You can do automatic schema cache reloading in a pure SQL way and forget about stale schema cache errors with an `event trigger <https://www.postgresql.org/docs/current/event-trigger-definition.html>`_ and ``NOTIFY``.
|
||||
|
||||
.. code-block:: postgresql
|
||||
|
||||
-- Create an event trigger function
|
||||
CREATE OR REPLACE FUNCTION pgrst_watch() RETURNS event_trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
NOTIFY pgrst, 'reload schema';
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- This event trigger will fire after every ddl_command_end event
|
||||
CREATE EVENT TRIGGER pgrst_watch
|
||||
ON ddl_command_end
|
||||
EXECUTE PROCEDURE pgrst_watch();
|
||||
|
||||
Now, whenever the ``pgrst_watch`` trigger is fired in the database, PostgREST will automatically reload the schema cache.
|
||||
|
||||
To disable auto reloading, drop the trigger:
|
||||
|
||||
.. code-block:: postgresql
|
||||
|
||||
DROP EVENT TRIGGER pgrst_watch
|
||||
|
||||
Finer-Grained Event Trigger
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can refine the previous event trigger and only react to the events relevant to the schema cache. This also prevents unnecessary
|
||||
reloading when creating temporary tables(``CREATE TEMP TABLE``) inside functions.
|
||||
|
||||
.. code-block:: postgresql
|
||||
|
||||
-- watch create and alter
|
||||
CREATE OR REPLACE FUNCTION pgrst_ddl_watch() RETURNS event_trigger AS $$
|
||||
DECLARE
|
||||
cmd record;
|
||||
BEGIN
|
||||
FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands()
|
||||
LOOP
|
||||
IF cmd.command_tag IN (
|
||||
'CREATE SCHEMA', 'ALTER SCHEMA'
|
||||
, 'CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO', 'ALTER TABLE'
|
||||
, 'CREATE FOREIGN TABLE', 'ALTER FOREIGN TABLE'
|
||||
, 'CREATE VIEW', 'ALTER VIEW'
|
||||
, 'CREATE MATERIALIZED VIEW', 'ALTER MATERIALIZED VIEW'
|
||||
, 'CREATE FUNCTION', 'ALTER FUNCTION'
|
||||
, 'CREATE TRIGGER'
|
||||
, 'CREATE TYPE', 'ALTER TYPE'
|
||||
, 'CREATE RULE'
|
||||
, 'COMMENT'
|
||||
)
|
||||
-- don't notify in case of CREATE TEMP table or other objects created on pg_temp
|
||||
AND cmd.schema_name is distinct from 'pg_temp'
|
||||
THEN
|
||||
NOTIFY pgrst, 'reload schema';
|
||||
END IF;
|
||||
END LOOP;
|
||||
END; $$ LANGUAGE plpgsql;
|
||||
|
||||
-- watch drop
|
||||
CREATE OR REPLACE FUNCTION pgrst_drop_watch() RETURNS event_trigger AS $$
|
||||
DECLARE
|
||||
obj record;
|
||||
BEGIN
|
||||
FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
|
||||
LOOP
|
||||
IF obj.object_type IN (
|
||||
'schema'
|
||||
, 'table'
|
||||
, 'foreign table'
|
||||
, 'view'
|
||||
, 'materialized view'
|
||||
, 'function'
|
||||
, 'trigger'
|
||||
, 'type'
|
||||
, 'rule'
|
||||
)
|
||||
AND obj.is_temporary IS false -- no pg_temp objects
|
||||
THEN
|
||||
NOTIFY pgrst, 'reload schema';
|
||||
END IF;
|
||||
END LOOP;
|
||||
END; $$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE EVENT TRIGGER pgrst_ddl_watch
|
||||
ON ddl_command_end
|
||||
EXECUTE PROCEDURE pgrst_ddl_watch();
|
||||
|
||||
CREATE EVENT TRIGGER pgrst_drop_watch
|
||||
ON sql_drop
|
||||
EXECUTE PROCEDURE pgrst_drop_watch();
|
||||
@@ -0,0 +1,96 @@
|
||||
|
||||
.. note::
|
||||
|
||||
This page is a work in progress.
|
||||
|
||||
.. _schema_isolation:
|
||||
|
||||
Schema Isolation
|
||||
================
|
||||
|
||||
A PostgREST instance exposes all the tables, views, and stored procedures of a single `PostgreSQL schema <https://www.postgresql.org/docs/current/ddl-schemas.html>`_ (a namespace of database objects). This means private data or implementation details can go inside different private schemas and be invisible to HTTP clients.
|
||||
|
||||
It is recommended that you don't expose tables on your API schema. Instead expose views and stored procedures which insulate the internal details from the outside world.
|
||||
This allows you to change the internals of your schema and maintain backwards compatibility. It also keeps your code easier to refactor, and provides a natural way to do API versioning.
|
||||
|
||||
.. image:: _static/db.png
|
||||
|
||||
.. _func_privs:
|
||||
|
||||
Functions
|
||||
=========
|
||||
|
||||
By default, when a function is created, the privilege to execute it is not restricted by role. The function access is ``PUBLIC`` — executable by all roles (more details at `PostgreSQL Privileges page <https://www.postgresql.org/docs/current/ddl-priv.html>`_). This is not ideal for an API schema. To disable this behavior, you can run the following SQL statement:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;
|
||||
|
||||
This will change the privileges for all functions created in the future in all schemas. Currently there is no way to limit it to a single schema. In our opinion it's a good practice anyway.
|
||||
|
||||
.. note::
|
||||
|
||||
It is however possible to limit the effect of this clause only to functions you define. You can put the above statement at the beginning of the API schema definition, and then at the end reverse it with:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
ALTER DEFAULT PRIVILEGES GRANT EXECUTE ON FUNCTIONS TO PUBLIC;
|
||||
|
||||
This will work because the :code:`alter default privileges` statement has effect on function created *after* it is executed. See `PostgreSQL alter default privileges <https://www.postgresql.org/docs/current/sql-alterdefaultprivileges.html>`_ for more details.
|
||||
|
||||
After that, you'll need to grant EXECUTE privileges on functions explicitly:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
GRANT EXECUTE ON FUNCTION login TO anonymous;
|
||||
GRANT EXECUTE ON FUNCTION signup TO anonymous;
|
||||
|
||||
You can also grant execute on all functions in a schema to a higher privileged role:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA api TO web_user;
|
||||
|
||||
Security definer
|
||||
----------------
|
||||
|
||||
A function is executed with the privileges of the user who calls it. This means that the user has to have all permissions to do the operations the procedure performs.
|
||||
If the function accesses private database objects, your :ref:`API roles <roles>` won't be able to successfully execute the function.
|
||||
|
||||
Another option is to define the function with the :code:`SECURITY DEFINER` option. Then only one permission check will take place, the permission to call the function, and the operations in the function will have the authority of the user who owns the function itself.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- login as a user wich has privileges on the private schemas
|
||||
|
||||
-- create a sample function
|
||||
create or replace function login(email text, pass text) returns jwt_token as $$
|
||||
begin
|
||||
-- access to a private schema called 'auth'
|
||||
select auth.user_role(email, pass) into _role;
|
||||
-- other operations
|
||||
-- ...
|
||||
end;
|
||||
$$ language plpgsql security definer;
|
||||
|
||||
Note the ``SECURITY DEFINER`` keywords at the end of the function. See `PostgreSQL documentation <https://www.postgresql.org/docs/current/sql-createfunction.html#SQL-CREATEFUNCTION-SECURITY>`_ for more details.
|
||||
|
||||
Views
|
||||
=====
|
||||
|
||||
Views are invoked with the privileges of the view owner, much like stored procedures with the ``SECURITY DEFINER`` option. When created by a SUPERUSER role, all `row-level security <https://www.postgresql.org/docs/current/ddl-rowsecurity.html>`_ will be bypassed unless a different, non-SUPERUSER owner is specified.
|
||||
|
||||
For changing this, we can create a non-SUPERUSER role and make this role the view's owner.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE ROLE api_views_owner NOINHERIT;
|
||||
ALTER VIEW sample_view OWNER TO api_views_owner;
|
||||
|
||||
Rules
|
||||
-----
|
||||
|
||||
Insertion on views with complex `rules <https://www.postgresql.org/docs/current/sql-createrule.html>`_ might not work out of the box with PostgREST.
|
||||
It's recommended that you `use triggers instead of rules <https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_rules>`_.
|
||||
If you want to keep using rules, a workaround is to wrap the view insertion in a stored procedure and call it through the :ref:`s_procs` interface.
|
||||
For more details, see this `github issue <https://github.com/PostgREST/postgrest/issues/1283>`_.
|
||||
@@ -0,0 +1,17 @@
|
||||
let
|
||||
docs =
|
||||
import ./default.nix;
|
||||
|
||||
inherit (docs) pkgs;
|
||||
in
|
||||
pkgs.mkShell {
|
||||
name = "postgrest-docs";
|
||||
|
||||
buildInputs = [
|
||||
docs.build
|
||||
docs.serve
|
||||
docs.spellcheck
|
||||
docs.dictcheck
|
||||
docs.linkcheck
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
.. _tut0:
|
||||
|
||||
Tutorial 0 - Get it Running
|
||||
===========================
|
||||
|
||||
:author: `begriffs <https://github.com/begriffs>`_
|
||||
|
||||
Welcome to PostgREST! In this pre-tutorial we're going to get things running so you can create your first simple API.
|
||||
|
||||
PostgREST is a standalone web server which turns a PostgreSQL database into a RESTful API. It serves an API that is customized based on the structure of the underlying database.
|
||||
|
||||
.. image:: ../_static/tuts/tut0-request-flow.png
|
||||
|
||||
To make an API we'll simply be building a database. All the endpoints and permissions come from database objects like tables, views, roles, and stored procedures. These tutorials will cover a number of common scenarios and how to model them in the database.
|
||||
|
||||
By the end of this tutorial you'll have a working database, PostgREST server, and a simple single-user todo list API.
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker run --name tutorial -p 5433:5432 \
|
||||
-e POSTGRES_PASSWORD=mysecretpassword \
|
||||
-d postgres
|
||||
|
||||
This will run the Docker instance as a daemon and expose port 5433 to the host system so that it looks like an ordinary PostgreSQL server to the rest of the system.
|
||||
|
||||
Step 2. Install PostgREST
|
||||
-------------------------
|
||||
|
||||
PostgREST is distributed as a single binary, with versions compiled for major distributions of Linux/BSD/Windows. Visit the `latest release <https://github.com/PostgREST/postgrest/releases/latest>`_ for a list of downloads. In the event that your platform is not among those already pre-built, see :ref:`build_source` for instructions how to build it yourself. Also let us know to add your platform in the next release.
|
||||
|
||||
The pre-built binaries for download are :code:`.tar.xz` compressed files (except Windows which is a zip file). To extract the binary, go into the terminal and run
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# download from https://github.com/PostgREST/postgrest/releases/latest
|
||||
|
||||
tar xJf postgrest-<version>-<platform>.tar.xz
|
||||
|
||||
The result will be a file named simply :code:`postgrest` (or :code:`postgrest.exe` on Windows). At this point try running it with
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./postgrest -h
|
||||
|
||||
If everything is working correctly it will print out its version and the available options. You can continue to run this binary from where you downloaded it, or copy it to a system directory like :code:`/usr/local/bin` on Linux so that you will be able to run it from any directory.
|
||||
|
||||
.. note::
|
||||
|
||||
PostgREST requires libpq, the PostgreSQL C library, to be installed on your system. Without the library you'll get an error like "error while loading shared libraries: libpq.so.5." Here's how to fix it:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<p>
|
||||
<details>
|
||||
<summary>Ubuntu or Debian</summary>
|
||||
<div class="highlight-bash"><div class="highlight">
|
||||
<pre>sudo apt-get install libpq-dev</pre>
|
||||
</div></div>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Fedora, CentOS, or Red Hat</summary>
|
||||
<div class="highlight-bash"><div class="highlight">
|
||||
<pre>sudo yum install postgresql-libs</pre>
|
||||
</div></div>
|
||||
</details>
|
||||
<details>
|
||||
<summary>OS X</summary>
|
||||
<div class="highlight-bash"><div class="highlight">
|
||||
<pre>brew install postgresql</pre>
|
||||
</div></div>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Windows</summary>
|
||||
<p>All of the DLL files that are required to run PostgREST are available in the windows installation of PostgreSQL server.
|
||||
Once installed they are found in the BIN folder, e.g: C:\Program Files\PostgreSQL\10\bin. Add this directory to your PATH
|
||||
variable. Run the following from an administrative command prompt (adjusting the actual BIN path as necessary of course)
|
||||
<pre>setx /m PATH "%PATH%;C:\Program Files\PostgreSQL\10\bin"</pre>
|
||||
</p>
|
||||
</details>
|
||||
</p>
|
||||
|
||||
Step 3. Create Database for API
|
||||
-------------------------------
|
||||
|
||||
Connect to the SQL console (psql) inside the container. To do so, run this from your command line:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker exec -it tutorial psql -U postgres
|
||||
|
||||
You should see the psql command prompt:
|
||||
|
||||
::
|
||||
|
||||
psql (9.6.3)
|
||||
Type "help" for help.
|
||||
|
||||
postgres=#
|
||||
|
||||
The first thing we'll do is create a `named schema <https://www.postgresql.org/docs/current/ddl-schemas.html>`_ for the database objects which will be exposed in the API. We can choose any name we like, so how about "api." Execute this and the other SQL statements inside the psql prompt you started.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create schema api;
|
||||
|
||||
Our API will have one endpoint, :code:`/todos`, which will come from a table.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create table api.todos (
|
||||
id serial primary key,
|
||||
done boolean not null default false,
|
||||
task text not null,
|
||||
due timestamptz
|
||||
);
|
||||
|
||||
insert into api.todos (task) values
|
||||
('finish tutorial 0'), ('pat self on back');
|
||||
|
||||
Next make a role to use for anonymous web requests. When a request comes in, PostgREST will switch into this role in the database to run queries.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create role web_anon nologin;
|
||||
|
||||
grant usage on schema api to web_anon;
|
||||
grant select on api.todos to web_anon;
|
||||
|
||||
The :code:`web_anon` role has permission to access things in the :code:`api` schema, and to read rows in the :code:`todos` table.
|
||||
|
||||
It's a good practice to create a dedicated role for connecting to the database, instead of using the highly privileged ``postgres`` role. So we'll do that, name the role ``authenticator`` and also grant it the ability to switch to the ``web_anon`` role :
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create role authenticator noinherit login password 'mysecretpassword';
|
||||
grant web_anon to authenticator;
|
||||
|
||||
|
||||
Now quit out of psql; it's time to start the API!
|
||||
|
||||
.. code-block:: psql
|
||||
|
||||
\q
|
||||
|
||||
Step 4. Run PostgREST
|
||||
---------------------
|
||||
|
||||
PostgREST can use a configuration file to tell it how to connect to the database. Create a file :code:`tutorial.conf` with this inside:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
db-uri = "postgres://authenticator:mysecretpassword@localhost:5433/postgres"
|
||||
db-schemas = "api"
|
||||
db-anon-role = "web_anon"
|
||||
|
||||
The configuration file has other :doc:`options <../configuration>`, but this is all we need.
|
||||
If you are not using Docker, make sure that your port number is correct and replace `postgres` with the name of the database where you added the todos table.
|
||||
|
||||
Now run the server:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./postgrest tutorial.conf
|
||||
|
||||
You should see
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Listening on port 3000
|
||||
Attempting to connect to the database...
|
||||
Connection successful
|
||||
|
||||
It's now ready to serve web requests. There are many nice graphical API exploration tools you can use, but for this tutorial we'll use :code:`curl` because it's likely to be installed on your system already. Open a new terminal (leaving the one open that PostgREST is running inside). Try doing an HTTP request for the todos.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl http://localhost:3000/todos
|
||||
|
||||
The API replies:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"done": false,
|
||||
"task": "finish tutorial 0",
|
||||
"due": null
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"done": false,
|
||||
"task": "pat self on back",
|
||||
"due": null
|
||||
}
|
||||
]
|
||||
|
||||
With the current role permissions, anonymous requests have read-only access to the :code:`todos` table. If we try to add a new todo we are not able.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl http://localhost:3000/todos -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"task": "do bad thing"}'
|
||||
|
||||
Response is 401 Unauthorized:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"hint": null,
|
||||
"details": null,
|
||||
"code": "42501",
|
||||
"message": "permission denied for table todos"
|
||||
}
|
||||
|
||||
There we have it, a basic API on top of the database! In the next tutorials we will see how to extend the example with more sophisticated user access controls, and more tables and queries.
|
||||
|
||||
Now that you have PostgREST running, try the next tutorial, :ref:`tut1`
|
||||
@@ -0,0 +1,263 @@
|
||||
.. _tut1:
|
||||
|
||||
Tutorial 1 - The Golden Key
|
||||
===========================
|
||||
|
||||
:author: `begriffs <https://github.com/begriffs>`_
|
||||
|
||||
In :ref:`tut0` we created a read-only API with a single endpoint to list todos. There are many directions we can go to make this API more interesting, but one good place to start would be allowing some users to change data in addition to reading it.
|
||||
|
||||
Step 1. Add a Trusted User
|
||||
--------------------------
|
||||
|
||||
The previous tutorial created a :code:`web_anon` role in the database with which to execute anonymous web requests. Let's make a role called :code:`todo_user` for users who authenticate with the API. This role will have the authority to do anything to the todo list.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
-- run this in psql using the database created
|
||||
-- in the previous tutorial
|
||||
|
||||
create role todo_user nologin;
|
||||
grant todo_user to authenticator;
|
||||
|
||||
grant usage on schema api to todo_user;
|
||||
grant all on api.todos to todo_user;
|
||||
grant usage, select on sequence api.todos_id_seq to todo_user;
|
||||
|
||||
Step 2. Make a Secret
|
||||
---------------------
|
||||
|
||||
Clients authenticate with the API using JSON Web Tokens. These are JSON objects which are cryptographically signed using a password known to only us and the server. Because clients do not know the password, they cannot tamper with the contents of their tokens. PostgREST will detect counterfeit tokens and will reject them.
|
||||
|
||||
Let's create a password and provide it to PostgREST. Think of a nice long one, or use a tool to generate it. **Your password must be at least 32 characters long.**
|
||||
|
||||
.. note::
|
||||
|
||||
Unix tools can generate a nice password for you:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Allow "tr" to process non-utf8 byte sequences
|
||||
export LC_CTYPE=C
|
||||
|
||||
# read random bytes and keep only alphanumerics
|
||||
< /dev/urandom tr -dc A-Za-z0-9 | head -c32
|
||||
|
||||
Open the :code:`tutorial.conf` (created in the previous tutorial) and add a line with the password:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# PASSWORD MUST BE AT LEAST 32 CHARS LONG
|
||||
# add this line to tutorial.conf:
|
||||
|
||||
jwt-secret = "<the password you made>"
|
||||
|
||||
If the PostgREST server is still running from the previous tutorial, restart it to load the updated configuration file.
|
||||
|
||||
Step 3. Sign a Token
|
||||
--------------------
|
||||
|
||||
Ordinarily your own code in the database or in another server will create and sign authentication tokens, but for this tutorial we will make one "by hand." Go to `jwt.io <https://jwt.io/#debugger-io>`_ and fill in the fields like this:
|
||||
|
||||
.. figure:: ../_static/tuts/tut1-jwt-io.png
|
||||
:alt: jwt.io interface
|
||||
|
||||
How to create a token at https://jwt.io
|
||||
|
||||
**Remember to fill in the password you generated rather than the word "secret".** After you have filled in the password and payload, the encoded data on the left will update. Copy the encoded token.
|
||||
|
||||
.. note::
|
||||
|
||||
While the token may look well obscured, it's easy to reverse engineer the payload. The token is merely signed, not encrypted, so don't put things inside that you don't want a determined client to see.
|
||||
|
||||
Step 4. Make a Request
|
||||
----------------------
|
||||
|
||||
Back in the terminal, let's use :code:`curl` to add a todo. The request will include an HTTP header containing the authentication token.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
export TOKEN="<paste token here>"
|
||||
|
||||
curl http://localhost:3000/todos -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"task": "learn how to auth"}'
|
||||
|
||||
And now we have completed all three items in our todo list, so let's set :code:`done` to true for them all with a :code:`PATCH` request.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl http://localhost:3000/todos -X PATCH \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"done": true}'
|
||||
|
||||
A request for the todos shows three of them, and all completed.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl http://localhost:3000/todos
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"done": true,
|
||||
"task": "finish tutorial 0",
|
||||
"due": null
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"done": true,
|
||||
"task": "pat self on back",
|
||||
"due": null
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"done": true,
|
||||
"task": "learn how to auth",
|
||||
"due": null
|
||||
}
|
||||
]
|
||||
|
||||
Step 5. Add Expiration
|
||||
----------------------
|
||||
|
||||
Currently our authentication token is valid for all eternity. The server, as long as it continues using the same JWT password, will honor the token.
|
||||
|
||||
It's better policy to include an expiration timestamp for tokens using the :code:`exp` claim. This is one of two JWT claims that PostgREST treats specially.
|
||||
|
||||
+--------------+----------------------------------------------------------------+
|
||||
| Claim | Interpretation |
|
||||
+==============+================================================================+
|
||||
| :code:`role` | The database role under which to execute SQL for API request |
|
||||
+--------------+----------------------------------------------------------------+
|
||||
| :code:`exp` | Expiration timestamp for token, expressed in "Unix epoch time" |
|
||||
+--------------+----------------------------------------------------------------+
|
||||
|
||||
.. note::
|
||||
|
||||
Epoch time is defined as the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC), January 1st 1970, minus the number of leap seconds that have taken place since then.
|
||||
|
||||
To observe expiration in action, we'll add an :code:`exp` claim of five minutes in the future to our previous token. First find the epoch value of five minutes from now. In psql run this:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
select extract(epoch from now() + '5 minutes'::interval) :: integer;
|
||||
|
||||
Go back to jwt.io and change the payload to
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"role": "todo_user",
|
||||
"exp": 123456789
|
||||
}
|
||||
|
||||
**NOTE**: Don't forget to change the dummy epoch value :code:`123456789` in the snippet above to the epoch value returned by the psql command.
|
||||
|
||||
Copy the updated token as before, and save it as a new environment variable.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
export NEW_TOKEN="<paste new token>"
|
||||
|
||||
Try issuing this request in curl before and after the expiration time:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl http://localhost:3000/todos \
|
||||
-H "Authorization: Bearer $NEW_TOKEN"
|
||||
|
||||
After expiration, the API returns HTTP 401 Unauthorized:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"hint": null,
|
||||
"details": null,
|
||||
"code": "PGRST301",
|
||||
"message": "JWT expired"
|
||||
}
|
||||
|
||||
Bonus Topic: Immediate Revocation
|
||||
---------------------------------
|
||||
|
||||
Even with token expiration there are times when you may want to immediately revoke access for a specific token. For instance, suppose you learn that a disgruntled employee is up to no good and his token is still valid.
|
||||
|
||||
To revoke a specific token we need a way to tell it apart from others. Let's add a custom :code:`email` claim that matches the email of the client issued the token.
|
||||
|
||||
Go ahead and make a new token with the payload
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"role": "todo_user",
|
||||
"email": "disgruntled@mycompany.com"
|
||||
}
|
||||
|
||||
Save it to an environment variable:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
export WAYWARD_TOKEN="<paste new token>"
|
||||
|
||||
PostgREST allows us to specify a stored procedure to run during attempted authentication. The function can do whatever it likes, including raising an exception to terminate the request.
|
||||
|
||||
First make a new schema and add the function:
|
||||
|
||||
.. code-block:: plpgsql
|
||||
|
||||
create schema auth;
|
||||
grant usage on schema auth to web_anon, todo_user;
|
||||
|
||||
create or replace function auth.check_token() returns void
|
||||
language plpgsql
|
||||
as $$
|
||||
begin
|
||||
if current_setting('request.jwt.claims', true)::json->>'email' =
|
||||
'disgruntled@mycompany.com' then
|
||||
raise insufficient_privilege
|
||||
using hint = 'Nope, we are on to you';
|
||||
end if;
|
||||
end
|
||||
$$;
|
||||
|
||||
Next update :code:`tutorial.conf` and specify the new function:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# add this line to tutorial.conf
|
||||
|
||||
db-pre-request = "auth.check_token"
|
||||
|
||||
Restart PostgREST for the change to take effect. Next try making a request with our original token and then with the revoked one.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# this request still works
|
||||
|
||||
curl http://localhost:3000/todos -X PATCH \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"done": true}'
|
||||
|
||||
# this one is rejected
|
||||
|
||||
curl http://localhost:3000/todos -X PATCH \
|
||||
-H "Authorization: Bearer $WAYWARD_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"task": "AAAHHHH!", "done": false}'
|
||||
|
||||
The server responds with 403 Forbidden:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"hint": "Nope, we are on to you",
|
||||
"details": null,
|
||||
"code": "42501",
|
||||
"message": "insufficient_privilege"
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
# This Dockerfile is only used as a development environment for
|
||||
# non-nix systems, i.e. Windows.
|
||||
|
||||
FROM nixos/nix:latest
|
||||
|
||||
RUN apk --no-cache add \
|
||||
wget
|
||||
|
||||
RUN nix-env -iA cachix -f https://cachix.org/api/v1/install \
|
||||
&& cachix use postgrest
|
||||
|
||||
# We need an unprivileged user here, to make PG run at all.
|
||||
RUN adduser --disabled-password --ingroup root nix \
|
||||
&& chown -R nix:root /nix
|
||||
USER nix:root
|
||||
ENV USER=nix
|
||||
|
||||
VOLUME /nix
|
||||
VOLUME /postgrest
|
||||
WORKDIR /postgrest
|
||||
|
||||
CMD nix-shell
|
||||
@@ -208,12 +208,6 @@ postgrest-loadtest-against master
|
||||
postgrest-loadtest-report
|
||||
```
|
||||
|
||||
Our query cost tests ensure that our generated queries don't surpass a threshold EXPLAIN cost.
|
||||
|
||||
```bash
|
||||
postgrest-test-querycost
|
||||
```
|
||||
|
||||
doctests for some of our modules are also available:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
nix:
|
||||
container_name: postgrest-nix
|
||||
build: .
|
||||
volumes:
|
||||
- ../:/postgrest
|
||||
- nix:/nix
|
||||
|
||||
volumes:
|
||||
nix:
|
||||
@@ -1,6 +1,6 @@
|
||||
# Pinned version of Nixpkgs, generated with postgrest-nixpkgs-upgrade.
|
||||
{
|
||||
date = "2022-10-28";
|
||||
rev = "f44ba1be526c8da9e79a5759feca2365204003f6";
|
||||
tarballHash = "0npbwsdjw88py5w2pjflwh94wgi4jmnmls0k1n7q8m6h94w1y1ps";
|
||||
date = "2023-01-12";
|
||||
rev = "92f9580a4c369b4b51a7b6a5e77da43720134c9f";
|
||||
tarballHash = "0w9bz4f2bmkj4a59n4z279zcgs9clyc40a4ny312rafyaknzghvw";
|
||||
}
|
||||
|
||||
@@ -29,33 +29,33 @@ let
|
||||
# To fill in the sha256:
|
||||
# update-nix-fetchgit nix/overlays/haskell-packages.nix
|
||||
|
||||
hashtables = lib.dontCheck prev.hashtables_1_3_1;
|
||||
hasql = lib.dontCheck prev.hasql_1_6_1_4;
|
||||
hasql-dynamic-statements = lib.dontCheck prev.hasql-dynamic-statements_0_3_1_2;
|
||||
hasql-pool = lib.dontCheck
|
||||
(prev.callHackageDirect
|
||||
{
|
||||
pkg = "hasql-pool";
|
||||
ver = "0.8.0.6";
|
||||
sha256 = "sha256-2u/cwPk8XfXffaDRzGeyzhL+9k2+2T4b8bGOZwz8AX0=";
|
||||
}
|
||||
{ });
|
||||
hasql-transaction = lib.dontCheck prev.hasql-transaction_1_0_1_2;
|
||||
isomorphism-class = lib.unmarkBroken prev.isomorphism-class;
|
||||
lens = lib.dontCheck prev.lens_5_2;
|
||||
postgresql-binary = lib.dontCheck prev.postgresql-binary_0_13_1;
|
||||
text-builder = lib.dontCheck prev.text-builder_0_6_7;
|
||||
text-builder-dev = lib.dontCheck prev.text-builder-dev_0_3_3;
|
||||
|
||||
postgresql-libpq = lib.dontCheck
|
||||
(prev.callCabal2nix "postgresql-libpq"
|
||||
(super.fetchFromGitHub {
|
||||
owner = "PostgREST";
|
||||
repo = "postgresql-libpq";
|
||||
rev = "cef92cb4c07b56568dffdbf4b719258b82183119"; # master
|
||||
sha256 = "0r59klrz47qcnd22s47h612mlz3jbg40wwalfj3f6djwg0cdyr85";
|
||||
rev = "890a0a16cf57dd401420fdc6c7d576fb696003bc"; # master
|
||||
sha256 = "1wmyhldk0k14y8whp1p4akrkqxf5snh8qsbm7fv5f7kz95nyffd0";
|
||||
})
|
||||
{ });
|
||||
|
||||
hasql-notifications = lib.dontCheck
|
||||
(prev.callHackageDirect
|
||||
{
|
||||
pkg = "hasql-notifications";
|
||||
ver = "0.2.0.4";
|
||||
sha256 = "sha256-fm1xiDyvDkb5WLOJ73/s8wrWEW23XFS7luAv2brfr8I=";
|
||||
}
|
||||
{ });
|
||||
|
||||
hasql-pool = lib.dontCheck
|
||||
(prev.callHackageDirect
|
||||
{
|
||||
pkg = "hasql-pool";
|
||||
ver = "0.9";
|
||||
sha256 = "sha256-5UshbbaBVY8eJ/9VagNVVxonRwMcd7UmGqDc35pJNFY=";
|
||||
}
|
||||
{ });
|
||||
} // extraOverrides final prev;
|
||||
in
|
||||
{
|
||||
|
||||
@@ -77,7 +77,6 @@ let
|
||||
}
|
||||
''
|
||||
${tests}/bin/postgrest-test-spec
|
||||
${tests}/bin/postgrest-test-querycost
|
||||
${tests}/bin/postgrest-test-doctests
|
||||
${tests}/bin/postgrest-test-io
|
||||
${style}/bin/postgrest-lint
|
||||
@@ -165,6 +164,7 @@ let
|
||||
# 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
|
||||
|
||||
@@ -106,7 +106,7 @@ let
|
||||
echo "Tagging ..."
|
||||
git tag "v$new_version" > /dev/null
|
||||
|
||||
trap "Couldn't find remote. Please push manually ..." ERR
|
||||
trap "echo Remote not found. Please push manually ..." ERR
|
||||
remote="$(git remote -v | grep PostgREST/postgrest | grep push | cut -f1)"
|
||||
trap "" ERR
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# PostgREST
|
||||
|
||||
[](https://gitter.im/begriffs/postgrest)
|
||||
[](https://www.patreon.com/postgrest)
|
||||
[](https://www.paypal.me/postgrest)
|
||||
[](http://postgrest.org)
|
||||
[](https://github.com/PostgREST/postgrest/actions?query=branch%3Amain)
|
||||
|
||||
|
||||
@@ -32,18 +32,6 @@ let
|
||||
test:spec -- "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
|
||||
testQuerycost =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-test-querycost";
|
||||
docs = "Run the Haskell test suite for query costs";
|
||||
inRootDir = true;
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
${withTools.withPg} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:querycost
|
||||
'';
|
||||
|
||||
testDoctests =
|
||||
checkedShellScript
|
||||
{
|
||||
@@ -140,7 +128,7 @@ let
|
||||
rm -rf coverage/*
|
||||
|
||||
# build once before running all the tests
|
||||
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest lib:postgrest test:spec test:querycost
|
||||
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest lib:postgrest test:spec
|
||||
|
||||
(
|
||||
trap 'echo Found dead code: Check file list above.' ERR ;
|
||||
@@ -155,14 +143,11 @@ let
|
||||
HPCTIXFILE="$tmpdir"/spec.tix \
|
||||
${withTools.withPg} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:spec
|
||||
|
||||
HPCTIXFILE="$tmpdir"/querycost.tix \
|
||||
${withTools.withPg} ${cabal-install}/bin/cabal v2-run ${devCabalOptions} test:querycost
|
||||
|
||||
# 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"/spec.tix "$tmpdir"/querycost.tix
|
||||
"$tmpdir"/io*.tix "$tmpdir"/spec.tix
|
||||
|
||||
# prepare the overlay
|
||||
${ghc}/bin/hpc overlay --output="$tmpdir"/overlay.tix test/coverage.overlay
|
||||
@@ -234,7 +219,6 @@ buildToolbox
|
||||
tools =
|
||||
[
|
||||
testSpec
|
||||
testQuerycost
|
||||
testDoctests
|
||||
testSpecIdempotence
|
||||
testIO
|
||||
|
||||
@@ -69,6 +69,7 @@ let
|
||||
pg_ctl -l "$tmpdir/db.log" -w start -o "-F -c listen_addresses=\"\" -c hba_file=$HBA_FILE -k $PGHOST -c log_statement=\"all\"" \
|
||||
>> "$setuplog"
|
||||
|
||||
# shellcheck disable=SC2317
|
||||
stop () {
|
||||
log "Stopping the database cluster..."
|
||||
pg_ctl stop -m i >> "$setuplog"
|
||||
@@ -260,6 +261,7 @@ let
|
||||
echo -n "Starting postgrest... "
|
||||
./result/bin/postgrest ${legacyConfig} > "$tmpdir"/run.log 2>&1 &
|
||||
pid=$!
|
||||
# shellcheck disable=SC2317
|
||||
cleanup() {
|
||||
kill "$pid" || true
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: postgrest
|
||||
version: 10.1.2
|
||||
version: 10.2.0
|
||||
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
|
||||
@@ -72,7 +72,7 @@ library
|
||||
PostgREST.Workers
|
||||
other-modules: Paths_postgrest
|
||||
build-depends: base >= 4.9 && < 4.17
|
||||
, HTTP >= 4000.3.7 && < 4000.4
|
||||
, HTTP >= 4000.3.7 && < 4000.5
|
||||
, Ranged-sets >= 0.3 && < 0.5
|
||||
, aeson >= 2.0.3 && < 2.2
|
||||
, auto-update >= 0.1.4 && < 0.2
|
||||
@@ -90,7 +90,7 @@ library
|
||||
, hasql >= 1.6.1.1 && < 1.7
|
||||
, hasql-dynamic-statements >= 0.3.1 && < 0.4
|
||||
, hasql-notifications >= 0.1 && < 0.3
|
||||
, hasql-pool >= 0.8.0.6 && < 0.9
|
||||
, hasql-pool >= 0.9 && < 0.10
|
||||
, hasql-transaction >= 1.0.1 && < 1.1
|
||||
, heredoc >= 0.2 && < 0.3
|
||||
, http-types >= 0.12.2 && < 0.13
|
||||
@@ -98,11 +98,11 @@ library
|
||||
, interpolatedstring-perl6 >= 1 && < 1.1
|
||||
, jose >= 0.8.5.1 && < 0.11
|
||||
, lens >= 4.14 && < 5.3
|
||||
, lens-aeson >= 1.0.1 && < 1.2
|
||||
, lens-aeson >= 1.0.1 && < 1.3
|
||||
, mtl >= 2.2.2 && < 2.3
|
||||
, network >= 2.6 && < 3.2
|
||||
, network-uri >= 2.6.1 && < 2.8
|
||||
, optparse-applicative >= 0.13 && < 0.17
|
||||
, optparse-applicative >= 0.13 && < 0.18
|
||||
, parsec >= 3.1.11 && < 3.2
|
||||
, protolude >= 0.3.1 && < 0.4
|
||||
, regex-tdfa >= 1.2.2 && < 1.4
|
||||
@@ -228,69 +228,32 @@ test-suite spec
|
||||
, bytestring >= 0.10.8 && < 0.12
|
||||
, case-insensitive >= 1.2 && < 1.3
|
||||
, containers >= 0.5.7 && < 0.7
|
||||
, hasql-pool >= 0.8.0.2 && < 0.9
|
||||
, hasql-pool >= 0.9 && < 0.10
|
||||
, hasql-transaction >= 1.0.1 && < 1.1
|
||||
, heredoc >= 0.2 && < 0.3
|
||||
, hspec >= 2.3 && < 2.9
|
||||
, hspec >= 2.3 && < 2.10
|
||||
, hspec-wai >= 0.10 && < 0.12
|
||||
, hspec-wai-json >= 0.10 && < 0.12
|
||||
, http-types >= 0.12.3 && < 0.13
|
||||
, lens >= 4.14 && < 5.3
|
||||
, lens-aeson >= 1.0.1 && < 1.2
|
||||
, lens-aeson >= 1.0.1 && < 1.3
|
||||
, monad-control >= 1.0.1 && < 1.1
|
||||
, postgrest
|
||||
, process >= 1.4.2 && < 1.7
|
||||
, protolude >= 0.3.1 && < 0.4
|
||||
, regex-tdfa >= 1.2.2 && < 1.4
|
||||
, scientific >= 0.3.4 && < 0.4
|
||||
, text >= 1.2.2 && < 1.3
|
||||
, transformers-base >= 0.4.4 && < 0.5
|
||||
, wai >= 3.2.1 && < 3.3
|
||||
, wai-extra >= 3.0.19 && < 3.2
|
||||
ghc-options: -O0 -Werror -Wall -fwarn-identities
|
||||
ghc-options: -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 querycost
|
||||
type: exitcode-stdio-1.0
|
||||
default-language: Haskell2010
|
||||
default-extensions: OverloadedStrings
|
||||
QuasiQuotes
|
||||
NoImplicitPrelude
|
||||
hs-source-dirs: test/spec
|
||||
main-is: QueryCost.hs
|
||||
other-modules: SpecHelper
|
||||
build-depends: base >= 4.9 && < 4.17
|
||||
, aeson >= 2.0.3 && < 2.2
|
||||
, base64-bytestring >= 1 && < 1.3
|
||||
, bytestring >= 0.10.8 && < 0.12
|
||||
, case-insensitive >= 1.2 && < 1.3
|
||||
, containers >= 0.5.7 && < 0.7
|
||||
, contravariant >= 1.4 && < 1.6
|
||||
, hasql >= 1.6 && < 1.7
|
||||
, hasql-dynamic-statements >= 0.3.1 && < 0.4
|
||||
, hasql-pool >= 0.8.0.2 && < 0.9
|
||||
, hasql-transaction >= 1.0.1 && < 1.1
|
||||
, heredoc >= 0.2 && < 0.3
|
||||
, hspec >= 2.3 && < 2.9
|
||||
, hspec-wai >= 0.10 && < 0.12
|
||||
, hspec-wai-json >= 0.10 && < 0.12
|
||||
, http-types >= 0.12.3 && < 0.13
|
||||
, lens >= 4.14 && < 5.3
|
||||
, lens-aeson >= 1.0.1 && < 1.2
|
||||
, postgrest
|
||||
, process >= 1.4.2 && < 1.7
|
||||
, protolude >= 0.3.1 && < 0.4
|
||||
, regex-tdfa >= 1.2.2 && < 1.4
|
||||
, wai-extra >= 3.0.19 && < 3.2
|
||||
ghc-options: -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=-K1K
|
||||
|
||||
test-suite doctests
|
||||
type: exitcode-stdio-1.0
|
||||
default-language: Haskell2010
|
||||
|
||||
@@ -139,7 +139,7 @@ parse qs =
|
||||
<*> (fmap snd <$> (pRequestFilter `traverse` filtersRoot))
|
||||
<*> pRequestFilter `traverse` filtersNotRoot
|
||||
<*> pure (S.fromList (fst <$> filters))
|
||||
<*> sequenceA (pRequestOnConflict <$> onConflict)
|
||||
<*> pRequestOnConflict `traverse` onConflict
|
||||
where
|
||||
logic = filter (endingIn ["and", "or"] . fst) nonemptyParams
|
||||
select = fromMaybe "*" $ lookupParam "select"
|
||||
|
||||
@@ -9,6 +9,7 @@ Some of its functionality includes:
|
||||
- Producing HTTP Headers according to RFCs.
|
||||
- Content Negotiation
|
||||
-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
module PostgREST.App
|
||||
( SignalHandlerInstaller
|
||||
@@ -19,13 +20,14 @@ module PostgREST.App
|
||||
|
||||
|
||||
import Control.Monad.Except (liftEither)
|
||||
import Data.Either.Combinators (mapLeft)
|
||||
import Data.Either.Combinators (mapLeft, whenLeft)
|
||||
import Data.Maybe (fromJust)
|
||||
import Data.String (IsString (..))
|
||||
import Network.Wai.Handler.Warp (defaultSettings, setHost, setPort,
|
||||
setServerName)
|
||||
import System.Posix.Types (FileMode)
|
||||
|
||||
import qualified Hasql.Pool as SQL
|
||||
import qualified Hasql.Transaction.Sessions as SQL
|
||||
import qualified Network.Wai as Wai
|
||||
import qualified Network.Wai.Handler.Warp as Warp
|
||||
@@ -153,9 +155,13 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache jsonDbS pgVer aut
|
||||
|
||||
runDbHandler :: AppState.AppState -> SQL.Mode -> Bool -> Bool -> DbHandler b -> Handler IO b
|
||||
runDbHandler appState mode authenticated prepared handler = do
|
||||
dbResp <-
|
||||
let transaction = if prepared then SQL.transaction else SQL.unpreparedTransaction in
|
||||
lift . AppState.usePool appState . transaction SQL.ReadCommitted mode $ runExceptT handler
|
||||
dbResp <- lift $ do
|
||||
let transaction = if prepared then SQL.transaction else SQL.unpreparedTransaction
|
||||
res <- AppState.usePool appState . transaction SQL.ReadCommitted mode $ runExceptT handler
|
||||
whenLeft res (\case
|
||||
SQL.AcquisitionTimeoutUsageError -> AppState.debounceLogAcquisitionTimeout appState -- this can happen rapidly for many requests, so we debounce
|
||||
_ -> pure ())
|
||||
return res
|
||||
|
||||
resp <-
|
||||
liftEither . mapLeft Error.PgErr $
|
||||
|
||||
@@ -16,6 +16,7 @@ module PostgREST.AppState
|
||||
, init
|
||||
, initWithPool
|
||||
, logWithZTime
|
||||
, logPgrstError
|
||||
, putConfig
|
||||
, putSchemaCache
|
||||
, putIsListenerOn
|
||||
@@ -25,13 +26,18 @@ module PostgREST.AppState
|
||||
, signalListener
|
||||
, usePool
|
||||
, waitListener
|
||||
, debounceLogAcquisitionTimeout
|
||||
) where
|
||||
|
||||
import qualified Hasql.Pool as SQL
|
||||
import qualified Hasql.Session as SQL
|
||||
import qualified Data.ByteString.Lazy as LBS
|
||||
import qualified Data.Text.Encoding as T
|
||||
import qualified Hasql.Pool as SQL
|
||||
import qualified Hasql.Session as SQL
|
||||
import qualified PostgREST.Error as Error
|
||||
|
||||
import Control.AutoUpdate (defaultUpdateSettings, mkAutoUpdate,
|
||||
updateAction)
|
||||
import Control.Debounce
|
||||
import Data.IORef (IORef, atomicWriteIORef, newIORef,
|
||||
readIORef)
|
||||
import Data.Time (ZonedTime, defaultTimeLocale, formatTime,
|
||||
@@ -47,29 +53,31 @@ import Protolude
|
||||
|
||||
data AppState = AppState
|
||||
-- | Database connection pool
|
||||
{ statePool :: SQL.Pool
|
||||
{ statePool :: SQL.Pool
|
||||
-- | Database server version, will be updated by the connectionWorker
|
||||
, statePgVersion :: IORef PgVersion
|
||||
, statePgVersion :: IORef PgVersion
|
||||
-- | No schema cache at the start. Will be filled in by the connectionWorker
|
||||
, stateSchemaCache :: IORef (Maybe SchemaCache)
|
||||
, stateSchemaCache :: IORef (Maybe SchemaCache)
|
||||
-- | Cached SchemaCache in json
|
||||
, stateJsonDbS :: IORef ByteString
|
||||
, stateJsonDbS :: IORef ByteString
|
||||
-- | Binary semaphore to make sure just one connectionWorker can run at a time
|
||||
, stateWorkerSem :: MVar ()
|
||||
, stateWorkerSem :: MVar ()
|
||||
-- | Binary semaphore used to sync the listener(NOTIFY reload) with the connectionWorker.
|
||||
, stateListener :: MVar ()
|
||||
, stateListener :: MVar ()
|
||||
-- | State of the LISTEN channel, used for the admin server checks
|
||||
, stateIsListenerOn :: IORef Bool
|
||||
, stateIsListenerOn :: IORef Bool
|
||||
-- | Config that can change at runtime
|
||||
, stateConf :: IORef AppConfig
|
||||
, stateConf :: IORef AppConfig
|
||||
-- | Time used for verifying JWT expiration
|
||||
, stateGetTime :: IO UTCTime
|
||||
, stateGetTime :: IO UTCTime
|
||||
-- | Time with time zone used for worker logs
|
||||
, stateGetZTime :: IO ZonedTime
|
||||
, stateGetZTime :: IO ZonedTime
|
||||
-- | Used for killing the main thread in case a subthread fails
|
||||
, stateMainThreadId :: ThreadId
|
||||
, stateMainThreadId :: ThreadId
|
||||
-- | Keeps track of when the next retry for connecting to database is scheduled
|
||||
, stateRetryNextIn :: IORef Int
|
||||
, stateRetryNextIn :: IORef Int
|
||||
-- | Logs a pool error with a debounce
|
||||
, debounceLogAcquisitionTimeout :: IO ()
|
||||
}
|
||||
|
||||
init :: AppConfig -> IO AppState
|
||||
@@ -78,8 +86,8 @@ init conf = do
|
||||
initWithPool pool conf
|
||||
|
||||
initWithPool :: SQL.Pool -> AppConfig -> IO AppState
|
||||
initWithPool pool conf =
|
||||
AppState pool
|
||||
initWithPool pool conf = do
|
||||
appState <- AppState pool
|
||||
<$> newIORef minimumPgVersion -- assume we're in a supported version when starting, this will be corrected on a later step
|
||||
<*> newIORef Nothing
|
||||
<*> newIORef mempty
|
||||
@@ -91,16 +99,28 @@ initWithPool pool conf =
|
||||
<*> mkAutoUpdate defaultUpdateSettings { updateAction = getZonedTime }
|
||||
<*> myThreadId
|
||||
<*> newIORef 0
|
||||
<*> pure (pure ())
|
||||
|
||||
deb <-
|
||||
let oneSecond = 1000000 in
|
||||
mkDebounce defaultDebounceSettings
|
||||
{ debounceAction = logPgrstError appState SQL.AcquisitionTimeoutUsageError
|
||||
, debounceFreq = 5*oneSecond
|
||||
, debounceEdge = leadingEdge -- logs at the start and the end
|
||||
}
|
||||
|
||||
return appState { debounceLogAcquisitionTimeout = deb }
|
||||
|
||||
destroy :: AppState -> IO ()
|
||||
destroy = destroyPool
|
||||
|
||||
initPool :: AppConfig -> IO SQL.Pool
|
||||
initPool AppConfig{..} =
|
||||
SQL.acquire configDbPoolSize timeoutMilliseconds $ toUtf8 configDbUri
|
||||
where
|
||||
timeoutMilliseconds = (* oneSecond) <$> configDbPoolAcquisitionTimeout
|
||||
oneSecond = 1000000
|
||||
SQL.acquire
|
||||
configDbPoolSize
|
||||
(fromIntegral configDbPoolAcquisitionTimeout)
|
||||
(fromIntegral configDbPoolMaxLifetime)
|
||||
(toUtf8 configDbUri)
|
||||
|
||||
-- | Run an action with a database connection.
|
||||
usePool :: AppState -> SQL.Session a -> IO (Either SQL.UsageError a)
|
||||
@@ -157,6 +177,9 @@ logWithZTime appState txt = do
|
||||
zTime <- stateGetZTime appState
|
||||
hPutStrLn stderr $ toS (formatTime defaultTimeLocale "%d/%b/%Y:%T %z: " zTime) <> txt
|
||||
|
||||
logPgrstError :: AppState -> SQL.UsageError -> IO ()
|
||||
logPgrstError appState e = logWithZTime appState . T.decodeUtf8 . LBS.toStrict $ Error.errorPayload $ Error.PgError False e
|
||||
|
||||
getMainThreadId :: AppState -> ThreadId
|
||||
getMainThreadId = stateMainThreadId
|
||||
|
||||
|
||||
@@ -151,6 +151,9 @@ exampleConfigFile =
|
||||
|## Time in seconds to wait to acquire a slot from the connection pool
|
||||
|# db-pool-acquisition-timeout = 10
|
||||
|
|
||||
|## Time in seconds after which to recycle pool connections
|
||||
|# db-pool-max-lifetime = 1800
|
||||
|
|
||||
|## Stored proc to exec immediately after auth
|
||||
|# db-pre-request = "stored_proc_name"
|
||||
|
|
||||
|
||||
@@ -70,7 +70,8 @@ data AppConfig = AppConfig
|
||||
, configDbMaxRows :: Maybe Integer
|
||||
, configDbPlanEnabled :: Bool
|
||||
, configDbPoolSize :: Int
|
||||
, configDbPoolAcquisitionTimeout :: Maybe Int
|
||||
, configDbPoolAcquisitionTimeout :: Int
|
||||
, configDbPoolMaxLifetime :: Int
|
||||
, configDbPreRequest :: Maybe QualifiedIdentifier
|
||||
, configDbPreparedStatements :: Bool
|
||||
, configDbRootSpec :: Maybe QualifiedIdentifier
|
||||
@@ -130,7 +131,8 @@ toText conf =
|
||||
,("db-max-rows", maybe "\"\"" show . configDbMaxRows)
|
||||
,("db-plan-enabled", T.toLower . show . configDbPlanEnabled)
|
||||
,("db-pool", show . configDbPoolSize)
|
||||
,("db-pool-acquisition-timeout", maybe "\"\"" show . configDbPoolAcquisitionTimeout)
|
||||
,("db-pool-acquisition-timeout", show . configDbPoolAcquisitionTimeout)
|
||||
,("db-pool-max-lifetime", show . configDbPoolMaxLifetime)
|
||||
,("db-pre-request", q . maybe mempty dumpQi . configDbPreRequest)
|
||||
,("db-prepared-statements", T.toLower . show . configDbPreparedStatements)
|
||||
,("db-root-spec", q . maybe mempty dumpQi . configDbRootSpec)
|
||||
@@ -219,7 +221,8 @@ parser optPath env dbSettings =
|
||||
(optInt "max-rows")
|
||||
<*> (fromMaybe False <$> optBool "db-plan-enabled")
|
||||
<*> (fromMaybe 10 <$> optInt "db-pool")
|
||||
<*> optInt "db-pool-acquisition-timeout"
|
||||
<*> (fromMaybe 10 <$> optInt "db-pool-acquisition-timeout")
|
||||
<*> (fromMaybe 1800 <$> optInt "db-pool-max-lifetime")
|
||||
<*> (fmap toQi <$> optWithAlias (optString "db-pre-request")
|
||||
(optString "pre-request"))
|
||||
<*> (fromMaybe True <$> optBool "db-prepared-statements")
|
||||
@@ -355,7 +358,8 @@ parser optPath env dbSettings =
|
||||
let dbSettingName = T.pack $ dashToUnderscore <$> toS key in
|
||||
if dbSettingName `notElem` [
|
||||
"server_host", "server_port", "server_unix_socket", "server_unix_socket_mode", "admin_server_port", "log_level",
|
||||
"db_uri", "db_channel_enabled", "db_channel", "db_pool", "db_pool_acquisition_timeout", "db_config"]
|
||||
"db_uri", "db_channel_enabled", "db_channel", "db_pool", "db_pool_acquisition_timeout",
|
||||
"db_pool_max_lifetime", "db_config"]
|
||||
then lookup dbSettingName dbSettings
|
||||
else Nothing
|
||||
|
||||
|
||||
@@ -424,13 +424,13 @@ pgErrorStatus authed (SQL.SessionUsageError (SQL.QueryError _ _ (SQL.ResultError
|
||||
|
||||
_ -> HTTP.status500
|
||||
|
||||
checkIsFatal :: PgError -> Maybe Text
|
||||
checkIsFatal (PgError _ (SQL.ConnectionUsageError e))
|
||||
checkIsFatal :: SQL.UsageError -> Maybe Text
|
||||
checkIsFatal (SQL.ConnectionUsageError e)
|
||||
| isAuthFailureMessage = Just $ toS failureMessage
|
||||
| otherwise = Nothing
|
||||
where isAuthFailureMessage = "FATAL: password authentication failed" `isInfixOf` failureMessage
|
||||
failureMessage = BS.unpack $ fromMaybe mempty e
|
||||
checkIsFatal (PgError _ (SQL.SessionUsageError (SQL.QueryError _ _ (SQL.ResultError serverError))))
|
||||
checkIsFatal(SQL.SessionUsageError (SQL.QueryError _ _ (SQL.ResultError serverError)))
|
||||
= case serverError of
|
||||
-- Check for a syntax error (42601 is the pg code). This would mean the error is on our part somehow, so we treat it as fatal.
|
||||
SQL.ServerError "42601" _ _ _ _
|
||||
|
||||
@@ -399,7 +399,7 @@ test | personnages_view | test | actors_view | personnage
|
||||
-}
|
||||
addViewM2OAndO2ORels :: [ViewKeyDependency] -> [Relationship] -> [Relationship]
|
||||
addViewM2OAndO2ORels keyDeps rels =
|
||||
rels ++ concat (viewRels <$> rels)
|
||||
rels ++ concatMap viewRels rels
|
||||
where
|
||||
isM2O card = case card of {M2O _ _ -> True; _ -> False;}
|
||||
isO2O card = case card of {O2O _ _ -> True; _ -> False;}
|
||||
@@ -449,7 +449,7 @@ addViewM2OAndO2ORels keyDeps rels =
|
||||
, keyDepColsTblVw <- expandKeyDepCols $ keyDepCols tblVw ]
|
||||
else []
|
||||
viewRels _ = []
|
||||
expandKeyDepCols kdc = zip (fst <$> kdc) <$> sequenceA (snd <$> kdc)
|
||||
expandKeyDepCols kdc = zip (fst <$> kdc) <$> traverse snd kdc
|
||||
|
||||
addInverseRels :: [Relationship] -> [Relationship]
|
||||
addInverseRels rels =
|
||||
@@ -485,7 +485,7 @@ addViewPrimaryKeys tabs keyDeps =
|
||||
-- * We don't have any logic that requires the client to name a PK column (compared to the column hints in embedding for FKs),
|
||||
-- so we don't need to know about the other references.
|
||||
-- * We need to choose a single reference for each column, otherwise we'd output too many columns in location headers etc.
|
||||
takeFirstPK pkCols = catMaybes $ head . snd <$> pkCols
|
||||
takeFirstPK = mapMaybe (head . snd)
|
||||
|
||||
allTables :: PgVersion -> Bool -> SQL.Statement [Schema] TablesMap
|
||||
allTables pgVer =
|
||||
|
||||
@@ -13,7 +13,6 @@ import qualified Data.Aeson as JSON
|
||||
import qualified Data.ByteString as BS
|
||||
import qualified Data.ByteString.Lazy as LBS
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as T
|
||||
import qualified Hasql.Notifications as SQL
|
||||
import qualified Hasql.Session as SQL
|
||||
import qualified Hasql.Transaction.Sessions as SQL
|
||||
@@ -32,8 +31,7 @@ import PostgREST.AppState (AppState)
|
||||
import PostgREST.Config (AppConfig (..), readAppConfig)
|
||||
import PostgREST.Config.Database (queryDbSettings, queryPgVersion)
|
||||
import PostgREST.Config.PgVersion (PgVersion (..), minimumPgVersion)
|
||||
import PostgREST.Error (PgError (PgError), checkIsFatal,
|
||||
errorPayload)
|
||||
import PostgREST.Error (checkIsFatal)
|
||||
import PostgREST.SchemaCache (querySchemaCache)
|
||||
|
||||
import qualified PostgREST.AppState as AppState
|
||||
@@ -131,9 +129,8 @@ establishConnection appState =
|
||||
pgVersion <- AppState.usePool appState queryPgVersion
|
||||
case pgVersion of
|
||||
Left e -> do
|
||||
let err = PgError False e
|
||||
AppState.logWithZTime appState . T.decodeUtf8 . LBS.toStrict $ errorPayload err
|
||||
case checkIsFatal err of
|
||||
AppState.logPgrstError appState e
|
||||
case checkIsFatal e of
|
||||
Just reason ->
|
||||
return $ FatalConnectionError reason
|
||||
Nothing ->
|
||||
@@ -168,19 +165,16 @@ loadSchemaCache appState = do
|
||||
querySchemaCache (toList configDbSchemas) configDbExtraSearchPath configDbPreparedStatements
|
||||
case result of
|
||||
Left e -> do
|
||||
let
|
||||
err = PgError False e
|
||||
putErr = AppState.logWithZTime appState . T.decodeUtf8 . LBS.toStrict $ errorPayload err
|
||||
case checkIsFatal err of
|
||||
case checkIsFatal e of
|
||||
Just hint -> do
|
||||
AppState.logWithZTime appState "A fatal error ocurred when loading the schema cache"
|
||||
putErr
|
||||
AppState.logPgrstError appState e
|
||||
AppState.logWithZTime appState hint
|
||||
return SCFatalFail
|
||||
Nothing -> do
|
||||
AppState.putSchemaCache appState Nothing
|
||||
AppState.logWithZTime appState "An error ocurred when loading the schema cache"
|
||||
putErr
|
||||
AppState.logPgrstError appState e
|
||||
return SCOnRetry
|
||||
|
||||
Right sCache -> do
|
||||
@@ -249,18 +243,15 @@ reReadConfig startingUp appState = do
|
||||
qDbSettings <- AppState.usePool appState $ queryDbSettings configDbPreparedStatements
|
||||
case qDbSettings of
|
||||
Left e -> do
|
||||
let
|
||||
err = PgError False e
|
||||
putErr = AppState.logWithZTime appState . T.decodeUtf8 . LBS.toStrict $ errorPayload err
|
||||
AppState.logWithZTime appState
|
||||
"An error ocurred when trying to query database settings for the config parameters"
|
||||
case checkIsFatal err of
|
||||
case checkIsFatal e of
|
||||
Just hint -> do
|
||||
putErr
|
||||
AppState.logPgrstError appState e
|
||||
AppState.logWithZTime appState hint
|
||||
killThread (AppState.getMainThreadId appState)
|
||||
Nothing -> do
|
||||
putErr
|
||||
AppState.logPgrstError appState e
|
||||
pure []
|
||||
Right x -> pure x
|
||||
else
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
resolver: lts-19.14 # 2022-07-01, GHC 9.0.2
|
||||
resolver: lts-20.6 # 2023-01-09, GHC 9.2.5
|
||||
|
||||
nix:
|
||||
packages:
|
||||
@@ -10,23 +10,7 @@ nix:
|
||||
pure: false
|
||||
|
||||
extra-deps:
|
||||
- HTTP-4000.3.16
|
||||
- configurator-pg-0.2.6
|
||||
- hashable-1.4.1.0
|
||||
- hashtables-1.3
|
||||
- hasql-1.6.1.1
|
||||
- hasql-dynamic-statements-0.3.1.2
|
||||
- hasql-implicits-0.1.0.5
|
||||
- hasql-notifications-0.2.0.3
|
||||
- hasql-pool-0.8.0.6
|
||||
- hasql-transaction-1.0.1.2
|
||||
- isomorphism-class-0.1.0.6
|
||||
- lens-aeson-1.1.3
|
||||
- optparse-applicative-0.16.1.0
|
||||
- postgresql-binary-0.12.5
|
||||
- protolude-0.3.2
|
||||
- ptr-0.16.8.2
|
||||
- text-builder-0.6.7
|
||||
- text-builder-dev-0.3.3
|
||||
- git: https://github.com/PostgREST/postgresql-libpq.git
|
||||
commit: 33ff97db570b5b432255f5f24a68db51453f6eb8
|
||||
commit: 890a0a16cf57dd401420fdc6c7d576fb696003bc
|
||||
- hasql-notifications-0.2.0.4
|
||||
- hasql-pool-0.9
|
||||
|
||||
@@ -5,145 +5,33 @@
|
||||
|
||||
packages:
|
||||
- completed:
|
||||
hackage: HTTP-4000.3.16@sha256:6042643c15a0b43e522a6693f1e322f05000d519543a84149cb80aeffee34f71,5947
|
||||
pantry-tree:
|
||||
size: 1428
|
||||
sha256: b73a7f6d21cf20bbf819e19039409c9010efb5000d2b72cdd8fd67a9027c14e8
|
||||
original:
|
||||
hackage: HTTP-4000.3.16
|
||||
- completed:
|
||||
hackage: configurator-pg-0.2.6@sha256:cd9b06a458428e493a4d6def725af7ab1ab0fef678fbd871f9586fc7f9aa70be,2849
|
||||
pantry-tree:
|
||||
size: 2463
|
||||
sha256: 97efe7a22afc93033bda5adcffdabc0f1c30dc32b2c3ba02114ce7cd74c942fd
|
||||
original:
|
||||
hackage: configurator-pg-0.2.6
|
||||
- completed:
|
||||
hackage: hashable-1.4.1.0@sha256:50b2f002c68fe67730ee7a3cd8607486197dd99b084255005ad51ecd6970a41b,5019
|
||||
pantry-tree:
|
||||
size: 1248
|
||||
sha256: 9af2f7a42674f7effcabbebc043f97057240783f1709338a77f58216f4a5f18c
|
||||
original:
|
||||
hackage: hashable-1.4.1.0
|
||||
- completed:
|
||||
hackage: hashtables-1.3@sha256:ab21804fdafbbd8ad918b2911dabb729ae0ea891780fe66bf7804cbcd07edadf,10379
|
||||
pantry-tree:
|
||||
size: 2895
|
||||
sha256: e71f113ad989dbc994e0fb52bcc219d62930de9afa8b3441bf7909e864481b33
|
||||
original:
|
||||
hackage: hashtables-1.3
|
||||
- completed:
|
||||
hackage: hasql-1.6.1.1@sha256:948a2137308cc5354e4997bc3666753867124cd25db792424cb9614b1c1b44cf,6626
|
||||
pantry-tree:
|
||||
size: 2622
|
||||
sha256: 28d21bf061522fc513f040e9c383b90532222b7258216cc094e07736add8be10
|
||||
original:
|
||||
hackage: hasql-1.6.1.1
|
||||
- completed:
|
||||
hackage: hasql-dynamic-statements-0.3.1.2@sha256:417aa533c84f074e2fa16bb2c4d4231326aa512097dd1025d915388e56acd1eb,2675
|
||||
pantry-tree:
|
||||
size: 595
|
||||
sha256: 91696d3f3e0ef3254772ae5a8e4e89be68285febb49b302ed83d85ac4037a417
|
||||
original:
|
||||
hackage: hasql-dynamic-statements-0.3.1.2
|
||||
- completed:
|
||||
hackage: hasql-implicits-0.1.0.5@sha256:d16aacad6dc21428d72447d3ae8bcc03839a2f0aa1ec29c797ed9aca4609f9af,1361
|
||||
pantry-tree:
|
||||
size: 264
|
||||
sha256: 0451b99a0a1d02db673d0c40acdf60d4e769e15852eed9e8dc05bffaf43efb70
|
||||
original:
|
||||
hackage: hasql-implicits-0.1.0.5
|
||||
- completed:
|
||||
hackage: hasql-notifications-0.2.0.3@sha256:aca3f7ee847a8f0b7ef6f989dc48f4a094a06c1a34e92aa3c8bb230085966ea6,2027
|
||||
pantry-tree:
|
||||
size: 452
|
||||
sha256: 999f0f2856a00d21f4498a8a58452bbefc4ea972fe2984fd234a68a5fe61d98b
|
||||
original:
|
||||
hackage: hasql-notifications-0.2.0.3
|
||||
- completed:
|
||||
hackage: hasql-pool-0.8.0.6@sha256:b63bb83409bab5bc20ff24f5d62205e9b117701a0fc24531ddeac20ab8c2a42c,1818
|
||||
pantry-tree:
|
||||
size: 346
|
||||
sha256: c4100946b7eae44375511e35a393abe2e1db0e5637c68cea8f53176b796bfd5b
|
||||
original:
|
||||
hackage: hasql-pool-0.8.0.6
|
||||
- completed:
|
||||
hackage: hasql-transaction-1.0.1.2@sha256:297b158cd1f0727f9b0e175bd7d3741c1bcb725a8094956d0ee79b41aafdb30a,2890
|
||||
pantry-tree:
|
||||
size: 983
|
||||
sha256: 3679e6d5c835cc17a8fa0c252b8221e282880044b7219aa1de2531bbd5c40691
|
||||
original:
|
||||
hackage: hasql-transaction-1.0.1.2
|
||||
- completed:
|
||||
hackage: isomorphism-class-0.1.0.6@sha256:d93da31287359c761953b876354de28381f409c5c50e3241c572a443e50c553d,1703
|
||||
pantry-tree:
|
||||
size: 465
|
||||
sha256: c97f922d1ae8f1a0db4c28fac9383d2716934879e95ff0b2b88ebb861d6fba14
|
||||
original:
|
||||
hackage: isomorphism-class-0.1.0.6
|
||||
- completed:
|
||||
hackage: lens-aeson-1.1.3@sha256:52c8eaecd2d1c2a969c0762277c4a8ee72c339a686727d5785932e72ef9c3050,1764
|
||||
pantry-tree:
|
||||
size: 541
|
||||
sha256: b31392b78f2a03111c805f4400007778eb93b49f998ab41dfbebaaf9b5526bad
|
||||
original:
|
||||
hackage: lens-aeson-1.1.3
|
||||
- completed:
|
||||
hackage: optparse-applicative-0.16.1.0@sha256:418c22ed6a19124d457d96bc66bd22c93ac22fad0c7100fe4972bbb4ac989731,4982
|
||||
pantry-tree:
|
||||
size: 2979
|
||||
sha256: dd092d843091c08691485d68a1908517079b1bc6f3d73928f37635a19dc27fc1
|
||||
original:
|
||||
hackage: optparse-applicative-0.16.1.0
|
||||
- completed:
|
||||
hackage: postgresql-binary-0.12.5@sha256:de9da3cba9be541d6c75ae8da2858c33d83dc1b2e0c639b0b9781816b78a91f4,5594
|
||||
pantry-tree:
|
||||
size: 1619
|
||||
sha256: b392337f91031a5b3407393e2f04dfe4e7a28019e88eae6a9370538b90e28c51
|
||||
original:
|
||||
hackage: postgresql-binary-0.12.5
|
||||
- completed:
|
||||
hackage: protolude-0.3.2@sha256:2a38b3dad40d238ab644e234b692c8911423f9d3ed0e36b62287c4a698d92cd1,2240
|
||||
pantry-tree:
|
||||
size: 1594
|
||||
sha256: a36d2912ac552d950ba4476de7d950b56b82dd28e48b9f4d0efee938f10bc525
|
||||
original:
|
||||
hackage: protolude-0.3.2
|
||||
- completed:
|
||||
hackage: ptr-0.16.8.2@sha256:708ebb95117f2872d2c5a554eb6804cf1126e86abe793b2673f913f14e5eb1ac,3959
|
||||
pantry-tree:
|
||||
size: 1303
|
||||
sha256: 557c438345de19f82bf01d676100da2a191ef06f624e7a4b90b09ac17cbb52a5
|
||||
original:
|
||||
hackage: ptr-0.16.8.2
|
||||
- completed:
|
||||
hackage: text-builder-0.6.7@sha256:efbb3e06107e9c8d1cfe85c963938ca9f375a74379af03da3173be4ef5c37bcf,2364
|
||||
pantry-tree:
|
||||
size: 425
|
||||
sha256: cd0ae197e6f9f3860a8ab71f5b87c4a8452ed1fce2fdfd35e36d68ded6e6648e
|
||||
original:
|
||||
hackage: text-builder-0.6.7
|
||||
- completed:
|
||||
hackage: text-builder-dev-0.3.3@sha256:79ec422defcc2e5b34f94129c72b98d34b2efc1ed8bbd945ccb8f4f535a892c3,2784
|
||||
pantry-tree:
|
||||
size: 724
|
||||
sha256: 8883631a132438e7892fcb13e89d6bbcdc0ac76c56fbea8df8d7aa482ce81f73
|
||||
original:
|
||||
hackage: text-builder-dev-0.3.3
|
||||
- completed:
|
||||
commit: 890a0a16cf57dd401420fdc6c7d576fb696003bc
|
||||
git: https://github.com/PostgREST/postgresql-libpq.git
|
||||
name: postgresql-libpq
|
||||
version: 0.9.4.3
|
||||
git: https://github.com/PostgREST/postgresql-libpq.git
|
||||
pantry-tree:
|
||||
size: 1081
|
||||
sha256: 0df271e48af32eb8292a45301af45e114110d54099ee73dbc609d39770e8175e
|
||||
commit: 33ff97db570b5b432255f5f24a68db51453f6eb8
|
||||
sha256: 074668b9669b9c49f3c522c8af5c608799a1965e203c463b188b2632995beac2
|
||||
size: 1414
|
||||
version: 0.9.4.3
|
||||
original:
|
||||
commit: 890a0a16cf57dd401420fdc6c7d576fb696003bc
|
||||
git: https://github.com/PostgREST/postgresql-libpq.git
|
||||
commit: 33ff97db570b5b432255f5f24a68db51453f6eb8
|
||||
- completed:
|
||||
hackage: hasql-notifications-0.2.0.4@sha256:9a09fa9b97feadd9492c8bd8bc6b9cffe0513510102f08374b0c45ecd479ed67,2028
|
||||
pantry-tree:
|
||||
sha256: 56f9e240728e7a65711dde45fa2e2075b914e32cd370424aaa4572392378a60e
|
||||
size: 452
|
||||
original:
|
||||
hackage: hasql-notifications-0.2.0.4
|
||||
- completed:
|
||||
hackage: hasql-pool-0.9@sha256:db7a37f6b3a922c37adc3c7ced47a7c10786d1f171e47a735a6e812a587ba44c,2111
|
||||
pantry-tree:
|
||||
sha256: 49b1181d28c6f5317e794671c2dae155754b834bdcfa30f7e5dbad28e4cf0249
|
||||
size: 346
|
||||
original:
|
||||
hackage: hasql-pool-0.9
|
||||
snapshots:
|
||||
- completed:
|
||||
size: 618951
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/19/14.yaml
|
||||
sha256: 4c31d4ef975b0211078862566aedf3b82b6cea569fc2cde4c72a51e5a8d236ce
|
||||
original: lts-19.14
|
||||
sha256: 4905c93319aa94aa53da8f41d614d7bacdbfe6c63a8c6132d32e6e62f24a9af4
|
||||
size: 649315
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/6.yaml
|
||||
original: lts-20.6
|
||||
|
||||