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
@@ -67,7 +67,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pgVersion: [11, 12, 13, 14, 15, 16]
|
||||
pgVersion: [12, 13, 14, 15, 16]
|
||||
name: PG ${{ matrix.pgVersion }}
|
||||
runs-on: ubuntu-22.04
|
||||
defaults:
|
||||
|
||||
@@ -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 10 - @wolfgangwalther
|
||||
- #2052, Dropped support for PostgreSQL 11 - @wolfgangwalther
|
||||
|
||||
## [12.2.0] - 2024-06-11
|
||||
|
||||
|
||||
@@ -36,9 +36,7 @@ let
|
||||
allOverlays.checked-shell-script
|
||||
allOverlays.gitignore
|
||||
allOverlays.postgresql-libpq
|
||||
allOverlays.postgresql-legacy
|
||||
allOverlays.postgresql-future
|
||||
allOverlays.postgis
|
||||
(allOverlays.haskell-packages { inherit compiler; })
|
||||
allOverlays.slocat
|
||||
];
|
||||
@@ -54,7 +52,6 @@ let
|
||||
{ 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-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
|
||||
|
||||
@@ -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
|
||||
=================
|
||||
|
||||
+12
-12
@@ -75,12 +75,12 @@ The PostgREST utilities available in `nix-shell` all have names that begin with
|
||||
postgrest-build postgrest-test-spec
|
||||
postgrest-check postgrest-watch
|
||||
postgrest-clean postgrest-with-all
|
||||
postgrest-coverage postgrest-with-postgresql-11
|
||||
postgrest-lint postgrest-with-postgresql-12
|
||||
postgrest-run postgrest-with-postgresql-13
|
||||
postgrest-style postgrest-with-postgresql-14
|
||||
postgrest-style-check postgrest-with-postgresql-15
|
||||
postgrest-test-io postgrest-with-postgresql-16
|
||||
postgrest-coverage postgrest-with-postgresql-12
|
||||
postgrest-lint postgrest-with-postgresql-13
|
||||
postgrest-run postgrest-with-postgresql-14
|
||||
postgrest-style postgrest-with-postgresql-15
|
||||
postgrest-style-check postgrest-with-postgresql-16
|
||||
postgrest-test-io
|
||||
...
|
||||
|
||||
[nix-shell]$
|
||||
@@ -99,12 +99,12 @@ $ nix-shell --arg memory true
|
||||
postgrest-build postgrest-test-spec
|
||||
postgrest-check postgrest-watch
|
||||
postgrest-clean postgrest-with-all
|
||||
postgrest-coverage postgrest-with-postgresql-11
|
||||
postgrest-lint postgrest-with-postgresql-12
|
||||
postgrest-run postgrest-with-postgresql-13
|
||||
postgrest-style postgrest-with-postgresql-14
|
||||
postgrest-style-check postgrest-with-postgresql-15
|
||||
postgrest-test-io postgrest-with-postgresql-16
|
||||
postgrest-coverage postgrest-with-postgresql-12
|
||||
postgrest-lint postgrest-with-postgresql-13
|
||||
postgrest-run postgrest-with-postgresql-14
|
||||
postgrest-style postgrest-with-postgresql-15
|
||||
postgrest-style-check postgrest-with-postgresql-16
|
||||
postgrest-test-io
|
||||
postgrest-test-memory
|
||||
...
|
||||
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
checked-shell-script = import ./checked-shell-script;
|
||||
gitignore = import ./gitignore.nix;
|
||||
haskell-packages = import ./haskell-packages.nix;
|
||||
postgis = import ./postgis.nix;
|
||||
postgresql-libpq = import ./postgresql-libpq.nix;
|
||||
postgresql-legacy = import ./postgresql-legacy.nix;
|
||||
postgresql-future = import ./postgresql-future.nix;
|
||||
slocat = import ./slocat.nix;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -3,9 +3,6 @@
|
||||
module PostgREST.Config.PgVersion
|
||||
( PgVersion(..)
|
||||
, minimumPgVersion
|
||||
, pgVersion112
|
||||
, pgVersion114
|
||||
, pgVersion120
|
||||
, pgVersion121
|
||||
, pgVersion130
|
||||
, pgVersion140
|
||||
@@ -29,16 +26,7 @@ instance Ord PgVersion where
|
||||
|
||||
-- | Tells the minimum PostgreSQL version required by this version of PostgREST
|
||||
minimumPgVersion :: PgVersion
|
||||
minimumPgVersion = pgVersion110
|
||||
|
||||
pgVersion110 :: PgVersion
|
||||
pgVersion110 = PgVersion 110000 "11.0" "11.0"
|
||||
|
||||
pgVersion112 :: PgVersion
|
||||
pgVersion112 = PgVersion 110002 "11.2" "11.2"
|
||||
|
||||
pgVersion114 :: PgVersion
|
||||
pgVersion114 = PgVersion 110004 "11.4" "11.4"
|
||||
minimumPgVersion = pgVersion120
|
||||
|
||||
pgVersion120 :: PgVersion
|
||||
pgVersion120 = PgVersion 120000 "12.0" "12.0"
|
||||
|
||||
@@ -45,9 +45,7 @@ import Text.InterpolatedString.Perl6 (q)
|
||||
|
||||
import PostgREST.Config (AppConfig (..))
|
||||
import PostgREST.Config.Database (TimezoneNames,
|
||||
pgVersionStatement,
|
||||
toIsolationLevel)
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion120)
|
||||
import PostgREST.SchemaCache.Identifiers (AccessSet, FieldName,
|
||||
QualifiedIdentifier (..),
|
||||
RelIdentifier (..),
|
||||
@@ -144,8 +142,7 @@ type SqlQuery = ByteString
|
||||
querySchemaCache :: AppConfig -> SQL.Transaction SchemaCache
|
||||
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
|
||||
pgVer <- SQL.statement mempty $ pgVersionStatement prepared
|
||||
tabs <- SQL.statement schemas $ allTables pgVer prepared
|
||||
tabs <- SQL.statement schemas $ allTables prepared
|
||||
keyDeps <- SQL.statement (schemas, configDbExtraSearchPath) $ allViewsKeyDependencies prepared
|
||||
m2oRels <- SQL.statement mempty $ allM2OandO2ORels 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.
|
||||
takeFirstPK = mapMaybe (head . snd)
|
||||
|
||||
allTables :: PgVersion -> Bool -> SQL.Statement [Schema] TablesMap
|
||||
allTables pgVer =
|
||||
SQL.Statement sql (arrayParam HE.text) decodeTables
|
||||
where
|
||||
sql = tablesSqlQuery pgVer
|
||||
allTables :: Bool -> SQL.Statement [Schema] TablesMap
|
||||
allTables =
|
||||
SQL.Statement tablesSqlQuery (arrayParam HE.text) decodeTables
|
||||
|
||||
-- | Gets tables with their PK cols
|
||||
tablesSqlQuery :: PgVersion -> SqlQuery
|
||||
tablesSqlQuery pgVer =
|
||||
tablesSqlQuery :: SqlQuery
|
||||
tablesSqlQuery =
|
||||
-- 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:
|
||||
-- (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,
|
||||
a.attname::name AS column_name,
|
||||
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,
|
||||
CASE
|
||||
WHEN t.typtype = 'd' THEN
|
||||
@@ -810,21 +811,6 @@ tablesSqlQuery pgVer =
|
||||
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
|
||||
AND not c.relispartition
|
||||
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
|
||||
allM2OandO2ORels :: Bool -> SQL.Statement () [Relationship]
|
||||
|
||||
@@ -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