feat: drop support for pg 9.5

This commit is contained in:
Wolfgang Walther
2021-11-24 19:42:53 +01:00
committed by Wolfgang Walther
parent 7ed8b10776
commit a65ac9b0f6
14 changed files with 141 additions and 177 deletions
-3
View File
@@ -63,9 +63,6 @@ jobs:
- name: Run the spec tests against PostgreSQL 9.6 - name: Run the spec tests against PostgreSQL 9.6
if: always() if: always()
run: postgrest-with-postgresql-9.6 postgrest-test-spec run: postgrest-with-postgresql-9.6 postgrest-test-spec
- name: Run the spec tests against PostgreSQL 9.5
if: always()
run: postgrest-with-postgresql-9.5 postgrest-test-spec
- name: Run query cost tests against all PostgreSQL versions - name: Run query cost tests against all PostgreSQL versions
if: always() if: always()
+1
View File
@@ -31,6 +31,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
+ Getting the value for a header GUC on PostgreSQL 14 is done using `current_setting('request.headers')::json->>'name-of-header'` and in a similar way for `request.cookies` and `request.jwt.claims` + Getting the value for a header GUC on PostgreSQL 14 is done using `current_setting('request.headers')::json->>'name-of-header'` and in a similar way for `request.cookies` and `request.jwt.claims`
+ PostgreSQL versions below 14 can opt in to the new JSON GUCs by setting the `db-use-legacy-gucs` config option to false (true by default) + PostgreSQL versions below 14 can opt in to the new JSON GUCs by setting the `db-use-legacy-gucs` config option to false (true by default)
- #1783, Partitions (created using `PARTITION OF`) are no longer included in the schema cache. - @laurenceisla - #1783, Partitions (created using `PARTITION OF`) are no longer included in the schema cache. - @laurenceisla
- #2038, Dropped support for PostgreSQL 9.5 - @wolfgangwalther
## [8.0.0] - 2021-07-25 ## [8.0.0] - 2021-07-25
-9
View File
@@ -112,15 +112,6 @@ the connection cannot do anything the user themselves couldn't. Other
forms of authentication can be built on top of the JWT primitive. See forms of authentication can be built on top of the JWT primitive. See
the docs for more information. the docs for more information.
Since PostgreSQL 9.5 supports true [row-level
security](http://www.postgresql.org/docs/9.5/static/ddl-rowsecurity.html).
In previous versions it can be simulated with triggers and
security-barrier views. Because the possible queries to the database
are limited to certain templates using
[leakproof](http://blog.2ndquadrant.com/how-do-postgresql-security_barrier-views-work/)
functions, the trigger workaround does not compromise row-level
security.
## Versioning ## Versioning
A robust long-lived API needs the freedom to exist in multiple A robust long-lived API needs the freedom to exist in multiple
-1
View File
@@ -52,7 +52,6 @@ let
{ name = "postgresql-11"; postgresql = pkgs.postgresql_11; } { name = "postgresql-11"; postgresql = pkgs.postgresql_11; }
{ name = "postgresql-10"; postgresql = pkgs.postgresql_10; } { name = "postgresql-10"; postgresql = pkgs.postgresql_10; }
{ name = "postgresql-9.6"; postgresql = pkgs.postgresql_9_6; } { name = "postgresql-9.6"; postgresql = pkgs.postgresql_9_6; }
{ name = "postgresql-9.5"; postgresql = pkgs.postgresql_9_5; }
]; ];
patches = patches =
+4 -4
View File
@@ -80,8 +80,8 @@ postgrest-coverage postgrest-with-postgresql-10
postgrest-lint postgrest-with-postgresql-11 postgrest-lint postgrest-with-postgresql-11
postgrest-run postgrest-with-postgresql-12 postgrest-run postgrest-with-postgresql-12
postgrest-style postgrest-with-postgresql-13 postgrest-style postgrest-with-postgresql-13
postgrest-style-check postgrest-with-postgresql-9.5 postgrest-style-check postgrest-with-postgresql-9.6
postgrest-test-io postgrest-with-postgresql-9.6 postgrest-test-io
... ...
[nix-shell]$ [nix-shell]$
@@ -104,8 +104,8 @@ postgrest-coverage postgrest-with-postgresql-10
postgrest-lint postgrest-with-postgresql-11 postgrest-lint postgrest-with-postgresql-11
postgrest-run postgrest-with-postgresql-12 postgrest-run postgrest-with-postgresql-12
postgrest-style postgrest-with-postgresql-13 postgrest-style postgrest-with-postgresql-13
postgrest-style-check postgrest-with-postgresql-9.5 postgrest-style-check postgrest-with-postgresql-9.6
postgrest-test-io postgrest-with-postgresql-9.6 postgrest-test-io
postgrest-test-memory postgrest-test-memory
... ...
+12 -12
View File
@@ -5,16 +5,16 @@ self: super:
# PostgreSQL 9.5 was removed from Nixpkgs with # PostgreSQL 9.5 was removed from Nixpkgs with
# https://github.com/NixOS/nixpkgs/commit/72ab382fb6b729b0d654f2c03f5eb25b39f11fbb # https://github.com/NixOS/nixpkgs/commit/72ab382fb6b729b0d654f2c03f5eb25b39f11fbb
# We pin its parent commit to get the last version that was available. # We pin its parent commit to get the last version that was available.
postgresql_9_5 = # postgresql_9_5 =
let # let
rev = "55ac7d4580c9ab67848c98cb9519317a1cc399c8"; # rev = "55ac7d4580c9ab67848c98cb9519317a1cc399c8";
tarballHash = "02ffj9f8s1hwhmxj85nx04sv64qb6jm7w0122a1dz9n32fymgklj"; # tarballHash = "02ffj9f8s1hwhmxj85nx04sv64qb6jm7w0122a1dz9n32fymgklj";
#
pinnedPkgs = # pinnedPkgs =
builtins.fetchTarball { # builtins.fetchTarball {
url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz"; # url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz";
sha256 = tarballHash; # sha256 = tarballHash;
}; # };
in # in
(import pinnedPkgs { }).pkgs.postgresql_9_5; # (import pinnedPkgs { }).pkgs.postgresql_9_5;
} }
-3
View File
@@ -258,7 +258,6 @@ handleRead headersOnly identifier context@RequestContext{..} = do
(shouldCount iPreferCount) (shouldCount iPreferCount)
(iAcceptContentType == CTTextCSV) (iAcceptContentType == CTTextCSV)
bField bField
ctxPgVersion
configDbPreparedStatements configDbPreparedStatements
total <- readTotal ctxConfig ctxApiRequest tableTotal countQuery total <- readTotal ctxConfig ctxApiRequest tableTotal countQuery
@@ -447,7 +446,6 @@ handleInvoke invMethod proc context@RequestContext{..} = do
(iAcceptContentType == CTTextCSV) (iAcceptContentType == CTTextCSV)
(iPreferParameters == Just MultipleObjects) (iPreferParameters == Just MultipleObjects)
bField bField
ctxPgVersion
(configDbPreparedStatements ctxConfig) (configDbPreparedStatements ctxConfig)
response <- liftEither $ gucResponse <$> gucStatus <*> gucHeaders response <- liftEither $ gucResponse <$> gucStatus <*> gucHeaders
@@ -532,7 +530,6 @@ writeQuery identifier@QualifiedIdentifier{..} isInsert pkCols context@RequestCon
(iAcceptContentType ctxApiRequest == CTTextCSV) (iAcceptContentType ctxApiRequest == CTTextCSV)
(iPreferRepresentation ctxApiRequest) (iPreferRepresentation ctxApiRequest)
pkCols pkCols
ctxPgVersion
(configDbPreparedStatements ctxConfig) (configDbPreparedStatements ctxConfig)
liftEither $ WriteQueryResult queryTotal fields body <$> gucStatus <*> gucHeaders liftEither $ WriteQueryResult queryTotal fields body <$> gucStatus <*> gucHeaders
+1 -5
View File
@@ -3,7 +3,6 @@
module PostgREST.Config.PgVersion module PostgREST.Config.PgVersion
( PgVersion(..) ( PgVersion(..)
, minimumPgVersion , minimumPgVersion
, pgVersion95
, pgVersion96 , pgVersion96
, pgVersion100 , pgVersion100
, pgVersion109 , pgVersion109
@@ -31,10 +30,7 @@ instance Ord PgVersion where
-- | Tells the minimum PostgreSQL version required by this version of PostgREST -- | Tells the minimum PostgreSQL version required by this version of PostgREST
minimumPgVersion :: PgVersion minimumPgVersion :: PgVersion
minimumPgVersion = pgVersion95 minimumPgVersion = pgVersion96
pgVersion95 :: PgVersion
pgVersion95 = PgVersion 90500 "9.5"
pgVersion96 :: PgVersion pgVersion96 :: PgVersion
pgVersion96 = PgVersion 90600 "9.6" pgVersion96 = PgVersion 90600 "9.6"
+4 -11
View File
@@ -46,7 +46,6 @@ import qualified Hasql.Encoders as HE
import Data.Foldable (foldr1) import Data.Foldable (foldr1)
import Text.InterpolatedString.Perl6 (qc) import Text.InterpolatedString.Perl6 (qc)
import PostgREST.Config.PgVersion (PgVersion, pgVersion96)
import PostgREST.DbStructure.Identifiers (FieldName, import PostgREST.DbStructure.Identifiers (FieldName,
QualifiedIdentifier (..)) QualifiedIdentifier (..))
import PostgREST.RangeQuery (NonnegRange, allRange, import PostgREST.RangeQuery (NonnegRange, allRange,
@@ -315,17 +314,11 @@ limitOffsetF range =
limit = maybe "ALL" (\l -> unknownEncoder (BS.pack $ show l)) $ rangeLimit range limit = maybe "ALL" (\l -> unknownEncoder (BS.pack $ show l)) $ rangeLimit range
offset = unknownEncoder (BS.pack . show $ rangeOffset range) offset = unknownEncoder (BS.pack . show $ rangeOffset range)
responseHeadersF :: PgVersion -> SqlFragment responseHeadersF :: SqlFragment
responseHeadersF pgVer = responseHeadersF = currentSettingF "response.headers"
if pgVer >= pgVersion96
then currentSettingF "response.headers"
else "null"
responseStatusF :: PgVersion -> SqlFragment responseStatusF :: SqlFragment
responseStatusF pgVer = responseStatusF = currentSettingF "response.status"
if pgVer >= pgVersion96
then currentSettingF "response.status"
else "null"
currentSettingF :: SqlFragment -> SqlFragment currentSettingF :: SqlFragment -> SqlFragment
currentSettingF setting = currentSettingF setting =
+14 -15
View File
@@ -30,9 +30,8 @@ import Data.Maybe (fromJust)
import Data.Text.Read (decimal) import Data.Text.Read (decimal)
import Network.HTTP.Types.Status (Status) import Network.HTTP.Types.Status (Status)
import PostgREST.Config.PgVersion (PgVersion) import PostgREST.Error (Error (..))
import PostgREST.Error (Error (..)) import PostgREST.GucHeader (GucHeader)
import PostgREST.GucHeader (GucHeader)
import PostgREST.DbStructure.Identifiers (FieldName) import PostgREST.DbStructure.Identifiers (FieldName)
import PostgREST.Query.SqlFragment import PostgREST.Query.SqlFragment
@@ -47,9 +46,9 @@ import Protolude
type ResultsWithCount = (Maybe Int64, Int64, [BS.ByteString], BS.ByteString, Either Error [GucHeader], Either Error (Maybe Status)) type ResultsWithCount = (Maybe Int64, Int64, [BS.ByteString], BS.ByteString, Either Error [GucHeader], Either Error (Maybe Status))
createWriteStatement :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> Bool -> createWriteStatement :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> Bool ->
PreferRepresentation -> [Text] -> PgVersion -> Bool -> PreferRepresentation -> [Text] -> Bool ->
SQL.Statement () ResultsWithCount SQL.Statement () ResultsWithCount
createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv rep pKeys pgVer = createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv rep pKeys =
SQL.dynamicallyParameterized snippet decodeStandard SQL.dynamicallyParameterized snippet decodeStandard
where where
snippet = snippet =
@@ -60,8 +59,8 @@ createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv rep pKeys
"pg_catalog.count(_postgrest_t) AS page_total, " <> "pg_catalog.count(_postgrest_t) AS page_total, " <>
locF <> " AS header, " <> locF <> " AS header, " <>
bodyF <> " AS body, " <> bodyF <> " AS body, " <>
responseHeadersF pgVer <> " AS response_headers, " <> responseHeadersF <> " AS response_headers, " <>
responseStatusF pgVer <> " AS response_status " responseStatusF <> " AS response_status "
) <> ) <>
"FROM (" <> selectF <> ") _postgrest_t" "FROM (" <> selectF <> ") _postgrest_t"
@@ -89,9 +88,9 @@ createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv rep pKeys
decodeStandard = decodeStandard =
fromMaybe (Nothing, 0, [], mempty, Right [], Right Nothing) <$> HD.rowMaybe standardRow fromMaybe (Nothing, 0, [], mempty, Right [], Right Nothing) <$> HD.rowMaybe standardRow
createReadStatement :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> Bool -> Maybe FieldName -> PgVersion -> Bool -> createReadStatement :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> Bool -> Maybe FieldName -> Bool ->
SQL.Statement () ResultsWithCount SQL.Statement () ResultsWithCount
createReadStatement selectQuery countQuery isSingle countTotal asCsv binaryField pgVer = createReadStatement selectQuery countQuery isSingle countTotal asCsv binaryField =
SQL.dynamicallyParameterized snippet decodeStandard SQL.dynamicallyParameterized snippet decodeStandard
where where
snippet = snippet =
@@ -103,8 +102,8 @@ createReadStatement selectQuery countQuery isSingle countTotal asCsv binaryField
"pg_catalog.count(_postgrest_t) AS page_total, " <> "pg_catalog.count(_postgrest_t) AS page_total, " <>
noLocationF <> " AS header, " <> noLocationF <> " AS header, " <>
bodyF <> " AS body, " <> bodyF <> " AS body, " <>
responseHeadersF pgVer <> " AS response_headers, " <> responseHeadersF <> " AS response_headers, " <>
responseStatusF pgVer <> " AS response_status " <> responseStatusF <> " AS response_status " <>
"FROM ( SELECT * FROM " <> sourceCTEName <> " ) _postgrest_t") "FROM ( SELECT * FROM " <> sourceCTEName <> " ) _postgrest_t")
(countCTEF, countResultF) = countF countQuery countTotal (countCTEF, countResultF) = countF countQuery countTotal
@@ -132,9 +131,9 @@ standardRow = (,,,,,) <$> nullableColumn HD.int8 <*> column HD.int8
type ProcResults = (Maybe Int64, Int64, ByteString, Either Error [GucHeader], Either Error (Maybe Status)) type ProcResults = (Maybe Int64, Int64, ByteString, Either Error [GucHeader], Either Error (Maybe Status))
callProcStatement :: Bool -> Bool -> SQL.Snippet -> SQL.Snippet -> SQL.Snippet -> Bool -> callProcStatement :: Bool -> Bool -> SQL.Snippet -> SQL.Snippet -> SQL.Snippet -> Bool ->
Bool -> Bool -> Bool -> Maybe FieldName -> PgVersion -> Bool -> Bool -> Bool -> Bool -> Maybe FieldName -> Bool ->
SQL.Statement () ProcResults SQL.Statement () ProcResults
callProcStatement returnsScalar returnsSingle callProcQuery selectQuery countQuery countTotal asSingle asCsv multObjects binaryField pgVer = callProcStatement returnsScalar returnsSingle callProcQuery selectQuery countQuery countTotal asSingle asCsv multObjects binaryField =
SQL.dynamicallyParameterized snippet decodeProc SQL.dynamicallyParameterized snippet decodeProc
where where
snippet = snippet =
@@ -145,8 +144,8 @@ callProcStatement returnsScalar returnsSingle callProcQuery selectQuery countQue
countResultF <> " AS total_result_set, " <> countResultF <> " AS total_result_set, " <>
"pg_catalog.count(_postgrest_t) AS page_total, " <> "pg_catalog.count(_postgrest_t) AS page_total, " <>
bodyF <> " AS body, " <> bodyF <> " AS body, " <>
responseHeadersF pgVer <> " AS response_headers, " <> responseHeadersF <> " AS response_headers, " <>
responseStatusF pgVer <> " AS response_status ") <> responseStatusF <> " AS response_status ") <>
"FROM (" <> selectQuery <> ") _postgrest_t" "FROM (" <> selectQuery <> ") _postgrest_t"
(countCTEF, countResultF) = countF countQuery countTotal (countCTEF, countResultF) = countF countQuery countTotal
+11 -14
View File
@@ -15,10 +15,8 @@ import Test.Hspec.Wai.JSON
import Protolude import Protolude
import SpecHelper import SpecHelper
import PostgREST.Config.PgVersion (PgVersion, pgVersion96) spec :: SpecWith ((), Application)
spec =
spec :: PgVersion -> SpecWith ((), Application)
spec actualPgVersion =
describe "multiple schemas in single instance" $ do describe "multiple schemas in single instance" $ do
context "Reading tables on different schemas" $ do context "Reading tables on different schemas" $ do
it "succeeds in reading table from default schema v1 if no schema is selected via header" $ it "succeeds in reading table from default schema v1 if no schema is selected via header" $
@@ -191,16 +189,15 @@ spec actualPgVersion =
[json|[{"id": 1, "name": "child v2-3", "parent_id": 3}]|] [json|[{"id": 1, "name": "child v2-3", "parent_id": 3}]|]
{ matchHeaders = ["Content-Profile" <:> "v2"] } { matchHeaders = ["Content-Profile" <:> "v2"] }
when (actualPgVersion >= pgVersion96) $ it "succeeds on PUT on the v2 schema" $
it "succeeds on PUT on the v2 schema" $ request methodPut "/children?id=eq.111" [("Content-Profile", "v2"), ("Prefer", "return=representation")]
request methodPut "/children?id=eq.111" [("Content-Profile", "v2"), ("Prefer", "return=representation")] [json| [ { "id": 111, "name": "child v2-111", "parent_id": null } ]|]
[json| [ { "id": 111, "name": "child v2-111", "parent_id": null } ]|] `shouldRespondWith`
`shouldRespondWith` [json|[{ "id": 111, "name": "child v2-111", "parent_id": null }]|]
[json|[{ "id": 111, "name": "child v2-111", "parent_id": null }]|] {
{ matchStatus = 200
matchStatus = 200 , matchHeaders = [matchContentTypeJson, "Content-Profile" <:> "v2"]
, matchHeaders = [matchContentTypeJson, "Content-Profile" <:> "v2"] }
}
context "OpenAPI output" $ do context "OpenAPI output" $ do
it "succeeds in reading table definition from default schema v1 if no schema is selected via header" $ do it "succeeds in reading table definition from default schema v1 if no schema is selected via header" $ do
+25 -27
View File
@@ -9,8 +9,7 @@ import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import PostgREST.Config.PgVersion (PgVersion, pgVersion110, import PostgREST.Config.PgVersion (PgVersion, pgVersion110,
pgVersion112, pgVersion121, pgVersion112, pgVersion121)
pgVersion96)
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper import SpecHelper
@@ -200,34 +199,33 @@ spec actualPgVersion = do
{"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|] {"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|]
{ matchHeaders = [matchContentTypeJson] } { matchHeaders = [matchContentTypeJson] }
when (actualPgVersion >= pgVersion96) $ context "Use of the phraseto_tsquery function" $ do
context "Use of the phraseto_tsquery function" $ do it "finds matches" $
it "finds matches" $ get "/tsearch?text_search_vector=phfts.The%20Fat%20Cats" `shouldRespondWith`
get "/tsearch?text_search_vector=phfts.The%20Fat%20Cats" `shouldRespondWith` [json| [{"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }] |]
[json| [{"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" }] |] { matchHeaders = [matchContentTypeJson] }
{ matchHeaders = [matchContentTypeJson] }
it "finds matches with different dictionaries" $ it "finds matches with different dictionaries" $
get "/tsearch?text_search_vector=phfts(german).Art%20Spass" `shouldRespondWith` get "/tsearch?text_search_vector=phfts(german).Art%20Spass" `shouldRespondWith`
[json| [{"text_search_vector": "'art':4 'spass':5 'unmog':7" }] |] [json| [{"text_search_vector": "'art':4 'spass':5 'unmog':7" }] |]
{ matchHeaders = [matchContentTypeJson] } { matchHeaders = [matchContentTypeJson] }
it "can be negated with not operator" $ it "can be negated with not operator" $
get "/tsearch?text_search_vector=not.phfts(english).The%20Fat%20Cats" `shouldRespondWith` get "/tsearch?text_search_vector=not.phfts(english).The%20Fat%20Cats" `shouldRespondWith`
[json| [ [json| [
{"text_search_vector": "'fun':5 'imposs':9 'kind':3"}, {"text_search_vector": "'fun':5 'imposs':9 'kind':3"},
{"text_search_vector": "'also':2 'fun':3 'possibl':8"}, {"text_search_vector": "'also':2 'fun':3 'possibl':8"},
{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4"}, {"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4"},
{"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|] {"text_search_vector": "'art':4 'spass':5 'unmog':7"}]|]
{ matchHeaders = [matchContentTypeJson] } { matchHeaders = [matchContentTypeJson] }
it "can be used with or query param" $ it "can be used with or query param" $
get "/tsearch?or=(text_search_vector.phfts(german).Art%20Spass, text_search_vector.phfts(french).amusant, text_search_vector.fts(english).impossible)" `shouldRespondWith` get "/tsearch?or=(text_search_vector.phfts(german).Art%20Spass, text_search_vector.phfts(french).amusant, text_search_vector.fts(english).impossible)" `shouldRespondWith`
[json|[ [json|[
{"text_search_vector": "'fun':5 'imposs':9 'kind':3" }, {"text_search_vector": "'fun':5 'imposs':9 'kind':3" },
{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }, {"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" },
{"text_search_vector": "'art':4 'spass':5 'unmog':7"} {"text_search_vector": "'art':4 'spass':5 'unmog':7"}
]|] { matchHeaders = [matchContentTypeJson] } ]|] { matchHeaders = [matchContentTypeJson] }
it "matches with computed column" $ it "matches with computed column" $
get "/items?always_true=eq.true&order=id.asc" `shouldRespondWith` get "/items?always_true=eq.true&order=id.asc" `shouldRespondWith`
+57 -59
View File
@@ -15,7 +15,7 @@ import Text.Heredoc
import PostgREST.Config.PgVersion (PgVersion, pgVersion100, import PostgREST.Config.PgVersion (PgVersion, pgVersion100,
pgVersion109, pgVersion110, pgVersion109, pgVersion110,
pgVersion112, pgVersion114, pgVersion112, pgVersion114,
pgVersion140, pgVersion96) pgVersion140)
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper import SpecHelper
@@ -1005,72 +1005,70 @@ spec actualPgVersion =
[json|[{"text_search_vector":"'fun':5 'imposs':9 'kind':3"}]|] [json|[{"text_search_vector":"'fun':5 'imposs':9 'kind':3"}]|]
{ matchHeaders = [matchContentTypeJson] } { matchHeaders = [matchContentTypeJson] }
when (actualPgVersion >= pgVersion96) $ it "should work with the phraseto_tsquery function" $
it "should work with the phraseto_tsquery function" $ get "/rpc/get_tsearch?text_search_vector=phfts(english).impossible" `shouldRespondWith`
get "/rpc/get_tsearch?text_search_vector=phfts(english).impossible" `shouldRespondWith` [json|[{"text_search_vector":"'fun':5 'imposs':9 'kind':3"}]|]
[json|[{"text_search_vector":"'fun':5 'imposs':9 'kind':3"}]|] { matchHeaders = [matchContentTypeJson] }
{ matchHeaders = [matchContentTypeJson] }
it "should work with an argument of custom type in public schema" $ it "should work with an argument of custom type in public schema" $
get "/rpc/test_arg?my_arg=something" `shouldRespondWith` get "/rpc/test_arg?my_arg=something" `shouldRespondWith`
[json|"foobar"|] [json|"foobar"|]
{ matchHeaders = [matchContentTypeJson] } { matchHeaders = [matchContentTypeJson] }
when (actualPgVersion >= pgVersion96) $ do context "GUC headers on function calls" $ do
context "GUC headers on function calls" $ do it "succeeds setting the headers" $ do
it "succeeds setting the headers" $ do get "/rpc/get_projects_and_guc_headers?id=eq.2&select=id"
get "/rpc/get_projects_and_guc_headers?id=eq.2&select=id" `shouldRespondWith` [json|[{"id": 2}]|]
`shouldRespondWith` [json|[{"id": 2}]|] {matchHeaders = [
{matchHeaders = [ matchContentTypeJson,
matchContentTypeJson, "X-Test" <:> "key1=val1; someValue; key2=val2",
"X-Test" <:> "key1=val1; someValue; key2=val2", "X-Test-2" <:> "key1=val1"]}
"X-Test-2" <:> "key1=val1"]} get "/rpc/get_int_and_guc_headers?num=1"
get "/rpc/get_int_and_guc_headers?num=1" `shouldRespondWith` [json|1|]
`shouldRespondWith` [json|1|] {matchHeaders = [
{matchHeaders = [ matchContentTypeJson,
matchContentTypeJson, "X-Test" <:> "key1=val1; someValue; key2=val2",
"X-Test" <:> "key1=val1; someValue; key2=val2", "X-Test-2" <:> "key1=val1"]}
"X-Test-2" <:> "key1=val1"]} post "/rpc/get_int_and_guc_headers" [json|{"num": 1}|]
post "/rpc/get_int_and_guc_headers" [json|{"num": 1}|] `shouldRespondWith` [json|1|]
`shouldRespondWith` [json|1|] {matchHeaders = [
{matchHeaders = [ matchContentTypeJson,
matchContentTypeJson, "X-Test" <:> "key1=val1; someValue; key2=val2",
"X-Test" <:> "key1=val1; someValue; key2=val2", "X-Test-2" <:> "key1=val1"]}
"X-Test-2" <:> "key1=val1"]}
it "fails when setting headers with wrong json structure" $ do it "fails when setting headers with wrong json structure" $ do
get "/rpc/bad_guc_headers_1" get "/rpc/bad_guc_headers_1"
`shouldRespondWith` `shouldRespondWith`
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|] [json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
{ matchStatus = 500 { matchStatus = 500
, matchHeaders = [ matchContentTypeJson ] , matchHeaders = [ matchContentTypeJson ]
} }
get "/rpc/bad_guc_headers_2" get "/rpc/bad_guc_headers_2"
`shouldRespondWith` `shouldRespondWith`
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|] [json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
{ matchStatus = 500 { matchStatus = 500
, matchHeaders = [ matchContentTypeJson ] , matchHeaders = [ matchContentTypeJson ]
} }
get "/rpc/bad_guc_headers_3" get "/rpc/bad_guc_headers_3"
`shouldRespondWith` `shouldRespondWith`
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|] [json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
{ matchStatus = 500 { matchStatus = 500
, matchHeaders = [ matchContentTypeJson ] , matchHeaders = [ matchContentTypeJson ]
} }
post "/rpc/bad_guc_headers_1" [json|{}|] post "/rpc/bad_guc_headers_1" [json|{}|]
`shouldRespondWith` `shouldRespondWith`
[json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|] [json|{"message":"response.headers guc must be a JSON array composed of objects with a single key and a string value"}|]
{ matchStatus = 500 { matchStatus = 500
, matchHeaders = [ matchContentTypeJson ] , matchHeaders = [ matchContentTypeJson ]
} }
it "can set the same http header twice" $ it "can set the same http header twice" $
get "/rpc/set_cookie_twice" get "/rpc/set_cookie_twice"
`shouldRespondWith` `shouldRespondWith`
"null" "null"
{ matchHeaders = [ matchContentTypeJson { matchHeaders = [ matchContentTypeJson
, "Set-Cookie" <:> "sessionid=38afes7a8; HttpOnly; Path=/" , "Set-Cookie" <:> "sessionid=38afes7a8; HttpOnly; Path=/"
, "Set-Cookie" <:> "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly" ]} , "Set-Cookie" <:> "id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly" ]}
it "can override the Location header on a trigger" $ it "can override the Location header on a trigger" $
post "/stuff" post "/stuff"
+12 -14
View File
@@ -9,13 +9,12 @@ import Data.List.NonEmpty (toList)
import Test.Hspec import Test.Hspec
import PostgREST.App (postgrest) import PostgREST.App (postgrest)
import PostgREST.Config (AppConfig (..), LogLevel (..)) import PostgREST.Config (AppConfig (..), LogLevel (..))
import PostgREST.Config.Database (queryPgVersion) import PostgREST.Config.Database (queryPgVersion)
import PostgREST.Config.PgVersion (pgVersion96) import PostgREST.DbStructure (queryDbStructure)
import PostgREST.DbStructure (queryDbStructure) import Protolude hiding (toList, toS)
import Protolude hiding (toList, toS) import Protolude.Conv (toS)
import Protolude.Conv (toS)
import SpecHelper import SpecHelper
import qualified PostgREST.AppState as AppState import qualified PostgREST.AppState as AppState
@@ -203,16 +202,15 @@ main = do
parallel $ before extraSearchPathApp $ parallel $ before extraSearchPathApp $
describe "Feature.ExtraSearchPathSpec" Feature.ExtraSearchPathSpec.spec describe "Feature.ExtraSearchPathSpec" Feature.ExtraSearchPathSpec.spec
when (actualPgVersion >= pgVersion96) $ do -- this test runs with a root spec function override
-- this test runs with a root spec function override parallel $ before rootSpecApp $
parallel $ before rootSpecApp $ describe "Feature.RootSpec" Feature.RootSpec.spec
describe "Feature.RootSpec" Feature.RootSpec.spec parallel $ before responseHeadersApp $
parallel $ before responseHeadersApp $ describe "Feature.RpcPreRequestGucsSpec" Feature.RpcPreRequestGucsSpec.spec
describe "Feature.RpcPreRequestGucsSpec" Feature.RpcPreRequestGucsSpec.spec
-- this test runs with multiple schemas -- this test runs with multiple schemas
parallel $ before multipleSchemaApp $ parallel $ before multipleSchemaApp $
describe "Feature.MultipleSchemaSpec" $ Feature.MultipleSchemaSpec.spec actualPgVersion describe "Feature.MultipleSchemaSpec" Feature.MultipleSchemaSpec.spec
-- this test runs with db-uses-legacy-gucs = false -- this test runs with db-uses-legacy-gucs = false
parallel $ before testCfgLegacyGucsApp $ parallel $ before testCfgLegacyGucsApp $