feat: Drop support for pg 10
This commit is contained in:
committed by
Wolfgang Walther
parent
daa77d17aa
commit
126178642b
@@ -67,7 +67,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
pgVersion: [10, 11, 12, 13, 14, 15, 16]
|
pgVersion: [11, 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:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- #2052, Dropped support for PostgreSQL 9.6 - @wolfgangwalther
|
- #2052, Dropped support for PostgreSQL 9.6 - @wolfgangwalther
|
||||||
|
- #2052, Dropped support for PostgreSQL 10 - @wolfgangwalther
|
||||||
|
|
||||||
## [12.2.0] - 2024-06-11
|
## [12.2.0] - 2024-06-11
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ let
|
|||||||
{ 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 ]); }
|
{ name = "postgresql-11"; postgresql = pkgs.postgresql_11.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||||
{ name = "postgresql-10"; postgresql = pkgs.postgresql_10.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# Dynamic derivation for PostgREST
|
# Dynamic derivation for PostgREST
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ Supported PostgreSQL versions
|
|||||||
=============================
|
=============================
|
||||||
|
|
||||||
=============== =================================
|
=============== =================================
|
||||||
**Supported** PostgreSQL >= 10
|
**Supported** PostgreSQL >= 11
|
||||||
=============== =================================
|
=============== =================================
|
||||||
|
|
||||||
PostgREST works with all PostgreSQL versions starting from 10.
|
PostgREST works with all PostgreSQL versions starting from 11.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Support for PostgreSQL versions 10 and 11 is deprecated. From this on version onwards, PostgREST will only support non-end-of-life PostgreSQL versions.
|
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/.
|
See https://www.postgresql.org/support/versioning/.
|
||||||
|
|
||||||
|
|||||||
+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-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-10
|
postgrest-coverage postgrest-with-postgresql-11
|
||||||
postgrest-lint postgrest-with-postgresql-11
|
postgrest-lint postgrest-with-postgresql-12
|
||||||
postgrest-run postgrest-with-postgresql-12
|
postgrest-run postgrest-with-postgresql-13
|
||||||
postgrest-style postgrest-with-postgresql-13
|
postgrest-style postgrest-with-postgresql-14
|
||||||
postgrest-style-check postgrest-with-postgresql-14
|
postgrest-style-check postgrest-with-postgresql-15
|
||||||
postgrest-test-io
|
postgrest-test-io postgrest-with-postgresql-16
|
||||||
...
|
...
|
||||||
|
|
||||||
[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-10
|
postgrest-coverage postgrest-with-postgresql-11
|
||||||
postgrest-lint postgrest-with-postgresql-11
|
postgrest-lint postgrest-with-postgresql-12
|
||||||
postgrest-run postgrest-with-postgresql-12
|
postgrest-run postgrest-with-postgresql-13
|
||||||
postgrest-style postgrest-with-postgresql-13
|
postgrest-style postgrest-with-postgresql-14
|
||||||
postgrest-style-check postgrest-with-postgresql-14
|
postgrest-style-check postgrest-with-postgresql-15
|
||||||
postgrest-test-io
|
postgrest-test-io postgrest-with-postgresql-16
|
||||||
postgrest-test-memory
|
postgrest-test-memory
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,4 @@ in
|
|||||||
postgis = prev.postgresql_11.pkgs.postgis.overrideAttrs (_: postgis_3_2_3);
|
postgis = prev.postgresql_11.pkgs.postgis.overrideAttrs (_: postgis_3_2_3);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
postgresql_10 = prev.postgresql_10.override { this = final.postgresql_11; } // {
|
|
||||||
pkgs = prev.postgresql_10.pkgs // {
|
|
||||||
postgis = prev.postgresql_10.pkgs.postgis.overrideAttrs (_: postgis_3_2_3);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,6 @@ _: _:
|
|||||||
# Overlay that adds legacy versions of PostgreSQL that are supported by
|
# Overlay that adds legacy versions of PostgreSQL that are supported by
|
||||||
# PostgREST.
|
# PostgREST.
|
||||||
{
|
{
|
||||||
# PostgreSQL 10 was removed from Nixpkgs with
|
|
||||||
# https://github.com/NixOS/nixpkgs/commit/aa1483114bb329fee7e1266100b8d8921ed4723f
|
|
||||||
# We pin its parent commit to get the last version that was available.
|
|
||||||
postgresql_10 =
|
|
||||||
let
|
|
||||||
rev = "79661ba7e2fb96ebefbb537458a5bbae9dc5bd1a";
|
|
||||||
tarballHash = "0rn796pfn4sg90ai9fdnwmr10a2s835p1arazzgz46h6s5cxvq97";
|
|
||||||
pinnedPkgs =
|
|
||||||
builtins.fetchTarball {
|
|
||||||
url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz";
|
|
||||||
sha256 = tarballHash;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
(import pinnedPkgs { }).pkgs.postgresql_10;
|
|
||||||
|
|
||||||
# PostgreSQL 11 was removed from Nixpkgs with
|
# PostgreSQL 11 was removed from Nixpkgs with
|
||||||
# https://github.com/NixOS/nixpkgs/commit/1220a4d4dd1a4590780a5e1c18d1333a121be366
|
# https://github.com/NixOS/nixpkgs/commit/1220a4d4dd1a4590780a5e1c18d1333a121be366
|
||||||
# We pin its parent commit to get the last version that was available.
|
# We pin its parent commit to get the last version that was available.
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
module PostgREST.Config.PgVersion
|
module PostgREST.Config.PgVersion
|
||||||
( PgVersion(..)
|
( PgVersion(..)
|
||||||
, minimumPgVersion
|
, minimumPgVersion
|
||||||
, pgVersion109
|
|
||||||
, pgVersion110
|
|
||||||
, pgVersion112
|
, pgVersion112
|
||||||
, pgVersion114
|
, pgVersion114
|
||||||
, pgVersion120
|
, pgVersion120
|
||||||
@@ -31,13 +29,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 = pgVersion100
|
minimumPgVersion = pgVersion110
|
||||||
|
|
||||||
pgVersion100 :: PgVersion
|
|
||||||
pgVersion100 = PgVersion 100000 "10" "10"
|
|
||||||
|
|
||||||
pgVersion109 :: PgVersion
|
|
||||||
pgVersion109 = PgVersion 100009 "10.9" "10.9"
|
|
||||||
|
|
||||||
pgVersion110 :: PgVersion
|
pgVersion110 :: PgVersion
|
||||||
pgVersion110 = PgVersion 110000 "11.0" "11.0"
|
pgVersion110 = PgVersion 110000 "11.0" "11.0"
|
||||||
|
|||||||
@@ -175,13 +175,13 @@ actionQuery (DbCall plan@CallReadPlan{..}) conf@AppConfig{..} apiReq@ApiRequest{
|
|||||||
failExceedsMaxAffectedPref (preferMaxAffected,preferHandling) resultSet
|
failExceedsMaxAffectedPref (preferMaxAffected,preferHandling) resultSet
|
||||||
pure $ DbCallResult plan resultSet
|
pure $ DbCallResult plan resultSet
|
||||||
|
|
||||||
actionQuery (MaybeDb plan@InspectPlan{ipSchema=tSchema}) AppConfig{..} _ pgVer sCache =
|
actionQuery (MaybeDb plan@InspectPlan{ipSchema=tSchema}) AppConfig{..} _ _ sCache =
|
||||||
lift $ case configOpenApiMode of
|
lift $ case configOpenApiMode of
|
||||||
OAFollowPriv -> do
|
OAFollowPriv -> do
|
||||||
tableAccess <- SQL.statement [tSchema] (SchemaCache.accessibleTables configDbPreparedStatements)
|
tableAccess <- SQL.statement [tSchema] (SchemaCache.accessibleTables configDbPreparedStatements)
|
||||||
MaybeDbResult plan . Just <$> ((,,)
|
MaybeDbResult plan . Just <$> ((,,)
|
||||||
(HM.filterWithKey (\qi _ -> S.member qi tableAccess) $ SchemaCache.dbTables sCache)
|
(HM.filterWithKey (\qi _ -> S.member qi tableAccess) $ SchemaCache.dbTables sCache)
|
||||||
<$> SQL.statement (tSchema, configDbHoistedTxSettings) (SchemaCache.accessibleFuncs pgVer configDbPreparedStatements)
|
<$> SQL.statement (tSchema, configDbHoistedTxSettings) (SchemaCache.accessibleFuncs configDbPreparedStatements)
|
||||||
<*> SQL.statement tSchema (SchemaCache.schemaDescription configDbPreparedStatements))
|
<*> SQL.statement tSchema (SchemaCache.schemaDescription configDbPreparedStatements))
|
||||||
OAIgnorePriv ->
|
OAIgnorePriv ->
|
||||||
MaybeDbResult plan . Just <$> ((,,)
|
MaybeDbResult plan . Just <$> ((,,)
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ import Data.Maybe (fromJust)
|
|||||||
import Data.Tree (Tree (..))
|
import Data.Tree (Tree (..))
|
||||||
|
|
||||||
import PostgREST.ApiRequest.Preferences (PreferResolution (..))
|
import PostgREST.ApiRequest.Preferences (PreferResolution (..))
|
||||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion110,
|
import PostgREST.Config.PgVersion (PgVersion, pgVersion130)
|
||||||
pgVersion130)
|
|
||||||
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..))
|
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..))
|
||||||
import PostgREST.SchemaCache.Relationship (Cardinality (..),
|
import PostgREST.SchemaCache.Relationship (Cardinality (..),
|
||||||
Junction (..),
|
Junction (..),
|
||||||
@@ -202,9 +201,9 @@ callPlanToQuery (FunctionCall qi params args returnsScalar returnsSetOfScalar re
|
|||||||
"LATERAL " <> callIt (fmtParams prms)
|
"LATERAL " <> callIt (fmtParams prms)
|
||||||
|
|
||||||
callIt :: SQL.Snippet -> SQL.Snippet
|
callIt :: SQL.Snippet -> SQL.Snippet
|
||||||
callIt argument | pgVer < pgVersion130 && pgVer >= pgVersion110 && returnsCompositeAlias = "(SELECT (" <> fromQi qi <> "(" <> argument <> ")).*) pgrst_call"
|
callIt argument | pgVer < pgVersion130 && returnsCompositeAlias = "(SELECT (" <> fromQi qi <> "(" <> argument <> ")).*) pgrst_call"
|
||||||
| returnsScalar || returnsSetOfScalar = "(SELECT " <> fromQi qi <> "(" <> argument <> ") pgrst_scalar) pgrst_call"
|
| returnsScalar || returnsSetOfScalar = "(SELECT " <> fromQi qi <> "(" <> argument <> ") pgrst_scalar) pgrst_call"
|
||||||
| otherwise = fromQi qi <> "(" <> argument <> ") pgrst_call"
|
| otherwise = fromQi qi <> "(" <> argument <> ") pgrst_call"
|
||||||
|
|
||||||
fmtParams :: [RoutineParam] -> SQL.Snippet
|
fmtParams :: [RoutineParam] -> SQL.Snippet
|
||||||
fmtParams prms = intercalateSnippet ", "
|
fmtParams prms = intercalateSnippet ", "
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ import PostgREST.Config (AppConfig (..))
|
|||||||
import PostgREST.Config.Database (TimezoneNames,
|
import PostgREST.Config.Database (TimezoneNames,
|
||||||
pgVersionStatement,
|
pgVersionStatement,
|
||||||
toIsolationLevel)
|
toIsolationLevel)
|
||||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion110,
|
import PostgREST.Config.PgVersion (PgVersion, pgVersion120)
|
||||||
pgVersion120)
|
|
||||||
import PostgREST.SchemaCache.Identifiers (AccessSet, FieldName,
|
import PostgREST.SchemaCache.Identifiers (AccessSet, FieldName,
|
||||||
QualifiedIdentifier (..),
|
QualifiedIdentifier (..),
|
||||||
RelIdentifier (..),
|
RelIdentifier (..),
|
||||||
@@ -148,11 +147,11 @@ querySchemaCache AppConfig{..} = do
|
|||||||
pgVer <- SQL.statement mempty $ pgVersionStatement prepared
|
pgVer <- SQL.statement mempty $ pgVersionStatement prepared
|
||||||
tabs <- SQL.statement schemas $ allTables pgVer 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 pgVer prepared
|
m2oRels <- SQL.statement mempty $ allM2OandO2ORels prepared
|
||||||
funcs <- SQL.statement (schemas, configDbHoistedTxSettings) $ allFunctions pgVer prepared
|
funcs <- SQL.statement (schemas, configDbHoistedTxSettings) $ allFunctions prepared
|
||||||
cRels <- SQL.statement mempty $ allComputedRels prepared
|
cRels <- SQL.statement mempty $ allComputedRels prepared
|
||||||
reps <- SQL.statement schemas $ dataRepresentations prepared
|
reps <- SQL.statement schemas $ dataRepresentations prepared
|
||||||
mHdlers <- SQL.statement schemas $ mediaHandlers pgVer prepared
|
mHdlers <- SQL.statement schemas $ mediaHandlers prepared
|
||||||
tzones <- SQL.statement mempty $ timezones prepared
|
tzones <- SQL.statement mempty $ timezones prepared
|
||||||
_ <-
|
_ <-
|
||||||
let sleepCall = SQL.Statement "select pg_sleep($1 / 1000.0)" (param HE.int4) HD.noResult prepared in
|
let sleepCall = SQL.Statement "select pg_sleep($1 / 1000.0)" (param HE.int4) HD.noResult prepared in
|
||||||
@@ -362,18 +361,18 @@ dataRepresentations = SQL.Statement sql (arrayParam HE.text) decodeRepresentatio
|
|||||||
OR (dst_t.typtype = 'd' AND c.castsource IN ('json'::regtype::oid , 'text'::regtype::oid)))
|
OR (dst_t.typtype = 'd' AND c.castsource IN ('json'::regtype::oid , 'text'::regtype::oid)))
|
||||||
|]
|
|]
|
||||||
|
|
||||||
allFunctions :: PgVersion -> Bool -> SQL.Statement ([Schema], [Text]) RoutineMap
|
allFunctions :: Bool -> SQL.Statement ([Schema], [Text]) RoutineMap
|
||||||
allFunctions pgVer = SQL.Statement sql (contrazip2 (arrayParam HE.text) (arrayParam HE.text)) decodeFuncs
|
allFunctions = SQL.Statement sql (contrazip2 (arrayParam HE.text) (arrayParam HE.text)) decodeFuncs
|
||||||
where
|
where
|
||||||
sql = funcsSqlQuery pgVer <> " AND pn.nspname = ANY($1)"
|
sql = funcsSqlQuery <> " AND pn.nspname = ANY($1)"
|
||||||
|
|
||||||
accessibleFuncs :: PgVersion -> Bool -> SQL.Statement (Schema, [Text]) RoutineMap
|
accessibleFuncs :: Bool -> SQL.Statement (Schema, [Text]) RoutineMap
|
||||||
accessibleFuncs pgVer = SQL.Statement sql (contrazip2 (param HE.text) (arrayParam HE.text)) decodeFuncs
|
accessibleFuncs = SQL.Statement sql (contrazip2 (param HE.text) (arrayParam HE.text)) decodeFuncs
|
||||||
where
|
where
|
||||||
sql = funcsSqlQuery pgVer <> " AND pn.nspname = $1 AND has_function_privilege(p.oid, 'execute')"
|
sql = funcsSqlQuery <> " AND pn.nspname = $1 AND has_function_privilege(p.oid, 'execute')"
|
||||||
|
|
||||||
funcsSqlQuery :: PgVersion -> SqlQuery
|
funcsSqlQuery :: SqlQuery
|
||||||
funcsSqlQuery pgVer = [q|
|
funcsSqlQuery = [q|
|
||||||
-- Recursively get the base types of domains
|
-- Recursively get the base types of domains
|
||||||
WITH
|
WITH
|
||||||
base_types AS (
|
base_types AS (
|
||||||
@@ -461,7 +460,7 @@ funcsSqlQuery pgVer = [q|
|
|||||||
WHERE setting ~ ANY($2)
|
WHERE setting ~ ANY($2)
|
||||||
) func_settings ON TRUE
|
) func_settings ON TRUE
|
||||||
WHERE t.oid <> 'trigger'::regtype AND COALESCE(a.callable, true)
|
WHERE t.oid <> 'trigger'::regtype AND COALESCE(a.callable, true)
|
||||||
|] <> (if pgVer >= pgVersion110 then "AND prokind = 'f'" else "AND NOT (proisagg OR proiswindow)")
|
AND prokind = 'f'|]
|
||||||
|
|
||||||
schemaDescription :: Bool -> SQL.Statement Schema (Maybe Text)
|
schemaDescription :: Bool -> SQL.Statement Schema (Maybe Text)
|
||||||
schemaDescription =
|
schemaDescription =
|
||||||
@@ -828,8 +827,8 @@ tablesSqlQuery pgVer =
|
|||||||
END|]
|
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 :: PgVersion -> Bool -> SQL.Statement () [Relationship]
|
allM2OandO2ORels :: Bool -> SQL.Statement () [Relationship]
|
||||||
allM2OandO2ORels pgVer =
|
allM2OandO2ORels =
|
||||||
SQL.Statement sql HE.noParams decodeRels
|
SQL.Statement sql HE.noParams decodeRels
|
||||||
where
|
where
|
||||||
-- We use jsonb_agg for comparing the uniques/pks instead of array_agg to avoid the ERROR: cannot accumulate arrays of different dimensionality
|
-- We use jsonb_agg for comparing the uniques/pks instead of array_agg to avoid the ERROR: cannot accumulate arrays of different dimensionality
|
||||||
@@ -875,11 +874,8 @@ allM2OandO2ORels pgVer =
|
|||||||
JOIN pg_namespace ns2 ON ns2.oid = other.relnamespace
|
JOIN pg_namespace ns2 ON ns2.oid = other.relnamespace
|
||||||
LEFT JOIN pks_uniques_cols pks_uqs ON pks_uqs.connamespace = traint.connamespace AND pks_uqs.conrelid = traint.conrelid
|
LEFT JOIN pks_uniques_cols pks_uqs ON pks_uqs.connamespace = traint.connamespace AND pks_uqs.conrelid = traint.conrelid
|
||||||
WHERE traint.contype = 'f'
|
WHERE traint.contype = 'f'
|
||||||
|] <>
|
AND traint.conparentid = 0
|
||||||
(if pgVer >= pgVersion110
|
ORDER BY traint.conrelid, traint.conname|]
|
||||||
then " and traint.conparentid = 0 "
|
|
||||||
else mempty) <>
|
|
||||||
"ORDER BY traint.conrelid, traint.conname"
|
|
||||||
|
|
||||||
allComputedRels :: Bool -> SQL.Statement () [Relationship]
|
allComputedRels :: Bool -> SQL.Statement () [Relationship]
|
||||||
allComputedRels =
|
allComputedRels =
|
||||||
@@ -1132,8 +1128,8 @@ initialMediaHandlers =
|
|||||||
HM.insert (RelAnyElement, MediaType.MTGeoJSON ) (BuiltinOvAggGeoJson, MediaType.MTGeoJSON)
|
HM.insert (RelAnyElement, MediaType.MTGeoJSON ) (BuiltinOvAggGeoJson, MediaType.MTGeoJSON)
|
||||||
HM.empty
|
HM.empty
|
||||||
|
|
||||||
mediaHandlers :: PgVersion -> Bool -> SQL.Statement [Schema] MediaHandlerMap
|
mediaHandlers :: Bool -> SQL.Statement [Schema] MediaHandlerMap
|
||||||
mediaHandlers pgVer =
|
mediaHandlers =
|
||||||
SQL.Statement sql (arrayParam HE.text) decodeMediaHandlers
|
SQL.Statement sql (arrayParam HE.text) decodeMediaHandlers
|
||||||
where
|
where
|
||||||
sql = [q|
|
sql = [q|
|
||||||
@@ -1195,7 +1191,7 @@ mediaHandlers pgVer =
|
|||||||
join pg_namespace typ_sch on typ_sch.oid = mtype.typnamespace
|
join pg_namespace typ_sch on typ_sch.oid = mtype.typnamespace
|
||||||
where
|
where
|
||||||
pro_sch.nspname = ANY($1) and NOT proretset
|
pro_sch.nspname = ANY($1) and NOT proretset
|
||||||
|] <> (if pgVer >= pgVersion110 then " AND prokind = 'f'" else " AND NOT (proisagg OR proiswindow)")
|
and prokind = 'f'|]
|
||||||
|
|
||||||
decodeMediaHandlers :: HD.Result MediaHandlerMap
|
decodeMediaHandlers :: HD.Result MediaHandlerMap
|
||||||
decodeMediaHandlers =
|
decodeMediaHandlers =
|
||||||
|
|||||||
@@ -11,14 +11,12 @@ import Network.HTTP.Types
|
|||||||
import Test.Hspec hiding (pendingWith)
|
import Test.Hspec hiding (pendingWith)
|
||||||
import Test.Hspec.Wai
|
import Test.Hspec.Wai
|
||||||
|
|
||||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion110)
|
|
||||||
|
|
||||||
import PostgREST.Version (docsVersion)
|
import PostgREST.Version (docsVersion)
|
||||||
import Protolude hiding (get)
|
import Protolude hiding (get)
|
||||||
import SpecHelper
|
import SpecHelper
|
||||||
|
|
||||||
spec :: PgVersion -> SpecWith ((), Application)
|
spec :: SpecWith ((), Application)
|
||||||
spec actualPgVersion = describe "OpenAPI" $ do
|
spec = describe "OpenAPI" $ do
|
||||||
it "root path returns a valid openapi spec" $ do
|
it "root path returns a valid openapi spec" $ do
|
||||||
validateOpenApiResponse [("Accept", "application/openapi+json")]
|
validateOpenApiResponse [("Accept", "application/openapi+json")]
|
||||||
request methodHead "/"
|
request methodHead "/"
|
||||||
@@ -290,8 +288,7 @@ spec actualPgVersion = describe "OpenAPI" $ do
|
|||||||
|
|
||||||
getParameterYear `shouldBe` Just "#/parameters/rowFilter.car_models.year"
|
getParameterYear `shouldBe` Just "#/parameters/rowFilter.car_models.year"
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
getParameterRef `shouldBe` Just "#/parameters/rowFilter.car_models.car_brand_name"
|
||||||
getParameterRef `shouldBe` Just "#/parameters/rowFilter.car_models.car_brand_name"
|
|
||||||
|
|
||||||
describe "Materialized view" $
|
describe "Materialized view" $
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,11 @@ import Network.HTTP.Types
|
|||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import Test.Hspec.Wai
|
import Test.Hspec.Wai
|
||||||
|
|
||||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion110)
|
|
||||||
|
|
||||||
import Protolude
|
import Protolude
|
||||||
import SpecHelper
|
import SpecHelper
|
||||||
|
|
||||||
spec :: PgVersion -> SpecWith ((), Application)
|
spec :: SpecWith ((), Application)
|
||||||
spec actualPgVersion = describe "Allow header" $ do
|
spec = describe "Allow header" $ do
|
||||||
context "a table" $ do
|
context "a table" $ do
|
||||||
it "includes read/write methods for writeable table" $ do
|
it "includes read/write methods for writeable table" $ do
|
||||||
r <- request methodOptions "/items" [] ""
|
r <- request methodOptions "/items" [] ""
|
||||||
@@ -29,12 +27,7 @@ spec actualPgVersion = describe "Allow header" $ do
|
|||||||
r <- request methodOptions "/car_models" [] ""
|
r <- request methodOptions "/car_models" [] ""
|
||||||
liftIO $
|
liftIO $
|
||||||
simpleHeaders r `shouldSatisfy`
|
simpleHeaders r `shouldSatisfy`
|
||||||
matchHeader "Allow" (
|
matchHeader "Allow" "OPTIONS,GET,HEAD,POST,PUT,PATCH,DELETE"
|
||||||
if actualPgVersion >= pgVersion110 then
|
|
||||||
"OPTIONS,GET,HEAD,POST,PUT,PATCH,DELETE"
|
|
||||||
else
|
|
||||||
"OPTIONS,GET,HEAD,POST,PATCH,DELETE"
|
|
||||||
)
|
|
||||||
|
|
||||||
context "a view" $ do
|
context "a view" $ do
|
||||||
context "auto updatable" $ do
|
context "auto updatable" $ do
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ 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, pgVersion110,
|
import PostgREST.Config.PgVersion (PgVersion, pgVersion112,
|
||||||
pgVersion112, pgVersion120,
|
pgVersion120, pgVersion130,
|
||||||
pgVersion130, pgVersion140)
|
pgVersion140)
|
||||||
|
|
||||||
import Protolude hiding (get)
|
import Protolude hiding (get)
|
||||||
import SpecHelper
|
import SpecHelper
|
||||||
@@ -141,19 +141,18 @@ spec actualPgVersion = do
|
|||||||
, "Preference-Applied" <:> "return=headers-only"]
|
, "Preference-Applied" <:> "return=headers-only"]
|
||||||
}
|
}
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
it "should not throw and return location header for partitioned tables when selecting without PK" $
|
||||||
it "should not throw and return location header for partitioned tables when selecting without PK" $
|
request methodPost "/car_models"
|
||||||
request methodPost "/car_models"
|
[("Prefer", "return=headers-only")]
|
||||||
[("Prefer", "return=headers-only")]
|
[json|{"name":"Enzo","year":2021}|]
|
||||||
[json|{"name":"Enzo","year":2021}|]
|
`shouldRespondWith`
|
||||||
`shouldRespondWith`
|
""
|
||||||
""
|
{ matchStatus = 201
|
||||||
{ matchStatus = 201
|
, matchHeaders = [ matchHeaderAbsent hContentType
|
||||||
, matchHeaders = [ matchHeaderAbsent hContentType
|
, "Location" <:> "/car_models?name=eq.Enzo&year=eq.2021"
|
||||||
, "Location" <:> "/car_models?name=eq.Enzo&year=eq.2021"
|
, "Content-Range" <:> "*/*"
|
||||||
, "Content-Range" <:> "*/*"
|
, "Preference-Applied" <:> "return=headers-only"]
|
||||||
, "Preference-Applied" <:> "return=headers-only"]
|
}
|
||||||
}
|
|
||||||
|
|
||||||
context "requesting no representation" $
|
context "requesting no representation" $
|
||||||
it "should not throw and return no location header when selecting without PK" $
|
it "should not throw and return no location header when selecting without PK" $
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ 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, pgVersion110,
|
import PostgREST.Config.PgVersion (PgVersion, pgVersion112,
|
||||||
pgVersion112, pgVersion121)
|
pgVersion121)
|
||||||
import Protolude hiding (get)
|
import Protolude hiding (get)
|
||||||
import SpecHelper
|
import SpecHelper
|
||||||
|
|
||||||
@@ -539,107 +539,106 @@ spec actualPgVersion = do
|
|||||||
[json|[{"id":1,"computed_overload":true}]|]
|
[json|[{"id":1,"computed_overload":true}]|]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $ do
|
describe "partitioned tables embedding" $ do
|
||||||
describe "partitioned tables embedding" $ do
|
it "can request a table as parent from a partitioned table" $
|
||||||
it "can request a table as parent from a partitioned table" $
|
get "/car_models?name=in.(DeLorean,Murcielago)&select=name,year,car_brands(name)&order=name.asc" `shouldRespondWith`
|
||||||
get "/car_models?name=in.(DeLorean,Murcielago)&select=name,year,car_brands(name)&order=name.asc" `shouldRespondWith`
|
[json|
|
||||||
|
[{"name":"DeLorean","year":1981,"car_brands":{"name":"DMC"}},
|
||||||
|
{"name":"Murcielago","year":2001,"car_brands":{"name":"Lamborghini"}}] |]
|
||||||
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
|
it "can request partitioned tables as children from a table" $
|
||||||
|
get "/car_brands?select=name,car_models(name,year)&order=name.asc&car_models.order=name.asc" `shouldRespondWith`
|
||||||
|
[json|
|
||||||
|
[{"name":"DMC","car_models":[{"name":"DeLorean","year":1981}]},
|
||||||
|
{"name":"Ferrari","car_models":[{"name":"F310-B","year":1997}]},
|
||||||
|
{"name":"Lamborghini","car_models":[{"name":"Murcielago","year":2001},{"name":"Veneno","year":2013}]}] |]
|
||||||
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
|
when (actualPgVersion >= pgVersion121) $ do
|
||||||
|
it "can request tables as children from a partitioned table" $
|
||||||
|
get "/car_models?name=in.(DeLorean,F310-B)&select=name,year,car_racers(name)&order=name.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"name":"DeLorean","year":1981,"car_brands":{"name":"DMC"}},
|
[{"name":"DeLorean","year":1981,"car_racers":[]},
|
||||||
{"name":"Murcielago","year":2001,"car_brands":{"name":"Lamborghini"}}] |]
|
{"name":"F310-B","year":1997,"car_racers":[{"name":"Michael Schumacher"}]}] |]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
it "can request partitioned tables as children from a table" $
|
it "can request a partitioned table as parent from a table" $
|
||||||
get "/car_brands?select=name,car_models(name,year)&order=name.asc&car_models.order=name.asc" `shouldRespondWith`
|
get "/car_racers?select=name,car_models(name,year)&order=name.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"name":"DMC","car_models":[{"name":"DeLorean","year":1981}]},
|
[{"name":"Alain Prost","car_models":null},
|
||||||
{"name":"Ferrari","car_models":[{"name":"F310-B","year":1997}]},
|
{"name":"Michael Schumacher","car_models":{"name":"F310-B","year":1997}}] |]
|
||||||
{"name":"Lamborghini","car_models":[{"name":"Murcielago","year":2001},{"name":"Veneno","year":2013}]}] |]
|
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion121) $ do
|
it "can request partitioned tables as children from a partitioned table" $
|
||||||
it "can request tables as children from a partitioned table" $
|
get "/car_models?name=in.(DeLorean,Murcielago,Veneno)&select=name,year,car_model_sales(date,quantity)&order=name.asc" `shouldRespondWith`
|
||||||
get "/car_models?name=in.(DeLorean,F310-B)&select=name,year,car_racers(name)&order=name.asc" `shouldRespondWith`
|
[json|
|
||||||
[json|
|
[{"name":"DeLorean","year":1981,"car_model_sales":[{"date":"2021-01-14","quantity":7},{"date":"2021-01-15","quantity":9}]},
|
||||||
[{"name":"DeLorean","year":1981,"car_racers":[]},
|
{"name":"Murcielago","year":2001,"car_model_sales":[{"date":"2021-02-11","quantity":1},{"date":"2021-02-12","quantity":3}]},
|
||||||
{"name":"F310-B","year":1997,"car_racers":[{"name":"Michael Schumacher"}]}] |]
|
{"name":"Veneno","year":2013,"car_model_sales":[]}] |]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
it "can request a partitioned table as parent from a table" $
|
it "can request a partitioned table as parent from a partitioned table" $ do
|
||||||
get "/car_racers?select=name,car_models(name,year)&order=name.asc" `shouldRespondWith`
|
get "/car_model_sales?date=in.(2021-01-15,2021-02-11)&select=date,quantity,car_models(name,year)&order=date.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"name":"Alain Prost","car_models":null},
|
[{"date":"2021-01-15","quantity":9,"car_models":{"name":"DeLorean","year":1981}},
|
||||||
{"name":"Michael Schumacher","car_models":{"name":"F310-B","year":1997}}] |]
|
{"date":"2021-02-11","quantity":1,"car_models":{"name":"Murcielago","year":2001}}] |]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
it "can request partitioned tables as children from a partitioned table" $
|
it "can request many to many relationships between partitioned tables ignoring the intermediate table partitions" $
|
||||||
get "/car_models?name=in.(DeLorean,Murcielago,Veneno)&select=name,year,car_model_sales(date,quantity)&order=name.asc" `shouldRespondWith`
|
get "/car_models?select=name,year,car_dealers(name,city)&order=name.asc&limit=4" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"name":"DeLorean","year":1981,"car_model_sales":[{"date":"2021-01-14","quantity":7},{"date":"2021-01-15","quantity":9}]},
|
[{"name":"DeLorean","year":1981,"car_dealers":[{"name":"Springfield Cars S.A.","city":"Springfield"}]},
|
||||||
{"name":"Murcielago","year":2001,"car_model_sales":[{"date":"2021-02-11","quantity":1},{"date":"2021-02-12","quantity":3}]},
|
{"name":"F310-B","year":1997,"car_dealers":[]},
|
||||||
{"name":"Veneno","year":2013,"car_model_sales":[]}] |]
|
{"name":"Murcielago","year":2001,"car_dealers":[{"name":"The Best Deals S.A.","city":"Franklin"}]},
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{"name":"Veneno","year":2013,"car_dealers":[]}] |]
|
||||||
|
{ matchStatus = 200
|
||||||
|
, matchHeaders = [matchContentTypeJson]
|
||||||
|
}
|
||||||
|
|
||||||
it "can request a partitioned table as parent from a partitioned table" $ do
|
it "cannot request partitions as children from a partitioned table" $
|
||||||
get "/car_model_sales?date=in.(2021-01-15,2021-02-11)&select=date,quantity,car_models(name,year)&order=date.asc" `shouldRespondWith`
|
get "/car_models?id=in.(1,2,4)&select=id,name,car_model_sales_202101(id)&order=id.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"date":"2021-01-15","quantity":9,"car_models":{"name":"DeLorean","year":1981}},
|
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_model_sales_202101'.",
|
||||||
{"date":"2021-02-11","quantity":1,"car_models":{"name":"Murcielago","year":2001}}] |]
|
"details":"Searched for a foreign key relationship between 'car_models' and 'car_model_sales_202101' in the schema 'test', but no matches were found.",
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
"code":"PGRST200",
|
||||||
|
"message":"Could not find a relationship between 'car_models' and 'car_model_sales_202101' in the schema cache"} |]
|
||||||
|
{ matchStatus = 400
|
||||||
|
, matchHeaders = [matchContentTypeJson]
|
||||||
|
}
|
||||||
|
|
||||||
it "can request many to many relationships between partitioned tables ignoring the intermediate table partitions" $
|
it "cannot request a partitioned table as parent from a partition" $
|
||||||
get "/car_models?select=name,year,car_dealers(name,city)&order=name.asc&limit=4" `shouldRespondWith`
|
get "/car_model_sales_202101?select=id,name,car_models(id,name)&order=id.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"name":"DeLorean","year":1981,"car_dealers":[{"name":"Springfield Cars S.A.","city":"Springfield"}]},
|
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_model_sales_202101'.",
|
||||||
{"name":"F310-B","year":1997,"car_dealers":[]},
|
"details":"Searched for a foreign key relationship between 'car_model_sales_202101' and 'car_models' in the schema 'test', but no matches were found.",
|
||||||
{"name":"Murcielago","year":2001,"car_dealers":[{"name":"The Best Deals S.A.","city":"Franklin"}]},
|
"code":"PGRST200",
|
||||||
{"name":"Veneno","year":2013,"car_dealers":[]}] |]
|
"message":"Could not find a relationship between 'car_model_sales_202101' and 'car_models' in the schema cache"} |]
|
||||||
{ matchStatus = 200
|
{ matchStatus = 400
|
||||||
, matchHeaders = [matchContentTypeJson]
|
, matchHeaders = [matchContentTypeJson]
|
||||||
}
|
}
|
||||||
|
|
||||||
it "cannot request partitions as children from a partitioned table" $
|
it "cannot request a partition as parent from a partitioned table" $
|
||||||
get "/car_models?id=in.(1,2,4)&select=id,name,car_model_sales_202101(id)&order=id.asc" `shouldRespondWith`
|
get "/car_model_sales?id=in.(1,3,4)&select=id,name,car_models_default(id,name)&order=id.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_model_sales_202101'.",
|
{"hint":"Perhaps you meant 'car_models' instead of 'car_models_default'.",
|
||||||
"details":"Searched for a foreign key relationship between 'car_models' and 'car_model_sales_202101' in the schema 'test', but no matches were found.",
|
"details":"Searched for a foreign key relationship between 'car_model_sales' and 'car_models_default' in the schema 'test', but no matches were found.",
|
||||||
"code":"PGRST200",
|
"code":"PGRST200",
|
||||||
"message":"Could not find a relationship between 'car_models' and 'car_model_sales_202101' in the schema cache"} |]
|
"message":"Could not find a relationship between 'car_model_sales' and 'car_models_default' in the schema cache"} |]
|
||||||
{ matchStatus = 400
|
{ matchStatus = 400
|
||||||
, matchHeaders = [matchContentTypeJson]
|
, matchHeaders = [matchContentTypeJson]
|
||||||
}
|
}
|
||||||
|
|
||||||
it "cannot request a partitioned table as parent from a partition" $
|
it "cannot request partitioned tables as children from a partition" $
|
||||||
get "/car_model_sales_202101?select=id,name,car_models(id,name)&order=id.asc" `shouldRespondWith`
|
get "/car_models_default?select=id,name,car_model_sales(id,name)&order=id.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_model_sales_202101'.",
|
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_models_default'.",
|
||||||
"details":"Searched for a foreign key relationship between 'car_model_sales_202101' and 'car_models' in the schema 'test', but no matches were found.",
|
"details":"Searched for a foreign key relationship between 'car_models_default' and 'car_model_sales' in the schema 'test', but no matches were found.",
|
||||||
"code":"PGRST200",
|
"code":"PGRST200",
|
||||||
"message":"Could not find a relationship between 'car_model_sales_202101' and 'car_models' in the schema cache"} |]
|
"message":"Could not find a relationship between 'car_models_default' and 'car_model_sales' in the schema cache"} |]
|
||||||
{ matchStatus = 400
|
{ matchStatus = 400
|
||||||
, matchHeaders = [matchContentTypeJson]
|
, matchHeaders = [matchContentTypeJson]
|
||||||
}
|
}
|
||||||
|
|
||||||
it "cannot request a partition as parent from a partitioned table" $
|
|
||||||
get "/car_model_sales?id=in.(1,3,4)&select=id,name,car_models_default(id,name)&order=id.asc" `shouldRespondWith`
|
|
||||||
[json|
|
|
||||||
{"hint":"Perhaps you meant 'car_models' instead of 'car_models_default'.",
|
|
||||||
"details":"Searched for a foreign key relationship between 'car_model_sales' and 'car_models_default' in the schema 'test', but no matches were found.",
|
|
||||||
"code":"PGRST200",
|
|
||||||
"message":"Could not find a relationship between 'car_model_sales' and 'car_models_default' in the schema cache"} |]
|
|
||||||
{ matchStatus = 400
|
|
||||||
, matchHeaders = [matchContentTypeJson]
|
|
||||||
}
|
|
||||||
|
|
||||||
it "cannot request partitioned tables as children from a partition" $
|
|
||||||
get "/car_models_default?select=id,name,car_model_sales(id,name)&order=id.asc" `shouldRespondWith`
|
|
||||||
[json|
|
|
||||||
{"hint":"Perhaps you meant 'car_model_sales' instead of 'car_models_default'.",
|
|
||||||
"details":"Searched for a foreign key relationship between 'car_models_default' and 'car_model_sales' in the schema 'test', but no matches were found.",
|
|
||||||
"code":"PGRST200",
|
|
||||||
"message":"Could not find a relationship between 'car_models_default' and 'car_model_sales' in the schema cache"} |]
|
|
||||||
{ matchStatus = 400
|
|
||||||
, matchHeaders = [matchContentTypeJson]
|
|
||||||
}
|
|
||||||
|
|
||||||
describe "view embedding" $ do
|
describe "view embedding" $ do
|
||||||
it "can detect fk relations through views to tables in the public schema" $
|
it "can detect fk relations through views to tables in the public schema" $
|
||||||
@@ -1362,22 +1361,19 @@ spec actualPgVersion = do
|
|||||||
{ matchStatus = 400
|
{ matchStatus = 400
|
||||||
, matchHeaders = [matchContentTypeJson]
|
, matchHeaders = [matchContentTypeJson]
|
||||||
}
|
}
|
||||||
-- Before PG 11, this will fail because we need arrays of domain type values. The docs should explain data reps are
|
it "uses text parser for filter with 'IN' predicates" $
|
||||||
-- not supported in this case.
|
get "/datarep_todos?select=id,due_at&label_color=in.(000100,01E240)" `shouldRespondWith`
|
||||||
when (actualPgVersion >= pgVersion110) $ do
|
[json| [
|
||||||
it "uses text parser for filter with 'IN' predicates" $
|
{"id":2, "due_at": "2018-01-03T00:00:00Z"},
|
||||||
get "/datarep_todos?select=id,due_at&label_color=in.(000100,01E240)" `shouldRespondWith`
|
{"id":3, "due_at": "2018-01-01T14:12:34.123456Z"}
|
||||||
[json| [
|
] |]
|
||||||
{"id":2, "due_at": "2018-01-03T00:00:00Z"},
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
{"id":3, "due_at": "2018-01-01T14:12:34.123456Z"}
|
it "uses text parser for filter with 'NOT IN' predicates" $
|
||||||
] |]
|
get "/datarep_todos?select=id,due_at&label_color=not.in.(000000,01E240)" `shouldRespondWith`
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
[json| [
|
||||||
it "uses text parser for filter with 'NOT IN' predicates" $
|
{"id":2, "due_at": "2018-01-03T00:00:00Z"}
|
||||||
get "/datarep_todos?select=id,due_at&label_color=not.in.(000000,01E240)" `shouldRespondWith`
|
] |]
|
||||||
[json| [
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
{"id":2, "due_at": "2018-01-03T00:00:00Z"}
|
|
||||||
] |]
|
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
|
||||||
it "uses text parser on value for filter across relations" $
|
it "uses text parser on value for filter across relations" $
|
||||||
get "/datarep_next_two_todos?select=id,name,datarep_todos!datarep_next_two_todos_first_item_id_fkey(label_color,due_at)&datarep_todos.label_color=neq.000100" `shouldRespondWith`
|
get "/datarep_next_two_todos?select=id,name,datarep_todos!datarep_next_two_todos_first_item_id_fkey(label_color,due_at)&datarep_todos.label_color=neq.000100" `shouldRespondWith`
|
||||||
[json| [{"id":1,"name":"school related","datarep_todos":null},{"id":2,"name":"do these first","datarep_todos":{"label_color":"#000000","due_at":"2018-01-02T00:00:00Z"}}] |]
|
[json| [{"id":1,"name":"school related","datarep_todos":null},{"id":2,"name":"do these first","datarep_todos":{"label_color":"#000000","due_at":"2018-01-02T00:00:00Z"}}] |]
|
||||||
@@ -1385,15 +1381,10 @@ spec actualPgVersion = do
|
|||||||
-- This is not supported by data reps (would be hard to make it work with high performance). So the test just
|
-- This is not supported by data reps (would be hard to make it work with high performance). So the test just
|
||||||
-- verifies we don't panic or add inappropriate SQL to the filters.
|
-- verifies we don't panic or add inappropriate SQL to the filters.
|
||||||
it "fails safely on user trying to use ilike operator on data reps column" $
|
it "fails safely on user trying to use ilike operator on data reps column" $
|
||||||
get "/datarep_todos?select=id,name&label_color=ilike.#*100" `shouldRespondWith` (
|
get "/datarep_todos?select=id,name&label_color=ilike.#*100" `shouldRespondWith`
|
||||||
if actualPgVersion >= pgVersion110 then
|
|
||||||
[json|
|
[json|
|
||||||
{"code":"42883","details":null,"hint":"No operator matches the given name and argument types. You might need to add explicit type casts.","message":"operator does not exist: public.color ~~* unknown"}
|
{"code":"42883","details":null,"hint":"No operator matches the given name and argument types. You might need to add explicit type casts.","message":"operator does not exist: public.color ~~* unknown"}
|
||||||
|]
|
|]
|
||||||
else
|
|
||||||
[json|
|
|
||||||
{"code":"42883","details":null,"hint":"No operator matches the given name and argument type(s). You might need to add explicit type casts.","message":"operator does not exist: public.color ~~* unknown"}
|
|
||||||
|])
|
|
||||||
{ matchStatus = 404
|
{ matchStatus = 404
|
||||||
, matchHeaders = [matchContentTypeJson]
|
, matchHeaders = [matchContentTypeJson]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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, pgVersion109,
|
import PostgREST.Config.PgVersion (PgVersion, pgVersion112,
|
||||||
pgVersion110, pgVersion112,
|
|
||||||
pgVersion114)
|
pgVersion114)
|
||||||
|
|
||||||
import Protolude hiding (get)
|
import Protolude hiding (get)
|
||||||
@@ -395,15 +394,14 @@ spec actualPgVersion =
|
|||||||
]|]
|
]|]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
it "can embed if rpc returns domain of table type" $ do
|
||||||
it "can embed if rpc returns domain of table type" $ do
|
post "/rpc/getproject_domain?select=id,name,client:clients(id),tasks(id)"
|
||||||
post "/rpc/getproject_domain?select=id,name,client:clients(id),tasks(id)"
|
[json| { "id": 1} |]
|
||||||
[json| { "id": 1} |]
|
`shouldRespondWith`
|
||||||
`shouldRespondWith`
|
[json|[{"id":1,"name":"Windows 7","client":{"id":1},"tasks":[{"id":1},{"id":2}]}]|]
|
||||||
[json|[{"id":1,"name":"Windows 7","client":{"id":1},"tasks":[{"id":1},{"id":2}]}]|]
|
get "/rpc/getproject_domain?id=1&select=id,name,client:clients(id),tasks(id)"
|
||||||
get "/rpc/getproject_domain?id=1&select=id,name,client:clients(id),tasks(id)"
|
`shouldRespondWith`
|
||||||
`shouldRespondWith`
|
[json|[{"id":1,"name":"Windows 7","client":{"id":1},"tasks":[{"id":1},{"id":2}]}]|]
|
||||||
[json|[{"id":1,"name":"Windows 7","client":{"id":1},"tasks":[{"id":1},{"id":2}]}]|]
|
|
||||||
|
|
||||||
context "a proc that returns an empty rowset" $
|
context "a proc that returns an empty rowset" $
|
||||||
it "returns empty json array" $ do
|
it "returns empty json array" $ do
|
||||||
@@ -466,12 +464,11 @@ spec actualPgVersion =
|
|||||||
it "cannot return composite type in hidden schema" $
|
it "cannot return composite type in hidden schema" $
|
||||||
post "/rpc/ret_point_3d" [json|{}|] `shouldRespondWith` 401
|
post "/rpc/ret_point_3d" [json|{}|] `shouldRespondWith` 401
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
it "returns domain of composite type" $
|
||||||
it "returns domain of composite type" $
|
post "/rpc/ret_composite_domain"
|
||||||
post "/rpc/ret_composite_domain"
|
[json|{}|]
|
||||||
[json|{}|]
|
`shouldRespondWith`
|
||||||
`shouldRespondWith`
|
[json|{"x": 10, "y": 5}|]
|
||||||
[json|{"x": 10, "y": 5}|]
|
|
||||||
|
|
||||||
it "returns single row from table" $
|
it "returns single row from table" $
|
||||||
post "/rpc/single_article?select=id"
|
post "/rpc/single_article?select=id"
|
||||||
@@ -494,26 +491,25 @@ spec actualPgVersion =
|
|||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json|null|]
|
[json|null|]
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $ do
|
it "returns a record type" $ do
|
||||||
it "returns a record type" $ do
|
post "/rpc/returns_record"
|
||||||
post "/rpc/returns_record"
|
""
|
||||||
""
|
`shouldRespondWith`
|
||||||
`shouldRespondWith`
|
[json|{"id":1,"name":"Windows 7","client_id":1}|]
|
||||||
[json|{"id":1,"name":"Windows 7","client_id":1}|]
|
post "/rpc/returns_record_params"
|
||||||
post "/rpc/returns_record_params"
|
[json|{"id":1, "name": "Windows%"}|]
|
||||||
[json|{"id":1, "name": "Windows%"}|]
|
`shouldRespondWith`
|
||||||
`shouldRespondWith`
|
[json|{"id":1,"name":"Windows 7","client_id":1}|]
|
||||||
[json|{"id":1,"name":"Windows 7","client_id":1}|]
|
|
||||||
|
|
||||||
it "returns a setof record type" $ do
|
it "returns a setof record type" $ do
|
||||||
post "/rpc/returns_setof_record"
|
post "/rpc/returns_setof_record"
|
||||||
""
|
""
|
||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json|[{"id":1,"name":"Windows 7","client_id":1},{"id":2,"name":"Windows 10","client_id":1}]|]
|
[json|[{"id":1,"name":"Windows 7","client_id":1},{"id":2,"name":"Windows 10","client_id":1}]|]
|
||||||
post "/rpc/returns_setof_record_params"
|
post "/rpc/returns_setof_record_params"
|
||||||
[json|{"id":1,"name":"Windows%"}|]
|
[json|{"id":1,"name":"Windows%"}|]
|
||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json|[{"id":1,"name":"Windows 7","client_id":1},{"id":2,"name":"Windows 10","client_id":1}]|]
|
[json|[{"id":1,"name":"Windows 7","client_id":1},{"id":2,"name":"Windows 10","client_id":1}]|]
|
||||||
|
|
||||||
context "different types when overloaded" $ do
|
context "different types when overloaded" $ do
|
||||||
it "returns composite type" $
|
it "returns composite type" $
|
||||||
@@ -603,8 +599,7 @@ spec actualPgVersion =
|
|||||||
[json|"object"|]
|
[json|"object"|]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
when ((actualPgVersion >= pgVersion109 && actualPgVersion < pgVersion110)
|
when (actualPgVersion >= pgVersion114) $
|
||||||
|| 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 }" } |]
|
||||||
|
|||||||
@@ -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, pgVersion110)
|
|
||||||
|
|
||||||
import Protolude hiding (get, put)
|
import Protolude hiding (get, put)
|
||||||
import SpecHelper
|
import SpecHelper
|
||||||
|
|
||||||
spec :: PgVersion -> SpecWith ((), Application)
|
spec :: SpecWith ((), Application)
|
||||||
spec actualPgVersion =
|
spec =
|
||||||
describe "UPSERT" $ do
|
describe "UPSERT" $ do
|
||||||
context "with POST" $ do
|
context "with POST" $ do
|
||||||
context "when Prefer: resolution=merge-duplicates is specified" $ do
|
context "when Prefer: resolution=merge-duplicates is specified" $ do
|
||||||
@@ -60,19 +58,18 @@ spec actualPgVersion =
|
|||||||
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates, return=representation", matchContentTypeJson]
|
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates, return=representation", matchContentTypeJson]
|
||||||
}
|
}
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
it "INSERTs and UPDATEs rows on composite pk conflict for partitioned tables" $
|
||||||
it "INSERTs and UPDATEs rows on composite pk conflict for partitioned tables" $
|
request methodPost "/car_models" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
||||||
request methodPost "/car_models" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
[json| [
|
||||||
[json| [
|
{ "name": "Murcielago", "year": 2001, "car_brand_name": null},
|
||||||
{ "name": "Murcielago", "year": 2001, "car_brand_name": null},
|
{ "name": "Roma", "year": 2021, "car_brand_name": "Ferrari" }
|
||||||
{ "name": "Roma", "year": 2021, "car_brand_name": "Ferrari" }
|
]|] `shouldRespondWith` [json| [
|
||||||
]|] `shouldRespondWith` [json| [
|
{ "name": "Murcielago", "year": 2001, "car_brand_name": null},
|
||||||
{ "name": "Murcielago", "year": 2001, "car_brand_name": null},
|
{ "name": "Roma", "year": 2021, "car_brand_name": "Ferrari" }
|
||||||
{ "name": "Roma", "year": 2021, "car_brand_name": "Ferrari" }
|
]|]
|
||||||
]|]
|
{ matchStatus = 201
|
||||||
{ matchStatus = 201
|
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates, return=representation", matchContentTypeJson]
|
||||||
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates, return=representation", matchContentTypeJson]
|
}
|
||||||
}
|
|
||||||
|
|
||||||
it "succeeds when the payload has no elements" $
|
it "succeeds when the payload has no elements" $
|
||||||
request methodPost "/articles" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
request methodPost "/articles" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
||||||
@@ -131,18 +128,17 @@ spec actualPgVersion =
|
|||||||
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates, return=representation", matchContentTypeJson]
|
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates, return=representation", matchContentTypeJson]
|
||||||
}
|
}
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
it "INSERTs and ignores rows on composite pk conflict for partitioned tables" $
|
||||||
it "INSERTs and ignores rows on composite pk conflict for partitioned tables" $
|
request methodPost "/car_models" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
|
||||||
request methodPost "/car_models" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
|
[json| [
|
||||||
[json| [
|
{ "name": "Murcielago", "year": 2001, "car_brand_name": "Ferrari" },
|
||||||
{ "name": "Murcielago", "year": 2001, "car_brand_name": "Ferrari" },
|
{ "name": "Huracán", "year": 2021, "car_brand_name": "Lamborghini" }
|
||||||
{ "name": "Huracán", "year": 2021, "car_brand_name": "Lamborghini" }
|
]|] `shouldRespondWith` [json| [
|
||||||
]|] `shouldRespondWith` [json| [
|
{ "name": "Huracán", "year": 2021, "car_brand_name": "Lamborghini" }
|
||||||
{ "name": "Huracán", "year": 2021, "car_brand_name": "Lamborghini" }
|
]|]
|
||||||
]|]
|
{ matchStatus = 201
|
||||||
{ matchStatus = 201
|
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates, return=representation", matchContentTypeJson]
|
||||||
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates, return=representation", matchContentTypeJson]
|
}
|
||||||
}
|
|
||||||
|
|
||||||
it "INSERTs and ignores rows on single unique key conflict" $
|
it "INSERTs and ignores rows on single unique key conflict" $
|
||||||
request methodPost "/single_unique?on_conflict=unique_key"
|
request methodPost "/single_unique?on_conflict=unique_key"
|
||||||
@@ -312,19 +308,18 @@ spec actualPgVersion =
|
|||||||
[json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "$48,000.00", "company": "GEX", "occupation": "Railroad engineer" } ]|]
|
[json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "$48,000.00", "company": "GEX", "occupation": "Railroad engineer" } ]|]
|
||||||
{ matchStatus = 201 }
|
{ matchStatus = 201 }
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
it "succeeds on a partitioned table with composite pk" $ do
|
||||||
it "succeeds on a partitioned table with composite pk" $ do
|
-- assert that the next request will indeed be an insert
|
||||||
-- assert that the next request will indeed be an insert
|
get "/car_models?name=eq.Supra&year=eq.2021"
|
||||||
get "/car_models?name=eq.Supra&year=eq.2021"
|
`shouldRespondWith`
|
||||||
`shouldRespondWith`
|
[json|[]|]
|
||||||
[json|[]|]
|
|
||||||
|
|
||||||
request methodPut "/car_models?name=eq.Supra&year=eq.2021"
|
request methodPut "/car_models?name=eq.Supra&year=eq.2021"
|
||||||
[("Prefer", "return=representation")]
|
[("Prefer", "return=representation")]
|
||||||
[json| [ { "name": "Supra", "year": 2021 } ]|]
|
[json| [ { "name": "Supra", "year": 2021 } ]|]
|
||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json| [ { "name": "Supra", "year": 2021, "car_brand_name": null } ]|]
|
[json| [ { "name": "Supra", "year": 2021, "car_brand_name": null } ]|]
|
||||||
{ matchStatus = 201 }
|
{ matchStatus = 201 }
|
||||||
|
|
||||||
it "succeeds if the table has only PK cols and no other cols" $ do
|
it "succeeds if the table has only PK cols and no other cols" $ do
|
||||||
-- assert that the next request will indeed be an insert
|
-- assert that the next request will indeed be an insert
|
||||||
@@ -378,18 +373,17 @@ spec actualPgVersion =
|
|||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json| [ { "first_name": "Frances M.", "last_name": "Roe", "salary": "$60,000.00", "company": "Gamma Gas", "occupation": "Railroad engineer" } ]|]
|
[json| [ { "first_name": "Frances M.", "last_name": "Roe", "salary": "$60,000.00", "company": "Gamma Gas", "occupation": "Railroad engineer" } ]|]
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
it "succeeds on a partitioned table with composite pk" $ do
|
||||||
it "succeeds on a partitioned table with composite pk" $ do
|
-- assert that the next request will indeed be an update
|
||||||
-- assert that the next request will indeed be an update
|
get "/car_models?name=eq.DeLorean&year=eq.1981"
|
||||||
get "/car_models?name=eq.DeLorean&year=eq.1981"
|
`shouldRespondWith`
|
||||||
`shouldRespondWith`
|
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": "DMC" } ]|]
|
||||||
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": "DMC" } ]|]
|
|
||||||
|
|
||||||
request methodPut "/car_models?name=eq.DeLorean&year=eq.1981"
|
request methodPut "/car_models?name=eq.DeLorean&year=eq.1981"
|
||||||
[("Prefer", "return=representation")]
|
[("Prefer", "return=representation")]
|
||||||
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": null } ]|]
|
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": null } ]|]
|
||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": null } ]|]
|
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": null } ]|]
|
||||||
|
|
||||||
it "succeeds if the table has only PK cols and no other cols" $ do
|
it "succeeds if the table has only PK cols and no other cols" $ do
|
||||||
-- assert that the next request will indeed be an update
|
-- assert that the next request will indeed be an update
|
||||||
|
|||||||
+3
-3
@@ -142,8 +142,8 @@ main = do
|
|||||||
, ("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 actualPgVersion)
|
, ("Feature.OpenApi.OpenApiSpec" , Feature.OpenApi.OpenApiSpec.spec)
|
||||||
, ("Feature.OptionsSpec" , Feature.OptionsSpec.spec actualPgVersion)
|
, ("Feature.OptionsSpec" , Feature.OptionsSpec.spec)
|
||||||
, ("Feature.Query.AndOrParamsSpec" , Feature.Query.AndOrParamsSpec.spec actualPgVersion)
|
, ("Feature.Query.AndOrParamsSpec" , Feature.Query.AndOrParamsSpec.spec actualPgVersion)
|
||||||
, ("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)
|
||||||
@@ -163,7 +163,7 @@ main = do
|
|||||||
, ("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)
|
||||||
, ("Feature.Query.UpsertSpec" , Feature.Query.UpsertSpec.spec actualPgVersion)
|
, ("Feature.Query.UpsertSpec" , Feature.Query.UpsertSpec.spec)
|
||||||
]
|
]
|
||||||
|
|
||||||
hspec $ do
|
hspec $ do
|
||||||
|
|||||||
Vendored
+9
-11
@@ -685,18 +685,16 @@ INSERT INTO test.car_models(name, year) VALUES ('F310-B',1997);
|
|||||||
INSERT INTO test.car_models(name, year) VALUES ('Veneno',2013);
|
INSERT INTO test.car_models(name, year) VALUES ('Veneno',2013);
|
||||||
INSERT INTO test.car_models(name, year) VALUES ('Murcielago',2001);
|
INSERT INTO test.car_models(name, year) VALUES ('Murcielago',2001);
|
||||||
|
|
||||||
|
INSERT INTO test.car_brands(name) VALUES ('DMC');
|
||||||
|
INSERT INTO test.car_brands(name) VALUES ('Ferrari');
|
||||||
|
INSERT INTO test.car_brands(name) VALUES ('Lamborghini');
|
||||||
|
|
||||||
|
UPDATE test.car_models SET car_brand_name = 'DMC' WHERE name = 'DeLorean';
|
||||||
|
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
|
DO $do$BEGIN
|
||||||
IF (SELECT current_setting('server_version_num')::INT >= 110000) THEN
|
|
||||||
INSERT INTO test.car_brands(name) VALUES ('DMC');
|
|
||||||
INSERT INTO test.car_brands(name) VALUES ('Ferrari');
|
|
||||||
INSERT INTO test.car_brands(name) VALUES ('Lamborghini');
|
|
||||||
|
|
||||||
UPDATE test.car_models SET car_brand_name = 'DMC' WHERE name = 'DeLorean';
|
|
||||||
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';
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF (SELECT current_setting('server_version_num')::INT >= 120000) THEN
|
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-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-01-15',9,'DeLorean',1981);
|
||||||
|
|||||||
Vendored
+20
-37
@@ -1046,19 +1046,14 @@ CREATE FUNCTION setprojects(id_l int, id_h int, name text) RETURNS SETOF project
|
|||||||
update test.projects set name = $3 WHERE id >= $1 AND id <= $2 returning *;
|
update test.projects set name = $3 WHERE id >= $1 AND id <= $2 returning *;
|
||||||
$_$;
|
$_$;
|
||||||
|
|
||||||
-- domains on tables are only supported from pg 11 on
|
CREATE DOMAIN projects_domain AS projects;
|
||||||
DO $do$BEGIN
|
|
||||||
IF (SELECT current_setting('server_version_num')::INT >= 110000) THEN
|
|
||||||
CREATE DOMAIN projects_domain AS projects;
|
|
||||||
|
|
||||||
CREATE FUNCTION getproject_domain(id int) RETURNS SETOF projects_domain
|
CREATE FUNCTION getproject_domain(id int) RETURNS SETOF projects_domain
|
||||||
LANGUAGE sql
|
LANGUAGE sql
|
||||||
STABLE
|
STABLE
|
||||||
AS $_$
|
AS $_$
|
||||||
SELECT projects::projects_domain FROM test.projects WHERE id = $1;
|
SELECT projects::projects_domain FROM test.projects WHERE id = $1;
|
||||||
$_$;
|
$_$;
|
||||||
END IF;
|
|
||||||
END$do$;
|
|
||||||
|
|
||||||
create table images (
|
create table images (
|
||||||
name text not null,
|
name text not null,
|
||||||
@@ -1119,16 +1114,11 @@ create function test.ret_point_overloaded(x json) returns json as $$
|
|||||||
select $1;
|
select $1;
|
||||||
$$ language sql;
|
$$ language sql;
|
||||||
|
|
||||||
-- domains on composite types are only supported from pg 11 on
|
create domain test.composite_domain as test.point_2d;
|
||||||
do $do$begin
|
|
||||||
if (SELECT current_setting('server_version_num')::int >= 110000) then
|
|
||||||
create domain test.composite_domain as test.point_2d;
|
|
||||||
|
|
||||||
create function test.ret_composite_domain() returns test.composite_domain as $$
|
create function test.ret_composite_domain() returns test.composite_domain as $$
|
||||||
select row(10, 5)::test.composite_domain;
|
select row(10, 5)::test.composite_domain;
|
||||||
$$ language sql;
|
$$ language sql;
|
||||||
end if;
|
|
||||||
end$do$;
|
|
||||||
|
|
||||||
create type private.point_3d as (x integer, y integer, z integer);
|
create type private.point_3d as (x integer, y integer, z integer);
|
||||||
|
|
||||||
@@ -2304,17 +2294,14 @@ create table test.car_models_2021 partition of test.car_models
|
|||||||
create table test.car_models_default partition of test.car_models
|
create table test.car_models_default partition of test.car_models
|
||||||
for values in (1981,1997,2001,2013);
|
for values in (1981,1997,2001,2013);
|
||||||
|
|
||||||
|
create table test.car_brands (
|
||||||
|
name varchar(64) primary key
|
||||||
|
);
|
||||||
|
|
||||||
|
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
|
do $do$begin
|
||||||
-- primary keys for partitioned tables are supported from pg v11
|
|
||||||
if (select current_setting('server_version_num')::int >= 110000) then
|
|
||||||
create table test.car_brands (
|
|
||||||
name varchar(64) primary key
|
|
||||||
);
|
|
||||||
|
|
||||||
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);
|
|
||||||
end if;
|
|
||||||
|
|
||||||
-- foreign keys referencing partitioned tables are supported from pg v12
|
-- foreign keys referencing partitioned tables are supported from pg v12
|
||||||
if (select current_setting('server_version_num')::int >= 120000) then
|
if (select current_setting('server_version_num')::int >= 120000) then
|
||||||
create table test.car_model_sales(
|
create table test.car_model_sales(
|
||||||
@@ -2518,12 +2505,8 @@ create table test.arrays (
|
|||||||
|
|
||||||
-- This procedure is to confirm that procedures don't show up in the OpenAPI output right now.
|
-- This procedure is to confirm that procedures don't show up in the OpenAPI output right now.
|
||||||
-- Procedures are not supported, yet.
|
-- Procedures are not supported, yet.
|
||||||
do $do$begin
|
CREATE PROCEDURE test.unsupported_proc ()
|
||||||
if (select current_setting('server_version_num')::int >= 110000) then
|
LANGUAGE SQL AS '';
|
||||||
CREATE PROCEDURE test.unsupported_proc ()
|
|
||||||
LANGUAGE SQL AS '';
|
|
||||||
end if;
|
|
||||||
end $do$;
|
|
||||||
|
|
||||||
CREATE FUNCTION public.dummy(int) RETURNS int
|
CREATE FUNCTION public.dummy(int) RETURNS int
|
||||||
LANGUAGE SQL AS $$ SELECT 1 $$;
|
LANGUAGE SQL AS $$ SELECT 1 $$;
|
||||||
|
|||||||
Reference in New Issue
Block a user