diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 04ad79c9f1..73ce1b3f93 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -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: diff --git a/default.nix b/default.nix index 173b99e86c..5910119377 100644 --- a/default.nix +++ b/default.nix @@ -39,7 +39,6 @@ let allOverlays.postgresql-libpq allOverlays.postgresql-legacy allOverlays.postgresql-future - allOverlays.postgis (allOverlays.haskell-packages { inherit compiler; }) allOverlays.slocat ]; @@ -55,7 +54,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 diff --git a/nix/overlays/default.nix b/nix/overlays/default.nix index 434ccc72d8..7d913d1ae5 100644 --- a/nix/overlays/default.nix +++ b/nix/overlays/default.nix @@ -3,7 +3,6 @@ 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; diff --git a/nix/overlays/postgis.nix b/nix/overlays/postgis.nix deleted file mode 100644 index 73040da28e..0000000000 --- a/nix/overlays/postgis.nix +++ /dev/null @@ -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); - }; - }; -} diff --git a/nix/overlays/postgresql-legacy.nix b/nix/overlays/postgresql-legacy.nix index 4b90d3d607..b485322a48 100644 --- a/nix/overlays/postgresql-legacy.nix +++ b/nix/overlays/postgresql-legacy.nix @@ -1,19 +1,4 @@ _: _: # 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; -} +{ } diff --git a/src/PostgREST/Config/PgVersion.hs b/src/PostgREST/Config/PgVersion.hs index 8f82e7cc1f..fd4952a87e 100644 --- a/src/PostgREST/Config/PgVersion.hs +++ b/src/PostgREST/Config/PgVersion.hs @@ -3,7 +3,6 @@ module PostgREST.Config.PgVersion ( PgVersion(..) , minimumPgVersion - , pgVersion120 , pgVersion121 , pgVersion130 , pgVersion140 @@ -27,14 +26,7 @@ instance Ord PgVersion where -- | Tells the minimum PostgreSQL version required by this version of PostgREST minimumPgVersion :: PgVersion -minimumPgVersion = pgVersion11 - --- PostgreSQL 11 is EOL already, so we only allow the last --- minor release as the minimum version. Theoretically. But --- the version we are using from legacy nix is only 11.21, --- so we are happy with that. -pgVersion11 :: PgVersion -pgVersion11 = PgVersion 110021 "11.21" "11.21" +minimumPgVersion = pgVersion120 pgVersion120 :: PgVersion pgVersion120 = PgVersion 120000 "12.0" "12.0" diff --git a/src/PostgREST/SchemaCache.hs b/src/PostgREST/SchemaCache.hs index 675141b04b..40779e125d 100644 --- a/src/PostgREST/SchemaCache.hs +++ b/src/PostgREST/SchemaCache.hs @@ -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 $ allFunctions prepared @@ -601,15 +598,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)); @@ -623,7 +618,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 @@ -809,21 +810,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] diff --git a/test/spec/Feature/Query/InsertSpec.hs b/test/spec/Feature/Query/InsertSpec.hs index 02bf12c438..e02675eb3d 100644 --- a/test/spec/Feature/Query/InsertSpec.hs +++ b/test/spec/Feature/Query/InsertSpec.hs @@ -11,8 +11,8 @@ import Test.Hspec.Wai import Test.Hspec.Wai.JSON import Text.Heredoc -import PostgREST.Config.PgVersion (PgVersion, pgVersion120, - pgVersion130, pgVersion140) +import PostgREST.Config.PgVersion (PgVersion, pgVersion130, + pgVersion140) import Protolude hiding (get) import SpecHelper @@ -541,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")] diff --git a/test/spec/Feature/Query/PlanSpec.hs b/test/spec/Feature/Query/PlanSpec.hs index d9e78d7709..182ac99a55 100644 --- a/test/spec/Feature/Query/PlanSpec.hs +++ b/test/spec/Feature/Query/PlanSpec.hs @@ -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 = diff --git a/test/spec/fixtures/data.sql b/test/spec/fixtures/data.sql index aef27d1f98..3a5af3862c 100644 --- a/test/spec/fixtures/data.sql +++ b/test/spec/fixtures/data.sql @@ -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_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_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_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_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); TRUNCATE TABLE test.products CASCADE; INSERT INTO test.products (id, name) VALUES (1,'product-1'), (2,'product-2'), (3,'product-3'); diff --git a/test/spec/fixtures/schema.sql b/test/spec/fixtures/schema.sql index e794faa69a..aad6b25055 100644 --- a/test/spec/fixtures/schema.sql +++ b/test/spec/fixtures/schema.sql @@ -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_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_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_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_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_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_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_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_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_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_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_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;