Allow specifying the constraint name to disambiguate an embedding (#1430)
Makes previous duck typing regex unnecessary since the FK can be renamed to a singular name or to any other format. * Remove embedding with duck typed column names * Allow embedding by foreign key name * Add junction disambiguation tests
This commit is contained in:
@@ -13,170 +13,306 @@ import SpecHelper
|
||||
spec :: SpecWith Application
|
||||
spec =
|
||||
describe "resource embedding disambiguation" $ do
|
||||
|
||||
it "gives a 300 Multiple Choices error when the request is ambiguous" $ do
|
||||
get "/message?select=id,body,sender(name,sent)" `shouldRespondWith`
|
||||
[json|
|
||||
{
|
||||
"details": [
|
||||
context "ambiguous requests that give 300 Multiple Choices" $ do
|
||||
it "errs when there's a table and view that point to the same fk" $
|
||||
get "/message?select=id,body,sender(name,sent)" `shouldRespondWith`
|
||||
[json|
|
||||
{
|
||||
"cardinality": "many-to-one",
|
||||
"source": "test.message[sender]",
|
||||
"target": "test.person[id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "many-to-one",
|
||||
"source": "test.message[sender]",
|
||||
"target": "test.person_detail[id]"
|
||||
"details": [
|
||||
{
|
||||
"cardinality": "m2o",
|
||||
"relationship": "message_sender_fkey[sender][id]",
|
||||
"origin": "test.message",
|
||||
"target": "test.person"
|
||||
},
|
||||
{
|
||||
"cardinality": "m2o",
|
||||
"relationship": "message_sender_fkey[sender][id]",
|
||||
"origin": "test.message",
|
||||
"target": "test.person_detail"
|
||||
}
|
||||
],
|
||||
"hint": "By following the 'details' key, disambiguate the request by changing the url to /origin?select=relationship(*) or /origin?select=target!relationship(*)",
|
||||
"message": "More than one relationship was found for message and sender"
|
||||
}
|
||||
],
|
||||
"hint": "Disambiguate by choosing a relationship from the `details` key",
|
||||
"message": "More than one relationship was found for message and sender"
|
||||
|]
|
||||
{ matchStatus = 300
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|]
|
||||
{ matchStatus = 300
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
get "/users?select=*,id(*)" `shouldRespondWith`
|
||||
[json|
|
||||
{
|
||||
"details": [
|
||||
it "errs when there are o2m and m2m cardinalities to the target table" $
|
||||
get "/sites?select=*,big_projects(*)" `shouldRespondWith`
|
||||
[json|
|
||||
{
|
||||
"details": [
|
||||
{
|
||||
"cardinality": "m2o",
|
||||
"relationship": "main_project[main_project_id][big_project_id]",
|
||||
"origin": "test.sites",
|
||||
"target": "test.big_projects"
|
||||
},
|
||||
{
|
||||
"cardinality": "m2m",
|
||||
"relationship": "test.jobs[jobs_site_id_fkey][jobs_big_project_id_fkey]",
|
||||
"origin": "test.sites",
|
||||
"target": "test.big_projects"
|
||||
},
|
||||
{
|
||||
"cardinality": "m2m",
|
||||
"relationship": "test.main_jobs[jobs_site_id_fkey][jobs_big_project_id_fkey]",
|
||||
"origin": "test.sites",
|
||||
"target": "test.big_projects"
|
||||
}
|
||||
],
|
||||
"hint": "By following the 'details' key, disambiguate the request by changing the url to /origin?select=relationship(*) or /origin?select=target!relationship(*)",
|
||||
"message": "More than one relationship was found for sites and big_projects"
|
||||
}
|
||||
|]
|
||||
{ matchStatus = 300
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "errs on an ambiguous embed that has a circular reference" $
|
||||
get "/agents?select=*,departments(*)" `shouldRespondWith`
|
||||
[json|
|
||||
{
|
||||
"details": [
|
||||
{
|
||||
"cardinality": "m2o",
|
||||
"relationship": "agents_department_id_fkey[department_id][id]",
|
||||
"origin": "test.agents",
|
||||
"target": "test.departments"
|
||||
},
|
||||
{
|
||||
"cardinality": "o2m",
|
||||
"relationship": "departments_head_id_fkey[id][head_id]",
|
||||
"origin": "test.agents",
|
||||
"target": "test.departments"
|
||||
}
|
||||
],
|
||||
"hint": "By following the 'details' key, disambiguate the request by changing the url to /origin?select=relationship(*) or /origin?select=target!relationship(*)",
|
||||
"message": "More than one relationship was found for agents and departments"
|
||||
}
|
||||
|]
|
||||
{ matchStatus = 300
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
it "errs when there are more than two fks on a junction table(currently impossible to disambiguate, only choice is to split the table)" $
|
||||
-- We have 4 possibilities for doing the junction JOIN here.
|
||||
-- This could be solved by specifying two additional fks, like whatev_projects!fk1!fk2(*)
|
||||
-- If the need arises this capability can be added later without causing a breaking change
|
||||
get "/whatev_sites?select=*,whatev_projects(*)" `shouldRespondWith`
|
||||
[json|
|
||||
{
|
||||
"details": [
|
||||
{
|
||||
"cardinality": "m2m",
|
||||
"relationship": "test.whatev_jobs[whatev_jobs_site_id_1_fkey][whatev_jobs_project_id_1_fkey]",
|
||||
"origin": "test.whatev_sites",
|
||||
"target": "test.whatev_projects"
|
||||
},
|
||||
{
|
||||
"cardinality": "m2m",
|
||||
"relationship": "test.whatev_jobs[whatev_jobs_site_id_1_fkey][whatev_jobs_project_id_2_fkey]",
|
||||
"origin": "test.whatev_sites",
|
||||
"target": "test.whatev_projects"
|
||||
},
|
||||
{
|
||||
"cardinality": "m2m",
|
||||
"relationship": "test.whatev_jobs[whatev_jobs_site_id_2_fkey][whatev_jobs_project_id_1_fkey]",
|
||||
"origin": "test.whatev_sites",
|
||||
"target": "test.whatev_projects"
|
||||
},
|
||||
{
|
||||
"cardinality": "m2m",
|
||||
"relationship": "test.whatev_jobs[whatev_jobs_site_id_2_fkey][whatev_jobs_project_id_2_fkey]",
|
||||
"origin": "test.whatev_sites",
|
||||
"target": "test.whatev_projects"
|
||||
}
|
||||
],
|
||||
"hint": "By following the 'details' key, disambiguate the request by changing the url to /origin?select=relationship(*) or /origin?select=target!relationship(*)",
|
||||
"message": "More than one relationship was found for whatev_sites and whatev_projects"
|
||||
}
|
||||
|]
|
||||
{ matchStatus = 300
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
context "disambiguating requests with embed hints" $ do
|
||||
|
||||
context "using FK to specify the relationship" $ do
|
||||
it "can embed by FK name" $
|
||||
get "/projects?id=in.(1,3)&select=id,name,client(id,name)" `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client":{"id":1,"name":"Microsoft"}},{"id":3,"name":"IOS","client":{"id":2,"name":"Apple"}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can embed by FK name and select the FK column at the same time" $
|
||||
get "/projects?id=in.(1,3)&select=id,name,client_id,client(id,name)" `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client_id":1,"client":{"id":1,"name":"Microsoft"}},{"id":3,"name":"IOS","client_id":2,"client":{"id":2,"name":"Apple"}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can embed parent with view!fk and grandparent by using fk" $
|
||||
get "/tasks?id=eq.1&select=id,name,projects_view!project(id,name,client(id,name))" `shouldRespondWith`
|
||||
[str|[{"id":1,"name":"Design w7","projects_view":{"id":1,"name":"Windows 7","client":{"id":1,"name":"Microsoft"}}}]|]
|
||||
|
||||
it "can embed by using a composite FK name" $
|
||||
get "/unit_workdays?select=unit_id,day,fst_shift(car_id,schedule(name)),snd_shift(camera_id,schedule(name))" `shouldRespondWith`
|
||||
[json| [
|
||||
{
|
||||
"cardinality": "one-to-many",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.articleStars[userId]"
|
||||
},
|
||||
{
|
||||
"cardinality": "one-to-many",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.limited_article_stars[user_id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "one-to-many",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.comments[commenter_id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "one-to-many",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.users_projects[user_id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "one-to-many",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.users_tasks[user_id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "many-to-many",
|
||||
"junction": "private.article_stars[user_id][article_id]",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.articles[id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "many-to-many",
|
||||
"junction": "test.articleStars[userId][articleId]",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.articles[id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "many-to-many",
|
||||
"junction": "test.limited_article_stars[user_id][article_id]",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.articles[id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "many-to-many",
|
||||
"junction": "test.users_projects[user_id][project_id]",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.projects[id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "many-to-many",
|
||||
"junction": "test.users_projects[user_id][project_id]",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.materialized_projects[id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "many-to-many",
|
||||
"junction": "test.users_projects[user_id][project_id]",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.projects_view[id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "many-to-many",
|
||||
"junction": "test.users_projects[user_id][project_id]",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.projects_view_alt[t_id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "many-to-many",
|
||||
"junction": "test.users_tasks[user_id][task_id]",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.tasks[id]"
|
||||
},
|
||||
{
|
||||
"cardinality": "many-to-many",
|
||||
"junction": "test.users_tasks[user_id][task_id]",
|
||||
"source": "test.users[id]",
|
||||
"target": "test.filtered_tasks[myId]"
|
||||
"day": "2019-12-02",
|
||||
"fst_shift": {
|
||||
"car_id": "CAR-349",
|
||||
"schedule": {
|
||||
"name": "morning"
|
||||
}
|
||||
},
|
||||
"snd_shift": {
|
||||
"camera_id": "CAM-123",
|
||||
"schedule": {
|
||||
"name": "night"
|
||||
}
|
||||
},
|
||||
"unit_id": 1
|
||||
}
|
||||
],
|
||||
"hint": "Disambiguate by choosing a relationship from the `details` key",
|
||||
"message": "More than one relationship was found for users and id"
|
||||
}
|
||||
|]
|
||||
{ matchStatus = 300
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "works when requesting children 2 levels" $
|
||||
get "/clients?id=eq.1&select=id,projects:projects!client_id(id,tasks(id))" `shouldRespondWith`
|
||||
[json|[{"id":1,"projects":[{"id":1,"tasks":[{"id":1},{"id":2}]},{"id":2,"tasks":[{"id":3},{"id":4}]}]}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "embeds by using two fks pointing to the same table" $
|
||||
get "/orders?id=eq.1&select=id, name, billing(address), shipping(address)" `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"order 1","billing":{"address": "address 1"},"shipping":{"address": "address 2"}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "works with parent relation" $
|
||||
get "/message?select=id,body,sender:person!sender(name),recipient:person!recipient(name)&id=lt.4" `shouldRespondWith`
|
||||
[json|
|
||||
[{"id":1,"body":"Hello Jane","sender":{"name":"John"},"recipient":{"name":"Jane"}},
|
||||
{"id":2,"body":"Hi John","sender":{"name":"Jane"},"recipient":{"name":"John"}},
|
||||
{"id":3,"body":"How are you doing?","sender":{"name":"John"},"recipient":{"name":"Jane"}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "fails if the fk is not known" $
|
||||
get "/message?select=id,sender:person!space(name)&id=lt.4" `shouldRespondWith`
|
||||
[json|{"message":"Could not find foreign keys between these entities. No relationship found between message and person"}|]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "fails with an unknown relation" $
|
||||
get "/message?select=id,sender:person!space(name)&id=lt.4" `shouldRespondWith`
|
||||
[json|{"message":"Could not find foreign keys between these entities, No relation found between message and person"}|]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson] }
|
||||
it "can request a parent with fk" $
|
||||
get "/comments?select=content,user(name)" `shouldRespondWith`
|
||||
[json|[ { "content": "Needs to be delivered ASAP", "user": { "name": "Angela Martin" } } ]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "works with a parent view relation" $
|
||||
get "/message?select=id,body,sender:person_detail!sender(name,sent),recipient:person_detail!recipient(name,received)&id=lt.4" `shouldRespondWith`
|
||||
[json|
|
||||
[{"id":1,"body":"Hello Jane","sender":{"name":"John","sent":2},"recipient":{"name":"Jane","received":2}},
|
||||
{"id":2,"body":"Hi John","sender":{"name":"Jane","sent":1},"recipient":{"name":"John","received":1}},
|
||||
{"id":3,"body":"How are you doing?","sender":{"name":"John","sent":2},"recipient":{"name":"Jane","received":2}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "can request two parents with fks" $
|
||||
get "/articleStars?select=createdAt,article(owner),user(name)&limit=1" `shouldRespondWith`
|
||||
[json|[{"createdAt":"2015-12-08T04:22:57.472738","article":{"owner": "postgrest_test_authenticator"},"user":{"name": "Angela Martin"}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "works with many<->many relation" $
|
||||
get "/tasks?select=id,users:users!users_tasks(id)" `shouldRespondWith`
|
||||
[json|[{"id":1,"users":[{"id":1},{"id":3}]},{"id":2,"users":[{"id":1}]},{"id":3,"users":[{"id":1}]},{"id":4,"users":[{"id":1}]},{"id":5,"users":[{"id":2},{"id":3}]},{"id":6,"users":[{"id":2}]},{"id":7,"users":[{"id":2}]},{"id":8,"users":[]}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "can specify a view!fk" $
|
||||
get "/message?select=id,body,sender:person_detail!message_sender_fkey(name,sent),recipient:person_detail!message_recipient_fkey(name,received)&id=lt.4" `shouldRespondWith`
|
||||
[json|
|
||||
[{"id":1,"body":"Hello Jane","sender":{"name":"John","sent":2},"recipient":{"name":"Jane","received":2}},
|
||||
{"id":2,"body":"Hi John","sender":{"name":"Jane","sent":1},"recipient":{"name":"John","received":1}},
|
||||
{"id":3,"body":"How are you doing?","sender":{"name":"John","sent":2},"recipient":{"name":"Jane","received":2}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
context "using FK col to specify the relationship" $ do
|
||||
it "can embed by FK column name" $
|
||||
get "/projects?id=in.(1,3)&select=id,name,client_id(id,name)" `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client_id":{"id":1,"name":"Microsoft"}},{"id":3,"name":"IOS","client_id":{"id":2,"name":"Apple"}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "can specify a table!fk hint and request children 2 levels" $
|
||||
get "/clients?id=eq.1&select=id,projects:projects!client(id,tasks(id))" `shouldRespondWith`
|
||||
[json|[{"id":1,"projects":[{"id":1,"tasks":[{"id":1},{"id":2}]},{"id":2,"tasks":[{"id":3},{"id":4}]}]}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can embed by FK column name and select the FK value at the same time, if aliased" $
|
||||
get "/projects?id=in.(1,3)&select=id,name,client_id,client:client_id(id,name)" `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client_id":1,"client":{"id":1,"name":"Microsoft"}},{"id":3,"name":"IOS","client_id":2,"client":{"id":2,"name":"Apple"}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
it "can disambiguate with the fk in case of an o2m and m2m relationship to the same table" $
|
||||
get "/sites?select=name,main_project(name)&site_id=eq.1" `shouldRespondWith`
|
||||
[json| [ { "name": "site 1", "main_project": { "name": "big project 1" } } ] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "requests parents two levels up" $
|
||||
get "/tasks?id=eq.1&select=id,name,project:projects!project_id(id,name,client:client_id(id,name))" `shouldRespondWith`
|
||||
[str|[{"id":1,"name":"Design w7","project":{"id":1,"name":"Windows 7","client":{"id":1,"name":"Microsoft"}}}]|]
|
||||
context "using the column name of the FK to specify the relationship" $ do
|
||||
it "can embed by column" $
|
||||
get "/projects?id=in.(1,3)&select=id,name,client_id(id,name)" `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client_id":{"id":1,"name":"Microsoft"}},{"id":3,"name":"IOS","client_id":{"id":2,"name":"Apple"}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can embed by column and select the column at the same time, if aliased" $
|
||||
get "/projects?id=in.(1,3)&select=id,name,client_id,client:client_id(id,name)" `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client_id":1,"client":{"id":1,"name":"Microsoft"}},{"id":3,"name":"IOS","client_id":2,"client":{"id":2,"name":"Apple"}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can embed parent by using view!column and grandparent by using the column" $
|
||||
get "/tasks?id=eq.1&select=id,name,project:projects_view!project_id(id,name,client:client_id(id,name))" `shouldRespondWith`
|
||||
[str|[{"id":1,"name":"Design w7","project":{"id":1,"name":"Windows 7","client":{"id":1,"name":"Microsoft"}}}]|]
|
||||
|
||||
it "can specify table!column" $
|
||||
get "/message?select=id,body,sender:person!sender(name),recipient:person!recipient(name)&id=lt.4" `shouldRespondWith`
|
||||
[json|
|
||||
[{"id":1,"body":"Hello Jane","sender":{"name":"John"},"recipient":{"name":"Jane"}},
|
||||
{"id":2,"body":"Hi John","sender":{"name":"Jane"},"recipient":{"name":"John"}},
|
||||
{"id":3,"body":"How are you doing?","sender":{"name":"John"},"recipient":{"name":"Jane"}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "will embed using a column that has uppercase chars" $
|
||||
get "/ghostBusters?select=escapeId(*)" `shouldRespondWith`
|
||||
[json| [{"escapeId":{"so6meIdColumn":1}},{"escapeId":{"so6meIdColumn":3}},{"escapeId":{"so6meIdColumn":5}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "embeds by using two columns pointing to the same table" $
|
||||
get "/orders?id=eq.1&select=id, name, billing_address_id(id), shipping_address_id(id)" `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"order 1","billing_address_id":{"id":1},"shipping_address_id":{"id":2}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can disambiguate with the column in case of an o2m and m2m relationship to the same table" $
|
||||
get "/sites?select=name,main_project_id(name)&site_id=eq.1" `shouldRespondWith`
|
||||
[json| [ { "name": "site 1", "main_project_id": { "name": "big project 1" } } ] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
context "using the junction to disambiguate the request" $
|
||||
it "can specify the junction of an m2m relationship" $ do
|
||||
get "/sites?select=*,big_projects!jobs(name)&site_id=in.(1,2)" `shouldRespondWith`
|
||||
[json|
|
||||
[
|
||||
{
|
||||
"big_projects": [
|
||||
{
|
||||
"name": "big project 1"
|
||||
}
|
||||
],
|
||||
"main_project_id": 1,
|
||||
"name": "site 1",
|
||||
"site_id": 1
|
||||
},
|
||||
{
|
||||
"big_projects": [
|
||||
{
|
||||
"name": "big project 1"
|
||||
},
|
||||
{
|
||||
"name": "big project 2"
|
||||
}
|
||||
],
|
||||
"main_project_id": null,
|
||||
"name": "site 2",
|
||||
"site_id": 2
|
||||
}
|
||||
]
|
||||
|]
|
||||
get "/sites?select=*,big_projects!main_jobs(name)&site_id=in.(1,2)" `shouldRespondWith`
|
||||
[json|
|
||||
[
|
||||
{
|
||||
"big_projects": [
|
||||
{
|
||||
"name": "big project 1"
|
||||
}
|
||||
],
|
||||
"main_project_id": 1,
|
||||
"name": "site 1",
|
||||
"site_id": 1
|
||||
},
|
||||
{
|
||||
"big_projects": [],
|
||||
"main_project_id": null,
|
||||
"name": "site 2",
|
||||
"site_id": 2
|
||||
}
|
||||
]
|
||||
|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
context "using a FK column and a FK to specify the relationship" $
|
||||
it "embeds by using a column and a fk pointing to the same table" $
|
||||
get "/orders?id=eq.1&select=id, name, billing_address_id(id), shipping(id)" `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"order 1","billing_address_id":{"id":1},"shipping":{"id":2}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
context "tables with self reference foreign keys" $ do
|
||||
context "one self reference foreign key" $ do
|
||||
@@ -286,7 +422,7 @@ spec =
|
||||
"refereeds":[]}]
|
||||
}]|] { matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
-- TODO Remove in next major version(7.0)
|
||||
-- TODO Remove in next major version
|
||||
describe "old dot '.' symbol, deprecated" $
|
||||
it "still works" $ do
|
||||
get "/clients?id=eq.1&select=id,projects:projects.client_id(id,tasks(id))" `shouldRespondWith`
|
||||
@@ -295,3 +431,4 @@ spec =
|
||||
get "/tasks?select=id,users:users.users_tasks(id)" `shouldRespondWith`
|
||||
[json|[{"id":1,"users":[{"id":1},{"id":3}]},{"id":2,"users":[{"id":1}]},{"id":3,"users":[{"id":1}]},{"id":4,"users":[{"id":1}]},{"id":5,"users":[{"id":2},{"id":3}]},{"id":6,"users":[{"id":2}]},{"id":7,"users":[{"id":2}]},{"id":8,"users":[]}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import Network.Wai (Application)
|
||||
import Network.Wai.Test (SResponse (simpleHeaders))
|
||||
|
||||
import Network.HTTP.Types
|
||||
import Test.Hspec
|
||||
import Test.Hspec hiding (pendingWith)
|
||||
import Test.Hspec.Wai
|
||||
import Test.Hspec.Wai.JSON
|
||||
|
||||
@@ -198,7 +198,7 @@ spec actualPgVersion = do
|
||||
|
||||
it "matches filtering nested items 2" $
|
||||
get "/clients?select=id,projects(id,tasks2(id,name))&projects.tasks.name=like.Design*"
|
||||
`shouldRespondWith` [json| {"message":"Could not find foreign keys between these entities, No relation found between projects and tasks2"}|]
|
||||
`shouldRespondWith` [json| {"message":"Could not find foreign keys between these entities. No relationship found between projects and tasks2"}|]
|
||||
{ matchStatus = 400
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
@@ -265,19 +265,8 @@ spec actualPgVersion = do
|
||||
[json|[{"id":1,"name":"Windows 7","clients":{"id":1,"name":"Microsoft"},"tasks":[{"id":1,"name":"Design w7"},{"id":2,"name":"Code w7"}]}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "requesting parent without specifying primary key" $
|
||||
get "/projects?select=name,client(name)" `shouldRespondWith`
|
||||
[json|[
|
||||
{"name":"Windows 7","client":{"name": "Microsoft"}},
|
||||
{"name":"Windows 10","client":{"name": "Microsoft"}},
|
||||
{"name":"IOS","client":{"name": "Apple"}},
|
||||
{"name":"OSX","client":{"name": "Apple"}},
|
||||
{"name":"Orphan","client":null}
|
||||
]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "requesting parent and renaming primary key" $
|
||||
get "/projects?select=name,client(clientId:id,name)" `shouldRespondWith`
|
||||
get "/projects?select=name,client:clients(clientId:id,name)" `shouldRespondWith`
|
||||
[json|[
|
||||
{"name":"Windows 7","client":{"name": "Microsoft", "clientId": 1}},
|
||||
{"name":"Windows 10","client":{"name": "Microsoft", "clientId": 1}},
|
||||
@@ -295,13 +284,8 @@ spec actualPgVersion = do
|
||||
[json|[{"id":1,"commenter_id":1,"user_id":2,"task_id":6,"content":"Needs to be delivered ASAP","users_tasks":{"taskId": 6}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "embed data with two fk pointing to the same table" $
|
||||
get "/orders?id=eq.1&select=id, name, billing_address_id(id), shipping_address_id(id)" `shouldRespondWith`
|
||||
[str|[{"id":1,"name":"order 1","billing_address_id":{"id":1},"shipping_address_id":{"id":2}}]|]
|
||||
|
||||
|
||||
it "requesting parents and children while renaming them" $
|
||||
get "/projects?id=eq.1&select=myId:id, name, project_client:client_id(*), project_tasks:tasks(id, name)" `shouldRespondWith`
|
||||
get "/projects?id=eq.1&select=myId:id, name, project_client:clients(*), project_tasks:tasks(id, name)" `shouldRespondWith`
|
||||
[json|[{"myId":1,"name":"Windows 7","project_client":{"id":1,"name":"Microsoft"},"project_tasks":[{"id":1,"name":"Design w7"},{"id":2,"name":"Code w7"}]}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
@@ -343,11 +327,6 @@ spec actualPgVersion = do
|
||||
[json|[{"user_id":2,"task_id":6,"comments":[{"content":"Needs to be delivered ASAP"}]}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can select by column name sans id" $
|
||||
get "/projects?id=in.(1,3)&select=id,name,client_id,client(id,name)" `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client_id":1,"client":{"id":1,"name":"Microsoft"}},{"id":3,"name":"IOS","client_id":2,"client":{"id":2,"name":"Apple"}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
describe "view embedding" $ do
|
||||
it "can detect fk relations through views to tables in the public schema" $
|
||||
get "/consumers_view?select=*,orders_view(*)" `shouldRespondWith` 200
|
||||
@@ -355,8 +334,8 @@ spec actualPgVersion = do
|
||||
it "can detect fk relations through materialized views to tables in the public schema" $
|
||||
get "/materialized_projects?select=*,users(*)" `shouldRespondWith` 200
|
||||
|
||||
it "can request parent without specifying primary key" $
|
||||
get "/articleStars?select=createdAt,article(owner),user(name)&limit=1" `shouldRespondWith`
|
||||
it "can request two parents" $
|
||||
get "/articleStars?select=createdAt,article:articles(owner),user:users(name)&limit=1" `shouldRespondWith`
|
||||
[json|[{"createdAt":"2015-12-08T04:22:57.472738","article":{"owner": "postgrest_test_authenticator"},"user":{"name": "Angela Martin"}}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
@@ -448,7 +427,7 @@ spec actualPgVersion = do
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "can embed a view that has group by" $
|
||||
get "/projects_count_grouped_by?select=number_of_projects,client(name)&order=number_of_projects" `shouldRespondWith`
|
||||
get "/projects_count_grouped_by?select=number_of_projects,client:clients(name)&order=number_of_projects" `shouldRespondWith`
|
||||
[json|
|
||||
[{"number_of_projects":1,"client":null},
|
||||
{"number_of_projects":2,"client":{"name":"Microsoft"}},
|
||||
@@ -612,10 +591,6 @@ spec actualPgVersion = do
|
||||
get "/projects?id=eq.1&select=id, name, clients(id, name)&clients.order=name.asc" `shouldRespondWith`
|
||||
[str|[{"id":1,"name":"Windows 7","clients":{"id":1,"name":"Microsoft"}}]|]
|
||||
|
||||
it "ordering embeded parents does not break things when using ducktape names" $
|
||||
get "/projects?id=eq.1&select=id, name, client(id, name)&client.order=name.asc" `shouldRespondWith`
|
||||
[str|[{"id":1,"name":"Windows 7","client":{"id":1,"name":"Microsoft"}}]|]
|
||||
|
||||
context "order syntax errors" $ do
|
||||
it "gives meaningful error messages when asc/desc/nulls{first,last} are misspelled" $ do
|
||||
get "/items?order=id.ac" `shouldRespondWith`
|
||||
@@ -745,11 +720,6 @@ spec actualPgVersion = do
|
||||
[json| [{"ghostBusters":[{"escapeId":1}]},{"ghostBusters":[]},{"ghostBusters":[{"escapeId":3}]},{"ghostBusters":[]},{"ghostBusters":[{"escapeId":5}]}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "will embed using a column" $
|
||||
get "/ghostBusters?select=escapeId(*)" `shouldRespondWith`
|
||||
[json| [{"escapeId":{"so6meIdColumn":1}},{"escapeId":{"so6meIdColumn":3}},{"escapeId":{"so6meIdColumn":5}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "will select and filter a column that has spaces" $
|
||||
get "/Server%20Today?select=Just%20A%20Server%20Model&Just%20A%20Server%20Model=like.*91*" `shouldRespondWith`
|
||||
[json|[
|
||||
|
||||
@@ -158,10 +158,10 @@ spec actualPgVersion =
|
||||
|
||||
context "foreign entities embedding" $ do
|
||||
it "can embed if related tables are in the exposed schema" $ do
|
||||
post "/rpc/getproject?select=id,name,client(id),tasks(id)" [json| { "id": 1} |] `shouldRespondWith`
|
||||
post "/rpc/getproject?select=id,name,client:clients(id),tasks(id)" [json| { "id": 1} |] `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client":{"id":1},"tasks":[{"id":1},{"id":2}]}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
get "/rpc/getproject?id=1&select=id,name,client(id),tasks(id)" `shouldRespondWith`
|
||||
get "/rpc/getproject?id=1&select=id,name,client:clients(id),tasks(id)" `shouldRespondWith`
|
||||
[json|[{"id":1,"name":"Windows 7","client":{"id":1},"tasks":[{"id":1},{"id":2}]}]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
|
||||
Vendored
+46
@@ -526,3 +526,49 @@ INSERT INTO private.pages VALUES (2, 'http://postgrest.org/en/v6.0/admin.html');
|
||||
TRUNCATE TABLE private.referrals CASCADE;
|
||||
INSERT INTO private.referrals VALUES ('github.com', 1);
|
||||
INSERT INTO private.referrals VALUES ('hub.docker.com', 2);
|
||||
|
||||
TRUNCATE TABLE big_projects CASCADE;
|
||||
INSERT INTO big_projects (big_project_id, name)
|
||||
VALUES (1, 'big project 1'),
|
||||
(2, 'big project 2');
|
||||
|
||||
TRUNCATE TABLE sites CASCADE;
|
||||
INSERT INTO sites (site_id, name, main_project_id)
|
||||
VALUES (1, 'site 1', 1),
|
||||
(2, 'site 2', null),
|
||||
(3, 'site 3', 2),
|
||||
(4, 'site 4', null);
|
||||
|
||||
TRUNCATE TABLE jobs CASCADE;
|
||||
INSERT INTO jobs (job_id, name, site_id, big_project_id)
|
||||
VALUES ('bc5d5362-b881-438f-b9f5-7417e08704ed', 'job 1-1', 1, 1),
|
||||
('3bd52697-033b-4edd-8a28-46a9c04b7c1e', 'job 2-1', 2, 1),
|
||||
('e6e67e4e-19b1-11e9-ab14-d663bd873d93', 'job 2-2', 2, 2);
|
||||
|
||||
TRUNCATE TABLE departments CASCADE;
|
||||
TRUNCATE TABLE agents CASCADE;
|
||||
INSERT INTO agents (id, name)
|
||||
VALUES (1, 'agent 1'),
|
||||
(2, 'agent 2'),
|
||||
(3, 'agent 3'),
|
||||
(4, 'agent 4');
|
||||
|
||||
INSERT INTO departments (id, name, head_id)
|
||||
VALUES (1, 'dep 1', 1),
|
||||
(2, 'dep 3', 3);
|
||||
|
||||
UPDATE agents SET department_id = 1 WHERE id in (1, 2);
|
||||
UPDATE agents SET department_id = 2 WHERE id in (3, 4);
|
||||
|
||||
TRUNCATE TABLE schedules CASCADE;
|
||||
INSERT INTO schedules VALUES(1, 'morning', '06:00:00', '11:59:00');
|
||||
INSERT INTO schedules VALUES(2, 'afternoon', '12:00:00', '17:59:00');
|
||||
INSERT INTO schedules VALUES(3, 'night', '18:00:00', '23:59:00');
|
||||
INSERT INTO schedules VALUES(4, 'early morning', '00:00:00', '05:59:00');
|
||||
|
||||
TRUNCATE TABLE activities CASCADE;
|
||||
INSERT INTO activities(id, schedule_id, car_id) VALUES(1, 1, 'CAR-349');
|
||||
INSERT INTO activities(id, schedule_id, camera_id) VALUES(2, 3, 'CAM-123');
|
||||
|
||||
TRUNCATE TABLE unit_workdays CASCADE;
|
||||
INSERT INTO unit_workdays VALUES(1, '2019-12-02', 1, 1, 2, 3);
|
||||
|
||||
Vendored
+12
@@ -108,6 +108,18 @@ GRANT ALL ON TABLE
|
||||
, web_content
|
||||
, pages
|
||||
, referrals
|
||||
, big_projects
|
||||
, sites
|
||||
, jobs
|
||||
, main_jobs
|
||||
, whatev_projects
|
||||
, whatev_sites
|
||||
, whatev_jobs
|
||||
, agents
|
||||
, departments
|
||||
, schedules
|
||||
, activities
|
||||
, unit_workdays
|
||||
TO postgrest_test_anonymous;
|
||||
|
||||
GRANT INSERT ON TABLE insertonly TO postgrest_test_anonymous;
|
||||
|
||||
Vendored
+166
-309
@@ -103,15 +103,10 @@ SET default_tablespace = '';
|
||||
|
||||
SET default_with_oids = false;
|
||||
|
||||
--
|
||||
-- Name: items; Type: TABLE; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE items (
|
||||
id bigint NOT NULL
|
||||
id bigserial primary key
|
||||
);
|
||||
|
||||
|
||||
CREATE FUNCTION always_true(test.items) RETURNS boolean
|
||||
LANGUAGE sql STABLE
|
||||
AS $$ SELECT true $$;
|
||||
@@ -364,59 +359,8 @@ CREATE TABLE auth (
|
||||
pass character(60) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
SET search_path = private, pg_catalog;
|
||||
|
||||
--
|
||||
-- Name: article_stars; Type: TABLE; Schema: private; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE article_stars (
|
||||
article_id integer NOT NULL,
|
||||
user_id integer NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: articles; Type: TABLE; Schema: private; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE articles (
|
||||
id integer NOT NULL,
|
||||
body text,
|
||||
owner name NOT NULL
|
||||
);
|
||||
|
||||
|
||||
SET search_path = test, pg_catalog;
|
||||
|
||||
CREATE VIEW limited_article_stars AS
|
||||
SELECT article_id, user_id, created_at FROM private.article_stars;
|
||||
|
||||
|
||||
--
|
||||
-- Name: articleStars; Type: VIEW; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
CREATE VIEW "articleStars" AS
|
||||
SELECT article_stars.article_id AS "articleId",
|
||||
article_stars.user_id AS "userId",
|
||||
article_stars.created_at AS "createdAt"
|
||||
FROM private.article_stars;
|
||||
|
||||
|
||||
--
|
||||
-- Name: articles; Type: VIEW; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
CREATE VIEW articles AS
|
||||
SELECT articles.id,
|
||||
articles.body,
|
||||
articles.owner
|
||||
FROM private.articles;
|
||||
|
||||
|
||||
--
|
||||
-- Name: authors_only; Type: TABLE; Schema: test; Owner: -
|
||||
--
|
||||
@@ -463,24 +407,10 @@ ALTER SEQUENCE auto_incrementing_pk_id_seq OWNED BY auto_incrementing_pk.id;
|
||||
--
|
||||
|
||||
CREATE TABLE clients (
|
||||
id integer NOT NULL,
|
||||
id integer primary key,
|
||||
name text NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: comments; Type: TABLE; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE comments (
|
||||
id integer NOT NULL,
|
||||
commenter_id integer NOT NULL,
|
||||
user_id integer NOT NULL,
|
||||
task_id integer NOT NULL,
|
||||
content text NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: complex_items; Type: TABLE; Schema: test; Owner: -
|
||||
--
|
||||
@@ -574,26 +504,6 @@ CREATE VIEW insertable_view_with_join AS
|
||||
FROM (has_fk
|
||||
JOIN auto_incrementing_pk USING (id));
|
||||
|
||||
|
||||
--
|
||||
-- Name: items_id_seq; Type: SEQUENCE; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE items_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: items_id_seq; Type: SEQUENCE OWNED BY; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE items_id_seq OWNED BY items.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: json; Type: TABLE; Schema: test; Owner: -
|
||||
--
|
||||
@@ -663,11 +573,11 @@ CREATE TABLE insertonly (
|
||||
--
|
||||
|
||||
CREATE TABLE projects (
|
||||
id integer NOT NULL,
|
||||
id integer primary key,
|
||||
name text NOT NULL,
|
||||
client_id integer
|
||||
client_id integer REFERENCES clients(id)
|
||||
);
|
||||
|
||||
alter table projects rename constraint projects_client_id_fkey to client;
|
||||
|
||||
--
|
||||
-- Name: projects_view; Type: VIEW; Schema: test; Owner: -
|
||||
@@ -695,25 +605,23 @@ CREATE TABLE simple_pk (
|
||||
extra character varying NOT NULL
|
||||
);
|
||||
|
||||
--
|
||||
-- Name: users_projects; Type: TABLE; Schema: test; Owner: -
|
||||
--
|
||||
CREATE TABLE users (
|
||||
id integer primary key,
|
||||
name text NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE users_projects (
|
||||
user_id integer NOT NULL,
|
||||
project_id integer NOT NULL
|
||||
user_id integer NOT NULL REFERENCES users(id),
|
||||
project_id integer NOT NULL REFERENCES projects(id),
|
||||
PRIMARY KEY (project_id, user_id)
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: tasks; Type: TABLE; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE tasks (
|
||||
id integer NOT NULL,
|
||||
id integer primary key,
|
||||
name text NOT NULL,
|
||||
project_id integer
|
||||
project_id integer REFERENCES projects(id)
|
||||
);
|
||||
alter table tasks rename constraint tasks_project_id_fkey to project;
|
||||
|
||||
CREATE OR REPLACE VIEW filtered_tasks AS
|
||||
SELECT id AS "myId", name, project_id AS "projectID"
|
||||
@@ -725,6 +633,53 @@ project_id IN (
|
||||
SELECT project_id FROM users_projects WHERE user_id = 1
|
||||
);
|
||||
|
||||
CREATE TABLE users_tasks (
|
||||
user_id integer NOT NULL REFERENCES users(id),
|
||||
task_id integer NOT NULL REFERENCES tasks(id),
|
||||
primary key (task_id, user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE comments (
|
||||
id integer primary key,
|
||||
commenter_id integer NOT NULL,
|
||||
user_id integer NOT NULL,
|
||||
task_id integer NOT NULL,
|
||||
content text NOT NULL
|
||||
);
|
||||
alter table only comments
|
||||
add constraint "user" foreign key (commenter_id) references users(id),
|
||||
add constraint comments_task_id_fkey foreign key (task_id, user_id) references users_tasks(task_id, user_id);
|
||||
|
||||
create table private.articles (
|
||||
id integer primary key,
|
||||
body text,
|
||||
owner name not null
|
||||
);
|
||||
|
||||
create table private.article_stars (
|
||||
article_id integer not null,
|
||||
user_id integer not null,
|
||||
created_at timestamp without time zone default now() not null,
|
||||
primary key (article_id, user_id)
|
||||
);
|
||||
alter table only private.article_stars
|
||||
add constraint article foreign key (article_id) references private.articles(id),
|
||||
add constraint "user" foreign key (user_id) references test.users(id);
|
||||
|
||||
CREATE VIEW limited_article_stars AS
|
||||
SELECT article_id, user_id, created_at FROM private.article_stars;
|
||||
|
||||
CREATE VIEW "articleStars" AS
|
||||
SELECT article_stars.article_id AS "articleId",
|
||||
article_stars.user_id AS "userId",
|
||||
article_stars.created_at AS "createdAt"
|
||||
FROM private.article_stars;
|
||||
|
||||
CREATE VIEW articles AS
|
||||
SELECT articles.id,
|
||||
articles.body,
|
||||
articles.owner
|
||||
FROM private.articles;
|
||||
|
||||
--
|
||||
-- Name: tsearch; Type: TABLE; Schema: test; Owner: -
|
||||
@@ -734,28 +689,6 @@ CREATE TABLE tsearch (
|
||||
text_search_vector tsvector
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: users; Type: TABLE; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE users (
|
||||
id integer NOT NULL,
|
||||
name text NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Name: users_tasks; Type: TABLE; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE users_tasks (
|
||||
user_id integer NOT NULL,
|
||||
task_id integer NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE "Escap3e;" (
|
||||
"so6meIdColumn" integer primary key
|
||||
);
|
||||
@@ -773,7 +706,6 @@ CREATE TABLE clashing_column (
|
||||
t text
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: id; Type: DEFAULT; Schema: test; Owner: -
|
||||
--
|
||||
@@ -788,13 +720,6 @@ ALTER TABLE ONLY auto_incrementing_pk ALTER COLUMN id SET DEFAULT nextval('auto_
|
||||
ALTER TABLE ONLY has_fk ALTER COLUMN id SET DEFAULT nextval('has_fk_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: id; Type: DEFAULT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY items ALTER COLUMN id SET DEFAULT nextval('items_id_seq'::regclass);
|
||||
|
||||
|
||||
SET search_path = postgrest, pg_catalog;
|
||||
|
||||
--
|
||||
@@ -805,24 +730,6 @@ ALTER TABLE ONLY auth
|
||||
ADD CONSTRAINT auth_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
SET search_path = private, pg_catalog;
|
||||
|
||||
--
|
||||
-- Name: articles_pkey; Type: CONSTRAINT; Schema: private; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY articles
|
||||
ADD CONSTRAINT articles_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_article; Type: CONSTRAINT; Schema: private; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY article_stars
|
||||
ADD CONSTRAINT user_article PRIMARY KEY (article_id, user_id);
|
||||
|
||||
|
||||
SET search_path = test, pg_catalog;
|
||||
|
||||
--
|
||||
@@ -840,23 +747,6 @@ ALTER TABLE ONLY authors_only
|
||||
ALTER TABLE ONLY auto_incrementing_pk
|
||||
ADD CONSTRAINT auto_incrementing_pk_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: clients_pkey; Type: CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY clients
|
||||
ADD CONSTRAINT clients_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: comments_pkey; Type: CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY comments
|
||||
ADD CONSTRAINT comments_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: complex_items_pkey; Type: CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
@@ -888,15 +778,6 @@ ALTER TABLE ONLY simple_pk
|
||||
ALTER TABLE ONLY has_fk
|
||||
ADD CONSTRAINT has_fk_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: items_pkey; Type: CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY items
|
||||
ADD CONSTRAINT items_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: menagerie_pkey; Type: CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
@@ -905,46 +786,6 @@ ALTER TABLE ONLY menagerie
|
||||
ADD CONSTRAINT menagerie_pkey PRIMARY KEY ("integer");
|
||||
|
||||
|
||||
--
|
||||
-- Name: project_user; Type: CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY users_projects
|
||||
ADD CONSTRAINT project_user PRIMARY KEY (project_id, user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_pkey; Type: CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY projects
|
||||
ADD CONSTRAINT projects_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: task_user; Type: CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY users_tasks
|
||||
ADD CONSTRAINT task_user PRIMARY KEY (task_id, user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: tasks_pkey; Type: CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY tasks
|
||||
ADD CONSTRAINT tasks_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: users_pkey; Type: CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY users
|
||||
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
SET search_path = postgrest, pg_catalog;
|
||||
|
||||
--
|
||||
@@ -971,43 +812,8 @@ SET search_path = test, pg_catalog;
|
||||
|
||||
CREATE TRIGGER secrets_owner_track BEFORE INSERT OR UPDATE ON authors_only FOR EACH ROW EXECUTE PROCEDURE postgrest.set_authors_only_owner();
|
||||
|
||||
|
||||
SET search_path = private, pg_catalog;
|
||||
|
||||
--
|
||||
-- Name: article_stars_article_id_fkey; Type: FK CONSTRAINT; Schema: private; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY article_stars
|
||||
ADD CONSTRAINT article_stars_article_id_fkey FOREIGN KEY (article_id) REFERENCES articles(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: article_stars_user_id_fkey; Type: FK CONSTRAINT; Schema: private; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY article_stars
|
||||
ADD CONSTRAINT article_stars_user_id_fkey FOREIGN KEY (user_id) REFERENCES test.users(id);
|
||||
|
||||
|
||||
SET search_path = test, pg_catalog;
|
||||
|
||||
--
|
||||
-- Name: comments_commenter_id_fkey; Type: FK CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY comments
|
||||
ADD CONSTRAINT comments_commenter_id_fkey FOREIGN KEY (commenter_id) REFERENCES users(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: comments_task_id_fkey; Type: FK CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY comments
|
||||
ADD CONSTRAINT comments_task_id_fkey FOREIGN KEY (task_id, user_id) REFERENCES users_tasks(task_id, user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: has_fk_fk_fkey; Type: FK CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
@@ -1023,55 +829,6 @@ ALTER TABLE ONLY has_fk
|
||||
ALTER TABLE ONLY has_fk
|
||||
ADD CONSTRAINT has_fk_simple_fk_fkey FOREIGN KEY (simple_fk) REFERENCES simple_pk(k);
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_client_id_fkey; Type: FK CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY projects
|
||||
ADD CONSTRAINT projects_client_id_fkey FOREIGN KEY (client_id) REFERENCES clients(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: tasks_project_id_fkey; Type: FK CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY tasks
|
||||
ADD CONSTRAINT tasks_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: users_projects_project_id_fkey; Type: FK CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY users_projects
|
||||
ADD CONSTRAINT users_projects_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: users_projects_user_id_fkey; Type: FK CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY users_projects
|
||||
ADD CONSTRAINT users_projects_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: users_tasks_task_id_fkey; Type: FK CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY users_tasks
|
||||
ADD CONSTRAINT users_tasks_task_id_fkey FOREIGN KEY (task_id) REFERENCES tasks(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: users_tasks_user_id_fkey; Type: FK CONSTRAINT; Schema: test; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY users_tasks
|
||||
ADD CONSTRAINT users_tasks_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);
|
||||
|
||||
|
||||
create table addresses (
|
||||
id int not null unique,
|
||||
address text not null
|
||||
@@ -1083,6 +840,8 @@ create table orders (
|
||||
billing_address_id int references addresses(id),
|
||||
shipping_address_id int references addresses(id)
|
||||
);
|
||||
alter table orders rename constraint orders_billing_address_id_fkey to billing;
|
||||
alter table orders rename constraint orders_shipping_address_id_fkey to shipping;
|
||||
|
||||
CREATE FUNCTION getproject(id int) RETURNS SETOF projects
|
||||
LANGUAGE sql
|
||||
@@ -1406,12 +1165,11 @@ create table test.managers (
|
||||
create table test.organizations (
|
||||
id integer primary key,
|
||||
name text,
|
||||
referee integer,
|
||||
auditor integer,
|
||||
referee integer references organizations(id),
|
||||
auditor integer references organizations(id),
|
||||
manager_id integer references managers(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 rename constraint organizations_manager_id_fkey to manager;
|
||||
|
||||
create table private.authors(
|
||||
id integer primary key,
|
||||
@@ -1785,3 +1543,102 @@ create table private.referrals (
|
||||
create view test.pages as select * from private.pages;
|
||||
|
||||
create view test.referrals as select * from private.referrals;
|
||||
|
||||
create table big_projects (
|
||||
big_project_id serial primary key,
|
||||
name text
|
||||
);
|
||||
|
||||
create table sites (
|
||||
site_id serial primary key
|
||||
, name text
|
||||
, main_project_id int null references big_projects (big_project_id)
|
||||
);
|
||||
alter table sites rename constraint sites_main_project_id_fkey to main_project;
|
||||
|
||||
create table jobs (
|
||||
job_id uuid primary key
|
||||
, name text
|
||||
, site_id int not null references sites (site_id)
|
||||
, big_project_id int not null references big_projects (big_project_id)
|
||||
);
|
||||
|
||||
create view main_jobs as
|
||||
select * from jobs
|
||||
where site_id in (select site_id from sites where main_project_id is not null);
|
||||
|
||||
-- junction in a private schema, just to make sure we don't leak it on resource embedding
|
||||
-- if it leaks it would show on the disambiguation error tests
|
||||
create view private.priv_jobs as
|
||||
select * from jobs;
|
||||
|
||||
-- tables to show our limitation when trying to do an m2m embed
|
||||
-- with a junction table that has more than two foreign keys
|
||||
create table whatev_projects (
|
||||
id serial primary key,
|
||||
name text
|
||||
);
|
||||
|
||||
create table whatev_sites (
|
||||
id serial primary key
|
||||
, name text
|
||||
);
|
||||
|
||||
create table whatev_jobs (
|
||||
job_id uuid primary key
|
||||
, name text
|
||||
, site_id_1 int not null references whatev_sites (id)
|
||||
, project_id_1 int not null references whatev_projects (id)
|
||||
, site_id_2 int not null references whatev_sites (id)
|
||||
, project_id_2 int not null references whatev_projects (id)
|
||||
);
|
||||
|
||||
-- circular reference
|
||||
create table agents (
|
||||
id int primary key
|
||||
, name text
|
||||
, department_id int
|
||||
);
|
||||
|
||||
create table departments (
|
||||
id int primary key
|
||||
, name text
|
||||
, head_id int references agents(id)
|
||||
);
|
||||
|
||||
ALTER TABLE agents
|
||||
ADD CONSTRAINT agents_department_id_fkey foreign key (department_id) REFERENCES departments(id);
|
||||
|
||||
-- composite key disambiguation
|
||||
create table schedules (
|
||||
id int primary key
|
||||
, name text
|
||||
, start_at timetz
|
||||
, end_at timetz
|
||||
);
|
||||
|
||||
create table activities (
|
||||
id int
|
||||
, schedule_id int
|
||||
, car_id text
|
||||
, camera_id text
|
||||
, primary key (id, schedule_id)
|
||||
);
|
||||
alter table activities
|
||||
add constraint schedule foreign key (schedule_id)
|
||||
references schedules (id);
|
||||
|
||||
create table unit_workdays (
|
||||
unit_id int
|
||||
, day date
|
||||
, fst_shift_activity_id int
|
||||
, fst_shift_schedule_id int
|
||||
, snd_shift_activity_id int
|
||||
, snd_shift_schedule_id int
|
||||
, primary key (unit_id, day)
|
||||
);
|
||||
alter table unit_workdays
|
||||
add constraint fst_shift foreign key (fst_shift_activity_id, fst_shift_schedule_id)
|
||||
references activities (id, schedule_id),
|
||||
add constraint snd_shift foreign key (snd_shift_activity_id, snd_shift_schedule_id)
|
||||
references activities (id, schedule_id);
|
||||
|
||||
Reference in New Issue
Block a user