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)