diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 189a84fe2..b94060545 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -75,7 +75,7 @@ jobs: fail-fast: false matrix: # Latest version is tested via `coverage` above. - pgVersion: [14, 15, 16, 17] + pgVersion: [14, 15, 16, 17, 18] name: PG ${{ matrix.pgVersion }} runs-on: ubuntu-24.04 defaults: diff --git a/cabal.project.freeze b/cabal.project.freeze index 2d06cc173..677895160 100644 --- a/cabal.project.freeze +++ b/cabal.project.freeze @@ -1 +1 @@ -index-state: hackage.haskell.org 2026-06-02T04:24:44Z +index-state: hackage.haskell.org 2026-07-01T00:00:00Z diff --git a/default.nix b/default.nix index e05bb45db..2d05fd133 100644 --- a/default.nix +++ b/default.nix @@ -52,6 +52,7 @@ let postgresqlVersions = [ + { name = "pg-19"; postgresql = pkgs.postgresql_19.withPackages (p: [ p.postgis p.pg_safeupdate ]); } { name = "pg-18"; postgresql = pkgs.postgresql_18.withPackages (p: [ p.postgis p.pg_safeupdate ]); } { name = "pg-17"; postgresql = pkgs.postgresql_17.withPackages (p: [ p.postgis p.pg_safeupdate ]); } { name = "pg-16"; postgresql = pkgs.postgresql_16.withPackages (p: [ p.postgis p.pg_safeupdate ]); } diff --git a/flake.lock b/flake.lock index 075ff9cc9..99eba90b2 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1780336545, - "narHash": "sha256-vhVhuXzFrIOfcssC/9hDHx7MHzDKjF3keHuREOQqQiQ=", + "lastModified": 1782918843, + "narHash": "sha256-ETYnV9U7Sr+A45dohzZdfCZKOss4qrTkO+wgNZNvEc0=", "owner": "nixos", "repo": "nixpkgs", - "rev": "4df1b885d76a54e1aa1a318f8d16fd6005b6401f", + "rev": "e8273b29fe1390ec8d4603f2477357555291432e", "type": "github" }, "original": { diff --git a/src/PostgREST/Config/PgVersion.hs b/src/PostgREST/Config/PgVersion.hs index 5129267f1..1739535fd 100644 --- a/src/PostgREST/Config/PgVersion.hs +++ b/src/PostgREST/Config/PgVersion.hs @@ -5,6 +5,7 @@ module PostgREST.Config.PgVersion , minimumPgVersion , pgVersion150 , pgVersion180 + , pgVersion190 ) where import qualified Data.Aeson as JSON @@ -34,3 +35,6 @@ pgVersion150 = PgVersion 150000 "15.0" "15.0" pgVersion180 :: PgVersion pgVersion180 = PgVersion 180000 "18.0" "18.0" + +pgVersion190 :: PgVersion +pgVersion190 = PgVersion 190000 "19.0" "19.0" diff --git a/stack.yaml b/stack.yaml index a3aa95bba..7d48f3c8d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,5 +1,5 @@ # When updating, update GHC version in .github/workflows/build.yaml as well. -resolver: lts-24.43 # 2026-05-29, GHC 9.10.3 +resolver: lts-24.48 # 2026-06-30, GHC 9.10.3 nix: packages: diff --git a/stack.yaml.lock b/stack.yaml.lock index f4bc9a5cd..3177ac899 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -55,7 +55,7 @@ packages: hackage: warp-3.4.13 snapshots: - completed: - sha256: 3c412a7c13dba6d3d808455a458e0776c58b6cf99b8a7961a2f5e55589d6f1d6 - size: 729011 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/24/43.yaml - original: lts-24.43 + sha256: 3286bb954fe0e7e0291ca61fac01a689b7165da9d7e8ba3c3a045be989c860fd + size: 732677 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/24/48.yaml + original: lts-24.48 diff --git a/test/io/test_io.py b/test/io/test_io.py index f3275344b..19cdaaa14 100644 --- a/test/io/test_io.py +++ b/test/io/test_io.py @@ -2056,11 +2056,7 @@ def test_db_pre_config_with_pg_reserved_words(defaultenv): with run(env=env, no_startup_stdout=False, wait_for=None) as postgrest: output = postgrest.read_stdout(nlines=8) - assert any( - 'Failed to query database settings for the config parameters.{"code":"42883","details":null,"hint":"No function matches the given name and argument types. You might need to add explicit type casts.","message":"function select() does not exist"}' - in line - for line in output - ) + assert any("function select() does not exist" in line for line in output) def test_server_timing_transaction_duration_with_role_statement_timeout( diff --git a/test/spec/Feature/Query/EmbedDisambiguationSpec.hs b/test/spec/Feature/Query/EmbedDisambiguationSpec.hs index 7b79483d9..81e8be132 100644 --- a/test/spec/Feature/Query/EmbedDisambiguationSpec.hs +++ b/test/spec/Feature/Query/EmbedDisambiguationSpec.hs @@ -365,7 +365,7 @@ spec withConfig = withConfig baseCfg $ context "tables with self reference foreign keys" $ do context "one self reference foreign key" $ do it "embeds parents recursively" $ - get "/family_tree?id=in.(3,4)&select=id,parent(id,name,parent(*))" `shouldRespondWith` + get "/family_tree?id=in.(3,4)&select=id,parent(id,name,parent(*))&order=id" `shouldRespondWith` [json|[ { "id": "3", "parent": { "id": "1", "name": "Parental Unit", "parent": null } }, { "id": "4", "parent": { "id": "2", "name": "Kid One", "parent": { "id": "1", "name": "Parental Unit", "parent": null } } } @@ -390,7 +390,7 @@ spec withConfig = withConfig baseCfg $ }]|] { matchHeaders = [matchContentTypeJson] } it "embeds parent and then embeds children on a view" $ - get "/job?select=id,parent_id(*),children:job!parent_id(id,parent_id)" `shouldRespondWith` + get "/job?select=id,parent_id(*),children:job!parent_id(id,parent_id)&order=id" `shouldRespondWith` [json|[ { "id": 1, @@ -527,13 +527,13 @@ spec withConfig = withConfig baseCfg $ context "embedding with col as a target doesn't consider views" $ do -- https://github.com/PostgREST/postgrest/issues/1643 it "works with self reference both ways(m2o and o2m)" $ do - get "/test?select=id,parent_id,parent:parent_id(id)" `shouldRespondWith` + get "/test?select=id,parent_id,parent:parent_id(id)&order=id" `shouldRespondWith` [json| [ { "id": 1, "parent_id": null, "parent": null }, { "id": 2, "parent_id": 1, "parent": { "id": 1 } } ] |] { matchHeaders = [matchContentTypeJson] } - get "/test?select=id,parent_id,childs:test(id)" `shouldRespondWith` + get "/test?select=id,parent_id,childs:test(id)&order=id" `shouldRespondWith` [json| [ { "id": 1, "parent_id": null, "childs": [ { "id": 2 } ] }, { "id": 2, "parent_id": 1, "childs": [] } diff --git a/test/spec/Feature/Query/JsonOperatorSpec.hs b/test/spec/Feature/Query/JsonOperatorSpec.hs index afcde23c6..4b91bc8a9 100644 --- a/test/spec/Feature/Query/JsonOperatorSpec.hs +++ b/test/spec/Feature/Query/JsonOperatorSpec.hs @@ -5,11 +5,13 @@ import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON +import PostgREST.Config.PgVersion (PgVersion, pgVersion190) + import Protolude hiding (get) import SpecHelper -spec :: SpecWithConfig -spec withConfig = withConfig baseCfg $ describe "json and jsonb operators" $ do +spec :: PgVersion -> SpecWithConfig +spec actualPgVersion withConfig = withConfig baseCfg $ describe "json and jsonb operators" $ do context "Shaping response with select parameter" $ do it "obtains a json subfield one level with casting" $ get "/complex_items?id=eq.1&select=settings->>foo::json" `shouldRespondWith` @@ -67,15 +69,27 @@ spec withConfig = withConfig baseCfg $ describe "json and jsonb operators" $ do it "fails when a double arrow ->> is followed with a single arrow ->" $ do get "/json_arr?select=data->>c->1" `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"} |] + (if actualPgVersion < pgVersion190 then + [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":"You might need to add explicit type casts.","details":"No operator of that name accepts the given argument types.", + "code":"42883","message":"operator does not exist: text -> integer"} |] + ) { matchStatus = 404 , matchHeaders = [] } get "/json_arr?select=data->>c->b" `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"} |] + (if actualPgVersion < pgVersion190 then + [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":"You might need to add explicit type casts.","details":"No operator of that name accepts the given argument types.", + "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 ca433ceb5..52805d865 100644 --- a/test/spec/Feature/Query/QuerySpec.hs +++ b/test/spec/Feature/Query/QuerySpec.hs @@ -7,11 +7,13 @@ import Test.Hspec hiding (pendingWith) import Test.Hspec.Wai import Test.Hspec.Wai.JSON +import PostgREST.Config.PgVersion (PgVersion, pgVersion190) + import Protolude hiding (get) import SpecHelper -spec :: SpecWithConfig -spec withConfig = withConfig baseCfg $ do +spec :: PgVersion -> SpecWithConfig +spec actualPgVersion withConfig = withConfig baseCfg $ do describe "Querying a table with a column called count" $ it "should not confuse count column with pg_catalog.count aggregate" $ @@ -1651,14 +1653,17 @@ spec withConfig = withConfig baseCfg $ do -- verifies we don't panic or add inappropriate SQL to the filters. it "fails safely on user trying to use ilike operator on data reps column" $ get "/datarep_todos?select=id,name&label_color=ilike.#*100" `shouldRespondWith` - [json| - {"code":"42883","details":null,"hint":"No operator matches the given name and argument types. You might need to add explicit type casts.","message":"operator does not exist: public.color ~~* unknown"} - |] + (if actualPgVersion < pgVersion190 then + [json| + {"code":"42883","details":null,"hint":"No operator matches the given name and argument types. You might need to add explicit type casts.","message":"operator does not exist: public.color ~~* unknown"} + |] + else + [json| + {"code":"42883","details":"No operator of that name accepts the given argument types.","hint":"You might need to add explicit type casts.","message":"operator does not exist: public.color ~~* unknown"} + |] + ) { matchStatus = 404 - , matchHeaders = [ - "Content-Length" <:> "200", - matchContentTypeJson - ] + , matchHeaders = [ matchContentTypeJson ] } context "searching for an empty string" $ do diff --git a/test/spec/Main.hs b/test/spec/Main.hs index 7d1222d34..4573a5f1a 100644 --- a/test/spec/Main.hs +++ b/test/spec/Main.hs @@ -140,7 +140,7 @@ main = do , ("Feature.Query.EmbedInnerJoinSpec" , Feature.Query.EmbedInnerJoinSpec.spec) , ("Feature.Query.ErrorSpec" , Feature.Query.ErrorSpec.spec) , ("Feature.Query.InsertSpec" , Feature.Query.InsertSpec.spec) - , ("Feature.Query.JsonOperatorSpec" , Feature.Query.JsonOperatorSpec.spec) + , ("Feature.Query.JsonOperatorSpec" , Feature.Query.JsonOperatorSpec.spec actualPgVersion) , ("Feature.Query.NullsStripSpec" , Feature.Query.NullsStripSpec.spec) , ("Feature.Query.PgSafeUpdateSpec.disabledSpec" , Feature.Query.PgSafeUpdateSpec.disabledSpec) , ("Feature.Query.PlanSpec.disabledSpec" , Feature.Query.PlanSpec.disabledSpec) @@ -149,7 +149,7 @@ main = do , ("Feature.Query.Preferences.MaxAffectedSpec" , Feature.Query.Preferences.MaxAffectedSpec.spec) , ("Feature.Query.Preferences.TimezoneSpec.enabledSpec", Feature.Query.Preferences.TimezoneSpec.enabledSpec) , ("Feature.Query.QueryLimitedSpec" , Feature.Query.QueryLimitedSpec.spec) - , ("Feature.Query.QuerySpec" , Feature.Query.QuerySpec.spec) + , ("Feature.Query.QuerySpec" , Feature.Query.QuerySpec.spec actualPgVersion) , ("Feature.Query.RangeSpec" , Feature.Query.RangeSpec.spec) , ("Feature.Query.RawOutputTypesSpec" , Feature.Query.RawOutputTypesSpec.spec) , ("Feature.Query.RelatedQueriesSpec" , Feature.Query.RelatedQueriesSpec.spec)