From a1c39034838aa375794583cbdee8e5dcb4b57c70 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sun, 22 Dec 2024 19:21:47 +0100 Subject: [PATCH 1/7] remove: drop support for PostgreSQL 12 This has been EOL since November and has been dropped from nixpkgs. --- .github/workflows/test.yaml | 2 +- default.nix | 1 - nix/README.md | 24 +++++++-------- src/PostgREST/App.hs | 9 ++---- src/PostgREST/Config/PgVersion.hs | 6 +--- src/PostgREST/Plan.hs | 2 -- src/PostgREST/Plan/CallPlan.hs | 13 ++++---- src/PostgREST/Query.hs | 27 ++++++++-------- src/PostgREST/Query/QueryBuilder.hs | 10 +++--- src/PostgREST/SchemaCache/Routine.hs | 7 ----- test/spec/Feature/Query/InsertSpec.hs | 9 ++---- test/spec/Feature/Query/PlanSpec.hs | 44 +++++++++------------------ 12 files changed, 57 insertions(+), 97 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7cfedb0b2b..96e6364a6c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -67,7 +67,7 @@ jobs: strategy: fail-fast: false matrix: - pgVersion: [12, 13, 14, 15, 16, 17] + pgVersion: [13, 14, 15, 16, 17] name: PG ${{ matrix.pgVersion }} runs-on: ubuntu-24.04 defaults: diff --git a/default.nix b/default.nix index af73cce9aa..ed08f33388 100644 --- a/default.nix +++ b/default.nix @@ -51,7 +51,6 @@ let { name = "postgresql-15"; postgresql = pkgs.postgresql_15.withPackages (p: [ p.postgis p.pg_safeupdate ]); } { name = "postgresql-14"; postgresql = pkgs.postgresql_14.withPackages (p: [ p.postgis p.pg_safeupdate ]); } { name = "postgresql-13"; postgresql = pkgs.postgresql_13.withPackages (p: [ p.postgis p.pg_safeupdate ]); } - { name = "postgresql-12"; postgresql = pkgs.postgresql_12.withPackages (p: [ p.postgis p.pg_safeupdate ]); } ]; # Dynamic derivation for PostgREST diff --git a/nix/README.md b/nix/README.md index 601fa4f555..6159168cfa 100644 --- a/nix/README.md +++ b/nix/README.md @@ -75,12 +75,12 @@ The PostgREST utilities available in `nix-shell` all have names that begin with postgrest-build postgrest-test-spec postgrest-check postgrest-watch postgrest-clean postgrest-with-all -postgrest-coverage postgrest-with-postgresql-12 -postgrest-lint postgrest-with-postgresql-13 -postgrest-run postgrest-with-postgresql-14 -postgrest-style postgrest-with-postgresql-15 -postgrest-style-check postgrest-with-postgresql-16 -postgrest-test-io postgrest-with-postgresql-17 +postgrest-coverage postgrest-with-postgresql-13 +postgrest-lint postgrest-with-postgresql-14 +postgrest-run postgrest-with-postgresql-15 +postgrest-style postgrest-with-postgresql-16 +postgrest-style-check postgrest-with-postgresql-17 +postgrest-test-io ... [nix-shell]$ @@ -99,12 +99,12 @@ $ nix-shell --arg memory true postgrest-build postgrest-test-spec postgrest-check postgrest-watch postgrest-clean postgrest-with-all -postgrest-coverage postgrest-with-postgresql-12 -postgrest-lint postgrest-with-postgresql-13 -postgrest-run postgrest-with-postgresql-14 -postgrest-style postgrest-with-postgresql-15 -postgrest-style-check postgrest-with-postgresql-16 -postgrest-test-io postgrest-with-postgresql-17 +postgrest-coverage postgrest-with-postgresql-13 +postgrest-lint postgrest-with-postgresql-14 +postgrest-run postgrest-with-postgresql-15 +postgrest-style postgrest-with-postgresql-16 +postgrest-style-check postgrest-with-postgresql-17 +postgrest-test-io postgrest-test-memory ... diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 99febebaca..38239beb4b 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -44,7 +44,6 @@ import PostgREST.ApiRequest (ApiRequest (..)) import PostgREST.AppState (AppState) import PostgREST.Auth (AuthResult (..)) import PostgREST.Config (AppConfig (..), LogLevel (..)) -import PostgREST.Config.PgVersion (PgVersion (..)) import PostgREST.Error (Error) import PostgREST.Network (resolveHost) import PostgREST.Observation (Observation (..)) @@ -107,12 +106,11 @@ postgrest logLevel appState connWorker = Right authResult -> do appConf <- AppState.getConfig appState -- the config must be read again because it can reload maybeSchemaCache <- AppState.getSchemaCache appState - pgVer <- AppState.getPgVersion appState let eitherResponse :: IO (Either Error Wai.Response) eitherResponse = - runExceptT $ postgrestResponse appState appConf maybeSchemaCache pgVer authResult req + runExceptT $ postgrestResponse appState appConf maybeSchemaCache authResult req response <- either Error.errorResponseFor identity <$> eitherResponse -- Launch the connWorker when the connection is down. The postgrest @@ -128,11 +126,10 @@ postgrestResponse :: AppState.AppState -> AppConfig -> Maybe SchemaCache - -> PgVersion -> AuthResult -> Wai.Request -> Handler IO Wai.Response -postgrestResponse appState conf@AppConfig{..} maybeSchemaCache pgVer authResult@AuthResult{..} req = do +postgrestResponse appState conf@AppConfig{..} maybeSchemaCache authResult@AuthResult{..} req = do sCache <- case maybeSchemaCache of Just sCache -> @@ -146,7 +143,7 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache pgVer authResult@ (parseTime, apiReq@ApiRequest{..}) <- withTiming $ liftEither . mapLeft Error.ApiRequestError $ ApiRequest.userApiRequest conf req body sCache (planTime, plan) <- withTiming $ liftEither $ Plan.actionPlan iAction conf apiReq sCache - (queryTime, queryResult) <- withTiming $ Query.runQuery appState conf authResult apiReq plan sCache pgVer (Just authRole /= configDbAnonRole) + (queryTime, queryResult) <- withTiming $ Query.runQuery appState conf authResult apiReq plan sCache (Just authRole /= configDbAnonRole) (respTime, resp) <- withTiming $ liftEither $ Response.actionResponse queryResult apiReq (T.decodeUtf8 prettyVersion, docsVersion) conf sCache iSchema iNegotiatedByProfile return $ toWaiResponse (ServerTiming jwtTime parseTime planTime queryTime respTime) resp diff --git a/src/PostgREST/Config/PgVersion.hs b/src/PostgREST/Config/PgVersion.hs index 273d629419..6db42e90b1 100644 --- a/src/PostgREST/Config/PgVersion.hs +++ b/src/PostgREST/Config/PgVersion.hs @@ -3,7 +3,6 @@ module PostgREST.Config.PgVersion ( PgVersion(..) , minimumPgVersion - , pgVersion130 , pgVersion140 , pgVersion150 , pgVersion170 @@ -26,10 +25,7 @@ instance Ord PgVersion where -- | Tells the minimum PostgreSQL version required by this version of PostgREST minimumPgVersion :: PgVersion -minimumPgVersion = pgVersion121 - -pgVersion121 :: PgVersion -pgVersion121 = PgVersion 120001 "12.1" "12.1" +minimumPgVersion = pgVersion130 pgVersion130 :: PgVersion pgVersion130 = PgVersion 130000 "13.0" "13.0" diff --git a/src/PostgREST/Plan.hs b/src/PostgREST/Plan.hs index 3a31d72a75..4b6ce496f9 100644 --- a/src/PostgREST/Plan.hs +++ b/src/PostgREST/Plan.hs @@ -66,7 +66,6 @@ import PostgREST.SchemaCache.Routine (MediaHandler (..), Routine (..), RoutineMap, RoutineParam (..), - funcReturnsCompositeAlias, funcReturnsScalar, funcReturnsSetOfScalar) import PostgREST.SchemaCache.Table (Column (..), Table (..), @@ -974,7 +973,6 @@ callPlan proc ApiRequest{} paramKeys args readReq = FunctionCall { , funCArgs = args , funCScalar = funcReturnsScalar proc , funCSetOfScalar = funcReturnsSetOfScalar proc -, funCRetCompositeAlias = funcReturnsCompositeAlias proc , funCReturning = inferColsEmbedNeeds readReq [] } where diff --git a/src/PostgREST/Plan/CallPlan.hs b/src/PostgREST/Plan/CallPlan.hs index 32ef34c359..472deefd75 100644 --- a/src/PostgREST/Plan/CallPlan.hs +++ b/src/PostgREST/Plan/CallPlan.hs @@ -19,13 +19,12 @@ import PostgREST.SchemaCache.Routine (Routine (..), import Protolude data CallPlan = FunctionCall - { funCQi :: QualifiedIdentifier - , funCParams :: CallParams - , funCArgs :: CallArgs - , funCScalar :: Bool - , funCSetOfScalar :: Bool - , funCRetCompositeAlias :: Bool - , funCReturning :: [FieldName] + { funCQi :: QualifiedIdentifier + , funCParams :: CallParams + , funCArgs :: CallArgs + , funCScalar :: Bool + , funCSetOfScalar :: Bool + , funCReturning :: [FieldName] } data CallParams diff --git a/src/PostgREST/Query.hs b/src/PostgREST/Query.hs index 17c5854708..ea09bae6e8 100644 --- a/src/PostgREST/Query.hs +++ b/src/PostgREST/Query.hs @@ -40,7 +40,6 @@ import PostgREST.ApiRequest.Preferences (PreferCount (..), import PostgREST.Auth (AuthResult (..)) import PostgREST.Config (AppConfig (..), OpenAPIMode (..)) -import PostgREST.Config.PgVersion (PgVersion (..)) import PostgREST.Error (Error) import PostgREST.MediaType (MediaType (..)) import PostgREST.Plan (ActionPlan (..), @@ -74,9 +73,9 @@ data QueryResult | NoDbResult InfoPlan -- TODO This function needs to be free from IO, only App.hs should do IO -runQuery :: AppState.AppState -> AppConfig -> AuthResult -> ApiRequest -> ActionPlan -> SchemaCache -> PgVersion -> Bool -> ExceptT Error IO QueryResult -runQuery _ _ _ _ (NoDb x) _ _ _ = pure $ NoDbResult x -runQuery appState config AuthResult{..} apiReq (Db plan) sCache pgVer authenticated = do +runQuery :: AppState.AppState -> AppConfig -> AuthResult -> ApiRequest -> ActionPlan -> SchemaCache -> Bool -> ExceptT Error IO QueryResult +runQuery _ _ _ _ (NoDb x) _ _ = pure $ NoDbResult x +runQuery appState config AuthResult{..} apiReq (Db plan) sCache authenticated = do dbResp <- lift $ do let transaction = if prepared then SQL.transaction else SQL.unpreparedTransaction AppState.usePool appState (transaction isoLvl txMode $ runExceptT dbHandler) @@ -93,7 +92,7 @@ runQuery appState config AuthResult{..} apiReq (Db plan) sCache pgVer authentica dbHandler = do setPgLocals plan config authClaims authRole apiReq runPreReq config - actionQuery plan config apiReq pgVer sCache + actionQuery plan config apiReq sCache planTxMode :: DbActionPlan -> SQL.Mode planTxMode (DbCrud x) = pTxMode x @@ -107,9 +106,9 @@ planIsoLvl AppConfig{configRoleIsoLvl} role actPlan = case actPlan of where roleIsoLvl = HM.findWithDefault SQL.ReadCommitted role configRoleIsoLvl -actionQuery :: DbActionPlan -> AppConfig -> ApiRequest -> PgVersion -> SchemaCache -> DbHandler QueryResult +actionQuery :: DbActionPlan -> AppConfig -> ApiRequest -> SchemaCache -> DbHandler QueryResult -actionQuery (DbCrud plan@WrappedReadPlan{..}) conf@AppConfig{..} apiReq@ApiRequest{iPreferences=Preferences{..}} _ _ = do +actionQuery (DbCrud plan@WrappedReadPlan{..}) conf@AppConfig{..} apiReq@ApiRequest{iPreferences=Preferences{..}} _ = do let countQuery = QueryBuilder.readPlanToCountQuery wrReadPlan resultSet <- lift . SQL.statement mempty $ @@ -129,13 +128,13 @@ actionQuery (DbCrud plan@WrappedReadPlan{..}) conf@AppConfig{..} apiReq@ApiReque optionalRollback conf apiReq DbCrudResult plan <$> resultSetWTotal conf apiReq resultSet countQuery -actionQuery (DbCrud plan@MutateReadPlan{mrMutation=MutationCreate, ..}) conf apiReq _ _ = do +actionQuery (DbCrud plan@MutateReadPlan{mrMutation=MutationCreate, ..}) conf apiReq _ = do resultSet <- writeQuery mrReadPlan mrMutatePlan mrMedia mrHandler apiReq conf failNotSingular mrMedia resultSet optionalRollback conf apiReq pure $ DbCrudResult plan resultSet -actionQuery (DbCrud plan@MutateReadPlan{mrMutation=MutationUpdate, ..}) conf apiReq@ApiRequest{iPreferences=Preferences{..}, ..} _ _ = do +actionQuery (DbCrud plan@MutateReadPlan{mrMutation=MutationUpdate, ..}) conf apiReq@ApiRequest{iPreferences=Preferences{..}, ..} _ = do resultSet <- writeQuery mrReadPlan mrMutatePlan mrMedia mrHandler apiReq conf failNotSingular mrMedia resultSet failExceedsMaxAffectedPref (preferMaxAffected,preferHandling) resultSet @@ -143,13 +142,13 @@ actionQuery (DbCrud plan@MutateReadPlan{mrMutation=MutationUpdate, ..}) conf api optionalRollback conf apiReq pure $ DbCrudResult plan resultSet -actionQuery (DbCrud plan@MutateReadPlan{mrMutation=MutationSingleUpsert, ..}) conf apiReq _ _ = do +actionQuery (DbCrud plan@MutateReadPlan{mrMutation=MutationSingleUpsert, ..}) conf apiReq _ = do resultSet <- writeQuery mrReadPlan mrMutatePlan mrMedia mrHandler apiReq conf failPut resultSet optionalRollback conf apiReq pure $ DbCrudResult plan resultSet -actionQuery (DbCrud plan@MutateReadPlan{mrMutation=MutationDelete, ..}) conf apiReq@ApiRequest{iPreferences=Preferences{..}, ..} _ _ = do +actionQuery (DbCrud plan@MutateReadPlan{mrMutation=MutationDelete, ..}) conf apiReq@ApiRequest{iPreferences=Preferences{..}, ..} _ = do resultSet <- writeQuery mrReadPlan mrMutatePlan mrMedia mrHandler apiReq conf failNotSingular mrMedia resultSet failExceedsMaxAffectedPref (preferMaxAffected,preferHandling) resultSet @@ -157,12 +156,12 @@ actionQuery (DbCrud plan@MutateReadPlan{mrMutation=MutationDelete, ..}) conf api optionalRollback conf apiReq pure $ DbCrudResult plan resultSet -actionQuery (DbCall plan@CallReadPlan{..}) conf@AppConfig{..} apiReq@ApiRequest{iPreferences=Preferences{..}} pgVer _ = do +actionQuery (DbCall plan@CallReadPlan{..}) conf@AppConfig{..} apiReq@ApiRequest{iPreferences=Preferences{..}} _ = do resultSet <- lift . SQL.statement mempty $ Statements.prepareCall crProc - (QueryBuilder.callPlanToQuery crCallPlan pgVer) + (QueryBuilder.callPlanToQuery crCallPlan) (QueryBuilder.readPlanToQuery crReadPlan) (QueryBuilder.readPlanToCountQuery crReadPlan) (shouldCount preferCount) @@ -175,7 +174,7 @@ actionQuery (DbCall plan@CallReadPlan{..}) conf@AppConfig{..} apiReq@ApiRequest{ failExceedsMaxAffectedPref (preferMaxAffected,preferHandling) resultSet pure $ DbCallResult plan resultSet -actionQuery (MaybeDb plan@InspectPlan{ipSchema=tSchema}) AppConfig{..} _ _ sCache = +actionQuery (MaybeDb plan@InspectPlan{ipSchema=tSchema}) AppConfig{..} _ sCache = lift $ case configOpenApiMode of OAFollowPriv -> do tableAccess <- SQL.statement [tSchema] (SchemaCache.accessibleTables configDbPreparedStatements) diff --git a/src/PostgREST/Query/QueryBuilder.hs b/src/PostgREST/Query/QueryBuilder.hs index 602ae27ef1..bb8ae0723c 100644 --- a/src/PostgREST/Query/QueryBuilder.hs +++ b/src/PostgREST/Query/QueryBuilder.hs @@ -27,7 +27,6 @@ import Data.Maybe (fromJust) import Data.Tree (Tree (..)) import PostgREST.ApiRequest.Preferences (PreferResolution (..)) -import PostgREST.Config.PgVersion (PgVersion, pgVersion130) import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..)) import PostgREST.SchemaCache.Relationship (Cardinality (..), Junction (..), @@ -193,8 +192,8 @@ mutatePlanToQuery (Delete mainQi logicForest range ordts returnings) whereLogic = if null logicForest then mempty else " WHERE " <> intercalateSnippet " AND " (pgFmtLogicTree mainQi <$> logicForest) (whereRangeIdF, rangeIdF) = mutRangeF mainQi (cfName . coField <$> ordts) -callPlanToQuery :: CallPlan -> PgVersion -> SQL.Snippet -callPlanToQuery (FunctionCall qi params arguments returnsScalar returnsSetOfScalar returnsCompositeAlias returnings) pgVer = +callPlanToQuery :: CallPlan -> SQL.Snippet +callPlanToQuery (FunctionCall qi params arguments returnsScalar returnsSetOfScalar returnings) = "SELECT " <> (if returnsScalar || returnsSetOfScalar then "pgrst_call.pgrst_scalar" else returnedColumns) <> " " <> fromCall where @@ -210,9 +209,8 @@ callPlanToQuery (FunctionCall qi params arguments returnsScalar returnsSetOfScal "LATERAL " <> callIt (fmtParams prms) callIt :: SQL.Snippet -> SQL.Snippet - callIt argument | pgVer < pgVersion130 && returnsCompositeAlias = "(SELECT (" <> fromQi qi <> "(" <> argument <> ")).*) pgrst_call" - | returnsScalar || returnsSetOfScalar = "(SELECT " <> fromQi qi <> "(" <> argument <> ") pgrst_scalar) pgrst_call" - | otherwise = fromQi qi <> "(" <> argument <> ") pgrst_call" + callIt argument | returnsScalar || returnsSetOfScalar = "(SELECT " <> fromQi qi <> "(" <> argument <> ") pgrst_scalar) pgrst_call" + | otherwise = fromQi qi <> "(" <> argument <> ") pgrst_call" fmtParams :: [RoutineParam] -> SQL.Snippet fmtParams prms = intercalateSnippet ", " diff --git a/src/PostgREST/SchemaCache/Routine.hs b/src/PostgREST/SchemaCache/Routine.hs index 248fb5682f..c0517d3808 100644 --- a/src/PostgREST/SchemaCache/Routine.hs +++ b/src/PostgREST/SchemaCache/Routine.hs @@ -14,7 +14,6 @@ module PostgREST.SchemaCache.Routine , funcReturnsSingleComposite , funcReturnsVoid , funcTableName - , funcReturnsCompositeAlias , funcReturnsSingle , MediaHandlerMap , ResolvedHandler @@ -127,12 +126,6 @@ funcReturnsSetOfScalar proc = case proc of Function{pdReturnType = SetOf (Scalar{})} -> True _ -> False -funcReturnsCompositeAlias :: Routine -> Bool -funcReturnsCompositeAlias proc = case proc of - Function{pdReturnType = Single (Composite _ True)} -> True - Function{pdReturnType = SetOf (Composite _ True)} -> True - _ -> False - funcReturnsSingleComposite :: Routine -> Bool funcReturnsSingleComposite proc = case proc of Function{pdReturnType = Single (Composite _ _)} -> True diff --git a/test/spec/Feature/Query/InsertSpec.hs b/test/spec/Feature/Query/InsertSpec.hs index 0f6a1b6e7a..f6bbfd0645 100644 --- a/test/spec/Feature/Query/InsertSpec.hs +++ b/test/spec/Feature/Query/InsertSpec.hs @@ -11,8 +11,7 @@ import Test.Hspec.Wai import Test.Hspec.Wai.JSON import Text.Heredoc -import PostgREST.Config.PgVersion (PgVersion, pgVersion130, - pgVersion140) +import PostgREST.Config.PgVersion (PgVersion, pgVersion140) import Protolude hiding (get) import SpecHelper @@ -205,11 +204,7 @@ spec actualPgVersion = do it "fails with 400 and error" $ post "/simple_pk" [json| { "extra":"foo"} |] `shouldRespondWith` - (if actualPgVersion >= pgVersion130 then - [json|{"hint":null,"details":"Failing row contains (null, foo).","code":"23502","message":"null value in column \"k\" of relation \"simple_pk\" violates not-null constraint"}|] - else - [json|{"hint":null,"details":"Failing row contains (null, foo).","code":"23502","message":"null value in column \"k\" violates not-null constraint"}|] - ) + [json|{"hint":null,"details":"Failing row contains (null, foo).","code":"23502","message":"null value in column \"k\" of relation \"simple_pk\" violates not-null constraint"}|] { matchStatus = 400 , matchHeaders = [matchContentTypeJson] } diff --git a/test/spec/Feature/Query/PlanSpec.hs b/test/spec/Feature/Query/PlanSpec.hs index 837332b21e..b478eb8bb8 100644 --- a/test/spec/Feature/Query/PlanSpec.hs +++ b/test/spec/Feature/Query/PlanSpec.hs @@ -15,8 +15,7 @@ import Test.Hspec hiding (pendingWith) import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import PostgREST.Config.PgVersion (PgVersion, pgVersion130, - pgVersion170) +import PostgREST.Config.PgVersion (PgVersion, pgVersion170) import Protolude hiding (get) import SpecHelper @@ -49,27 +48,15 @@ spec actualPgVersion = do resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" } totalCost `shouldBe` 24.28 - it "outputs blocks info when using the buffers option" $ - if actualPgVersion >= pgVersion130 - then do - r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=buffers") "" + it "outputs blocks info when using the buffers option" $ do + r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=buffers") "" - let resBody = simpleBody r - resHeaders = simpleHeaders r - - liftIO $ do - resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=buffers; charset=utf-8") - resBody `shouldSatisfy` (\t -> T.isInfixOf "Shared Hit Blocks" (decodeUtf8 $ LBS.toStrict t)) - else do - -- analyze is required for buffers on pg < 13 - r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=analyze|buffers") "" - - let blocks = simpleBody r ^? nth 0 . key "Plan" . key "Shared Hit Blocks" - resHeaders = simpleHeaders r + let resBody = simpleBody r + resHeaders = simpleHeaders r - liftIO $ do - resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=analyze|buffers; charset=utf-8") - blocks `shouldBe` Just [aesonQQ| 1.0 |] + liftIO $ do + resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=buffers; charset=utf-8") + resBody `shouldSatisfy` (\t -> T.isInfixOf "Shared Hit Blocks" (decodeUtf8 $ LBS.toStrict t)) it "outputs the search path when using the settings option" $ do r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=settings") "" @@ -86,16 +73,15 @@ spec actualPgVersion = do } |] - when (actualPgVersion >= pgVersion130) $ - it "outputs WAL info when using the wal option" $ do - r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=analyze|wal") "" + it "outputs WAL info when using the wal option" $ do + r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=analyze|wal") "" - let walRecords = simpleBody r ^? nth 0 . key "Plan" . key "WAL Records" - resHeaders = simpleHeaders r + let walRecords = simpleBody r ^? nth 0 . key "Plan" . key "WAL Records" + resHeaders = simpleHeaders r - liftIO $ do - resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=analyze|wal; charset=utf-8") - walRecords `shouldBe` Just [aesonQQ|0|] + liftIO $ do + resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=analyze|wal; charset=utf-8") + walRecords `shouldBe` Just [aesonQQ|0|] it "outputs columns info when using the verbose option" $ do r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=verbose") "" From d75bb73d65da4d88945f7b04c12371144ed86820 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sun, 22 Dec 2024 19:10:22 +0100 Subject: [PATCH 2/7] chore(deps): update nixpkgs to unstable 2025-01-11 --- nix/nixpkgs-version.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nix/nixpkgs-version.nix b/nix/nixpkgs-version.nix index 52dcfe645d..5be16d8ed5 100644 --- a/nix/nixpkgs-version.nix +++ b/nix/nixpkgs-version.nix @@ -2,8 +2,8 @@ { owner = "NixOS"; repo = "nixpkgs"; - ref = "refs/heads/nixpkgs-unstable-darwin"; - date = "2024-11-09"; - rev = "a90280100f41a10914edfe729a4053e60c92b8e3"; - tarballHash = "1vwr665b6l6gma24w45q5hic86vbd8alc01mziwwr621hwlca88f"; + ref = "refs/heads/nixpkgs-unstable"; + date = "2025-01-11"; + rev = "32af3611f6f05655ca166a0b1f47b57c762b5192"; + tarballHash = "0shknvd56nfqh4awklgsxwaavpfixgh766m428qdlxihjmmqvhbl"; } From 4119f2afb48ca6cbf827c428ae890fedea827934 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Mon, 23 Dec 2024 22:02:21 +0100 Subject: [PATCH 3/7] nix: refactor derivation tests for static package Instead of rolling our own, we can use some tooling from nix / nixpkgs. We drop the "statically linked" check, because our goal is to compile mostly-static executables to darwin, too. However, those will never be fully static, because they always link to the platform's libc. --- nix/static.nix | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/nix/static.nix b/nix/static.nix index 0e626d1410..af427c67f1 100644 --- a/nix/static.nix +++ b/nix/static.nix @@ -41,31 +41,16 @@ let lib.compose.justStaticExecutables # To successfully compile a redistributable, fully static executable we need to: - # 1. make executable really statically linked. - # 2. avoid any references to /nix/store to prevent blowing up the closure size. - # 3. be able to run the executable. - # When checking for references, we ignore the following: - # - eeee... are removed references which don't actually exist - # - openssl-etc references are purposely designed to be very small - (lib.compose.overrideCabal (drv: { - postFixup = drv.postFixup + '' - exe="$out/bin/postgrest" - - if ! (file "$exe" | grep 'statically linked') then - echo "not a static executable, ldd output:" - ldd "$exe" - exit 1 - fi - - echo "Checking for references to /nix/store..." - (${pkgsStatic.binutils}/bin/strings "$exe" \ - | grep -v /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee \ - | grep -v -etc/etc/ssl \ - | grep /nix/store || exit 0 && exit 1) - echo "No references to /nix/store found" - - "$exe" --help - ''; + # 1. avoid any references to /nix/store to prevent blowing up the closure size. + # 2. be able to run the executable. + (drv: drv.overrideAttrs (finalAttrs: { + allowedReferences = [ + pkgsStatic.openssl.etc + ]; + + passthru.tests.version = pkgsStatic.testers.testVersion { + package = finalAttrs.finalPackage; + }; })) ]; From e0d826bd0116bddb45dd8ab52e85a78746e8f75d Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Mon, 23 Dec 2024 22:04:05 +0100 Subject: [PATCH 4/7] nix: remove libpq overlay We have fixed the static build of PostgreSQL upstream, so we don't need our separate overlay anymore. One more step towards building a static executable on other platforms. --- default.nix | 1 - nix/libpq.nix | 61 ------------------------------- nix/overlays/default.nix | 1 - nix/overlays/haskell-packages.nix | 21 +++++++---- nix/overlays/postgresql-libpq.nix | 6 --- nix/static.nix | 17 --------- 6 files changed, 13 insertions(+), 94 deletions(-) delete mode 100644 nix/libpq.nix delete mode 100644 nix/overlays/postgresql-libpq.nix diff --git a/default.nix b/default.nix index ed08f33388..a133b1256d 100644 --- a/default.nix +++ b/default.nix @@ -35,7 +35,6 @@ let allOverlays.build-toolbox allOverlays.checked-shell-script allOverlays.gitignore - allOverlays.postgresql-libpq (allOverlays.haskell-packages { inherit compiler; }) allOverlays.slocat ]; diff --git a/nix/libpq.nix b/nix/libpq.nix deleted file mode 100644 index 1500294621..0000000000 --- a/nix/libpq.nix +++ /dev/null @@ -1,61 +0,0 @@ -# Creating a separate libpq package is is discussed in -# https://github.com/NixOS/nixpkgs/issues/61580, but nixpkgs has not moved -# forward, yet. -# This package is passed to postgresql-libpq (haskell) which needs to be -# cross-compiled to the static build and possibly other architectures as -# as well. To reduce the number of dependencies that need to be built with -# it, this derivation focuses on building the client libraries only. No -# server, no tests. -{ stdenv -, lib -, openssl -, zlib -, postgresql -, pkg-config -, tzdata -}: - -stdenv.mkDerivation { - pname = "libpq"; - inherit (postgresql) src version patches; - - __structuredAttrs = true; - env.CFLAGS = "-fdata-sections -ffunction-sections" - + (if stdenv.cc.isClang then " -flto" else " -fmerge-constants -Wl,--gc-sections"); - - configureFlags = [ - "--without-gssapi" - "--without-icu" - "--without-readline" - "--with-openssl" - "--with-system-tzdata=${tzdata}/share/zoneinfo" - "--sysconfdir=/etc/postgresql" - ]; - - nativeBuildInputs = [ pkg-config tzdata ]; - buildInputs = [ openssl zlib ]; - - buildFlags = [ "submake-libpq" "submake-libpgport" ]; - - installPhase = '' - runHook preInstall - - make -C src/bin/pg_config install - make -C src/common install - make -C src/include install - make -C src/interfaces/libpq install - make -C src/port install - - rm -rfv $out/share - - runHook postInstall - ''; - - outputs = [ "out" ]; - - meta = with lib; { - homepage = "https://www.postgresql.org"; - description = "Client API library for PostgreSQL"; - license = licenses.postgresql; - }; -} diff --git a/nix/overlays/default.nix b/nix/overlays/default.nix index 18900f0a15..4a5f88643d 100644 --- a/nix/overlays/default.nix +++ b/nix/overlays/default.nix @@ -3,6 +3,5 @@ checked-shell-script = import ./checked-shell-script; gitignore = import ./gitignore.nix; haskell-packages = import ./haskell-packages.nix; - postgresql-libpq = import ./postgresql-libpq.nix; slocat = import ./slocat.nix; } diff --git a/nix/overlays/haskell-packages.nix b/nix/overlays/haskell-packages.nix index 917ab2478a..f4b1b4a222 100644 --- a/nix/overlays/haskell-packages.nix +++ b/nix/overlays/haskell-packages.nix @@ -60,15 +60,20 @@ let jose-jwt = prev.jose-jwt_0_10_0; - postgresql-libpq = lib.dontCheck (prev.callHackageDirect + postgresql-libpq = lib.overrideCabal + (lib.dontCheck (prev.callHackageDirect + { + pkg = "postgresql-libpq"; + ver = "0.10.1.0"; + sha256 = "sha256-tXOMqCO8opMilI9rx0D+njqjIjbZsH168Bzb8Aq8Ff4="; + } + { } + )) { - pkg = "postgresql-libpq"; - ver = "0.10.1.0"; - sha256 = "sha256-tXOMqCO8opMilI9rx0D+njqjIjbZsH168Bzb8Aq8Ff4="; - } - { - postgresql = super.libpq; - }); + configureFlags = [ "-fuse-pkg-config" ]; + libraryPkgconfigDepends = [ super.postgresql_16 ]; + librarySystemDepends = [ ]; + }; }; in { diff --git a/nix/overlays/postgresql-libpq.nix b/nix/overlays/postgresql-libpq.nix deleted file mode 100644 index 2d1841e608..0000000000 --- a/nix/overlays/postgresql-libpq.nix +++ /dev/null @@ -1,6 +0,0 @@ -_: super: -{ - libpq = super.callPackage ../libpq.nix { - postgresql = super.postgresql_16; - }; -} diff --git a/nix/static.nix b/nix/static.nix index af427c67f1..5334a03d28 100644 --- a/nix/static.nix +++ b/nix/static.nix @@ -18,23 +18,6 @@ let # Cross compiling with native bignum works better than with gmp enableNativeBignum = true; }; - - overrides = pkgs.lib.composeExtensions old.overrides (_: prev: { - postgresql-libpq = (lib.overrideCabal prev.postgresql-libpq { - # TODO: This section can be simplified when this PR has made it's way to us: - # https://github.com/NixOS/nixpkgs/pull/286370 - # Additionally, we need to use the default version in nixpkgs, otherwise the - # override will not be active as well. - # Using use-pkg-config flag, because pg_config won't work when cross-compiling - configureFlags = [ "-fuse-pkg-config" ]; - # postgresql doesn't build in the fully static overlay - but the default - # derivation is built with static libraries anyway. - libraryPkgconfigDepends = [ pkgsStatic.libpq ]; - librarySystemDepends = [ ]; - }).overrideAttrs (_: prevAttrs: { - buildInputs = prevAttrs.buildInputs ++ [ pkgsStatic.openssl ]; - }); - }); }); makeExecutableStatic = drv: pkgs.lib.pipe drv [ From a7993ab5e56b5df578f1d1a54a294e4cacc677a6 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Mon, 23 Dec 2024 22:04:54 +0100 Subject: [PATCH 5/7] nix: use upstream GHC for static build This way we benefit from NixOS' binary cache to deliver GHC for us and don't need to cache it ourselves. This will become relevant once we do that for more platforms. --- nix/static.nix | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/nix/static.nix b/nix/static.nix index 5334a03d28..b99dac9463 100644 --- a/nix/static.nix +++ b/nix/static.nix @@ -8,17 +8,7 @@ let inherit (pkgs) pkgsStatic; inherit (pkgsStatic.haskell) lib; - packagesStatic = - pkgsStatic.haskell.packages."${compiler}".override (old: { - ghc = pkgsStatic.pkgsBuildHost.haskell.compiler."${compiler}".override { - # Using the bundled libffi generally works better for cross-compiling - libffi = null; - # Building sphinx fails on some platforms - enableDocs = false; - # Cross compiling with native bignum works better than with gmp - enableNativeBignum = true; - }; - }); + packagesStatic = pkgsStatic.haskell.packages.native-bignum."${compiler}"; makeExecutableStatic = drv: pkgs.lib.pipe drv [ lib.compose.justStaticExecutables From 84ac6cad6e239f18c0eb36fb23aef9a3ddbf4a53 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sat, 18 Jan 2025 20:28:55 +0100 Subject: [PATCH 6/7] nix: fix some darwin sandbox issues --- nix/overlays/checked-shell-script/checked-shell-script.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nix/overlays/checked-shell-script/checked-shell-script.nix b/nix/overlays/checked-shell-script/checked-shell-script.nix index 64297d19c4..df6f03d491 100644 --- a/nix/overlays/checked-shell-script/checked-shell-script.nix +++ b/nix/overlays/checked-shell-script/checked-shell-script.nix @@ -6,6 +6,7 @@ , coreutils , git , lib +, moreutils , runCommand , shellcheck , stdenv @@ -56,7 +57,7 @@ let # Example: This way `postgrest-watch -h` will return the help output for watch, while # `postgrest-watch postgrest-test-spec -h` will return the help output for test-spec. # Taken from: https://github.com/matejak/argbash/issues/114#issuecomment-557108274 - sed '/_positionals_count + 1/a\\t\t\t\tset -- "''${@:1:1}" "--" "''${@:2}"' -i $out + sed '/_positionals_count + 1/a\\t\t\t\tset -- "''${@:1:1}" "--" "''${@:2}"' $out | ${moreutils}/bin/sponge $out ''; bash-completion = @@ -66,7 +67,7 @@ let '' + lib.optionalString (positionalCompletion != "") '' - sed 's#COMPREPLY.*compgen -o bashdefault .*$#${escape positionalCompletion}#' -i $out + sed 's#COMPREPLY.*compgen -o bashdefault .*$#${escape positionalCompletion}#' $out | ${moreutils}/bin/sponge $out '' ); From 377a3a726ece2340f782ea9dabe53f115a556213 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sun, 19 Jan 2025 16:08:54 +0100 Subject: [PATCH 7/7] fixup! chore(deps): update nixpkgs to unstable 2025-01-11 --- docs/how-tos/sql-user-management.rst | 2 +- docs/references/api/domain_representations.rst | 2 +- docs/references/api/resource_embedding.rst | 2 +- docs/references/auth.rst | 2 +- docs/references/configuration.rst | 2 +- docs/references/connection_pool.rst | 4 ++-- docs/references/listener.rst | 2 +- nix/tools/docs.nix | 9 +++++++++ 8 files changed, 17 insertions(+), 8 deletions(-) diff --git a/docs/how-tos/sql-user-management.rst b/docs/how-tos/sql-user-management.rst index c167e99b70..5b78867aab 100644 --- a/docs/how-tos/sql-user-management.rst +++ b/docs/how-tos/sql-user-management.rst @@ -3,7 +3,7 @@ SQL User Management =================== -As mentioned on :ref:`jwt_generation`, an external service can provide user management and coordinate with the PostgREST server using JWT. It’s also possible to support logins entirely through SQL. It’s a fair bit of work, so get ready. +As mentioned on :ref:`jwt_generation`, an external service can provide user management and coordinate with the PostgREST server using JWT. It's also possible to support logins entirely through SQL. It's a fair bit of work, so get ready. Storing Users and Passwords --------------------------- diff --git a/docs/references/api/domain_representations.rst b/docs/references/api/domain_representations.rst index 156af90d45..caa579e550 100644 --- a/docs/references/api/domain_representations.rst +++ b/docs/references/api/domain_representations.rst @@ -173,4 +173,4 @@ Domain Representations avoid all the above drawbacks. Their only drawback is tha Why not create a `base type `_ instead? ``CREATE TYPE app_uuid (INTERNALLENGTH = 22, INPUT = app_uuid_parser, OUTPUT = app_uuid_formatter)``. - Creating base types need superuser, which is restricted on cloud hosted databases. Additionally this way lets “how the data is presented” dictate “how the data is stored” which would be backwards. + Creating base types need superuser, which is restricted on cloud hosted databases. Additionally this way lets "how the data is presented" dictate "how the data is stored" which would be backwards. diff --git a/docs/references/api/resource_embedding.rst b/docs/references/api/resource_embedding.rst index 3aafa76a64..db72798fda 100644 --- a/docs/references/api/resource_embedding.rst +++ b/docs/references/api/resource_embedding.rst @@ -143,7 +143,7 @@ Since the table name is plural, we can be more accurate by making it singular wi One-to-many relationships ------------------------- -The **foreign key reference** establishes the inverse one-to-many relationship. In this case, ``films`` returns as a JSON array because of the “to-many” end. +The **foreign key reference** establishes the inverse one-to-many relationship. In this case, ``films`` returns as a JSON array because of the "to-many" end. .. code-block:: bash diff --git a/docs/references/auth.rst b/docs/references/auth.rst index cbfbd522ef..eaad16a32e 100644 --- a/docs/references/auth.rst +++ b/docs/references/auth.rst @@ -110,7 +110,7 @@ Symmetric Keys ~~~~~~~~~~~~~~ Each token is cryptographically signed with a secret key. In the case of symmetric cryptography the signer and verifier share the same secret passphrase, which can be configured with :ref:`jwt-secret`. -If it is set to a simple string value like “reallyreallyreallyreallyverysafe” then PostgREST interprets it as an HMAC-SHA256 passphrase. +If it is set to a simple string value like "reallyreallyreallyreallyverysafe" then PostgREST interprets it as an HMAC-SHA256 passphrase. .. _asym_keys: diff --git a/docs/references/configuration.rst b/docs/references/configuration.rst index 3d6c1d2b67..d4718486ed 100644 --- a/docs/references/configuration.rst +++ b/docs/references/configuration.rst @@ -405,7 +405,7 @@ db-pool-max-idletime **In-Database** `n/a` =============== ================================= - *For backwards compatibility, this config parameter is also available as “db-pool-timeout”.* + *For backwards compatibility, this config parameter is also available as "db-pool-timeout".* Time in seconds to close idle pool connections. diff --git a/docs/references/connection_pool.rst b/docs/references/connection_pool.rst index 13fdf5c4d2..74385b2253 100644 --- a/docs/references/connection_pool.rst +++ b/docs/references/connection_pool.rst @@ -15,7 +15,7 @@ Dynamic Connection Pool To conserve system resources, PostgREST uses a dynamic connection pool. This enables the number of connections in the pool to increase and decrease depending on request traffic. -- If all the connections are being used, a new connection is added. The pool can grow until it reaches the :ref:`db-pool` size. Note that it’s pointless to set this higher than the ``max_connections`` setting in your database. +- If all the connections are being used, a new connection is added. The pool can grow until it reaches the :ref:`db-pool` size. Note that it's pointless to set this higher than the ``max_connections`` setting in your database. - If a connection is unused for a period of time (:ref:`db-pool-max-idletime`), it will be released. - For connecting to the database, the :ref:`authenticator ` role is used. You can configure this using :ref:`db-uri`. @@ -106,4 +106,4 @@ Also set :ref:`db-channel-enabled` to ``false`` since ``LISTEN`` is not compatib .. note:: - It’s not recommended to use an external connection pooler. `Our benchmarks `_ indicate it provides much lower performance than PostgREST built-in pool. + It's not recommended to use an external connection pooler. `Our benchmarks `_ indicate it provides much lower performance than PostgREST built-in pool. diff --git a/docs/references/listener.rst b/docs/references/listener.rst index ad11a3411e..3c380b3458 100644 --- a/docs/references/listener.rst +++ b/docs/references/listener.rst @@ -4,7 +4,7 @@ Listener ######## PostgREST uses `LISTEN `_ to reload its :ref:`Schema Cache ` and :ref:`Configuration ` via `NOTIFY `_. -This is useful in environments where you can’t send SIGUSR1 or SIGUSR2 Unix Signals. +This is useful in environments where you can't send SIGUSR1 or SIGUSR2 Unix Signals. Like on cloud managed containers or on Windows systems. .. code:: postgresql diff --git a/nix/tools/docs.nix b/nix/tools/docs.nix index e6352389b5..ec7bddd506 100644 --- a/nix/tools/docs.nix +++ b/nix/tools/docs.nix @@ -35,6 +35,9 @@ let workingDir = "/docs"; } '' + # https://github.com/sphinx-doc/sphinx/issues/11739 + export LC_ALL=C + function build() { ${python}/bin/sphinx-build --color -W -a -n . -b "$@" } @@ -119,6 +122,8 @@ let workingDir = "/docs"; } '' + export LC_ALL=C + FILES=$(find . -type f -iname '*.rst' | tr '\n' ' ') # shellcheck disable=SC2086 disable=SC2016 @@ -139,6 +144,8 @@ let workingDir = "/docs"; } '' + export LC_ALL=C + FILES=$(find . -type f -iname '*.rst' | tr '\n' ' ') tail -n+2 postgrest.dict \ @@ -157,6 +164,8 @@ let workingDir = "/docs"; } '' + export LC_ALL=C + ${python}/bin/sphinx-build --color -b linkcheck . ../.docs-build '';