Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d89826b982 | ||
|
|
94350fdde6 | ||
|
|
230eb3630c |
@@ -4,6 +4,26 @@ All notable changes to this project will be documented in this file. From versio
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## [14.5] - 2026-02-12
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Log error when `db-schemas` config contains schema `pg_catalog` or `information_schema` by @taimoorzaeem in #4359
|
||||||
|
- Add a `HINT` when the LISTEN channel stops working due to a PostgreSQL bug by @laurenceisla in #4581
|
||||||
|
- Add string slicing operator for `jwt-role-claim-key` by @taimoorzaeem in #4599
|
||||||
|
- Log host, port and pg version of listener database connection by @mkleczek in #4617 #4618
|
||||||
|
- Optimize requests with `Prefer: count=exact` that do not use ranges or `db-max-rows` by @laurenceisla in #3957
|
||||||
|
+ Removed unnecessary double count when building the `Content-Range`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Don't hide async exceptions in logs by @stevechavez in #4646
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Log error when `db-schemas` config contains schema `pg_catalog` or `information_schema` by @taimoorzaeem in #4359
|
||||||
|
+ Now fails at startup. Prior to this, it failed with `PGRST205` on requests related to these schemas.
|
||||||
|
|
||||||
## [14.4] - 2026-01-29
|
## [14.4] - 2026-01-29
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Supported PostgreSQL versions
|
|||||||
=============================
|
=============================
|
||||||
|
|
||||||
=============== =================================
|
=============== =================================
|
||||||
**Supported** PostgreSQL >= 12
|
**Supported** PostgreSQL >= 13
|
||||||
=============== =================================
|
=============== =================================
|
||||||
|
|
||||||
PostgREST works with all PostgreSQL versions still `officially supported <https://www.postgresql.org/support/versioning/>`_.
|
PostgREST works with all PostgreSQL versions still `officially supported <https://www.postgresql.org/support/versioning/>`_.
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
name: postgrest
|
name: postgrest
|
||||||
version: 14.4
|
version: 14.5
|
||||||
synopsis: REST API for any Postgres database
|
synopsis: REST API for any Postgres database
|
||||||
description: Reads the schema of a PostgreSQL database and creates RESTful routes
|
description: Reads the schema of a PostgreSQL database and creates RESTful routes
|
||||||
for tables, views, and functions, supporting all HTTP methods that security
|
for tables, views, and functions, supporting all HTTP methods that security
|
||||||
|
|||||||
+26
-4
@@ -9,18 +9,24 @@ Some of its functionality includes:
|
|||||||
- Producing HTTP Headers according to RFCs.
|
- Producing HTTP Headers according to RFCs.
|
||||||
- Content Negotiation
|
- Content Negotiation
|
||||||
-}
|
-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
module PostgREST.App
|
module PostgREST.App
|
||||||
( postgrest
|
( postgrest
|
||||||
, run
|
, run
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
|
||||||
|
import GHC.IO.Exception (IOErrorType (..))
|
||||||
|
import System.IO.Error (ioeGetErrorType)
|
||||||
|
|
||||||
import Control.Monad.Except (liftEither)
|
import Control.Monad.Except (liftEither)
|
||||||
import Data.Either.Combinators (mapLeft, whenLeft)
|
import Data.Either.Combinators (mapLeft, whenLeft)
|
||||||
import Data.Maybe (fromJust)
|
import Data.Maybe (fromJust)
|
||||||
import Data.String (IsString (..))
|
import Data.String (IsString (..))
|
||||||
import Network.Wai.Handler.Warp (defaultSettings, setHost, setPort,
|
import Network.Wai.Handler.Warp (defaultSettings, setHost,
|
||||||
|
setOnException, setPort,
|
||||||
setServerName)
|
setServerName)
|
||||||
|
|
||||||
import qualified Data.Text.Encoding as T
|
import qualified Data.Text.Encoding as T
|
||||||
@@ -63,7 +69,6 @@ type Handler = ExceptT Error
|
|||||||
|
|
||||||
run :: AppState -> IO ()
|
run :: AppState -> IO ()
|
||||||
run appState = do
|
run appState = do
|
||||||
let observer = AppState.getObserver appState
|
|
||||||
conf@AppConfig{..} <- AppState.getConfig appState
|
conf@AppConfig{..} <- AppState.getConfig appState
|
||||||
|
|
||||||
AppState.schemaCacheLoader appState -- Loads the initial SchemaCache
|
AppState.schemaCacheLoader appState -- Loads the initial SchemaCache
|
||||||
@@ -79,7 +84,24 @@ run appState = do
|
|||||||
address <- resolveSocketToAddress (AppState.getSocketREST appState)
|
address <- resolveSocketToAddress (AppState.getSocketREST appState)
|
||||||
observer $ AppServerAddressObs address
|
observer $ AppServerAddressObs address
|
||||||
|
|
||||||
Warp.runSettingsSocket (serverSettings conf) (AppState.getSocketREST appState) app
|
Warp.runSettingsSocket (serverSettings conf & setOnException onWarpException) (AppState.getSocketREST appState) app
|
||||||
|
where
|
||||||
|
observer = AppState.getObserver appState
|
||||||
|
|
||||||
|
onWarpException :: Maybe Wai.Request -> SomeException -> IO ()
|
||||||
|
onWarpException _ ex =
|
||||||
|
when (shouldDisplayException ex) $
|
||||||
|
observer $ WarpErrorObs $ show ex
|
||||||
|
|
||||||
|
-- Similar to wai defaultShouldDisplayException in
|
||||||
|
-- https://github.com/yesodweb/wai//blob/8c3882c60f6abe043889fc20c7efd3fa9747fa4a/warp/Network/Wai/Handler/Warp/Settings.hs#L251-L258
|
||||||
|
-- but without omitting AsyncException since it's important to log for ThreadKilled, StackOverflow and other cases.
|
||||||
|
-- We want to reuse this to avoid flooding the logs for some transient failure cases.
|
||||||
|
shouldDisplayException :: SomeException -> Bool
|
||||||
|
shouldDisplayException se
|
||||||
|
| Just (_ :: Warp.InvalidRequest) <- fromException se = False
|
||||||
|
| Just (ioeGetErrorType -> et) <- fromException se, et == ResourceVanished || et == InvalidArgument = False
|
||||||
|
| otherwise = True
|
||||||
|
|
||||||
serverSettings :: AppConfig -> Warp.Settings
|
serverSettings :: AppConfig -> Warp.Settings
|
||||||
serverSettings AppConfig{..} =
|
serverSettings AppConfig{..} =
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ data Observation
|
|||||||
| PoolRequestFullfilled
|
| PoolRequestFullfilled
|
||||||
| JwtCacheLookup Bool
|
| JwtCacheLookup Bool
|
||||||
| JwtCacheEviction
|
| JwtCacheEviction
|
||||||
|
| WarpErrorObs Text
|
||||||
|
|
||||||
data ObsFatalError = ServerAuthError | ServerPgrstBug | ServerError42P05 | ServerError08P01
|
data ObsFatalError = ServerAuthError | ServerPgrstBug | ServerError42P05 | ServerError08P01
|
||||||
|
|
||||||
@@ -157,6 +158,8 @@ observationMessage = \case
|
|||||||
"Looked up a JWT in JWT cache"
|
"Looked up a JWT in JWT cache"
|
||||||
JwtCacheEviction ->
|
JwtCacheEviction ->
|
||||||
"Evicted entry from JWT cache"
|
"Evicted entry from JWT cache"
|
||||||
|
WarpErrorObs txt ->
|
||||||
|
"Warp server error: " <> txt
|
||||||
where
|
where
|
||||||
showMillis :: Double -> Text
|
showMillis :: Double -> Text
|
||||||
showMillis x = toS $ showFFloat (Just 1) x ""
|
showMillis x = toS $ showFFloat (Just 1) x ""
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import requests
|
||||||
|
|
||||||
from postgrest import run
|
from postgrest import run
|
||||||
|
|
||||||
@@ -54,6 +55,25 @@ def test_openapi_in_big_schema(defaultenv):
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_stackoverflow_is_logged(defaultenv):
|
||||||
|
"Stack overflow errors should be logged with the Warp error message"
|
||||||
|
|
||||||
|
env = {
|
||||||
|
**defaultenv,
|
||||||
|
"PGRST_DB_SCHEMAS": "apflora",
|
||||||
|
"PGRST_DB_ANON_ROLE": "postgrest_test_anonymous",
|
||||||
|
}
|
||||||
|
|
||||||
|
with run(env=env, wait_max_seconds=30, no_startup_stdout=False) as postgrest:
|
||||||
|
with pytest.raises(requests.exceptions.ConnectionError):
|
||||||
|
postgrest.session.get("/")
|
||||||
|
|
||||||
|
output = postgrest.read_stdout(nlines=10)
|
||||||
|
output.extend(postgrest.read_stdout(nlines=10))
|
||||||
|
|
||||||
|
assert any("Warp server error: stack overflow" in line for line in output)
|
||||||
|
|
||||||
|
|
||||||
# See: https://github.com/PostgREST/postgrest/issues/3329
|
# See: https://github.com/PostgREST/postgrest/issues/3329
|
||||||
def test_should_not_fail_with_stack_overflow(defaultenv):
|
def test_should_not_fail_with_stack_overflow(defaultenv):
|
||||||
"requesting a non-existent relationship should not fail with stack overflow due to fuzzy search of candidates"
|
"requesting a non-existent relationship should not fail with stack overflow due to fuzzy search of candidates"
|
||||||
|
|||||||
Reference in New Issue
Block a user