feat: Drop support for pg 11

PostgreSQL 11 is EOL since November 2023.
This commit is contained in:
Wolfgang Walther
2024-06-15 17:23:34 +02:00
committed by Wolfgang Walther
parent 126178642b
commit bb96c2dc74
20 changed files with 206 additions and 362 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
pgVersion: [11, 12, 13, 14, 15, 16] pgVersion: [12, 13, 14, 15, 16]
name: PG ${{ matrix.pgVersion }} name: PG ${{ matrix.pgVersion }}
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
defaults: defaults:
+1
View File
@@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- #2052, Dropped support for PostgreSQL 9.6 - @wolfgangwalther - #2052, Dropped support for PostgreSQL 9.6 - @wolfgangwalther
- #2052, Dropped support for PostgreSQL 10 - @wolfgangwalther - #2052, Dropped support for PostgreSQL 10 - @wolfgangwalther
- #2052, Dropped support for PostgreSQL 11 - @wolfgangwalther
## [12.2.0] - 2024-06-11 ## [12.2.0] - 2024-06-11
-3
View File
@@ -36,9 +36,7 @@ let
allOverlays.checked-shell-script allOverlays.checked-shell-script
allOverlays.gitignore allOverlays.gitignore
allOverlays.postgresql-libpq allOverlays.postgresql-libpq
allOverlays.postgresql-legacy
allOverlays.postgresql-future allOverlays.postgresql-future
allOverlays.postgis
(allOverlays.haskell-packages { inherit compiler; }) (allOverlays.haskell-packages { inherit compiler; })
allOverlays.slocat allOverlays.slocat
]; ];
@@ -54,7 +52,6 @@ let
{ name = "postgresql-14"; postgresql = pkgs.postgresql_14.withPackages (p: [ p.postgis p.pg_safeupdate ]); } { name = "postgresql-14"; postgresql = pkgs.postgresql_14.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
{ name = "postgresql-13"; postgresql = pkgs.postgresql_13.withPackages (p: [ p.postgis p.pg_safeupdate ]); } { name = "postgresql-13"; postgresql = pkgs.postgresql_13.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
{ name = "postgresql-12"; postgresql = pkgs.postgresql_12.withPackages (p: [ p.postgis p.pg_safeupdate ]); } { name = "postgresql-12"; postgresql = pkgs.postgresql_12.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
{ name = "postgresql-11"; postgresql = pkgs.postgresql_11.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
]; ];
# Dynamic derivation for PostgREST # Dynamic derivation for PostgREST
+2 -7
View File
@@ -16,16 +16,11 @@ Supported PostgreSQL versions
============================= =============================
=============== ================================= =============== =================================
**Supported** PostgreSQL >= 11 **Supported** PostgreSQL >= 12
=============== ================================= =============== =================================
PostgREST works with all PostgreSQL versions starting from 11. PostgREST works with all PostgreSQL versions still `officially supported <https://www.postgresql.org/support/versioning/>`_.
.. note::
Support for PostgreSQL version 11 is deprecated. From this on version onwards, PostgREST will only support non-end-of-life PostgreSQL versions.
See https://www.postgresql.org/support/versioning/.
Running PostgREST Running PostgREST
================= =================
+12 -12
View File
@@ -75,12 +75,12 @@ The PostgREST utilities available in `nix-shell` all have names that begin with
postgrest-build postgrest-test-spec postgrest-build postgrest-test-spec
postgrest-check postgrest-watch postgrest-check postgrest-watch
postgrest-clean postgrest-with-all postgrest-clean postgrest-with-all
postgrest-coverage postgrest-with-postgresql-11 postgrest-coverage postgrest-with-postgresql-12
postgrest-lint postgrest-with-postgresql-12 postgrest-lint postgrest-with-postgresql-13
postgrest-run postgrest-with-postgresql-13 postgrest-run postgrest-with-postgresql-14
postgrest-style postgrest-with-postgresql-14 postgrest-style postgrest-with-postgresql-15
postgrest-style-check postgrest-with-postgresql-15 postgrest-style-check postgrest-with-postgresql-16
postgrest-test-io postgrest-with-postgresql-16 postgrest-test-io
... ...
[nix-shell]$ [nix-shell]$
@@ -99,12 +99,12 @@ $ nix-shell --arg memory true
postgrest-build postgrest-test-spec postgrest-build postgrest-test-spec
postgrest-check postgrest-watch postgrest-check postgrest-watch
postgrest-clean postgrest-with-all postgrest-clean postgrest-with-all
postgrest-coverage postgrest-with-postgresql-11 postgrest-coverage postgrest-with-postgresql-12
postgrest-lint postgrest-with-postgresql-12 postgrest-lint postgrest-with-postgresql-13
postgrest-run postgrest-with-postgresql-13 postgrest-run postgrest-with-postgresql-14
postgrest-style postgrest-with-postgresql-14 postgrest-style postgrest-with-postgresql-15
postgrest-style-check postgrest-with-postgresql-15 postgrest-style-check postgrest-with-postgresql-16
postgrest-test-io postgrest-with-postgresql-16 postgrest-test-io
postgrest-test-memory postgrest-test-memory
... ...
-2
View File
@@ -3,9 +3,7 @@
checked-shell-script = import ./checked-shell-script; checked-shell-script = import ./checked-shell-script;
gitignore = import ./gitignore.nix; gitignore = import ./gitignore.nix;
haskell-packages = import ./haskell-packages.nix; haskell-packages = import ./haskell-packages.nix;
postgis = import ./postgis.nix;
postgresql-libpq = import ./postgresql-libpq.nix; postgresql-libpq = import ./postgresql-libpq.nix;
postgresql-legacy = import ./postgresql-legacy.nix;
postgresql-future = import ./postgresql-future.nix; postgresql-future = import ./postgresql-future.nix;
slocat = import ./slocat.nix; slocat = import ./slocat.nix;
} }
-18
View File
@@ -1,18 +0,0 @@
final: prev:
let
postgis_3_2_3 = rec {
version = "3.2.3";
src = final.fetchurl {
url = "https://download.osgeo.org/postgis/source/postgis-${version}.tar.gz";
sha256 = "sha256-G02LXHVuWrpZ77wYM7Iu/k1lYneO7KVvpJf+susTZow=";
};
meta.broken = false;
};
in
{
postgresql_11 = prev.postgresql_11.override { this = final.postgresql_11; } // {
pkgs = prev.postgresql_11.pkgs // {
postgis = prev.postgresql_11.pkgs.postgis.overrideAttrs (_: postgis_3_2_3);
};
};
}
-19
View File
@@ -1,19 +0,0 @@
_: _:
# Overlay that adds legacy versions of PostgreSQL that are supported by
# PostgREST.
{
# PostgreSQL 11 was removed from Nixpkgs with
# https://github.com/NixOS/nixpkgs/commit/1220a4d4dd1a4590780a5e1c18d1333a121be366
# We pin its parent commit to get the last version that was available.
postgresql_11 =
let
rev = "f5458516e42cc5cb4123cc2d93f45c240548aa18";
tarballHash = "1h03621sxfhw4z6ya74k6c2lyx3z7pvf2jcg4vs7i01yz2m6w3cv";
pinnedPkgs =
builtins.fetchTarball {
url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz";
sha256 = tarballHash;
};
in
(import pinnedPkgs { }).pkgs.postgresql_11;
}
+1 -13
View File
@@ -3,9 +3,6 @@
module PostgREST.Config.PgVersion module PostgREST.Config.PgVersion
( PgVersion(..) ( PgVersion(..)
, minimumPgVersion , minimumPgVersion
, pgVersion112
, pgVersion114
, pgVersion120
, pgVersion121 , pgVersion121
, pgVersion130 , pgVersion130
, pgVersion140 , pgVersion140
@@ -29,16 +26,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 = pgVersion110 minimumPgVersion = pgVersion120
pgVersion110 :: PgVersion
pgVersion110 = PgVersion 110000 "11.0" "11.0"
pgVersion112 :: PgVersion
pgVersion112 = PgVersion 110002 "11.2" "11.2"
pgVersion114 :: PgVersion
pgVersion114 = PgVersion 110004 "11.4" "11.4"
pgVersion120 :: PgVersion pgVersion120 :: PgVersion
pgVersion120 = PgVersion 120000 "12.0" "12.0" pgVersion120 = PgVersion 120000 "12.0" "12.0"
+13 -27
View File
@@ -45,9 +45,7 @@ import Text.InterpolatedString.Perl6 (q)
import PostgREST.Config (AppConfig (..)) import PostgREST.Config (AppConfig (..))
import PostgREST.Config.Database (TimezoneNames, import PostgREST.Config.Database (TimezoneNames,
pgVersionStatement,
toIsolationLevel) toIsolationLevel)
import PostgREST.Config.PgVersion (PgVersion, pgVersion120)
import PostgREST.SchemaCache.Identifiers (AccessSet, FieldName, import PostgREST.SchemaCache.Identifiers (AccessSet, FieldName,
QualifiedIdentifier (..), QualifiedIdentifier (..),
RelIdentifier (..), RelIdentifier (..),
@@ -144,8 +142,7 @@ type SqlQuery = ByteString
querySchemaCache :: AppConfig -> SQL.Transaction SchemaCache querySchemaCache :: AppConfig -> SQL.Transaction SchemaCache
querySchemaCache AppConfig{..} = do querySchemaCache AppConfig{..} = do
SQL.sql "set local schema ''" -- This voids the search path. The following queries need this for getting the fully qualified name(schema.name) of every db object SQL.sql "set local schema ''" -- This voids the search path. The following queries need this for getting the fully qualified name(schema.name) of every db object
pgVer <- SQL.statement mempty $ pgVersionStatement prepared tabs <- SQL.statement schemas $ allTables prepared
tabs <- SQL.statement schemas $ allTables pgVer prepared
keyDeps <- SQL.statement (schemas, configDbExtraSearchPath) $ allViewsKeyDependencies prepared keyDeps <- SQL.statement (schemas, configDbExtraSearchPath) $ allViewsKeyDependencies prepared
m2oRels <- SQL.statement mempty $ allM2OandO2ORels prepared m2oRels <- SQL.statement mempty $ allM2OandO2ORels prepared
funcs <- SQL.statement (schemas, configDbHoistedTxSettings) $ allFunctions prepared funcs <- SQL.statement (schemas, configDbHoistedTxSettings) $ allFunctions prepared
@@ -602,15 +599,13 @@ addViewPrimaryKeys tabs keyDeps =
-- * We need to choose a single reference for each column, otherwise we'd output too many columns in location headers etc. -- * We need to choose a single reference for each column, otherwise we'd output too many columns in location headers etc.
takeFirstPK = mapMaybe (head . snd) takeFirstPK = mapMaybe (head . snd)
allTables :: PgVersion -> Bool -> SQL.Statement [Schema] TablesMap allTables :: Bool -> SQL.Statement [Schema] TablesMap
allTables pgVer = allTables =
SQL.Statement sql (arrayParam HE.text) decodeTables SQL.Statement tablesSqlQuery (arrayParam HE.text) decodeTables
where
sql = tablesSqlQuery pgVer
-- | Gets tables with their PK cols -- | Gets tables with their PK cols
tablesSqlQuery :: PgVersion -> SqlQuery tablesSqlQuery :: SqlQuery
tablesSqlQuery pgVer = tablesSqlQuery =
-- the tbl_constraints/key_col_usage CTEs are based on the standard "information_schema.table_constraints"/"information_schema.key_column_usage" views, -- the tbl_constraints/key_col_usage CTEs are based on the standard "information_schema.table_constraints"/"information_schema.key_column_usage" views,
-- we cannot use those directly as they include the following privilege filter: -- we cannot use those directly as they include the following privilege filter:
-- (pg_has_role(ss.relowner, 'USAGE'::text) OR has_column_privilege(ss.roid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'::text)); -- (pg_has_role(ss.relowner, 'USAGE'::text) OR has_column_privilege(ss.roid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'::text));
@@ -624,7 +619,13 @@ tablesSqlQuery pgVer =
c.relname::name AS table_name, c.relname::name AS table_name,
a.attname::name AS column_name, a.attname::name AS column_name,
d.description AS description, d.description AS description,
|] <> columnDefault <> [q| AS column_default, -- typbasetype and typdefaultbin handles `CREATE DOMAIN .. DEFAULT val`, attidentity/attgenerated handles generated columns, pg_get_expr gets the default of a column
CASE
WHEN t.typbasetype != 0 THEN pg_get_expr(t.typdefaultbin, 0)
WHEN a.attidentity = 'd' THEN format('nextval(%s)', quote_literal(seqsch.nspname || '.' || seqclass.relname))
WHEN a.attgenerated = 's' THEN null
ELSE pg_get_expr(ad.adbin, ad.adrelid)::text
END AS column_default,
not (a.attnotnull OR t.typtype = 'd' AND t.typnotnull) AS is_nullable, not (a.attnotnull OR t.typtype = 'd' AND t.typnotnull) AS is_nullable,
CASE CASE
WHEN t.typtype = 'd' THEN WHEN t.typtype = 'd' THEN
@@ -810,21 +811,6 @@ tablesSqlQuery pgVer =
AND n.nspname NOT IN ('pg_catalog', 'information_schema') AND n.nspname NOT IN ('pg_catalog', 'information_schema')
AND not c.relispartition AND not c.relispartition
ORDER BY table_schema, table_name|] ORDER BY table_schema, table_name|]
where
columnDefault -- typbasetype and typdefaultbin handles `CREATE DOMAIN .. DEFAULT val`, attidentity/attgenerated handles generated columns, pg_get_expr gets the default of a column
| pgVer >= pgVersion120 = [q|
CASE
WHEN t.typbasetype != 0 THEN pg_get_expr(t.typdefaultbin, 0)
WHEN a.attidentity = 'd' THEN format('nextval(%s)', quote_literal(seqsch.nspname || '.' || seqclass.relname))
WHEN a.attgenerated = 's' THEN null
ELSE pg_get_expr(ad.adbin, ad.adrelid)::text
END|]
| otherwise = [q|
CASE
WHEN t.typbasetype != 0 THEN pg_get_expr(t.typdefaultbin, 0)
WHEN a.attidentity = 'd' THEN format('nextval(%s)', quote_literal(seqsch.nspname || '.' || seqclass.relname))
ELSE pg_get_expr(ad.adbin, ad.adrelid)::text
END|]
-- | Gets many-to-one relationships and one-to-one(O2O) relationships, which are a refinement of the many-to-one's -- | Gets many-to-one relationships and one-to-one(O2O) relationships, which are a refinement of the many-to-one's
allM2OandO2ORels :: Bool -> SQL.Statement () [Relationship] allM2OandO2ORels :: Bool -> SQL.Statement () [Relationship]
+14 -32
View File
@@ -7,30 +7,20 @@ import Test.Hspec
import Test.Hspec.Wai import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import PostgREST.Config.PgVersion (PgVersion, pgVersion112)
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper import SpecHelper
spec :: PgVersion -> SpecWith ((), Application) spec :: SpecWith ((), Application)
spec actualPgVersion = describe "authorization" $ do spec = describe "authorization" $ do
let single = ("Accept","application/vnd.pgrst.object+json") let single = ("Accept","application/vnd.pgrst.object+json")
it "denies access to tables that anonymous does not own" $ it "denies access to tables that anonymous does not own" $
get "/authors_only" `shouldRespondWith` ( get "/authors_only" `shouldRespondWith`
if actualPgVersion >= pgVersion112 then [json| {
[json| { "hint":null,
"hint":null, "details":null,
"details":null, "code":"42501",
"code":"42501", "message":"permission denied for table authors_only"} |]
"message":"permission denied for table authors_only"} |]
else
[json| {
"hint":null,
"details":null,
"code":"42501",
"message":"permission denied for relation authors_only"} |]
)
{ matchStatus = 401 { matchStatus = 401
, matchHeaders = ["WWW-Authenticate" <:> "Bearer"] , 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" $ it "denies access to tables that postgrest_test_author does not own" $
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA" in let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA" in
request methodGet "/private_table" [auth] "" request methodGet "/private_table" [auth] ""
`shouldRespondWith` ( `shouldRespondWith`
if actualPgVersion >= pgVersion112 then [json| {
[json| { "hint":null,
"hint":null, "details":null,
"details":null, "code":"42501",
"code":"42501", "message":"permission denied for table private_table"} |]
"message":"permission denied for table private_table"} |]
else
[json| {
"hint":null,
"details":null,
"code":"42501",
"message":"permission denied for relation private_table"} |]
)
{ matchStatus = 403 { matchStatus = 403
, matchHeaders = [] , matchHeaders = []
} }
+12 -15
View File
@@ -7,13 +7,11 @@ import Test.Hspec
import Test.Hspec.Wai import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import PostgREST.Config.PgVersion (PgVersion, pgVersion112)
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper import SpecHelper
spec :: PgVersion -> SpecWith ((), Application) spec :: SpecWith ((), Application)
spec actualPgVersion = spec =
describe "and/or params used for complex boolean logic" $ do describe "and/or params used for complex boolean logic" $ do
context "used with GET" $ do context "used with GET" $ do
context "or param" $ do context "or param" $ do
@@ -96,17 +94,16 @@ spec actualPgVersion =
get "/entities?and=(id.gte.2,arr.isdistinct.{1,2})&select=id" `shouldRespondWith` get "/entities?and=(id.gte.2,arr.isdistinct.{1,2})&select=id" `shouldRespondWith`
[json|[{ "id": 3 }, { "id": 4 }]|] { matchHeaders = [matchContentTypeJson] } [json|[{ "id": 3 }, { "id": 4 }]|] { matchHeaders = [matchContentTypeJson] }
when (actualPgVersion >= pgVersion112) $ it "can handle wfts (websearch_to_tsquery)" $
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)"
get "/tsearch?or=(text_search_vector.plfts(german).Art,text_search_vector.plfts(french).amusant,text_search_vector.not.wfts(english).impossible)" `shouldRespondWith`
`shouldRespondWith` [json|[
[json|[ {"text_search_vector": "'also':2 'fun':3 'possibl':8" },
{"text_search_vector": "'also':2 'fun':3 'possibl':8" }, {"text_search_vector": "'ate':3 'cat':2 'fat':1 'rat':4" },
{"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": "'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 handle cs and cd" $ it "can handle cs and cd" $
get "/entities?or=(arr.cs.{1,2,3},arr.cd.{1})&select=id" `shouldRespondWith` get "/entities?or=(arr.cs.{1,2,3},arr.cd.{1})&select=id" `shouldRespondWith`
+24 -34
View File
@@ -11,8 +11,7 @@ import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import Text.Heredoc import Text.Heredoc
import PostgREST.Config.PgVersion (PgVersion, pgVersion112, import PostgREST.Config.PgVersion (PgVersion, pgVersion130,
pgVersion120, pgVersion130,
pgVersion140) pgVersion140)
import Protolude hiding (get) import Protolude hiding (get)
@@ -542,28 +541,27 @@ spec actualPgVersion = do
, matchHeaders = ["Preference-Applied" <:> "missing=default, return=representation"] , matchHeaders = ["Preference-Applied" <:> "missing=default, return=representation"]
} }
when (actualPgVersion >= pgVersion120) $ it "fails with a good error message on generated always columns" $
it "fails with a good error message on generated always columns" $ request methodPost "/foo?columns=a,b" [("Prefer", "return=representation"), ("Prefer", "missing=default")]
request methodPost "/foo?columns=a,b" [("Prefer", "return=representation"), ("Prefer", "missing=default")] [json| [
[json| [ {"a": "val"},
{"a": "val"}, {"a": "val", "b": "val"}
{"a": "val", "b": "val"} ]|]
]|] `shouldRespondWith`
`shouldRespondWith` (if actualPgVersion < pgVersion140
(if actualPgVersion < pgVersion140 then [json| {
then [json| { "code": "42601",
"code": "42601", "details": "Column \"b\" is a generated column.",
"details": "Column \"b\" is a generated column.", "hint": null,
"hint": null, "message": "cannot insert into column \"b\""
"message": "cannot insert into column \"b\"" }|]
}|] else [json| {
else [json| { "code": "428C9",
"code": "428C9", "details": "Column \"b\" is a generated column.",
"details": "Column \"b\" is a generated column.", "hint": null,
"hint": null, "message": "cannot insert a non-DEFAULT value into column \"b\""
"message": "cannot insert a non-DEFAULT value into column \"b\"" }|])
}|]) { matchStatus = 400 }
{ matchStatus = 400 }
it "inserts a default on a DOMAIN with default" $ it "inserts a default on a DOMAIN with default" $
request methodPost "/evil_friends?columns=id,name" [("Prefer", "return=representation"), ("Prefer", "missing=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" $ it "fails inserting if more columns are selected" $
request methodPost "/limited_article_stars?select=article_id,user_id,created_at" [("Prefer", "return=representation")] request methodPost "/limited_article_stars?select=article_id,user_id,created_at" [("Prefer", "return=representation")]
[json| {"article_id": 2, "user_id": 2} |] `shouldRespondWith` ( [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"}|] [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 { matchStatus = 401
, matchHeaders = [] , matchHeaders = []
} }
it "fails inserting if select is not specified" $ it "fails inserting if select is not specified" $
request methodPost "/limited_article_stars" [("Prefer", "return=representation")] request methodPost "/limited_article_stars" [("Prefer", "return=representation")]
[json| {"article_id": 3, "user_id": 1} |] `shouldRespondWith` ( [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"}|] [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 { matchStatus = 401
, matchHeaders = [] , matchHeaders = []
} }
+3 -16
View File
@@ -7,8 +7,7 @@ import Test.Hspec
import Test.Hspec.Wai import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import PostgREST.Config.PgVersion (PgVersion, pgVersion112, import PostgREST.Config.PgVersion (PgVersion, pgVersion121)
pgVersion121)
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper 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 -- 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 it "fails when a double arrow ->> is followed with a single arrow ->" $ do
get "/json_arr?select=data->>c->1" get "/json_arr?select=data->>c->1"
`shouldRespondWith` ( `shouldRespondWith`
if actualPgVersion >= pgVersion112 then
[json| [json|
{"hint":"No operator matches the given name and argument types. You might need to add explicit type casts.", {"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"} |] "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 = [] } { matchStatus = 404 , matchHeaders = [] }
get "/json_arr?select=data->>c->b" get "/json_arr?select=data->>c->b"
`shouldRespondWith` ( `shouldRespondWith`
if actualPgVersion >= pgVersion112 then
[json| [json|
{"hint":"No operator matches the given name and argument types. You might need to add explicit type casts.", {"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"} |] "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 = [] } { matchStatus = 404 , matchHeaders = [] }
context "with array index" $ do context "with array index" $ do
+18 -34
View File
@@ -15,8 +15,7 @@ import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import PostgREST.Config.PgVersion (PgVersion, pgVersion120, import PostgREST.Config.PgVersion (PgVersion, pgVersion130)
pgVersion130)
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper import SpecHelper
@@ -34,10 +33,7 @@ spec actualPgVersion = do
liftIO $ do liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8") resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" } resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` totalCost `shouldBe` 15.63
if actualPgVersion > pgVersion120
then 15.63
else 15.69
it "outputs the total cost for a single filter on a view" $ do it "outputs the total cost for a single filter on a view" $ do
r <- request methodGet "/projects_view?id=gt.2" r <- request methodGet "/projects_view?id=gt.2"
@@ -50,10 +46,7 @@ spec actualPgVersion = do
liftIO $ do liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8") resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; charset=utf-8")
resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" } resStatus `shouldBe` Status { statusCode = 200, statusMessage="OK" }
totalCost `shouldBe` totalCost `shouldBe` 24.28
if actualPgVersion > pgVersion120
then 24.28
else 32.27
it "outputs blocks info when using the buffers option" $ it "outputs blocks info when using the buffers option" $
if actualPgVersion >= pgVersion130 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") 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 |] blocks `shouldBe` Just [aesonQQ| 1.0 |]
when (actualPgVersion >= pgVersion120) $ it "outputs the search path when using the settings option" $ do
it "outputs the search path when using the settings option" $ do r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=settings") ""
r <- request methodGet "/projects" (acceptHdrs "application/vnd.pgrst.plan+json; options=settings") ""
let searchPath = simpleBody r ^? nth 0 . key "Settings" let searchPath = simpleBody r ^? nth 0 . key "Settings"
resHeaders = simpleHeaders r resHeaders = simpleHeaders r
liftIO $ do liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=settings; charset=utf-8") resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=settings; charset=utf-8")
searchPath `shouldBe` searchPath `shouldBe`
Just [aesonQQ| Just [aesonQQ|
{ {
"search_path": "\"test\"" "search_path": "\"test\""
} }
|] |]
when (actualPgVersion >= pgVersion130) $ when (actualPgVersion >= pgVersion130) $
it "outputs WAL info when using the wal option" $ do it "outputs WAL info when using the wal option" $ do
@@ -123,9 +115,7 @@ spec actualPgVersion = do
liftIO $ do liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=verbose; charset=utf-8") resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/json\"; options=verbose; charset=utf-8")
aggCol `shouldBe` aggCol `shouldBe`
if actualPgVersion >= pgVersion120 Just [aesonQQ| "COALESCE(json_agg(ROW(projects.id, projects.name, projects.client_id)), '[]'::json)" |]
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)" |]
it "outputs the plan for application/vnd.pgrst.object " $ do 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") "" 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 liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/vnd.pgrst.object+json\"; options=verbose; charset=utf-8") resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/vnd.pgrst.object+json\"; options=verbose; charset=utf-8")
aggCol `shouldBe` aggCol `shouldBe`
if actualPgVersion >= pgVersion120 Just [aesonQQ| "COALESCE((json_agg(ROW(projects.id, projects.name, projects.client_id)) -> 0), 'null'::json)" |]
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)" |]
describe "writes plans" $ do describe "writes plans" $ do
it "outputs the total cost for an insert" $ do it "outputs the total cost for an insert" $ do
@@ -452,11 +440,7 @@ spec actualPgVersion = do
liftIO $ do liftIO $ do
resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/vnd.twkb\"; options=verbose; charset=utf-8") resHeaders `shouldSatisfy` elem ("Content-Type", "application/vnd.pgrst.plan+json; for=\"application/vnd.twkb\"; options=verbose; charset=utf-8")
aggCol `shouldBe` aggCol `shouldBe`
( Just [aesonQQ| "twkb_agg(ROW(lines.id, lines.name, lines.geom)::lines)" |]
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)" |]
)
disabledSpec :: SpecWith ((), Application) disabledSpec :: SpecWith ((), Application)
disabledSpec = disabledSpec =
+31 -35
View File
@@ -8,8 +8,7 @@ import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import PostgREST.Config.PgVersion (PgVersion, pgVersion112, import PostgREST.Config.PgVersion (PgVersion, pgVersion121)
pgVersion121)
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper import SpecHelper
@@ -176,28 +175,27 @@ spec actualPgVersion = do
[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] }
when (actualPgVersion >= pgVersion112) $ do it "finds matches with websearch_to_tsquery" $
it "finds matches with websearch_to_tsquery" $ get "/tsearch?text_search_vector=wfts.The%20Fat%20Rats" `shouldRespondWith`
get "/tsearch?text_search_vector=wfts.The%20Fat%20Rats" `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 "can use boolean operators(and, or, -) in websearch_to_tsquery" $ do it "can use boolean operators(and, or, -) in websearch_to_tsquery" $ do
get "/tsearch?text_search_vector=wfts.fun%20and%20possible" get "/tsearch?text_search_vector=wfts.fun%20and%20possible"
`shouldRespondWith` `shouldRespondWith`
[json| [ {"text_search_vector": "'also':2 'fun':3 'possibl':8"}] |] [json| [ {"text_search_vector": "'also':2 'fun':3 'possibl':8"}] |]
{ matchHeaders = [matchContentTypeJson] } { matchHeaders = [matchContentTypeJson] }
get "/tsearch?text_search_vector=wfts.impossible%20or%20possible" get "/tsearch?text_search_vector=wfts.impossible%20or%20possible"
`shouldRespondWith` `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"}]
|] |]
{ matchHeaders = [matchContentTypeJson] } { matchHeaders = [matchContentTypeJson] }
get "/tsearch?text_search_vector=wfts.fun%20and%20-possible" get "/tsearch?text_search_vector=wfts.fun%20and%20-possible"
`shouldRespondWith` `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 "finds matches with different dictionaries" $ do it "finds matches with different dictionaries" $ do
get "/tsearch?text_search_vector=fts(french).amusant" `shouldRespondWith` 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" }] |] [json| [{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }] |]
{ matchHeaders = [matchContentTypeJson] } { matchHeaders = [matchContentTypeJson] }
when (actualPgVersion >= pgVersion112) $ get "/tsearch?text_search_vector=wfts(french).amusant%20impossible"
get "/tsearch?text_search_vector=wfts(french).amusant%20impossible" `shouldRespondWith`
`shouldRespondWith` [json| [{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }] |]
[json| [{"text_search_vector": "'amus':5 'fair':7 'impossibl':9 'peu':4" }] |] { matchHeaders = [matchContentTypeJson] }
{ matchHeaders = [matchContentTypeJson] }
it "can be negated with not operator" $ do it "can be negated with not operator" $ do
get "/tsearch?text_search_vector=not.fts.impossible%7Cfat%7Cfun" `shouldRespondWith` 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": "'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] }
when (actualPgVersion >= pgVersion112) $ get "/tsearch?text_search_vector=not.wfts(english).impossible%20or%20fat%20or%20fun"
get "/tsearch?text_search_vector=not.wfts(english).impossible%20or%20fat%20or%20fun" `shouldRespondWith`
`shouldRespondWith` [json| [
[json| [ {"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] }
context "Use of the phraseto_tsquery function" $ do context "Use of the phraseto_tsquery function" $ do
it "finds matches" $ it "finds matches" $
+11 -16
View File
@@ -11,14 +11,11 @@ import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import Text.Heredoc import Text.Heredoc
import PostgREST.Config.PgVersion (PgVersion, pgVersion112,
pgVersion114)
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper import SpecHelper
spec :: PgVersion -> SpecWith ((), Application) spec :: SpecWith ((), Application)
spec actualPgVersion = spec =
describe "remote procedure call" $ do describe "remote procedure call" $ do
context "a proc that returns a set" $ do context "a proc that returns a set" $ do
context "returns paginated results" $ do context "returns paginated results" $ do
@@ -599,13 +596,12 @@ spec actualPgVersion =
[json|"object"|] [json|"object"|]
{ matchHeaders = [matchContentTypeJson] } { matchHeaders = [matchContentTypeJson] }
when (actualPgVersion >= pgVersion114) $ it "parses quoted JSON arguments as JSON string (from Postgres 10.9, 11.4)" $
it "parses quoted JSON arguments as JSON string (from Postgres 10.9, 11.4)" $ post "/rpc/json_argument"
post "/rpc/json_argument" [json| { "arg": "{ \"key\": 3 }" } |]
[json| { "arg": "{ \"key\": 3 }" } |] `shouldRespondWith`
`shouldRespondWith` [json|"string"|]
[json|"string"|] { matchHeaders = [matchContentTypeJson] }
{ matchHeaders = [matchContentTypeJson] }
context "improper input" $ do context "improper input" $ do
it "rejects unknown content type even if payload is good" $ 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` 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"}]|] [json|[{"text_search_vector":"'amus':5 'fair':7 'impossibl':9 'peu':4"},{"text_search_vector":"'art':4 'spass':5 'unmog':7"}]|]
{ matchHeaders = [matchContentTypeJson] } { matchHeaders = [matchContentTypeJson] }
when (actualPgVersion >= pgVersion112) $ get "/rpc/get_tsearch?text_search_vector=wfts.impossible" `shouldRespondWith`
get "/rpc/get_tsearch?text_search_vector=wfts.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 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`
+3 -3
View File
@@ -137,14 +137,14 @@ main = do
analyzeTable "child_entities" analyzeTable "child_entities"
specs = uncurry describe <$> [ specs = uncurry describe <$> [
("Feature.Auth.AuthSpec" , Feature.Auth.AuthSpec.spec actualPgVersion) ("Feature.Auth.AuthSpec" , Feature.Auth.AuthSpec.spec)
, ("Feature.ConcurrentSpec" , Feature.ConcurrentSpec.spec) , ("Feature.ConcurrentSpec" , Feature.ConcurrentSpec.spec)
, ("Feature.CorsSpec" , Feature.CorsSpec.spec) , ("Feature.CorsSpec" , Feature.CorsSpec.spec)
, ("Feature.CustomMediaSpec" , Feature.Query.CustomMediaSpec.spec) , ("Feature.CustomMediaSpec" , Feature.Query.CustomMediaSpec.spec)
, ("Feature.NoSuperuserSpec" , Feature.NoSuperuserSpec.spec) , ("Feature.NoSuperuserSpec" , Feature.NoSuperuserSpec.spec)
, ("Feature.OpenApi.OpenApiSpec" , Feature.OpenApi.OpenApiSpec.spec) , ("Feature.OpenApi.OpenApiSpec" , Feature.OpenApi.OpenApiSpec.spec)
, ("Feature.OptionsSpec" , Feature.OptionsSpec.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.ComputedRelsSpec" , Feature.Query.ComputedRelsSpec.spec)
, ("Feature.Query.DeleteSpec" , Feature.Query.DeleteSpec.spec) , ("Feature.Query.DeleteSpec" , Feature.Query.DeleteSpec.spec)
, ("Feature.Query.EmbedDisambiguationSpec" , Feature.Query.EmbedDisambiguationSpec.spec) , ("Feature.Query.EmbedDisambiguationSpec" , Feature.Query.EmbedDisambiguationSpec.spec)
@@ -159,7 +159,7 @@ main = do
, ("Feature.Query.QuerySpec" , Feature.Query.QuerySpec.spec actualPgVersion) , ("Feature.Query.QuerySpec" , Feature.Query.QuerySpec.spec actualPgVersion)
, ("Feature.Query.RawOutputTypesSpec" , Feature.Query.RawOutputTypesSpec.spec) , ("Feature.Query.RawOutputTypesSpec" , Feature.Query.RawOutputTypesSpec.spec)
, ("Feature.Query.RelatedQueriesSpec" , Feature.Query.RelatedQueriesSpec.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.SingularSpec" , Feature.Query.SingularSpec.spec)
, ("Feature.Query.SpreadQueriesSpec" , Feature.Query.SpreadQueriesSpec.spec) , ("Feature.Query.SpreadQueriesSpec" , Feature.Query.SpreadQueriesSpec.spec)
, ("Feature.Query.UpdateSpec" , Feature.Query.UpdateSpec.spec) , ("Feature.Query.UpdateSpec" , Feature.Query.UpdateSpec.spec)
+10 -14
View File
@@ -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 = 'Veneno';
UPDATE test.car_models SET car_brand_name = 'Lamborghini' WHERE name = 'Murcielago'; UPDATE test.car_models SET car_brand_name = 'Lamborghini' WHERE name = 'Murcielago';
DO $do$BEGIN INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-01-14',7,'DeLorean',1981);
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-15',9,'DeLorean',1981);
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-02-11',1,'Murcielago',2001);
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-12',3,'Murcielago',2001);
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) 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, 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 ('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 ('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 ('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); 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$;
TRUNCATE TABLE test.products CASCADE; TRUNCATE TABLE test.products CASCADE;
INSERT INTO test.products (id, name) VALUES (1,'product-1'), (2,'product-2'), (3,'product-3'); INSERT INTO test.products (id, name) VALUES (1,'product-1'), (2,'product-2'), (3,'product-3');
+50 -61
View File
@@ -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 primary key (name, year);
alter table test.car_models add column car_brand_name varchar(64) references test.car_brands(name); alter table test.car_models add column car_brand_name varchar(64) references test.car_brands(name);
do $do$begin create table test.car_model_sales(
-- foreign keys referencing partitioned tables are supported from pg v12 date varchar(64) not null,
if (select current_setting('server_version_num')::int >= 120000) then quantity int not null,
create table test.car_model_sales( car_model_name varchar(64),
date varchar(64) not null, car_model_year int,
quantity int not null, primary key (date, car_model_name, car_model_year),
car_model_name varchar(64), foreign key (car_model_name, car_model_year) references test.car_models (name, year)
car_model_year int, ) partition by range (date);
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 create table test.car_model_sales_202101 partition of test.car_model_sales
for values from ('2021-01-01') to ('2021-01-31'); for values from ('2021-01-01') to ('2021-01-31');
create table test.car_model_sales_default partition of test.car_model_sales create table test.car_model_sales_default partition of test.car_model_sales
default; default;
create table test.car_racers ( create table test.car_racers (
name varchar(64) not null primary key, name varchar(64) not null primary key,
car_model_name varchar(64), car_model_name varchar(64),
car_model_year int, car_model_year int,
foreign key (car_model_name, car_model_year) references test.car_models (name, year) foreign key (car_model_name, car_model_year) references test.car_models (name, year)
); );
create table test.car_dealers ( create table test.car_dealers (
name varchar(64) not null, name varchar(64) not null,
city varchar(64) not null, city varchar(64) not null,
primary key (name, city) primary key (name, city)
) partition by list (city); ) partition by list (city);
create table test.car_dealers_springfield partition of test.car_dealers create table test.car_dealers_springfield partition of test.car_dealers
for values in ('Springfield'); for values in ('Springfield');
create table test.car_dealers_default partition of test.car_dealers create table test.car_dealers_default partition of test.car_dealers
default; default;
create table test.car_models_car_dealers ( create table test.car_models_car_dealers (
car_model_name varchar(64) not null, car_model_name varchar(64) not null,
car_model_year int not null, car_model_year int not null,
car_dealer_name varchar(64) not null, car_dealer_name varchar(64) not null,
car_dealer_city varchar(64) not null, car_dealer_city varchar(64) not null,
quantity int not null, quantity int not null,
foreign key (car_model_name, car_model_year) references test.car_models (name, year), 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), 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) primary key (car_model_name, car_model_year, car_dealer_name, car_dealer_city, quantity)
) partition by range (quantity); ) partition by range (quantity);
create table test.car_models_car_dealers_10to20 partition of test.car_models_car_dealers create table test.car_models_car_dealers_10to20 partition of test.car_models_car_dealers
for values from (10) to (20); for values from (10) to (20);
create table test.car_models_car_dealers_default partition of test.car_models_car_dealers create table test.car_models_car_dealers_default partition of test.car_models_car_dealers
default; default;
end if;
end$do$;
create or replace function test.unnamed_json_param(json) returns json as $$ create or replace function test.unnamed_json_param(json) returns json as $$
select $1; select $1;
@@ -3261,21 +3256,15 @@ AS $$
select current_setting('is_superuser')::boolean; select current_setting('is_superuser')::boolean;
$$; $$;
DO $do$ CREATE TABLE test.foo (
BEGIN a text,
IF current_setting('server_version_num')::INT >= 120000 THEN b text GENERATED ALWAYS AS (
CREATE TABLE test.foo ( case WHEN a = 'telegram' THEN 'im'
a text, WHEN a = 'proton' THEN 'email'
b text GENERATED ALWAYS AS ( WHEN a = 'infinity' THEN 'idea'
case WHEN a = 'telegram' THEN 'im' ELSE 'bad idea'
WHEN a = 'proton' THEN 'email' end) stored
WHEN a = 'infinity' THEN 'idea' );
ELSE 'bad idea'
end) stored
);
END IF;
END
$do$;
create domain devil_int as int create domain devil_int as int
default 666; default 666;