From ec24f87a3f297bbab0f3ed513eb7bba36a1451c1 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Sat, 10 Feb 2024 19:32:33 +0100 Subject: [PATCH] feat: Raise minimum supported pg version to 11.21 PostgreSQL 11 is EOL already, so we only support the latest minor release, which is 11.22. Since the legacy nixpkgs we import PG in for our tests only has 11.21, we are happy with that as well. --- CHANGELOG.md | 1 + src/PostgREST/Config/PgVersion.hs | 20 +++---- test/spec/Feature/Auth/AuthSpec.hs | 46 +++++--------- test/spec/Feature/Query/AndOrParamsSpec.hs | 27 ++++----- test/spec/Feature/Query/InsertSpec.hs | 21 ++----- test/spec/Feature/Query/JsonOperatorSpec.hs | 19 +----- test/spec/Feature/Query/QuerySpec.hs | 66 ++++++++++----------- test/spec/Feature/Query/RpcSpec.hs | 27 ++++----- test/spec/Main.hs | 6 +- 9 files changed, 89 insertions(+), 144 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcfadf80c38..4ec5598af2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Changed - #2052, Dropped support for PostgreSQL 9.6 - @wolfgangwalther - #2052, Dropped support for PostgreSQL 10 - @wolfgangwalther + - #2052, Raise minimum required PostgreSQL version to 11.21 - @wolfgangwalther ### Deprecated diff --git a/src/PostgREST/Config/PgVersion.hs b/src/PostgREST/Config/PgVersion.hs index 8bd074dab2e..22df5c41a88 100644 --- a/src/PostgREST/Config/PgVersion.hs +++ b/src/PostgREST/Config/PgVersion.hs @@ -3,8 +3,6 @@ module PostgREST.Config.PgVersion ( PgVersion(..) , minimumPgVersion - , pgVersion112 - , pgVersion114 , pgVersion120 , pgVersion121 , pgVersion130 @@ -28,16 +26,14 @@ instance Ord PgVersion where -- | Tells the minimum PostgreSQL version required by this version of PostgREST minimumPgVersion :: PgVersion -minimumPgVersion = pgVersion110 - -pgVersion110 :: PgVersion -pgVersion110 = PgVersion 110000 "11.0" - -pgVersion112 :: PgVersion -pgVersion112 = PgVersion 110002 "11.2" - -pgVersion114 :: PgVersion -pgVersion114 = PgVersion 110004 "11.4" +minimumPgVersion = pgVersion11 + +-- PostgreSQL 11 is EOL already, so we only allow the last +-- minor release as the minimum version. Theoretically. But +-- the version we are using from legacy nix is only 11.21, +-- so we are happy with that. +pgVersion11 :: PgVersion +pgVersion11 = PgVersion 110021 "11.21" pgVersion120 :: PgVersion pgVersion120 = PgVersion 120000 "12.0" diff --git a/test/spec/Feature/Auth/AuthSpec.hs b/test/spec/Feature/Auth/AuthSpec.hs index fa0f1736265..2b5f264ae7d 100644 --- a/test/spec/Feature/Auth/AuthSpec.hs +++ b/test/spec/Feature/Auth/AuthSpec.hs @@ -7,30 +7,20 @@ import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import PostgREST.Config.PgVersion (PgVersion, pgVersion112) - import Protolude hiding (get) import SpecHelper -spec :: PgVersion -> SpecWith ((), Application) -spec actualPgVersion = describe "authorization" $ do +spec :: SpecWith ((), Application) +spec = describe "authorization" $ do let single = ("Accept","application/vnd.pgrst.object+json") it "denies access to tables that anonymous does not own" $ - get "/authors_only" `shouldRespondWith` ( - if actualPgVersion >= pgVersion112 then - [json| { - "hint":null, - "details":null, - "code":"42501", - "message":"permission denied for table authors_only"} |] - else - [json| { - "hint":null, - "details":null, - "code":"42501", - "message":"permission denied for relation authors_only"} |] - ) + get "/authors_only" `shouldRespondWith` + [json| { + "hint":null, + "details":null, + "code":"42501", + "message":"permission denied for table authors_only"} |] { matchStatus = 401 , matchHeaders = ["WWW-Authenticate" <:> "Bearer"] } @@ -38,20 +28,12 @@ spec actualPgVersion = describe "authorization" $ do it "denies access to tables that postgrest_test_author does not own" $ let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA" in request methodGet "/private_table" [auth] "" - `shouldRespondWith` ( - if actualPgVersion >= pgVersion112 then - [json| { - "hint":null, - "details":null, - "code":"42501", - "message":"permission denied for table private_table"} |] - else - [json| { - "hint":null, - "details":null, - "code":"42501", - "message":"permission denied for relation private_table"} |] - ) + `shouldRespondWith` + [json| { + "hint":null, + "details":null, + "code":"42501", + "message":"permission denied for table private_table"} |] { matchStatus = 403 , matchHeaders = [] } diff --git a/test/spec/Feature/Query/AndOrParamsSpec.hs b/test/spec/Feature/Query/AndOrParamsSpec.hs index aa6e5051676..f3a8882f8e3 100644 --- a/test/spec/Feature/Query/AndOrParamsSpec.hs +++ b/test/spec/Feature/Query/AndOrParamsSpec.hs @@ -7,13 +7,11 @@ import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import PostgREST.Config.PgVersion (PgVersion, pgVersion112) - import Protolude hiding (get) import SpecHelper -spec :: PgVersion -> SpecWith ((), Application) -spec actualPgVersion = +spec :: SpecWith ((), Application) +spec = describe "and/or params used for complex boolean logic" $ do context "used with GET" $ do context "or param" $ do @@ -96,17 +94,16 @@ spec actualPgVersion = get "/entities?and=(id.gte.2,arr.isdistinct.{1,2})&select=id" `shouldRespondWith` [json|[{ "id": 3 }, { "id": 4 }]|] { matchHeaders = [matchContentTypeJson] } - when (actualPgVersion >= pgVersion112) $ - it "can handle wfts (websearch_to_tsquery)" $ - get "/tsearch?or=(text_search_vector.plfts(german).Art,text_search_vector.plfts(french).amusant,text_search_vector.not.wfts(english).impossible)" - `shouldRespondWith` - [json|[ - {"text_search_vector": "'also':2 'fun':3 'possibl':8" }, - {"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }, - {"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }, - {"text_search_vector": "'art':4 'spass':5 'unmog':7" } - ]|] - { matchHeaders = [matchContentTypeJson] } + it "can handle wfts (websearch_to_tsquery)" $ + get "/tsearch?or=(text_search_vector.plfts(german).Art,text_search_vector.plfts(french).amusant,text_search_vector.not.wfts(english).impossible)" + `shouldRespondWith` + [json|[ + {"text_search_vector": "'also':2 'fun':3 'possibl':8" }, + {"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }, + {"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }, + {"text_search_vector": "'art':4 'spass':5 'unmog':7" } + ]|] + { matchHeaders = [matchContentTypeJson] } it "can handle cs and cd" $ get "/entities?or=(arr.cs.{1,2,3},arr.cd.{1})&select=id" `shouldRespondWith` diff --git a/test/spec/Feature/Query/InsertSpec.hs b/test/spec/Feature/Query/InsertSpec.hs index 019672d3490..1f5ad0d5383 100644 --- a/test/spec/Feature/Query/InsertSpec.hs +++ b/test/spec/Feature/Query/InsertSpec.hs @@ -11,9 +11,8 @@ import Test.Hspec.Wai import Test.Hspec.Wai.JSON import Text.Heredoc -import PostgREST.Config.PgVersion (PgVersion, pgVersion112, - pgVersion120, pgVersion130, - pgVersion140) +import PostgREST.Config.PgVersion (PgVersion, pgVersion120, + pgVersion130, pgVersion140) import Protolude hiding (get) import SpecHelper @@ -694,24 +693,16 @@ spec actualPgVersion = do it "fails inserting if more columns are selected" $ request methodPost "/limited_article_stars?select=article_id,user_id,created_at" [("Prefer", "return=representation")] - [json| {"article_id": 2, "user_id": 2} |] `shouldRespondWith` ( - if actualPgVersion >= pgVersion112 then - [json|{"hint":null,"details":null,"code":"42501","message":"permission denied for view limited_article_stars"}|] - else - [json|{"hint":null,"details":null,"code":"42501","message":"permission denied for relation limited_article_stars"}|] - ) + [json| {"article_id": 2, "user_id": 2} |] `shouldRespondWith` + [json|{"hint":null,"details":null,"code":"42501","message":"permission denied for view limited_article_stars"}|] { matchStatus = 401 , matchHeaders = [] } it "fails inserting if select is not specified" $ request methodPost "/limited_article_stars" [("Prefer", "return=representation")] - [json| {"article_id": 3, "user_id": 1} |] `shouldRespondWith` ( - if actualPgVersion >= pgVersion112 then - [json|{"hint":null,"details":null,"code":"42501","message":"permission denied for view limited_article_stars"}|] - else - [json|{"hint":null,"details":null,"code":"42501","message":"permission denied for relation limited_article_stars"}|] - ) + [json| {"article_id": 3, "user_id": 1} |] `shouldRespondWith` + [json|{"hint":null,"details":null,"code":"42501","message":"permission denied for view limited_article_stars"}|] { matchStatus = 401 , matchHeaders = [] } diff --git a/test/spec/Feature/Query/JsonOperatorSpec.hs b/test/spec/Feature/Query/JsonOperatorSpec.hs index fea74093f53..8dbdd8455a6 100644 --- a/test/spec/Feature/Query/JsonOperatorSpec.hs +++ b/test/spec/Feature/Query/JsonOperatorSpec.hs @@ -7,8 +7,7 @@ import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import PostgREST.Config.PgVersion (PgVersion, pgVersion112, - pgVersion121) +import PostgREST.Config.PgVersion (PgVersion, pgVersion121) import Protolude hiding (get) import SpecHelper @@ -75,28 +74,16 @@ spec actualPgVersion = describe "json and jsonb operators" $ do -- this works fine for /rpc/unexistent requests, but for this case a 500 seems more appropriate it "fails when a double arrow ->> is followed with a single arrow ->" $ do get "/json_arr?select=data->>c->1" - `shouldRespondWith` ( - if actualPgVersion >= pgVersion112 then + `shouldRespondWith` [json| {"hint":"No operator matches the given name and argument types. You might need to add explicit type casts.", "details":null,"code":"42883","message":"operator does not exist: text -> integer"} |] - else - [json| - {"hint":"No operator matches the given name and argument type(s). You might need to add explicit type casts.", - "details":null,"code":"42883","message":"operator does not exist: text -> integer"} |] - ) { matchStatus = 404 , matchHeaders = [] } get "/json_arr?select=data->>c->b" - `shouldRespondWith` ( - if actualPgVersion >= pgVersion112 then + `shouldRespondWith` [json| {"hint":"No operator matches the given name and argument types. You might need to add explicit type casts.", "details":null,"code":"42883","message":"operator does not exist: text -> unknown"} |] - else - [json| - {"hint":"No operator matches the given name and argument type(s). You might need to add explicit type casts.", - "details":null,"code":"42883","message":"operator does not exist: text -> unknown"} |] - ) { matchStatus = 404 , matchHeaders = [] } context "with array index" $ do diff --git a/test/spec/Feature/Query/QuerySpec.hs b/test/spec/Feature/Query/QuerySpec.hs index 7ed6d9beab1..6266b48f0c1 100644 --- a/test/spec/Feature/Query/QuerySpec.hs +++ b/test/spec/Feature/Query/QuerySpec.hs @@ -8,8 +8,7 @@ import Test.Hspec hiding (pendingWith) import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import PostgREST.Config.PgVersion (PgVersion, pgVersion112, - pgVersion121) +import PostgREST.Config.PgVersion (PgVersion, pgVersion121) import Protolude hiding (get) import SpecHelper @@ -176,29 +175,28 @@ spec actualPgVersion = do [json| [ {"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }] |] { matchHeaders = [matchContentTypeJson] } - when (actualPgVersion >= pgVersion112) $ do - it "finds matches with websearch_to_tsquery" $ - get "/tsearch?text_search_vector=wfts.The%20Fat%20Rats" `shouldRespondWith` - [json| [ {"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }] |] - { matchHeaders = [matchContentTypeJson] } - - it "can use boolean operators(and, or, -) in websearch_to_tsquery" $ do - get "/tsearch?text_search_vector=wfts.fun%20and%20possible" - `shouldRespondWith` - [json| [ {"text_search_vector": "'also':2 'fun':3 'possibl':8"}] |] - { matchHeaders = [matchContentTypeJson] } - get "/tsearch?text_search_vector=wfts.impossible%20or%20possible" - `shouldRespondWith` - [json| [ - {"text_search_vector": "'fun':5 'imposs':9 'kind':3"}, - {"text_search_vector": "'also':2 'fun':3 'possibl':8"}] - |] - { matchHeaders = [matchContentTypeJson] } - get "/tsearch?text_search_vector=wfts.fun%20and%20-possible" - `shouldRespondWith` - [json| [ {"text_search_vector": "'fun':5 'imposs':9 'kind':3"}] |] + it "finds matches with websearch_to_tsquery" $ + get "/tsearch?text_search_vector=wfts.The%20Fat%20Rats" `shouldRespondWith` + [json| [ {"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }] |] { matchHeaders = [matchContentTypeJson] } + it "can use boolean operators(and, or, -) in websearch_to_tsquery" $ do + get "/tsearch?text_search_vector=wfts.fun%20and%20possible" + `shouldRespondWith` + [json| [ {"text_search_vector": "'also':2 'fun':3 'possibl':8"}] |] + { matchHeaders = [matchContentTypeJson] } + get "/tsearch?text_search_vector=wfts.impossible%20or%20possible" + `shouldRespondWith` + [json| [ + {"text_search_vector": "'fun':5 'imposs':9 'kind':3"}, + {"text_search_vector": "'also':2 'fun':3 'possibl':8"}] + |] + { matchHeaders = [matchContentTypeJson] } + get "/tsearch?text_search_vector=wfts.fun%20and%20-possible" + `shouldRespondWith` + [json| [ {"text_search_vector": "'fun':5 'imposs':9 'kind':3"}] |] + { matchHeaders = [matchContentTypeJson] } + it "finds matches with different dictionaries" $ do get "/tsearch?text_search_vector=fts(french).amusant" `shouldRespondWith` [json| [{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }] |] @@ -207,11 +205,10 @@ spec actualPgVersion = do [json| [{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }] |] { matchHeaders = [matchContentTypeJson] } - when (actualPgVersion >= pgVersion112) $ - get "/tsearch?text_search_vector=wfts(french).amusant%20impossible" - `shouldRespondWith` - [json| [{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }] |] - { matchHeaders = [matchContentTypeJson] } + get "/tsearch?text_search_vector=wfts(french).amusant%20impossible" + `shouldRespondWith` + [json| [{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }] |] + { matchHeaders = [matchContentTypeJson] } it "can be negated with not operator" $ do get "/tsearch?text_search_vector=not.fts.impossible%7Cfat%7Cfun" `shouldRespondWith` @@ -231,13 +228,12 @@ spec actualPgVersion = do {"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4"}, {"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|] { matchHeaders = [matchContentTypeJson] } - when (actualPgVersion >= pgVersion112) $ - get "/tsearch?text_search_vector=not.wfts(english).impossible%20or%20fat%20or%20fun" - `shouldRespondWith` - [json| [ - {"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4"}, - {"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|] - { matchHeaders = [matchContentTypeJson] } + get "/tsearch?text_search_vector=not.wfts(english).impossible%20or%20fat%20or%20fun" + `shouldRespondWith` + [json| [ + {"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4"}, + {"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|] + { matchHeaders = [matchContentTypeJson] } context "Use of the phraseto_tsquery function" $ do it "finds matches" $ diff --git a/test/spec/Feature/Query/RpcSpec.hs b/test/spec/Feature/Query/RpcSpec.hs index d095f59d1f4..795b86b6707 100644 --- a/test/spec/Feature/Query/RpcSpec.hs +++ b/test/spec/Feature/Query/RpcSpec.hs @@ -11,14 +11,11 @@ import Test.Hspec.Wai import Test.Hspec.Wai.JSON import Text.Heredoc -import PostgREST.Config.PgVersion (PgVersion, pgVersion112, - pgVersion114) - import Protolude hiding (get) import SpecHelper -spec :: PgVersion -> SpecWith ((), Application) -spec actualPgVersion = +spec :: SpecWith ((), Application) +spec = describe "remote procedure call" $ do context "a proc that returns a set" $ do context "returns paginated results" $ do @@ -599,13 +596,12 @@ spec actualPgVersion = [json|"object"|] { matchHeaders = [matchContentTypeJson] } - when (actualPgVersion >= pgVersion114) $ - it "parses quoted JSON arguments as JSON string (from Postgres 10.9, 11.4)" $ - post "/rpc/json_argument" - [json| { "arg": "{ \"key\": 3 }" } |] - `shouldRespondWith` - [json|"string"|] - { matchHeaders = [matchContentTypeJson] } + it "parses quoted JSON arguments as JSON string (from Postgres 10.9, 11.4)" $ + post "/rpc/json_argument" + [json| { "arg": "{ \"key\": 3 }" } |] + `shouldRespondWith` + [json|"string"|] + { matchHeaders = [matchContentTypeJson] } context "improper input" $ do it "rejects unknown content type even if payload is good" $ do @@ -1019,10 +1015,9 @@ spec actualPgVersion = get "/rpc/get_tsearch?text_search_vector=not.fts(english).fun%7Crat" `shouldRespondWith` [json|[{"text_search_vector":"'amus':5 'fair':7 'impossibl':9 'peu':4"},{"text_search_vector":"'art':4 'spass':5 'unmog':7"}]|] { matchHeaders = [matchContentTypeJson] } - when (actualPgVersion >= pgVersion112) $ - get "/rpc/get_tsearch?text_search_vector=wfts.impossible" `shouldRespondWith` - [json|[{"text_search_vector":"'fun':5 'imposs':9 'kind':3"}]|] - { matchHeaders = [matchContentTypeJson] } + get "/rpc/get_tsearch?text_search_vector=wfts.impossible" `shouldRespondWith` + [json|[{"text_search_vector":"'fun':5 'imposs':9 'kind':3"}]|] + { matchHeaders = [matchContentTypeJson] } it "should work with the phraseto_tsquery function" $ get "/rpc/get_tsearch?text_search_vector=phfts(english).impossible" `shouldRespondWith` diff --git a/test/spec/Main.hs b/test/spec/Main.hs index c1beec53f7e..5f37a567579 100644 --- a/test/spec/Main.hs +++ b/test/spec/Main.hs @@ -126,8 +126,8 @@ main = do analyzeTable "child_entities" specs = uncurry describe <$> [ - ("Feature.Query.AndOrParamsSpec" , Feature.Query.AndOrParamsSpec.spec actualPgVersion) - , ("Feature.Auth.AuthSpec" , Feature.Auth.AuthSpec.spec actualPgVersion) + ("Feature.Query.AndOrParamsSpec" , Feature.Query.AndOrParamsSpec.spec) + , ("Feature.Auth.AuthSpec" , Feature.Auth.AuthSpec.spec) , ("Feature.ConcurrentSpec" , Feature.ConcurrentSpec.spec) , ("Feature.CorsSpec" , Feature.CorsSpec.spec) , ("Feature.CustomMediaSpec" , Feature.Query.CustomMediaSpec.spec) @@ -143,7 +143,7 @@ main = do , ("Feature.Query.PreferencesSpec" , Feature.Query.PreferencesSpec.spec) , ("Feature.Query.QuerySpec" , Feature.Query.QuerySpec.spec actualPgVersion) , ("Feature.Query.RawOutputTypesSpec" , Feature.Query.RawOutputTypesSpec.spec) - , ("Feature.Query.RpcSpec" , Feature.Query.RpcSpec.spec actualPgVersion) + , ("Feature.Query.RpcSpec" , Feature.Query.RpcSpec.spec) , ("Feature.Query.SingularSpec" , Feature.Query.SingularSpec.spec) , ("Feature.Query.NullsStripSpec" , Feature.Query.NullsStripSpec.spec) , ("Feature.Query.UpdateSpec" , Feature.Query.UpdateSpec.spec)