Remove partitions from the schema cache
breaking: Embedding, OpenApi and other features related to the schema cache are no longer allowed on partitions.
This commit is contained in:
@@ -30,6 +30,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- #1857, Make GUC names for headers, cookies and jwt claims compatible with PostgreSQL v14 - @laurenceisla, @robertsosinski
|
- #1857, Make GUC names for headers, cookies and jwt claims compatible with PostgreSQL v14 - @laurenceisla, @robertsosinski
|
||||||
+ Getting the value for a header GUC on PostgreSQL 14 is done using `current_setting('request.headers')::json->>'name-of-header'` and in a similar way for `request.cookies` and `request.jwt.claims`
|
+ Getting the value for a header GUC on PostgreSQL 14 is done using `current_setting('request.headers')::json->>'name-of-header'` and in a similar way for `request.cookies` and `request.jwt.claims`
|
||||||
+ PostgreSQL versions below 14 can opt in to the new JSON GUCs by setting the `db-use-legacy-gucs` config option to false (true by default)
|
+ PostgreSQL versions below 14 can opt in to the new JSON GUCs by setting the `db-use-legacy-gucs` config option to false (true by default)
|
||||||
|
- #1783, Partitions (created using `PARTITION OF`) are no longer included in the schema cache. - @laurenceisla
|
||||||
|
|
||||||
## [8.0.0] - 2021-07-25
|
## [8.0.0] - 2021-07-25
|
||||||
|
|
||||||
|
|||||||
@@ -462,12 +462,12 @@ handleInvoke invMethod proc context@RequestContext{..} = do
|
|||||||
(if invMethod == InvHead then mempty else LBS.fromStrict body)
|
(if invMethod == InvHead then mempty else LBS.fromStrict body)
|
||||||
|
|
||||||
handleOpenApi :: Bool -> Schema -> RequestContext -> DbHandler Wai.Response
|
handleOpenApi :: Bool -> Schema -> RequestContext -> DbHandler Wai.Response
|
||||||
handleOpenApi headersOnly tSchema (RequestContext conf@AppConfig{..} dbStructure apiRequest _) = do
|
handleOpenApi headersOnly tSchema (RequestContext conf@AppConfig{..} dbStructure apiRequest ctxPgVersion) = do
|
||||||
body <-
|
body <-
|
||||||
lift $ case configOpenApiMode of
|
lift $ case configOpenApiMode of
|
||||||
OAFollowPriv ->
|
OAFollowPriv ->
|
||||||
OpenAPI.encode conf dbStructure
|
OpenAPI.encode conf dbStructure
|
||||||
<$> SQL.statement tSchema (DbStructure.accessibleTables configDbPreparedStatements)
|
<$> SQL.statement tSchema (DbStructure.accessibleTables ctxPgVersion configDbPreparedStatements)
|
||||||
<*> SQL.statement tSchema (DbStructure.accessibleProcs configDbPreparedStatements)
|
<*> SQL.statement tSchema (DbStructure.accessibleProcs configDbPreparedStatements)
|
||||||
<*> SQL.statement tSchema (DbStructure.schemaDescription configDbPreparedStatements)
|
<*> SQL.statement tSchema (DbStructure.schemaDescription configDbPreparedStatements)
|
||||||
OAIgnorePriv ->
|
OAIgnorePriv ->
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ main installSignalHandlers runAppWithSocket CLI{cliCommand, cliPath} = do
|
|||||||
dumpSchema :: AppState -> IO LBS.ByteString
|
dumpSchema :: AppState -> IO LBS.ByteString
|
||||||
dumpSchema appState = do
|
dumpSchema appState = do
|
||||||
AppConfig{..} <- AppState.getConfig appState
|
AppConfig{..} <- AppState.getConfig appState
|
||||||
|
actualPgVersion <- AppState.getPgVersion appState
|
||||||
result <-
|
result <-
|
||||||
let transaction = if configDbPreparedStatements then SQL.transaction else SQL.unpreparedTransaction in
|
let transaction = if configDbPreparedStatements then SQL.transaction else SQL.unpreparedTransaction in
|
||||||
SQL.use (AppState.getPool appState) $
|
SQL.use (AppState.getPool appState) $
|
||||||
@@ -60,6 +61,7 @@ dumpSchema appState = do
|
|||||||
queryDbStructure
|
queryDbStructure
|
||||||
(toList configDbSchemas)
|
(toList configDbSchemas)
|
||||||
configDbExtraSearchPath
|
configDbExtraSearchPath
|
||||||
|
actualPgVersion
|
||||||
configDbPreparedStatements
|
configDbPreparedStatements
|
||||||
SQL.release $ AppState.getPool appState
|
SQL.release $ AppState.getPool appState
|
||||||
case result of
|
case result of
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import Data.Set as S (fromList)
|
|||||||
import Data.Text (split)
|
import Data.Text (split)
|
||||||
import Text.InterpolatedString.Perl6 (q)
|
import Text.InterpolatedString.Perl6 (q)
|
||||||
|
|
||||||
|
import PostgREST.Config.PgVersion (PgVersion, pgVersion100)
|
||||||
import PostgREST.DbStructure.Identifiers (QualifiedIdentifier (..),
|
import PostgREST.DbStructure.Identifiers (QualifiedIdentifier (..),
|
||||||
Schema, TableName)
|
Schema, TableName)
|
||||||
import PostgREST.DbStructure.Proc (PgType (..),
|
import PostgREST.DbStructure.Proc (PgType (..),
|
||||||
@@ -82,10 +83,10 @@ type ViewColumn = Column
|
|||||||
-- | A SQL query that can be executed independently
|
-- | A SQL query that can be executed independently
|
||||||
type SqlQuery = ByteString
|
type SqlQuery = ByteString
|
||||||
|
|
||||||
queryDbStructure :: [Schema] -> [Schema] -> Bool -> SQL.Transaction DbStructure
|
queryDbStructure :: [Schema] -> [Schema] -> PgVersion -> Bool -> SQL.Transaction DbStructure
|
||||||
queryDbStructure schemas extraSearchPath prepared = do
|
queryDbStructure schemas extraSearchPath pgVer prepared = do
|
||||||
SQL.sql "set local schema ''" -- This voids the search path. The following queries need this for getting the fully qualified name(schema.name) of every db object
|
SQL.sql "set local schema ''" -- This voids the search path. The following queries need this for getting the fully qualified name(schema.name) of every db object
|
||||||
tabs <- SQL.statement mempty $ allTables prepared
|
tabs <- SQL.statement mempty $ allTables pgVer prepared
|
||||||
cols <- SQL.statement schemas $ allColumns tabs prepared
|
cols <- SQL.statement schemas $ allColumns tabs prepared
|
||||||
srcCols <- SQL.statement (schemas, extraSearchPath) $ pfkSourceColumns cols prepared
|
srcCols <- SQL.statement (schemas, extraSearchPath) $ pfkSourceColumns cols prepared
|
||||||
m2oRels <- SQL.statement mempty $ allM2ORels tabs cols prepared
|
m2oRels <- SQL.statement mempty $ allM2ORels tabs cols prepared
|
||||||
@@ -311,8 +312,8 @@ schemaDescription =
|
|||||||
where
|
where
|
||||||
n.nspname = $1 |]
|
n.nspname = $1 |]
|
||||||
|
|
||||||
accessibleTables :: Bool -> SQL.Statement Schema [Table]
|
accessibleTables :: PgVersion -> Bool -> SQL.Statement Schema [Table]
|
||||||
accessibleTables =
|
accessibleTables pgVer =
|
||||||
SQL.Statement sql (param HE.text) decodeTables
|
SQL.Statement sql (param HE.text) decodeTables
|
||||||
where
|
where
|
||||||
sql = [q|
|
sql = [q|
|
||||||
@@ -350,7 +351,8 @@ accessibleTables =
|
|||||||
left join pg_catalog.pg_description as d on d.objoid = c.oid and d.objsubid = 0
|
left join pg_catalog.pg_description as d on d.objoid = c.oid and d.objsubid = 0
|
||||||
where
|
where
|
||||||
c.relkind in ('v','r','m','f','p')
|
c.relkind in ('v','r','m','f','p')
|
||||||
and n.nspname = $1
|
and n.nspname = $1 |]
|
||||||
|
<> relIsNotPartition pgVer <> [q|
|
||||||
and (
|
and (
|
||||||
pg_has_role(c.relowner, 'USAGE')
|
pg_has_role(c.relowner, 'USAGE')
|
||||||
or has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
|
or has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
|
||||||
@@ -445,8 +447,8 @@ addViewPrimaryKeys srcCols = concatMap (\pk ->
|
|||||||
filter (\(col, _) -> colTable col == pkTable pk && colName col == pkName pk) srcCols in
|
filter (\(col, _) -> colTable col == pkTable pk && colName col == pkName pk) srcCols in
|
||||||
pk : viewPks)
|
pk : viewPks)
|
||||||
|
|
||||||
allTables :: Bool -> SQL.Statement () [Table]
|
allTables :: PgVersion -> Bool -> SQL.Statement () [Table]
|
||||||
allTables =
|
allTables pgVer =
|
||||||
SQL.Statement sql HE.noParams decodeTables
|
SQL.Statement sql HE.noParams decodeTables
|
||||||
where
|
where
|
||||||
sql = [q|
|
sql = [q|
|
||||||
@@ -484,9 +486,13 @@ allTables =
|
|||||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||||
LEFT JOIN pg_catalog.pg_description as d on d.objoid = c.oid and d.objsubid = 0
|
LEFT JOIN pg_catalog.pg_description as d on d.objoid = c.oid and d.objsubid = 0
|
||||||
WHERE c.relkind IN ('v','r','m','f','p')
|
WHERE c.relkind IN ('v','r','m','f','p')
|
||||||
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
|
AND n.nspname NOT IN ('pg_catalog', 'information_schema') |]
|
||||||
|
<> relIsNotPartition pgVer <> [q|
|
||||||
ORDER BY table_schema, table_name |]
|
ORDER BY table_schema, table_name |]
|
||||||
|
|
||||||
|
relIsNotPartition :: PgVersion -> SqlQuery
|
||||||
|
relIsNotPartition pgVer = if pgVer >= pgVersion100 then " AND not c.relispartition " else mempty
|
||||||
|
|
||||||
allColumns :: [Table] -> Bool -> SQL.Statement [Schema] [Column]
|
allColumns :: [Table] -> Bool -> SQL.Statement [Schema] [Column]
|
||||||
allColumns tabs =
|
allColumns tabs =
|
||||||
SQL.Statement sql (arrayParam HE.text) (decodeColumns tabs)
|
SQL.Statement sql (arrayParam HE.text) (decodeColumns tabs)
|
||||||
|
|||||||
@@ -152,10 +152,11 @@ connectionStatus appState =
|
|||||||
loadSchemaCache :: AppState -> IO SCacheStatus
|
loadSchemaCache :: AppState -> IO SCacheStatus
|
||||||
loadSchemaCache appState = do
|
loadSchemaCache appState = do
|
||||||
AppConfig{..} <- AppState.getConfig appState
|
AppConfig{..} <- AppState.getConfig appState
|
||||||
|
actualPgVersion <- AppState.getPgVersion appState
|
||||||
result <-
|
result <-
|
||||||
let transaction = if configDbPreparedStatements then SQL.transaction else SQL.unpreparedTransaction in
|
let transaction = if configDbPreparedStatements then SQL.transaction else SQL.unpreparedTransaction in
|
||||||
SQL.use (AppState.getPool appState) . transaction SQL.ReadCommitted SQL.Read $
|
SQL.use (AppState.getPool appState) . transaction SQL.ReadCommitted SQL.Read $
|
||||||
queryDbStructure (toList configDbSchemas) configDbExtraSearchPath configDbPreparedStatements
|
queryDbStructure (toList configDbSchemas) configDbExtraSearchPath actualPgVersion configDbPreparedStatements
|
||||||
case result of
|
case result of
|
||||||
Left e -> do
|
Left e -> do
|
||||||
let
|
let
|
||||||
|
|||||||
@@ -122,10 +122,10 @@ spec actualPgVersion = do
|
|||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
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 "/partitioned_a" [("Prefer", "return=headers-only")]
|
request methodPost "/car_models" [("Prefer", "return=headers-only")]
|
||||||
[json|{"id":5,"name":"first"}|] `shouldRespondWith` ""
|
[json|{"name":"Enzo","year":2021}|] `shouldRespondWith` ""
|
||||||
{ matchStatus = 201
|
{ matchStatus = 201
|
||||||
, matchHeaders = [ "Location" <:> "/partitioned_a?id=eq.5&name=eq.first"
|
, matchHeaders = [ "Location" <:> "/car_models?name=eq.Enzo&year=eq.2021"
|
||||||
, "Content-Range" <:> "*/*" ]
|
, "Content-Range" <:> "*/*" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -204,11 +204,11 @@ spec actualPgVersion = describe "OpenAPI" $ do
|
|||||||
it "includes partitioned table properties" $ do
|
it "includes partitioned table properties" $ do
|
||||||
r <- simpleBody <$> get "/"
|
r <- simpleBody <$> get "/"
|
||||||
|
|
||||||
let method s = key "paths" . key "/partitioned_a" . key s
|
let method s = key "paths" . key "/car_models" . key s
|
||||||
getSummary = r ^? method "get" . key "summary"
|
getSummary = r ^? method "get" . key "summary"
|
||||||
getDescription = r ^? method "get" . key "description"
|
getDescription = r ^? method "get" . key "description"
|
||||||
getParameterId = r ^? method "get" . key "parameters" . nth 0 . key "$ref"
|
getParameterName = r ^? method "get" . key "parameters" . nth 0 . key "$ref"
|
||||||
getParameterName = r ^? method "get" . key "parameters" . nth 1 . key "$ref"
|
getParameterYear = r ^? method "get" . key "parameters" . nth 1 . key "$ref"
|
||||||
getParameterRef = r ^? method "get" . key "parameters" . nth 2 . key "$ref"
|
getParameterRef = r ^? method "get" . key "parameters" . nth 2 . key "$ref"
|
||||||
|
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
@@ -217,12 +217,12 @@ spec actualPgVersion = describe "OpenAPI" $ do
|
|||||||
|
|
||||||
getDescription `shouldBe` Just "A test for partitioned tables"
|
getDescription `shouldBe` Just "A test for partitioned tables"
|
||||||
|
|
||||||
getParameterId `shouldBe` Just "#/parameters/rowFilter.partitioned_a.id"
|
getParameterName `shouldBe` Just "#/parameters/rowFilter.car_models.name"
|
||||||
|
|
||||||
getParameterName `shouldBe` Just "#/parameters/rowFilter.partitioned_a.name"
|
getParameterYear `shouldBe` Just "#/parameters/rowFilter.car_models.year"
|
||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
when (actualPgVersion >= pgVersion110) $
|
||||||
getParameterRef `shouldBe` Just "#/parameters/rowFilter.partitioned_a.id_ref"
|
getParameterRef `shouldBe` Just "#/parameters/rowFilter.car_models.car_brand_name"
|
||||||
|
|
||||||
describe "Materialized view" $
|
describe "Materialized view" $
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ spec actualPgVersion = describe "Allow header" $ do
|
|||||||
when (actualPgVersion >= pgVersion100) $
|
when (actualPgVersion >= pgVersion100) $
|
||||||
context "a partitioned table" $ do
|
context "a partitioned table" $ do
|
||||||
it "includes read/write verbs for writeable partitioned tables" $ do
|
it "includes read/write verbs for writeable partitioned tables" $ do
|
||||||
r <- request methodOptions "/partitioned_a" [] ""
|
r <- request methodOptions "/car_models" [] ""
|
||||||
liftIO $
|
liftIO $
|
||||||
simpleHeaders r `shouldSatisfy`
|
simpleHeaders r `shouldSatisfy`
|
||||||
matchHeader "Allow" (
|
matchHeader "Allow" (
|
||||||
|
|||||||
+59
-41
@@ -399,78 +399,96 @@ spec actualPgVersion = do
|
|||||||
when (actualPgVersion >= pgVersion110) $ do
|
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 "/partitioned_a?id=in.(1,2)&select=id,name,reference_from_partitioned(id)&order=id.asc" `shouldRespondWith`
|
get "/car_models?name=in.(DeLorean,Murcielago)&select=name,year,car_brands(name)&order=name.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"id":1,"name":"first","reference_from_partitioned":{"id":1}},
|
[{"name":"DeLorean","year":1981,"car_brands":{"name":"DMC"}},
|
||||||
{"id":2,"name":"first","reference_from_partitioned":null}] |]
|
{"name":"Murcielago","year":2001,"car_brands":{"name":"Lamborghini"}}] |]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
it "can request partitioned tables as children from a table" $
|
it "can request partitioned tables as children from a table" $
|
||||||
get "/reference_from_partitioned?select=id,partitioned_a(id,name)&order=id.asc" `shouldRespondWith`
|
get "/car_brands?select=name,car_models(name,year)&order=name.asc&car_models.order=name.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"id":1,"partitioned_a":[{"id":1,"name":"first"}]},
|
[{"name":"DMC","car_models":[{"name":"DeLorean","year":1981}]},
|
||||||
{"id":2,"partitioned_a":[]}] |]
|
{"name":"Ferrari","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
|
when (actualPgVersion >= pgVersion121) $ do
|
||||||
it "can request tables as children from a partitioned table" $
|
it "can request tables as children from a partitioned table" $
|
||||||
get "/partitioned_a?id=in.(1,2)&select=id,name,reference_to_partitioned(id)&order=id.asc" `shouldRespondWith`
|
get "/car_models?name=in.(DeLorean,F310-B)&select=name,year,car_racers(name)&order=name.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"id":1,"name":"first","reference_to_partitioned":[]},
|
[{"name":"DeLorean","year":1981,"car_racers":[]},
|
||||||
{"id":2,"name":"first","reference_to_partitioned":[{"id":2}]}] |]
|
{"name":"F310-B","year":1997,"car_racers":[{"name":"Michael Schumacher"}]}] |]
|
||||||
{ 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 table" $
|
||||||
get "/reference_to_partitioned?select=id,partitioned_a(id,name)&order=id.asc" `shouldRespondWith`
|
get "/car_racers?select=name,car_models(name,year)&order=name.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"id":1,"partitioned_a":null},
|
[{"name":"Alain Prost","car_models":null},
|
||||||
{"id":2,"partitioned_a":{"id":2,"name":"first"}}] |]
|
{"name":"Michael Schumacher","car_models":{"name":"F310-B","year":1997}}] |]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
it "can request partitioned tables as children from a partitioned table" $
|
it "can request partitioned tables as children from a partitioned table" $
|
||||||
get "/partitioned_a?id=in.(1,2,4)&select=id,name,partitioned_b(id,name)&order=id.asc" `shouldRespondWith`
|
get "/car_models?name=in.(DeLorean,Murcielago,Veneno)&select=name,year,car_model_sales(date,quantity)&order=name.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"id":1,"name":"first","partitioned_b":[]},
|
[{"name":"DeLorean","year":1981,"car_model_sales":[{"date":"2021-01-14","quantity":7},{"date":"2021-01-15","quantity":9}]},
|
||||||
{"id":2,"name":"first","partitioned_b":[{"id":2,"name":"first_b"}]},
|
{"name":"Murcielago","year":2001,"car_model_sales":[{"date":"2021-02-11","quantity":1},{"date":"2021-02-12","quantity":3}]},
|
||||||
{"id":4,"name":"second","partitioned_b":[{"id":4,"name":"second_b"}]}] |]
|
{"name":"Veneno","year":2013,"car_model_sales":[]}] |]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
it "can request a partitioned table as parent from a partitioned table" $ do
|
it "can request a partitioned table as parent from a partitioned table" $ do
|
||||||
get "/partitioned_b?id=in.(2,4)&select=id,name,partitioned_a(id,name)&order=id.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|
|
||||||
[{"id":2,"name":"first_b","partitioned_a":{"id":2,"name":"first"}},
|
[{"date":"2021-01-15","quantity":9,"car_models":{"name":"DeLorean","year":1981}},
|
||||||
{"id":4,"name":"second_b","partitioned_a":{"id":4,"name":"second"}}] |]
|
{"date":"2021-02-11","quantity":1,"car_models":{"name":"Murcielago","year":2001}}] |]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
it "can request partitions as children from a partitioned table" $
|
it "can request many to many relationships between partitioned tables ignoring the intermediate table partitions" $
|
||||||
get "/partitioned_a?id=in.(1,2,4)&select=id,name,first_partition_b(id)&order=id.asc" `shouldRespondWith`
|
get "/car_models?select=name,year,car_dealers(name,city)&order=name.asc&limit=4" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"id":1,"name":"first","first_partition_b":[]},
|
[{"name":"DeLorean","year":1981,"car_dealers":[{"name":"Springfield Cars S.A.","city":"Springfield"}]},
|
||||||
{"id":2,"name":"first","first_partition_b":[{"id":2}]},
|
{"name":"F310-B","year":1997,"car_dealers":[]},
|
||||||
{"id":4,"name":"second","first_partition_b":[]}] |]
|
{"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 partition" $
|
it "cannot request partitions as children from a partitioned table" $
|
||||||
get "/first_partition_b?select=id,name,partitioned_a(id,name)&order=id.asc" `shouldRespondWith`
|
get "/car_models?id=in.(1,2,4)&select=id,name,car_model_sales_202101(id)&order=id.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"id":1,"name":"first_b","partitioned_a":null},
|
{"hint":"If a new foreign key between these entities was created in the database, try reloading the schema cache.",
|
||||||
{"id":2,"name":"first_b","partitioned_a":{"id":2,"name":"first"}}] |]
|
"message":"Could not find a relationship between car_models and car_model_sales_202101 in the schema cache"} |]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchStatus = 400
|
||||||
|
, matchHeaders = [matchContentTypeJson]
|
||||||
|
}
|
||||||
|
|
||||||
it "can request a partition as parent from a partitioned table" $
|
it "cannot request a partitioned table as parent from a partition" $
|
||||||
get "/partitioned_b?id=in.(1,3,4)&select=id,name,second_partition_a(id,name)&order=id.asc" `shouldRespondWith`
|
get "/car_model_sales_202101?select=id,name,car_models(id,name)&order=id.asc" `shouldRespondWith`
|
||||||
[json|
|
[json|
|
||||||
[{"id":1,"name":"first_b","second_partition_a":null},
|
{"hint":"If a new foreign key between these entities was created in the database, try reloading the schema cache.",
|
||||||
{"id":3,"name":"second_b","second_partition_a":null},
|
"message":"Could not find a relationship between car_model_sales_202101 and car_models in the schema cache"} |]
|
||||||
{"id":4,"name":"second_b","second_partition_a":{"id":4,"name":"second"}}] |]
|
{ matchStatus = 400
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
, matchHeaders = [matchContentTypeJson]
|
||||||
|
}
|
||||||
|
|
||||||
it "can request partitioned tables as children from a partition" $
|
it "cannot request a partition as parent from a partitioned table" $
|
||||||
get "/second_partition_a?select=id,name,partitioned_b(id,name)&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|
|
||||||
[{"id":3,"name":"second","partitioned_b":[]},
|
{"hint":"If a new foreign key between these entities was created in the database, try reloading the schema cache.",
|
||||||
{"id":4,"name":"second","partitioned_b":[{"id":4,"name":"second_b"}]}] |]
|
"message":"Could not find a relationship between car_model_sales and car_models_default in the schema cache"} |]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ 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":"If a new foreign key between these entities was created in the database, try reloading the schema cache.",
|
||||||
|
"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" $
|
||||||
|
|||||||
+18
-18
@@ -47,13 +47,13 @@ spec actualPgVersion =
|
|||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
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 "/partitioned_a" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
request methodPost "/car_models" [("Prefer", "return=representation"), ("Prefer", "resolution=merge-duplicates")]
|
||||||
[json| [
|
[json| [
|
||||||
{ "id": 4, "name": "second", "id_ref": 2},
|
{ "name": "Murcielago", "year": 2001, "car_brand_name": null},
|
||||||
{ "id": 6, "name": "first", "id_ref": 1 }
|
{ "name": "Roma", "year": 2021, "car_brand_name": "Ferrari" }
|
||||||
]|] `shouldRespondWith` [json| [
|
]|] `shouldRespondWith` [json| [
|
||||||
{ "id": 4, "name": "second", "id_ref": 2 },
|
{ "name": "Murcielago", "year": 2001, "car_brand_name": null},
|
||||||
{ "id": 6, "name": "first", "id_ref": 1 }
|
{ "name": "Roma", "year": 2021, "car_brand_name": "Ferrari" }
|
||||||
]|]
|
]|]
|
||||||
{ matchStatus = 201
|
{ matchStatus = 201
|
||||||
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates", matchContentTypeJson]
|
, matchHeaders = ["Preference-Applied" <:> "resolution=merge-duplicates", matchContentTypeJson]
|
||||||
@@ -117,12 +117,12 @@ spec actualPgVersion =
|
|||||||
|
|
||||||
when (actualPgVersion >= pgVersion110) $
|
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 "/partitioned_a" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
|
request methodPost "/car_models" [("Prefer", "return=representation"), ("Prefer", "resolution=ignore-duplicates")]
|
||||||
[json| [
|
[json| [
|
||||||
{ "id": 4, "name": "second", "id_ref": 1 },
|
{ "name": "Murcielago", "year": 2001, "car_brand_name": "Ferrari" },
|
||||||
{ "id": 7, "name": "second", "id_ref": 2 }
|
{ "name": "Huracán", "year": 2021, "car_brand_name": "Lamborghini" }
|
||||||
]|] `shouldRespondWith` [json| [
|
]|] `shouldRespondWith` [json| [
|
||||||
{ "id": 7, "name": "second", "id_ref": 2 }
|
{ "name": "Huracán", "year": 2021, "car_brand_name": "Lamborghini" }
|
||||||
]|]
|
]|]
|
||||||
{ matchStatus = 201
|
{ matchStatus = 201
|
||||||
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates", matchContentTypeJson]
|
, matchHeaders = ["Preference-Applied" <:> "resolution=ignore-duplicates", matchContentTypeJson]
|
||||||
@@ -296,15 +296,15 @@ spec actualPgVersion =
|
|||||||
when (actualPgVersion >= pgVersion110) $
|
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 "/partitioned_a?id=eq.8&name=eq.first"
|
get "/car_models?name=eq.Supra&year=eq.2021"
|
||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json|[]|]
|
[json|[]|]
|
||||||
|
|
||||||
request methodPut "/partitioned_a?id=eq.8&name=eq.first"
|
request methodPut "/car_models?name=eq.Supra&year=eq.2021"
|
||||||
[("Prefer", "return=representation")]
|
[("Prefer", "return=representation")]
|
||||||
[json| [ { "id": 8, "name": "first" } ]|]
|
[json| [ { "name": "Supra", "year": 2021 } ]|]
|
||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json| [ { "id": 8, "name": "first", "id_ref": null } ]|]
|
[json| [ { "name": "Supra", "year": 2021, "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 insert
|
-- assert that the next request will indeed be an insert
|
||||||
@@ -360,15 +360,15 @@ spec actualPgVersion =
|
|||||||
when (actualPgVersion >= pgVersion110) $
|
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 "/partitioned_a?id=eq.2&name=eq.first"
|
get "/car_models?name=eq.DeLorean&year=eq.1981"
|
||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json| [ { "id": 2, "name": "first", "id_ref": null } ]|]
|
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": "DMC" } ]|]
|
||||||
|
|
||||||
request methodPut "/partitioned_a?id=eq.2&name=eq.first"
|
request methodPut "/car_models?name=eq.DeLorean&year=eq.1981"
|
||||||
[("Prefer", "return=representation")]
|
[("Prefer", "return=representation")]
|
||||||
[json| [ { "id": 2, "name": "first", "id_ref": 1 } ]|]
|
[json| [ { "name": "DeLorean", "year": 1981, "car_brand_name": null } ]|]
|
||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json| [ { "id": 2, "name": "first", "id_ref": 1 } ]|]
|
[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
|
||||||
|
|||||||
+4
-2
@@ -69,6 +69,7 @@ main = do
|
|||||||
loadDbStructure pool
|
loadDbStructure pool
|
||||||
(configDbSchemas $ testCfg testDbConn)
|
(configDbSchemas $ testCfg testDbConn)
|
||||||
(configDbExtraSearchPath $ testCfg testDbConn)
|
(configDbExtraSearchPath $ testCfg testDbConn)
|
||||||
|
actualPgVersion
|
||||||
|
|
||||||
let
|
let
|
||||||
-- For tests that run with the same refDbStructure
|
-- For tests that run with the same refDbStructure
|
||||||
@@ -88,6 +89,7 @@ main = do
|
|||||||
loadDbStructure pool
|
loadDbStructure pool
|
||||||
(configDbSchemas config)
|
(configDbSchemas config)
|
||||||
(configDbExtraSearchPath config)
|
(configDbExtraSearchPath config)
|
||||||
|
actualPgVersion
|
||||||
appState <- AppState.initWithPool pool config
|
appState <- AppState.initWithPool pool config
|
||||||
AppState.putPgVersion appState actualPgVersion
|
AppState.putPgVersion appState actualPgVersion
|
||||||
AppState.putDbStructure appState customDbStructure
|
AppState.putDbStructure appState customDbStructure
|
||||||
@@ -237,5 +239,5 @@ main = do
|
|||||||
describe "Feature.RollbackForcedSpec" Feature.RollbackSpec.forced
|
describe "Feature.RollbackForcedSpec" Feature.RollbackSpec.forced
|
||||||
|
|
||||||
where
|
where
|
||||||
loadDbStructure pool schemas extraSearchPath =
|
loadDbStructure pool schemas extraSearchPath actualPgVersion =
|
||||||
either (panic.show) id <$> P.use pool (HT.transaction HT.ReadCommitted HT.Read $ queryDbStructure (toList schemas) extraSearchPath True)
|
either (panic.show) id <$> P.use pool (HT.transaction HT.ReadCommitted HT.Read $ queryDbStructure (toList schemas) extraSearchPath actualPgVersion True)
|
||||||
|
|||||||
Vendored
+23
-12
@@ -670,26 +670,37 @@ INSERT INTO private.personnages (film_id, role_id, character) VALUES (12,1,'méc
|
|||||||
|
|
||||||
DO $do$BEGIN
|
DO $do$BEGIN
|
||||||
IF (SELECT current_setting('server_version_num')::INT >= 100000) THEN
|
IF (SELECT current_setting('server_version_num')::INT >= 100000) THEN
|
||||||
INSERT INTO test.partitioned_a(id, name) VALUES (1,'first');
|
INSERT INTO test.car_models(name, year) VALUES ('DeLorean',1981);
|
||||||
INSERT INTO test.partitioned_a(id, name) VALUES (2,'first');
|
INSERT INTO test.car_models(name, year) VALUES ('F310-B',1997);
|
||||||
INSERT INTO test.partitioned_a(id, name) VALUES (3,'second');
|
INSERT INTO test.car_models(name, year) VALUES ('Veneno',2013);
|
||||||
INSERT INTO test.partitioned_a(id, name) VALUES (4,'second');
|
INSERT INTO test.car_models(name, year) VALUES ('Murcielago',2001);
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF (SELECT current_setting('server_version_num')::INT >= 110000) THEN
|
IF (SELECT current_setting('server_version_num')::INT >= 110000) THEN
|
||||||
INSERT INTO test.reference_from_partitioned(id) VALUES (1),(2);
|
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.partitioned_a SET id_ref = 1 WHERE id = 1;
|
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;
|
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.partitioned_b(id, name) VALUES (1,'first_b');
|
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-01-14',7,'DeLorean',1981);
|
||||||
INSERT INTO test.partitioned_b(id, name, id_a, name_a) VALUES (2,'first_b', 2, 'first');
|
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-01-15',9,'DeLorean',1981);
|
||||||
INSERT INTO test.partitioned_b(id, name) VALUES (3,'second_b');
|
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-02-11',1,'Murcielago',2001);
|
||||||
INSERT INTO test.partitioned_b(id, name, id_a, name_a) VALUES (4,'second_b', 4, 'second');
|
INSERT INTO test.car_model_sales(date, quantity, car_model_name, car_model_year) VALUES ('2021-02-12',3,'Murcielago',2001);
|
||||||
|
|
||||||
INSERT INTO test.reference_to_partitioned(id) VALUES (1);
|
INSERT INTO test.car_racers(name) VALUES ('Alain Prost');
|
||||||
INSERT INTO test.reference_to_partitioned(id, id_a, name_a) VALUES (2, 2, 'first');
|
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 IF;
|
||||||
END$do$;
|
END$do$;
|
||||||
|
|
||||||
|
|||||||
Vendored
+14
-8
@@ -192,19 +192,25 @@ GRANT USAGE ON SCHEMA test TO postgrest_test_default_role;
|
|||||||
|
|
||||||
DO $do$BEGIN
|
DO $do$BEGIN
|
||||||
IF (SELECT current_setting('server_version_num')::INT >= 100000) THEN
|
IF (SELECT current_setting('server_version_num')::INT >= 100000) THEN
|
||||||
GRANT ALL ON TABLE test.partitioned_a TO postgrest_test_anonymous;
|
GRANT ALL ON TABLE test.car_models TO postgrest_test_anonymous;
|
||||||
GRANT ALL ON TABLE test.first_partition_a TO postgrest_test_anonymous;
|
GRANT ALL ON TABLE test.car_models_2021 TO postgrest_test_anonymous;
|
||||||
GRANT ALL ON TABLE test.second_partition_a TO postgrest_test_anonymous;
|
GRANT ALL ON TABLE test.car_models_default TO postgrest_test_anonymous;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF (SELECT current_setting('server_version_num')::INT >= 110000) THEN
|
IF (SELECT current_setting('server_version_num')::INT >= 110000) THEN
|
||||||
GRANT ALL ON TABLE test.reference_from_partitioned TO postgrest_test_anonymous;
|
GRANT ALL ON TABLE test.car_brands TO postgrest_test_anonymous;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF (SELECT current_setting('server_version_num')::INT >= 120000) THEN
|
IF (SELECT current_setting('server_version_num')::INT >= 120000) THEN
|
||||||
GRANT ALL ON TABLE test.partitioned_b TO postgrest_test_anonymous;
|
GRANT ALL ON TABLE test.car_model_sales TO postgrest_test_anonymous;
|
||||||
GRANT ALL ON TABLE test.first_partition_b TO postgrest_test_anonymous;
|
GRANT ALL ON TABLE test.car_model_sales_202101 TO postgrest_test_anonymous;
|
||||||
GRANT ALL ON TABLE test.second_partition_b TO postgrest_test_anonymous;
|
GRANT ALL ON TABLE test.car_model_sales_default TO postgrest_test_anonymous;
|
||||||
GRANT ALL ON TABLE test.reference_to_partitioned TO postgrest_test_anonymous;
|
GRANT ALL ON TABLE test.car_racers TO postgrest_test_anonymous;
|
||||||
|
GRANT ALL ON TABLE test.car_dealers TO postgrest_test_anonymous;
|
||||||
|
GRANT ALL ON TABLE test.car_dealers_springfield TO postgrest_test_anonymous;
|
||||||
|
GRANT ALL ON TABLE test.car_dealers_default TO postgrest_test_anonymous;
|
||||||
|
GRANT ALL ON TABLE test.car_models_car_dealers TO postgrest_test_anonymous;
|
||||||
|
GRANT ALL ON TABLE test.car_models_car_dealers_10to20 TO postgrest_test_anonymous;
|
||||||
|
GRANT ALL ON TABLE test.car_models_car_dealers_default TO postgrest_test_anonymous;
|
||||||
END IF;
|
END IF;
|
||||||
END$do$;
|
END$do$;
|
||||||
Vendored
+58
-31
@@ -2245,56 +2245,83 @@ create table private.rollen (
|
|||||||
do $do$begin
|
do $do$begin
|
||||||
-- partitioned tables using the PARTITION syntax are supported from pg v10
|
-- partitioned tables using the PARTITION syntax are supported from pg v10
|
||||||
if (select current_setting('server_version_num')::int >= 100000) then
|
if (select current_setting('server_version_num')::int >= 100000) then
|
||||||
create table test.partitioned_a(
|
create table test.car_models(
|
||||||
id int not null,
|
name varchar(64) not null,
|
||||||
name varchar(64) not null
|
year int not null
|
||||||
) partition by list (name);
|
) partition by list (year);
|
||||||
|
|
||||||
comment on table test.partitioned_a is
|
comment on table test.car_models is
|
||||||
$$A partitioned table
|
$$A partitioned table
|
||||||
|
|
||||||
A test for partitioned tables$$;
|
A test for partitioned tables$$;
|
||||||
|
|
||||||
create table test.first_partition_a partition of test.partitioned_a
|
create table test.car_models_2021 partition of test.car_models
|
||||||
for values in ('first');
|
for values in (2021);
|
||||||
|
create table test.car_models_default partition of test.car_models
|
||||||
create table test.second_partition_a partition of test.partitioned_a
|
for values in (1981,1997,2001,2013);
|
||||||
for values in ('second');
|
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
-- primary keys for partitioned tables are supported from pg v11
|
-- primary keys for partitioned tables are supported from pg v11
|
||||||
if (select current_setting('server_version_num')::int >= 110000) then
|
if (select current_setting('server_version_num')::int >= 110000) then
|
||||||
create table test.reference_from_partitioned (
|
create table test.car_brands (
|
||||||
id int primary key
|
name varchar(64) primary key
|
||||||
);
|
);
|
||||||
|
|
||||||
alter table test.partitioned_a add primary key (id, name);
|
alter table test.car_models add primary key (name, year);
|
||||||
alter table test.partitioned_a add column id_ref int references test.reference_from_partitioned(id);
|
alter table test.car_models add column car_brand_name varchar(64) references test.car_brands(name);
|
||||||
end if;
|
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.partitioned_b(
|
create table test.car_model_sales(
|
||||||
id int not null,
|
date varchar(64) not null,
|
||||||
name varchar(64) not null,
|
quantity int not null,
|
||||||
id_a int,
|
car_model_name varchar(64),
|
||||||
name_a varchar(64),
|
car_model_year int,
|
||||||
primary key (id, name),
|
primary key (date, car_model_name, car_model_year),
|
||||||
foreign key (id_a, name_a) references test.partitioned_a (id, name)
|
foreign key (car_model_name, car_model_year) references test.car_models (name, year)
|
||||||
) partition by list (name);
|
) partition by range (date);
|
||||||
|
|
||||||
create table test.first_partition_b partition of test.partitioned_b
|
create table test.car_model_sales_202101 partition of test.car_model_sales
|
||||||
for values in ('first_b');
|
for values from ('2021-01-01') to ('2021-01-31');
|
||||||
|
|
||||||
create table test.second_partition_b partition of test.partitioned_b
|
create table test.car_model_sales_default partition of test.car_model_sales
|
||||||
for values in ('second_b');
|
default;
|
||||||
|
|
||||||
create table test.reference_to_partitioned (
|
create table test.car_racers (
|
||||||
id int not null primary key,
|
name varchar(64) not null primary key,
|
||||||
id_a int,
|
car_model_name varchar(64),
|
||||||
name_a varchar(64),
|
car_model_year int,
|
||||||
foreign key (id_a, name_a) references test.partitioned_a (id, name)
|
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)
|
||||||
|
) 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 if;
|
||||||
end$do$;
|
end$do$;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user