feat: Drop support for pg 11
PostgreSQL 11 is EOL since November 2023.
This commit is contained in:
committed by
Wolfgang Walther
parent
126178642b
commit
bb96c2dc74
@@ -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 = []
|
||||
}
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -11,8 +11,7 @@ import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
import Text.Heredoc
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion112,
|
||||
pgVersion120, pgVersion130,
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion130,
|
||||
pgVersion140)
|
||||
|
||||
import Protolude hiding (get)
|
||||
@@ -542,28 +541,27 @@ spec actualPgVersion = do
|
||||
, matchHeaders = ["Preference-Applied" <:> "missing=default, return=representation"]
|
||||
}
|
||||
|
||||
when (actualPgVersion >= pgVersion120) $
|
||||
it "fails with a good error message on generated always columns" $
|
||||
request methodPost "/foo?columns=a,b" [("Prefer", "return=representation"), ("Prefer", "missing=default")]
|
||||
[json| [
|
||||
{"a": "val"},
|
||||
{"a": "val", "b": "val"}
|
||||
]|]
|
||||
`shouldRespondWith`
|
||||
(if actualPgVersion < pgVersion140
|
||||
then [json| {
|
||||
"code": "42601",
|
||||
"details": "Column \"b\" is a generated column.",
|
||||
"hint": null,
|
||||
"message": "cannot insert into column \"b\""
|
||||
}|]
|
||||
else [json| {
|
||||
"code": "428C9",
|
||||
"details": "Column \"b\" is a generated column.",
|
||||
"hint": null,
|
||||
"message": "cannot insert a non-DEFAULT value into column \"b\""
|
||||
}|])
|
||||
{ matchStatus = 400 }
|
||||
it "fails with a good error message on generated always columns" $
|
||||
request methodPost "/foo?columns=a,b" [("Prefer", "return=representation"), ("Prefer", "missing=default")]
|
||||
[json| [
|
||||
{"a": "val"},
|
||||
{"a": "val", "b": "val"}
|
||||
]|]
|
||||
`shouldRespondWith`
|
||||
(if actualPgVersion < pgVersion140
|
||||
then [json| {
|
||||
"code": "42601",
|
||||
"details": "Column \"b\" is a generated column.",
|
||||
"hint": null,
|
||||
"message": "cannot insert into column \"b\""
|
||||
}|]
|
||||
else [json| {
|
||||
"code": "428C9",
|
||||
"details": "Column \"b\" is a generated column.",
|
||||
"hint": null,
|
||||
"message": "cannot insert a non-DEFAULT value into column \"b\""
|
||||
}|])
|
||||
{ matchStatus = 400 }
|
||||
|
||||
it "inserts a default on a DOMAIN with default" $
|
||||
request methodPost "/evil_friends?columns=id,name" [("Prefer", "return=representation"), ("Prefer", "missing=default")]
|
||||
@@ -710,24 +708,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| {"article_id": 2, "user_id": 2} |] `shouldRespondWith`
|
||||
[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"}|]
|
||||
)
|
||||
{ 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| {"article_id": 3, "user_id": 1} |] `shouldRespondWith`
|
||||
[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"}|]
|
||||
)
|
||||
{ matchStatus = 401
|
||||
, matchHeaders = []
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,8 +15,7 @@ import Test.Hspec hiding (pendingWith)
|
||||
import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion120,
|
||||
pgVersion130)
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion130)
|
||||
import Protolude hiding (get)
|
||||
import SpecHelper
|
||||
|
||||
@@ -34,10 +33,7 @@ spec actualPgVersion = do
|
||||
liftIO $ do
|
||||
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
|
||||
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
|
||||
totalCost `shouldBe`
|
||||
if actualPgVersion > pgVersion120
|
||||
then 15.63
|
||||
else 15.69
|
||||
totalCost `shouldBe` 15.63
|
||||
|
||||
it "outputs the total cost for a single filter on a view" $ do
|
||||
r <- request methodGet "/projects_view?id=gt.2"
|
||||
@@ -50,10 +46,7 @@ spec actualPgVersion = do
|
||||
liftIO $ do
|
||||
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
|
||||
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
|
||||
totalCost `shouldBe`
|
||||
if actualPgVersion > pgVersion120
|
||||
then 24.28
|
||||
else 32.27
|
||||
totalCost `shouldBe` 24.28
|
||||
|
||||
it "outputs blocks info when using the buffers option" $
|
||||
if actualPgVersion >= pgVersion130
|
||||
@@ -77,21 +70,20 @@ spec actualPgVersion = 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 |]
|
||||
|
||||
when (actualPgVersion >= pgVersion120) $
|
||||
it "outputs the search path when using the settings option" $ do
|
||||
r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=settings") ""
|
||||
it "outputs the search path when using the settings option" $ do
|
||||
r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=settings") ""
|
||||
|
||||
let searchPath = simpleBody r ^? nth 0 . key "Settings"
|
||||
resHeaders = simpleHeaders r
|
||||
let searchPath = simpleBody r ^? nth 0 . key "Settings"
|
||||
resHeaders = simpleHeaders r
|
||||
|
||||
liftIO $ do
|
||||
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=settings; charset=utf-8")
|
||||
searchPath `shouldBe`
|
||||
Just [aesonQQ|
|
||||
{
|
||||
"search_path": "\"test\""
|
||||
}
|
||||
|]
|
||||
liftIO $ do
|
||||
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=settings; charset=utf-8")
|
||||
searchPath `shouldBe`
|
||||
Just [aesonQQ|
|
||||
{
|
||||
"search_path": "\"test\""
|
||||
}
|
||||
|]
|
||||
|
||||
when (actualPgVersion >= pgVersion130) $
|
||||
it "outputs WAL info when using the wal option" $ do
|
||||
@@ -123,9 +115,7 @@ spec actualPgVersion = do
|
||||
liftIO $ do
|
||||
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=verbose; charset=utf-8")
|
||||
aggCol `shouldBe`
|
||||
if actualPgVersion >= pgVersion120
|
||||
then Just [aesonQQ| "COALESCE(json_agg(ROW(projects.id, projects.name, projects.client_id)), '[]'::json)" |]
|
||||
else Just [aesonQQ| "COALESCE(json_agg(ROW(pgrst_source.id, pgrst_source.name, pgrst_source.client_id)), '[]'::json)" |]
|
||||
Just [aesonQQ| "COALESCE(json_agg(ROW(projects.id, projects.name, projects.client_id)), '[]'::json)" |]
|
||||
|
||||
it "outputs the plan for application/vnd.pgrst.object " $ do
|
||||
r <- request methodGet "/projects_view" (acceptHdrs "application/vnd.pgrst.plan+json; for=\"application/vnd.pgrst.object\"; options=verbose") ""
|
||||
@@ -136,9 +126,7 @@ spec actualPgVersion = do
|
||||
liftIO $ do
|
||||
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/vnd.pgrst.object+json\"; options=verbose; charset=utf-8")
|
||||
aggCol `shouldBe`
|
||||
if actualPgVersion >= pgVersion120
|
||||
then Just [aesonQQ| "COALESCE((json_agg(ROW(projects.id, projects.name, projects.client_id)) -> 0), 'null'::json)" |]
|
||||
else Just [aesonQQ| "COALESCE((json_agg(ROW(pgrst_source.id, pgrst_source.name, pgrst_source.client_id)) -> 0), 'null'::json)" |]
|
||||
Just [aesonQQ| "COALESCE((json_agg(ROW(projects.id, projects.name, projects.client_id)) -> 0), 'null'::json)" |]
|
||||
|
||||
describe "writes plans" $ do
|
||||
it "outputs the total cost for an insert" $ do
|
||||
@@ -452,11 +440,7 @@ spec actualPgVersion = do
|
||||
liftIO $ do
|
||||
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/vnd.twkb\"; options=verbose; charset=utf-8")
|
||||
aggCol `shouldBe`
|
||||
(
|
||||
if actualPgVersion >= pgVersion120
|
||||
then Just [aesonQQ| "twkb_agg(ROW(lines.id, lines.name, lines.geom)::lines)" |]
|
||||
else Just [aesonQQ| "twkb_agg(ROW(pgrst_source.id, pgrst_source.name, pgrst_source.geom)::lines)" |]
|
||||
)
|
||||
Just [aesonQQ| "twkb_agg(ROW(lines.id, lines.name, lines.geom)::lines)" |]
|
||||
|
||||
disabledSpec :: SpecWith ((), Application)
|
||||
disabledSpec =
|
||||
|
||||
@@ -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,28 +175,27 @@ 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 "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 "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`
|
||||
@@ -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" $
|
||||
|
||||
@@ -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`
|
||||
|
||||
+3
-3
@@ -137,14 +137,14 @@ main = do
|
||||
analyzeTable "child_entities"
|
||||
|
||||
specs = uncurry describe <$> [
|
||||
("Feature.Auth.AuthSpec" , Feature.Auth.AuthSpec.spec actualPgVersion)
|
||||
("Feature.Auth.AuthSpec" , Feature.Auth.AuthSpec.spec)
|
||||
, ("Feature.ConcurrentSpec" , Feature.ConcurrentSpec.spec)
|
||||
, ("Feature.CorsSpec" , Feature.CorsSpec.spec)
|
||||
, ("Feature.CustomMediaSpec" , Feature.Query.CustomMediaSpec.spec)
|
||||
, ("Feature.NoSuperuserSpec" , Feature.NoSuperuserSpec.spec)
|
||||
, ("Feature.OpenApi.OpenApiSpec" , Feature.OpenApi.OpenApiSpec.spec)
|
||||
, ("Feature.OptionsSpec" , Feature.OptionsSpec.spec)
|
||||
, ("Feature.Query.AndOrParamsSpec" , Feature.Query.AndOrParamsSpec.spec actualPgVersion)
|
||||
, ("Feature.Query.AndOrParamsSpec" , Feature.Query.AndOrParamsSpec.spec)
|
||||
, ("Feature.Query.ComputedRelsSpec" , Feature.Query.ComputedRelsSpec.spec)
|
||||
, ("Feature.Query.DeleteSpec" , Feature.Query.DeleteSpec.spec)
|
||||
, ("Feature.Query.EmbedDisambiguationSpec" , Feature.Query.EmbedDisambiguationSpec.spec)
|
||||
@@ -159,7 +159,7 @@ main = do
|
||||
, ("Feature.Query.QuerySpec" , Feature.Query.QuerySpec.spec actualPgVersion)
|
||||
, ("Feature.Query.RawOutputTypesSpec" , Feature.Query.RawOutputTypesSpec.spec)
|
||||
, ("Feature.Query.RelatedQueriesSpec" , Feature.Query.RelatedQueriesSpec.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.SpreadQueriesSpec" , Feature.Query.SpreadQueriesSpec.spec)
|
||||
, ("Feature.Query.UpdateSpec" , Feature.Query.UpdateSpec.spec)
|
||||
|
||||
Vendored
+10
-14
@@ -694,23 +694,19 @@ UPDATE test.car_models SET car_brand_name = 'Ferrari' WHERE name = 'F310-B';
|
||||
UPDATE test.car_models SET car_brand_name = 'Lamborghini' WHERE name = 'Veneno';
|
||||
UPDATE test.car_models SET car_brand_name = 'Lamborghini' WHERE name = 'Murcielago';
|
||||
|
||||
DO $do$BEGIN
|
||||
IF (SELECT current_setting('server_version_num')::INT >= 120000) THEN
|
||||
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-01-14',7,'DeLorean',1981);
|
||||
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-01-15',9,'DeLorean',1981);
|
||||
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-02-11',1,'Murcielago',2001);
|
||||
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-02-12',3,'Murcielago',2001);
|
||||
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-01-14',7,'DeLorean',1981);
|
||||
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-01-15',9,'DeLorean',1981);
|
||||
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-02-11',1,'Murcielago',2001);
|
||||
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-02-12',3,'Murcielago',2001);
|
||||
|
||||
INSERT INTO test.car_racers(name) VALUES ('Alain Prost');
|
||||
INSERT INTO test.car_racers(name, car_model_name, car_model_year) VALUES ('Michael Schumacher', 'F310-B', 1997);
|
||||
INSERT INTO test.car_racers(name) VALUES ('Alain Prost');
|
||||
INSERT INTO test.car_racers(name, car_model_name, car_model_year) VALUES ('Michael Schumacher', 'F310-B', 1997);
|
||||
|
||||
INSERT INTO test.car_dealers(name,city) VALUES ('Springfield Cars S.A.','Springfield');
|
||||
INSERT INTO test.car_dealers(name,city) VALUES ('The Best Deals S.A.','Franklin');
|
||||
INSERT INTO test.car_dealers(name,city) VALUES ('Springfield Cars S.A.','Springfield');
|
||||
INSERT INTO test.car_dealers(name,city) VALUES ('The Best Deals S.A.','Franklin');
|
||||
|
||||
INSERT INTO test.car_models_car_dealers(car_model_name, car_model_year, car_dealer_name, car_dealer_city, quantity) VALUES ('DeLorean',1981,'Springfield Cars S.A.','Springfield',15);
|
||||
INSERT INTO test.car_models_car_dealers(car_model_name, car_model_year, car_dealer_name, car_dealer_city, quantity) VALUES ('Murcielago',2001,'The Best Deals S.A.','Franklin',2);
|
||||
END IF;
|
||||
END$do$;
|
||||
INSERT INTO test.car_models_car_dealers(car_model_name, car_model_year, car_dealer_name, car_dealer_city, quantity) VALUES ('DeLorean',1981,'Springfield Cars S.A.','Springfield',15);
|
||||
INSERT INTO test.car_models_car_dealers(car_model_name, car_model_year, car_dealer_name, car_dealer_city, quantity) VALUES ('Murcielago',2001,'The Best Deals S.A.','Franklin',2);
|
||||
|
||||
TRUNCATE TABLE test.products CASCADE;
|
||||
INSERT INTO test.products (id, name) VALUES (1,'product-1'), (2,'product-2'), (3,'product-3');
|
||||
|
||||
Vendored
+50
-61
@@ -2301,61 +2301,56 @@ create table test.car_brands (
|
||||
alter table test.car_models add primary key (name, year);
|
||||
alter table test.car_models add column car_brand_name varchar(64) references test.car_brands(name);
|
||||
|
||||
do $do$begin
|
||||
-- foreign keys referencing partitioned tables are supported from pg v12
|
||||
if (select current_setting('server_version_num')::int >= 120000) then
|
||||
create table test.car_model_sales(
|
||||
date varchar(64) not null,
|
||||
quantity int not null,
|
||||
car_model_name varchar(64),
|
||||
car_model_year int,
|
||||
primary key (date, car_model_name, car_model_year),
|
||||
foreign key (car_model_name, car_model_year) references test.car_models (name, year)
|
||||
) partition by range (date);
|
||||
create table test.car_model_sales(
|
||||
date varchar(64) not null,
|
||||
quantity int not null,
|
||||
car_model_name varchar(64),
|
||||
car_model_year int,
|
||||
primary key (date, car_model_name, car_model_year),
|
||||
foreign key (car_model_name, car_model_year) references test.car_models (name, year)
|
||||
) partition by range (date);
|
||||
|
||||
create table test.car_model_sales_202101 partition of test.car_model_sales
|
||||
for values from ('2021-01-01') to ('2021-01-31');
|
||||
create table test.car_model_sales_202101 partition of test.car_model_sales
|
||||
for values from ('2021-01-01') to ('2021-01-31');
|
||||
|
||||
create table test.car_model_sales_default partition of test.car_model_sales
|
||||
default;
|
||||
create table test.car_model_sales_default partition of test.car_model_sales
|
||||
default;
|
||||
|
||||
create table test.car_racers (
|
||||
name varchar(64) not null primary key,
|
||||
car_model_name varchar(64),
|
||||
car_model_year int,
|
||||
foreign key (car_model_name, car_model_year) references test.car_models (name, year)
|
||||
);
|
||||
create table test.car_racers (
|
||||
name varchar(64) not null primary key,
|
||||
car_model_name varchar(64),
|
||||
car_model_year int,
|
||||
foreign key (car_model_name, car_model_year) references test.car_models (name, year)
|
||||
);
|
||||
|
||||
create table test.car_dealers (
|
||||
name varchar(64) not null,
|
||||
city varchar(64) not null,
|
||||
primary key (name, city)
|
||||
) partition by list (city);
|
||||
create table test.car_dealers (
|
||||
name varchar(64) not null,
|
||||
city varchar(64) not null,
|
||||
primary key (name, city)
|
||||
) partition by list (city);
|
||||
|
||||
create table test.car_dealers_springfield partition of test.car_dealers
|
||||
for values in ('Springfield');
|
||||
create table test.car_dealers_springfield partition of test.car_dealers
|
||||
for values in ('Springfield');
|
||||
|
||||
create table test.car_dealers_default partition of test.car_dealers
|
||||
default;
|
||||
create table test.car_dealers_default partition of test.car_dealers
|
||||
default;
|
||||
|
||||
create table test.car_models_car_dealers (
|
||||
car_model_name varchar(64) not null,
|
||||
car_model_year int not null,
|
||||
car_dealer_name varchar(64) not null,
|
||||
car_dealer_city varchar(64) not null,
|
||||
quantity int not null,
|
||||
foreign key (car_model_name, car_model_year) references test.car_models (name, year),
|
||||
foreign key (car_dealer_name, car_dealer_city) references test.car_dealers (name, city),
|
||||
primary key (car_model_name, car_model_year, car_dealer_name, car_dealer_city, quantity)
|
||||
) partition by range (quantity);
|
||||
create table test.car_models_car_dealers (
|
||||
car_model_name varchar(64) not null,
|
||||
car_model_year int not null,
|
||||
car_dealer_name varchar(64) not null,
|
||||
car_dealer_city varchar(64) not null,
|
||||
quantity int not null,
|
||||
foreign key (car_model_name, car_model_year) references test.car_models (name, year),
|
||||
foreign key (car_dealer_name, car_dealer_city) references test.car_dealers (name, city),
|
||||
primary key (car_model_name, car_model_year, car_dealer_name, car_dealer_city, quantity)
|
||||
) partition by range (quantity);
|
||||
|
||||
create table test.car_models_car_dealers_10to20 partition of test.car_models_car_dealers
|
||||
for values from (10) to (20);
|
||||
create table test.car_models_car_dealers_10to20 partition of test.car_models_car_dealers
|
||||
for values from (10) to (20);
|
||||
|
||||
create table test.car_models_car_dealers_default partition of test.car_models_car_dealers
|
||||
default;
|
||||
end if;
|
||||
end$do$;
|
||||
create table test.car_models_car_dealers_default partition of test.car_models_car_dealers
|
||||
default;
|
||||
|
||||
create or replace function test.unnamed_json_param(json) returns json as $$
|
||||
select $1;
|
||||
@@ -3261,21 +3256,15 @@ AS $$
|
||||
select current_setting('is_superuser')::boolean;
|
||||
$$;
|
||||
|
||||
DO $do$
|
||||
BEGIN
|
||||
IF current_setting('server_version_num')::INT >= 120000 THEN
|
||||
CREATE TABLE test.foo (
|
||||
a text,
|
||||
b text GENERATED ALWAYS AS (
|
||||
case WHEN a = 'telegram' THEN 'im'
|
||||
WHEN a = 'proton' THEN 'email'
|
||||
WHEN a = 'infinity' THEN 'idea'
|
||||
ELSE 'bad idea'
|
||||
end) stored
|
||||
);
|
||||
END IF;
|
||||
END
|
||||
$do$;
|
||||
CREATE TABLE test.foo (
|
||||
a text,
|
||||
b text GENERATED ALWAYS AS (
|
||||
case WHEN a = 'telegram' THEN 'im'
|
||||
WHEN a = 'proton' THEN 'email'
|
||||
WHEN a = 'infinity' THEN 'idea'
|
||||
ELSE 'bad idea'
|
||||
end) stored
|
||||
);
|
||||
|
||||
create domain devil_int as int
|
||||
default 666;
|
||||
|
||||
Reference in New Issue
Block a user