Fix Parent Relation when having many views(#1044)
This commit is contained in:
committed by
Steve Chávez
parent
ff709a65e5
commit
349a5ae076
@@ -15,6 +15,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- #828, Fix computed column only working in public schema - @steve-chavez
|
- #828, Fix computed column only working in public schema - @steve-chavez
|
||||||
- #925, Fix RPC high memory usage by using parametrized query and avoiding json encoding - @steve-chavez
|
- #925, Fix RPC high memory usage by using parametrized query and avoiding json encoding - @steve-chavez
|
||||||
- #987, Fix embedding with self-reference foreign key - @steve-chavez
|
- #987, Fix embedding with self-reference foreign key - @steve-chavez
|
||||||
|
- #1044, Fix view parent embedding when having many views - @steve-chavez
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
{-# LANGUAGE TypeSynonymInstances #-}
|
{-# LANGUAGE TypeSynonymInstances #-}
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
module PostgREST.DbStructure (
|
module PostgREST.DbStructure (
|
||||||
getDbStructure
|
getDbStructure
|
||||||
, accessibleTables
|
, accessibleTables
|
||||||
@@ -18,9 +19,8 @@ import qualified Hasql.Query as H
|
|||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import qualified Data.HashMap.Strict as M
|
import qualified Data.HashMap.Strict as M
|
||||||
import Data.List (elemIndex)
|
import qualified Data.List as L
|
||||||
import qualified Data.List as List
|
import Data.Set as S (fromList)
|
||||||
import Data.Maybe (fromJust)
|
|
||||||
import Data.Text (split, strip,
|
import Data.Text (split, strip,
|
||||||
breakOn, dropAround,
|
breakOn, dropAround,
|
||||||
splitOn)
|
splitOn)
|
||||||
@@ -38,21 +38,21 @@ import Contravariant.Extras (contrazip2)
|
|||||||
|
|
||||||
getDbStructure :: Schema -> PgVersion -> H.Session DbStructure
|
getDbStructure :: Schema -> PgVersion -> H.Session DbStructure
|
||||||
getDbStructure schema pgVer = do
|
getDbStructure schema pgVer = do
|
||||||
tabs <- H.query () allTables
|
tabs <- H.query () allTables
|
||||||
cols <- H.query schema $ allColumns tabs
|
cols <- H.query schema $ allColumns tabs
|
||||||
syns <- H.query () $ allSynonyms cols
|
syns <- H.query () $ allSynonyms cols
|
||||||
rels <- H.query () $ allRelations tabs cols
|
childRels <- H.query () $ allChildRelations tabs cols
|
||||||
keys <- H.query () $ allPrimaryKeys tabs
|
keys <- H.query () $ allPrimaryKeys tabs
|
||||||
procs <- H.query schema allProcs
|
procs <- H.query schema allProcs
|
||||||
|
|
||||||
let rels' = (addManyToManyRelations . raiseRelations schema syns . addParentRelations . addSynonymousRelations syns) rels
|
let rels = addManyToManyRelations . addParentRelations $ addViewRelations syns childRels
|
||||||
cols' = addForeignKeys rels' cols
|
cols' = addForeignKeys rels cols
|
||||||
keys' = synonymousPrimaryKeys syns keys
|
keys' = synonymousPrimaryKeys syns keys
|
||||||
|
|
||||||
return DbStructure {
|
return DbStructure {
|
||||||
dbTables = tabs
|
dbTables = tabs
|
||||||
, dbColumns = cols'
|
, dbColumns = cols'
|
||||||
, dbRelations = rels'
|
, dbRelations = rels
|
||||||
, dbPrimaryKeys = keys'
|
, dbPrimaryKeys = keys'
|
||||||
, dbProcs = procs
|
, dbProcs = procs
|
||||||
, pgVersion = pgVer
|
, pgVersion = pgVer
|
||||||
@@ -100,7 +100,7 @@ decodePks tables =
|
|||||||
where
|
where
|
||||||
pkRow = (,,) <$> HD.value HD.text <*> HD.value HD.text <*> HD.value HD.text
|
pkRow = (,,) <$> HD.value HD.text <*> HD.value HD.text <*> HD.value HD.text
|
||||||
|
|
||||||
decodeSynonyms :: [Column] -> HD.Result [(Column,Column)]
|
decodeSynonyms :: [Column] -> HD.Result [Synonym]
|
||||||
decodeSynonyms cols =
|
decodeSynonyms cols =
|
||||||
mapMaybe (synonymFromRow cols) <$> HD.rowsList synRow
|
mapMaybe (synonymFromRow cols) <$> HD.rowsList synRow
|
||||||
where
|
where
|
||||||
@@ -228,18 +228,6 @@ accessibleTables =
|
|||||||
)
|
)
|
||||||
order by relname |]
|
order by relname |]
|
||||||
|
|
||||||
synonymousColumns :: [(Column,Column)] -> [Column] -> [[Column]]
|
|
||||||
synonymousColumns allSyns cols = synCols'
|
|
||||||
where
|
|
||||||
syns = case headMay cols of
|
|
||||||
Just firstCol -> sort $ filter ((== colTable firstCol) . colTable . fst) allSyns
|
|
||||||
Nothing -> []
|
|
||||||
synCols = transpose $ map (\c -> map snd $ filter ((== c) . fst) syns) cols
|
|
||||||
synCols' = (filter sameTable . filter matchLength) synCols
|
|
||||||
matchLength cs = length cols == length cs
|
|
||||||
sameTable (c:cs) = all (\cc -> colTable c == colTable cc) (c:cs)
|
|
||||||
sameTable [] = False
|
|
||||||
|
|
||||||
addForeignKeys :: [Relation] -> [Column] -> [Column]
|
addForeignKeys :: [Relation] -> [Column] -> [Column]
|
||||||
addForeignKeys rels = map addFk
|
addForeignKeys rels = map addFk
|
||||||
where
|
where
|
||||||
@@ -248,17 +236,71 @@ addForeignKeys rels = map addFk
|
|||||||
lookupFn :: Column -> Relation -> Bool
|
lookupFn :: Column -> Relation -> Bool
|
||||||
lookupFn c Relation{relColumns=cs, relType=rty} = c `elem` cs && rty==Child
|
lookupFn c Relation{relColumns=cs, relType=rty} = c `elem` cs && rty==Child
|
||||||
relToFk col Relation{relColumns=cols, relFColumns=colsF} = do
|
relToFk col Relation{relColumns=cols, relFColumns=colsF} = do
|
||||||
pos <- elemIndex col cols
|
pos <- L.elemIndex col cols
|
||||||
colF <- atMay colsF pos
|
colF <- atMay colsF pos
|
||||||
return $ ForeignKey colF
|
return $ ForeignKey colF
|
||||||
|
|
||||||
addSynonymousRelations :: [(Column,Column)] -> [Relation] -> [Relation]
|
{-
|
||||||
addSynonymousRelations _ [] = []
|
Adds Views Child Relations based on Synonyms found, the logic is as follows:
|
||||||
addSynonymousRelations syns (rel:rels) = rel : synRelsP ++ synRelsF ++ addSynonymousRelations syns rels
|
|
||||||
where
|
Having a Relation{relTable=t1, relColumns=[c1], relFTable=t2, relFColumns=[c2], relType=Child} represented by:
|
||||||
synRelsP = synRels (relColumns rel) (\t cs -> rel{relTable=t,relColumns=cs})
|
|
||||||
synRelsF = synRels (relFColumns rel) (\t cs -> rel{relFTable=t,relFColumns=cs})
|
t1.c1------t2.c2
|
||||||
synRels cols mapFn = map (\cs -> mapFn (colTable $ unsafeHead cs) cs) $ synonymousColumns syns cols
|
|
||||||
|
When only having a t1_view.c1 synonym, we need to add a View to Table Relation
|
||||||
|
|
||||||
|
t1.c1----t2.c2 t1.c1----------t2.c2
|
||||||
|
-> --------/
|
||||||
|
/
|
||||||
|
t1_view.c1 t1_view.c1
|
||||||
|
|
||||||
|
|
||||||
|
When only having a t2_view.c2 synonym, we need to add a Table to View Relation
|
||||||
|
|
||||||
|
t1.c1----t2.c2 t1.c1----------t2.c2
|
||||||
|
-> \--------
|
||||||
|
\
|
||||||
|
t2_view.c2 t2_view.c1
|
||||||
|
|
||||||
|
When having t1_view.c1 and a t2_view.c2 synonyms, we need to add a View to View Relation in addition to the prior
|
||||||
|
|
||||||
|
t1.c1----t2.c2 t1.c1----------t2.c2
|
||||||
|
-> \--------/
|
||||||
|
/ \
|
||||||
|
t1_view.c1 t2_view.c2 t1_view.c1-------t2_view.c1
|
||||||
|
|
||||||
|
The logic for composite pks is similar just need to make sure all the Relation columns have synonyms.
|
||||||
|
-}
|
||||||
|
addViewRelations :: [Synonym] -> [Relation] -> [Relation]
|
||||||
|
addViewRelations _ [] = []
|
||||||
|
addViewRelations allSyns (rel:rels) =
|
||||||
|
case rel of
|
||||||
|
Relation{relType=Child, relTable, relColumns, relFTable, relFColumns} ->
|
||||||
|
|
||||||
|
let colSynsGroupedByView :: [Column] -> [[Synonym]]
|
||||||
|
colSynsGroupedByView relCols = L.groupBy (\(_, viewCol1) (_, viewCol2) -> colTable viewCol1 == colTable viewCol2) $
|
||||||
|
filter (\(c, _) -> c `elem` relCols) allSyns
|
||||||
|
colsSyns = colSynsGroupedByView relColumns
|
||||||
|
fColsSyns = colSynsGroupedByView relFColumns
|
||||||
|
getView :: [Synonym] -> Table
|
||||||
|
getView = colTable . snd . unsafeHead
|
||||||
|
syns `allSynsOf` cols = S.fromList (fst <$> syns) == S.fromList cols in
|
||||||
|
|
||||||
|
-- View Table Relations
|
||||||
|
[Relation (getView syns) (snd <$> syns) relFTable relFColumns Child Nothing Nothing Nothing
|
||||||
|
| syns <- colsSyns, syns `allSynsOf` relColumns] ++
|
||||||
|
|
||||||
|
-- Table View Relations
|
||||||
|
[Relation relTable relColumns (getView fSyns) (snd <$> fSyns) Child Nothing Nothing Nothing
|
||||||
|
| fSyns <- fColsSyns, fSyns `allSynsOf` relFColumns] ++
|
||||||
|
|
||||||
|
-- View View Relations
|
||||||
|
[Relation (getView syns) (snd <$> syns) (getView fSyns) (snd <$> fSyns) Child Nothing Nothing Nothing
|
||||||
|
| syns <- colsSyns, fSyns <- fColsSyns, syns `allSynsOf` relColumns, fSyns `allSynsOf` relFColumns] ++
|
||||||
|
|
||||||
|
rel : addViewRelations allSyns rels
|
||||||
|
|
||||||
|
_ -> rel : addViewRelations allSyns rels
|
||||||
|
|
||||||
addParentRelations :: [Relation] -> [Relation]
|
addParentRelations :: [Relation] -> [Relation]
|
||||||
addParentRelations [] = []
|
addParentRelations [] = []
|
||||||
@@ -269,7 +311,7 @@ addManyToManyRelations rels = rels ++ addMirrorRelation (mapMaybe link2Relation
|
|||||||
where
|
where
|
||||||
links = join $ map (combinations 2) $ filter (not . null) $ groupWith groupFn $ filter ( (==Child). relType) rels
|
links = join $ map (combinations 2) $ filter (not . null) $ groupWith groupFn $ filter ( (==Child). relType) rels
|
||||||
groupFn :: Relation -> Text
|
groupFn :: Relation -> Text
|
||||||
groupFn Relation{relTable=Table{tableSchema=s, tableName=t}} = s<>"_"<>t
|
groupFn Relation{relTable=Table{tableSchema=s, tableName=t}} = s <> "_" <> t
|
||||||
-- Reference : https://wiki.haskell.org/99_questions/Solutions/26
|
-- Reference : https://wiki.haskell.org/99_questions/Solutions/26
|
||||||
combinations :: Int -> [a] -> [[a]]
|
combinations :: Int -> [a] -> [[a]]
|
||||||
combinations 0 _ = [ [] ]
|
combinations 0 _ = [ [] ]
|
||||||
@@ -285,20 +327,7 @@ addManyToManyRelations rels = rels ++ addMirrorRelation (mapMaybe link2Relation
|
|||||||
| otherwise = Nothing
|
| otherwise = Nothing
|
||||||
link2Relation _ = Nothing
|
link2Relation _ = Nothing
|
||||||
|
|
||||||
raiseRelations :: Schema -> [(Column,Column)] -> [Relation] -> [Relation]
|
synonymousPrimaryKeys :: [Synonym] -> [PrimaryKey] -> [PrimaryKey]
|
||||||
raiseRelations schema syns = map raiseRel
|
|
||||||
where
|
|
||||||
raiseRel rel
|
|
||||||
| tableSchema table == schema = rel
|
|
||||||
| isJust newCols = rel{relFTable=fromJust newTable,relFColumns=fromJust newCols}
|
|
||||||
| otherwise = rel
|
|
||||||
where
|
|
||||||
cols = relFColumns rel
|
|
||||||
table = relFTable rel
|
|
||||||
newCols = listToMaybe $ filter ((== schema) . tableSchema . colTable . unsafeHead) (synonymousColumns syns cols)
|
|
||||||
newTable = (colTable . unsafeHead) <$> newCols
|
|
||||||
|
|
||||||
synonymousPrimaryKeys :: [(Column,Column)] -> [PrimaryKey] -> [PrimaryKey]
|
|
||||||
synonymousPrimaryKeys _ [] = []
|
synonymousPrimaryKeys _ [] = []
|
||||||
synonymousPrimaryKeys syns (key:keys) = key : newKeys ++ synonymousPrimaryKeys syns keys
|
synonymousPrimaryKeys syns (key:keys) = key : newKeys ++ synonymousPrimaryKeys syns keys
|
||||||
where
|
where
|
||||||
@@ -515,8 +544,8 @@ columnFromRow tabs (s, t, n, desc, pos, nul, typ, u, l, p, d, e) = buildColumn <
|
|||||||
parseEnum :: Maybe Text -> [Text]
|
parseEnum :: Maybe Text -> [Text]
|
||||||
parseEnum str = fromMaybe [] $ split (==',') <$> str
|
parseEnum str = fromMaybe [] $ split (==',') <$> str
|
||||||
|
|
||||||
allRelations :: [Table] -> [Column] -> H.Query () [Relation]
|
allChildRelations :: [Table] -> [Column] -> H.Query () [Relation]
|
||||||
allRelations tabs cols =
|
allChildRelations tabs cols =
|
||||||
H.statement sql HE.unit (decodeRelations tabs cols) True
|
H.statement sql HE.unit (decodeRelations tabs cols) True
|
||||||
where
|
where
|
||||||
sql = [q|
|
sql = [q|
|
||||||
@@ -666,7 +695,7 @@ pkFromRow :: [Table] -> (Schema, Text, Text) -> Maybe PrimaryKey
|
|||||||
pkFromRow tabs (s, t, n) = PrimaryKey <$> table <*> pure n
|
pkFromRow tabs (s, t, n) = PrimaryKey <$> table <*> pure n
|
||||||
where table = find (\tbl -> tableSchema tbl == s && tableName tbl == t) tabs
|
where table = find (\tbl -> tableSchema tbl == s && tableName tbl == t) tabs
|
||||||
|
|
||||||
allSynonyms :: [Column] -> H.Query () [(Column,Column)]
|
allSynonyms :: [Column] -> H.Query () [Synonym]
|
||||||
allSynonyms cols =
|
allSynonyms cols =
|
||||||
H.statement sql HE.unit (decodeSynonyms cols) True
|
H.statement sql HE.unit (decodeSynonyms cols) True
|
||||||
where
|
where
|
||||||
@@ -741,7 +770,7 @@ allSynonyms cols =
|
|||||||
order by c.view_schema, c.view_name, c.table_name, c.view_column_name
|
order by c.view_schema, c.view_name, c.table_name, c.view_column_name
|
||||||
|]
|
|]
|
||||||
|
|
||||||
synonymFromRow :: [Column] -> (Text,Text,Text,Text,Text,Text) -> Maybe (Column,Column)
|
synonymFromRow :: [Column] -> (Text,Text,Text,Text,Text,Text) -> Maybe Synonym
|
||||||
synonymFromRow allCols (s1,t1,c1,s2,t2,c2) = (,) <$> col1 <*> col2
|
synonymFromRow allCols (s1,t1,c1,s2,t2,c2) = (,) <$> col1 <*> col2
|
||||||
where
|
where
|
||||||
col1 = findCol s1 t1 c1
|
col1 = findCol s1 t1 c1
|
||||||
@@ -758,11 +787,11 @@ fillSessionWithSettings :: [(Text, Text)] -> H.Session ()
|
|||||||
fillSessionWithSettings settings =
|
fillSessionWithSettings settings =
|
||||||
-- Send all of the config settings to the set_config function, using pgsql's `unnest` to transform arrays of values
|
-- Send all of the config settings to the set_config function, using pgsql's `unnest` to transform arrays of values
|
||||||
H.query settings $ H.statement "SELECT set_config(k, v, false) FROM unnest($1, $2) AS f1(k, v)" encoder HD.unit False
|
H.query settings $ H.statement "SELECT set_config(k, v, false) FROM unnest($1, $2) AS f1(k, v)" encoder HD.unit False
|
||||||
|
|
||||||
where
|
where
|
||||||
-- Take a list of (key, value) pairs and encode each as an array to later bind to the query
|
-- Take a list of (key, value) pairs and encode each as an array to later bind to the query
|
||||||
-- see Insert Many section at https://hackage.haskell.org/package/hasql-1.1.1/docs/Hasql-Encoders.html
|
-- see Insert Many section at https://hackage.haskell.org/package/hasql-1.1.1/docs/Hasql-Encoders.html
|
||||||
encoder = contramap List.unzip $ contrazip2 (vector HE.text) (vector HE.text)
|
encoder = contramap L.unzip $ contrazip2 (vector HE.text) (vector HE.text)
|
||||||
where
|
where
|
||||||
vector value =
|
vector value =
|
||||||
HE.value $ HE.array $ HE.arrayDimension foldl' $ HE.arrayValue value
|
HE.value $ HE.array $ HE.arrayDimension foldl' $ HE.arrayValue value
|
||||||
|
|||||||
@@ -107,7 +107,9 @@ data Column =
|
|||||||
, colFK :: Maybe ForeignKey
|
, colFK :: Maybe ForeignKey
|
||||||
} deriving (Show, Ord)
|
} deriving (Show, Ord)
|
||||||
|
|
||||||
type Synonym = (Column,Column)
|
-- | A view column that refers to a table column
|
||||||
|
type Synonym = (Column, ViewColumn)
|
||||||
|
type ViewColumn = Column
|
||||||
|
|
||||||
data PrimaryKey = PrimaryKey {
|
data PrimaryKey = PrimaryKey {
|
||||||
pkTable :: Table
|
pkTable :: Table
|
||||||
|
|||||||
@@ -398,6 +398,19 @@ spec = do
|
|||||||
[json|[{"t_id":1,"name":"Windows 7","clients":{"id":1,"name":"Microsoft"},"tasks":[{"id":1,"name":"Design w7"},{"id":2,"name":"Code w7"}]}]|]
|
[json|[{"t_id":1,"name":"Windows 7","clients":{"id":1,"name":"Microsoft"},"tasks":[{"id":1,"name":"Design w7"},{"id":2,"name":"Code w7"}]}]|]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
|
it "detects parent relations when having many views of a private table" $ do
|
||||||
|
get "/books?select=title,author(name)&id=eq.5" `shouldRespondWith`
|
||||||
|
[json|[ { "title": "Farenheit 451", "author": { "name": "Ray Bradbury" } } ]|]
|
||||||
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
get "/forties_books?select=title,author(name)&limit=1" `shouldRespondWith`
|
||||||
|
[json|[ { "title": "1984", "author": { "name": "George Orwell" } } ]|]
|
||||||
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
get "/fifties_books?select=title,author(name)&limit=1" `shouldRespondWith`
|
||||||
|
[json|[ { "title": "The Catcher in the Rye", "author": { "name": "J.D. Salinger" } } ]|]
|
||||||
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
get "/sixties_books?select=title,author(name)&limit=1" `shouldRespondWith`
|
||||||
|
[json|[ { "title": "To Kill a Mockingbird", "author": { "name": "Harper Lee" } } ]|]
|
||||||
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
it "requesting children with composite key" $
|
it "requesting children with composite key" $
|
||||||
get "/users_tasks?user_id=eq.2&task_id=eq.6&select=*, comments{content}" `shouldRespondWith`
|
get "/users_tasks?user_id=eq.2&task_id=eq.6&select=*, comments{content}" `shouldRespondWith`
|
||||||
|
|||||||
Vendored
+24
@@ -359,3 +359,27 @@ INSERT INTO organizations VALUES (1, 'Referee Org', null, null);
|
|||||||
INSERT INTO organizations VALUES (2, 'Auditor Org', null, null);
|
INSERT INTO organizations VALUES (2, 'Auditor Org', null, null);
|
||||||
INSERT INTO organizations VALUES (3, 'Acme', 1, 2);
|
INSERT INTO organizations VALUES (3, 'Acme', 1, 2);
|
||||||
INSERT INTO organizations VALUES (4, 'Umbrella', 1, 2);
|
INSERT INTO organizations VALUES (4, 'Umbrella', 1, 2);
|
||||||
|
|
||||||
|
SET search_path = private, pg_catalog;
|
||||||
|
|
||||||
|
TRUNCATE TABLE authors CASCADE;
|
||||||
|
INSERT INTO authors VALUES (1, 'George Orwell');
|
||||||
|
INSERT INTO authors VALUES (2, 'Anne Frank');
|
||||||
|
INSERT INTO authors VALUES (3, 'Antoine de Saint-Exupéry');
|
||||||
|
INSERT INTO authors VALUES (4, 'J.D. Salinger');
|
||||||
|
INSERT INTO authors VALUES (5, 'Ray Bradbury');
|
||||||
|
INSERT INTO authors VALUES (6, 'William Golding');
|
||||||
|
INSERT INTO authors VALUES (7, 'Harper Lee');
|
||||||
|
INSERT INTO authors VALUES (8, 'Kurt Vonnegut');
|
||||||
|
INSERT INTO authors VALUES (9, 'Ken Kesey');
|
||||||
|
|
||||||
|
TRUNCATE TABLE books CASCADE;
|
||||||
|
INSERT INTO books VALUES (1, '1984', 1949, 1);
|
||||||
|
INSERT INTO books VALUES (2, 'The Diary of a Young Girl', 1947, 2);
|
||||||
|
INSERT INTO books VALUES (3, 'The Little Prince', 1947, 3);
|
||||||
|
INSERT INTO books VALUES (4, 'The Catcher in the Rye', 1951, 4);
|
||||||
|
INSERT INTO books VALUES (5, 'Farenheit 451', 1953, 5);
|
||||||
|
INSERT INTO books VALUES (6, 'Lord of the Flies', 1954, 6);
|
||||||
|
INSERT INTO books VALUES (7, 'To Kill a Mockingbird', 1960, 7);
|
||||||
|
INSERT INTO books VALUES (8, 'Slaughterhouse-Five', 1969, 8);
|
||||||
|
INSERT INTO books VALUES (9, 'One Flew Over the Cuckoo''s Nest', 1962, 9);
|
||||||
|
|||||||
Vendored
+5
@@ -68,6 +68,11 @@ GRANT ALL ON TABLE
|
|||||||
, only_pk
|
, only_pk
|
||||||
, family_tree
|
, family_tree
|
||||||
, organizations
|
, organizations
|
||||||
|
, authors
|
||||||
|
, books
|
||||||
|
, forties_books
|
||||||
|
, fifties_books
|
||||||
|
, sixties_books
|
||||||
TO postgrest_test_anonymous;
|
TO postgrest_test_anonymous;
|
||||||
|
|
||||||
GRANT INSERT ON TABLE insertonly TO postgrest_test_anonymous;
|
GRANT INSERT ON TABLE insertonly TO postgrest_test_anonymous;
|
||||||
|
|||||||
Vendored
+19
@@ -1390,3 +1390,22 @@ create table test.organizations (
|
|||||||
);
|
);
|
||||||
alter table only test.organizations add constraint pptr1 foreign key (referee) references test.organizations(id);
|
alter table only test.organizations add constraint pptr1 foreign key (referee) references test.organizations(id);
|
||||||
alter table only test.organizations add constraint pptr2 foreign key (auditor) references test.organizations(id);
|
alter table only test.organizations add constraint pptr2 foreign key (auditor) references test.organizations(id);
|
||||||
|
|
||||||
|
create table private.authors(
|
||||||
|
id integer primary key,
|
||||||
|
name text
|
||||||
|
);
|
||||||
|
|
||||||
|
create table private.books(
|
||||||
|
id integer primary key,
|
||||||
|
title text,
|
||||||
|
publication_year smallint,
|
||||||
|
author_id integer references private.authors(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
create view test.authors as select id, name from private.authors;
|
||||||
|
|
||||||
|
create view test.books as select id, title, publication_year, author_id from private.books;
|
||||||
|
create view test.forties_books as select id, title, publication_year, author_id from private.books where publication_year >= 1940 and publication_year < 1950;
|
||||||
|
create view test.fifties_books as select id, title, publication_year, author_id from private.books where publication_year >= 1950 and publication_year < 1960;
|
||||||
|
create view test.sixties_books as select id, title, publication_year, author_id from private.books where publication_year >= 1960 and publication_year < 1970;
|
||||||
|
|||||||
Reference in New Issue
Block a user