Correct inner join to use proper aliases (#1978)
"_" was used for all the internal json_agg aliases. Now an alias with "_" plus a local table name is used. Co-authored-by: Cole Arendt <cole.arendt@outlook.com>
This commit is contained in:
co-authored by
Cole Arendt
parent
42d7cf8ae5
commit
3b83f536ca
@@ -50,7 +50,7 @@ readRequestToQuery (Node (Select colSelects mainQi tblAlias implJoins logicFores
|
||||
(joins, selects) = foldr getJoinsSelects ([],[]) forest
|
||||
|
||||
getJoinsSelects :: ReadRequest -> ([H.Snippet], [H.Snippet]) -> ([H.Snippet], [H.Snippet])
|
||||
getJoinsSelects rr@(Node (_, (name, Just Relationship{relCardinality=card,relTable=Table{tableName=table}}, alias, _, Just joinType, _)) _) (j,s) =
|
||||
getJoinsSelects rr@(Node (_, (name, Just Relationship{relCardinality=card,relTable=Table{tableName=table}}, alias, _, Just joinType, _)) _) (joins,selects) =
|
||||
let subquery = readRequestToQuery rr in
|
||||
case card of
|
||||
M2O _ ->
|
||||
@@ -59,21 +59,25 @@ getJoinsSelects rr@(Node (_, (name, Just Relationship{relCardinality=card,relTab
|
||||
sel = H.sql ("row_to_json(" <> localTableName <> ".*) AS " <> pgFmtIdent aliasOrName)
|
||||
joi = (if joinType == JTInner then " INNER" else " LEFT")
|
||||
<> " JOIN LATERAL( " <> subquery <> " ) AS " <> H.sql localTableName <> " ON TRUE " in
|
||||
(joi:j,sel:s)
|
||||
(joi:joins,sel:selects)
|
||||
_ -> case joinType of
|
||||
JTInner ->
|
||||
let aliasOrName = fromMaybe name alias
|
||||
localTableName = pgFmtIdent $ table <> "_" <> aliasOrName
|
||||
sel = H.sql $ localTableName <> "._ AS " <> pgFmtIdent aliasOrName
|
||||
joi = "INNER JOIN LATERAL( SELECT json_agg(_) AS _ FROM (" <> subquery <> " ) _) AS " <>
|
||||
H.sql localTableName <> " ON " <> H.sql localTableName <> "IS NOT NULL" in
|
||||
(joi:j,sel:s)
|
||||
locTblName = table <> "_" <> aliasOrName
|
||||
localTableName = pgFmtIdent locTblName
|
||||
internalTableName = pgFmtIdent $ "_" <> locTblName
|
||||
sel = H.sql $ localTableName <> "." <> internalTableName <> " AS " <> pgFmtIdent aliasOrName
|
||||
joi = "INNER JOIN LATERAL(" <>
|
||||
"SELECT json_agg(" <> H.sql internalTableName <> ") AS " <> H.sql internalTableName <>
|
||||
"FROM (" <> subquery <> " ) AS " <> H.sql internalTableName <>
|
||||
") AS " <> H.sql localTableName <> " ON " <> H.sql localTableName <> "IS NOT NULL" in
|
||||
(joi:joins,sel:selects)
|
||||
JTLeft ->
|
||||
let sel = "COALESCE (("
|
||||
<> "SELECT json_agg(" <> H.sql (pgFmtIdent table) <> ".*) "
|
||||
<> "FROM (" <> subquery <> ") " <> H.sql (pgFmtIdent table) <> " "
|
||||
<> "), '[]') AS " <> H.sql (pgFmtIdent (fromMaybe name alias)) in
|
||||
(j,sel:s)
|
||||
(joins,sel:selects)
|
||||
getJoinsSelects _ _ = ([], [])
|
||||
|
||||
mutateRequestToQuery :: MutateRequest -> H.Snippet
|
||||
|
||||
@@ -220,6 +220,26 @@ spec =
|
||||
[json| [{"id":3,"client":{"id":2}}, {"id":4,"client":{"id":2}}] |]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "works with many one-to-many relationships" $ do
|
||||
-- https://github.com/PostgREST/postgrest/issues/1977
|
||||
get "/client?select=id,name,contact!inner(name),clientinfo!inner(other)" `shouldRespondWith`
|
||||
[json|[
|
||||
{"id":1,"name":"Walmart","contact":[{"name":"Wally Walton"}, {"name":"Wilma Wellers"}],"clientinfo":[{"other":"123 Main St"}]},
|
||||
{"id":2,"name":"Target", "contact":[{"name":"Tabby Targo"}],"clientinfo":[{"other":"456 South 3rd St"}]},
|
||||
{"id":3,"name":"Big Lots","contact":[{"name":"Bobby Bots"}, {"name":"Bonnie Bits"}, {"name":"Billy Boats"}],"clientinfo":[{"other":"789 Palm Tree Ln"}]}
|
||||
]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
get "/client?select=id,name,contact!inner(name),clientinfo!inner(other)&contact.name=eq.Wally%20Walton" `shouldRespondWith`
|
||||
[json|[
|
||||
{"id":1,"name":"Walmart","contact":[{"name":"Wally Walton"}],"clientinfo":[{"other":"123 Main St"}]}
|
||||
]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
get "/client?select=id,name,contact!inner(name),clientinfo!inner(other)&clientinfo.other=eq.456%20South%203rd%20St" `shouldRespondWith`
|
||||
[json|[
|
||||
{"id":2,"name":"Target","clientinfo":[{"other":"456 South 3rd St"}],"contact":[{"name":"Tabby Targo"}]}
|
||||
]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
notDefaultConfig :: SpecWith ((), Application)
|
||||
notDefaultConfig =
|
||||
describe "Embedding with a default inner join(db-embed-default-join = 'inner')" $ do
|
||||
|
||||
Vendored
+10
-1
@@ -706,4 +706,13 @@ TRUNCATE TABLE test.trade_unions CASCADE;
|
||||
INSERT INTO test.trade_unions (id, name) VALUES (1,'union-1'), (2,'union-2'), (3, 'union-3'), (4, 'union-4');
|
||||
|
||||
TRUNCATE TABLE test.suppliers_trade_unions CASCADE;
|
||||
INSERT INTO test.suppliers_trade_unions (supplier_id, trade_union_id) VALUES (1,1), (1,2), (2,3), (2,4);
|
||||
INSERT INTO test.suppliers_trade_unions (supplier_id, trade_union_id) VALUES (1,1), (1,2), (2,3), (2,4);
|
||||
|
||||
TRUNCATE TABLE test.client CASCADE;
|
||||
INSERT INTO test.client (id,name) values (1,'Walmart'),(2,'Target'),(3,'Big Lots');
|
||||
|
||||
TRUNCATE TABLE test.contact CASCADE;
|
||||
INSERT INTO test.contact (id,name, clientid) values (1,'Wally Walton',1),(2,'Wilma Wellers',1),(3,'Tabby Targo',2),(4,'Bobby Bots',3),(5,'Bonnie Bits',3),(6,'Billy Boats',3) returning *;
|
||||
|
||||
TRUNCATE TABLE test.clientinfo CASCADE;
|
||||
INSERT INTO test.clientinfo (id,clientid, other) values (1,1,'123 Main St'),(2,2,'456 South 3rd St'),(3,3,'789 Palm Tree Ln');
|
||||
|
||||
Vendored
+3
@@ -156,6 +156,9 @@ GRANT ALL ON TABLE
|
||||
, products_suppliers
|
||||
, trade_unions
|
||||
, suppliers_trade_unions
|
||||
, client
|
||||
, clientinfo
|
||||
, contact
|
||||
TO postgrest_test_anonymous;
|
||||
|
||||
GRANT INSERT ON TABLE insertonly TO postgrest_test_anonymous;
|
||||
|
||||
Vendored
+19
-1
@@ -2347,4 +2347,22 @@ create table suppliers_trade_unions(
|
||||
supplier_id int references suppliers(id),
|
||||
trade_union_id int references trade_unions(id),
|
||||
primary key (supplier_id, trade_union_id)
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE client (
|
||||
id int primary key
|
||||
, name text
|
||||
);
|
||||
|
||||
CREATE TABLE contact (
|
||||
id int primary key
|
||||
, name text
|
||||
, clientid int references client(id)
|
||||
);
|
||||
|
||||
CREATE TABLE clientinfo (
|
||||
id serial primary key
|
||||
, clientid int unique references client(id)
|
||||
, other text
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user