From 45e7aac218f30ded07a7cdd52a83eb6a5b793db3 Mon Sep 17 00:00:00 2001 From: steve-chavez Date: Thu, 27 Oct 2022 00:42:20 -0500 Subject: [PATCH] fix: embedding computed with normal relationship --- CHANGELOG.md | 1 + src/PostgREST/Plan.hs | 15 +++++++++------ src/PostgREST/Plan/ReadPlan.hs | 4 +--- test/spec/Feature/Query/ComputedRelsSpec.hs | 15 +++++++++++++++ test/spec/fixtures/schema.sql | 8 ++++++++ 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b8264169..c8363c468 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #2283, Fix infinite recursion when loading schema cache with self-referencing view - @wolfgangwalther - #2343, Return status code 200 for PATCH requests which don't affect any rows - @wolfgangwalther - #2481, Treat computed relationships not marked SETOF as M2O/O2O relationship - @wolfgangwalther + - #2534, Fix embedding a computed relationship with a normal relationship - @steve-chavez ### Changed diff --git a/src/PostgREST/Plan.hs b/src/PostgREST/Plan.hs index 0948514b6..fd5982991 100644 --- a/src/PostgREST/Plan.hs +++ b/src/PostgREST/Plan.hs @@ -126,23 +126,26 @@ treeRestrictRange maxRows _ request = pure $ nodeRestrictRange maxRows <$> reque nodeRestrictRange :: Maybe Integer -> ReadPlan -> ReadPlan nodeRestrictRange m q@ReadPlan{range_=r} = q{range_=restrictRange m r } --- add relationships to the nodes of the tree by traversing the forest while keeping track of the parentNode, also adds aliasing +-- add relationships to the nodes of the tree by traversing the forest while keeping track of the parentNode(https://stackoverflow.com/questions/22721064/get-the-parent-of-a-node-in-data-tree-haskell#comment34627048_22721064) +-- also adds aliasing addRels :: Schema -> Action -> RelationshipsMap -> Maybe ReadPlanTree -> ReadPlanTree -> Either ApiRequestError ReadPlanTree addRels schema action allRels parentNode (Node rPlan@ReadPlan{relName,relHint,relAlias,depth} forest) = case parentNode of - Just (Node ReadPlan{from=parentNodeQi, fromAlias} _) -> + Just (Node ReadPlan{from=parentNodeQi, fromAlias=parentAlias} _) -> let newReadPlan = (\r -> let newAlias = Just (qiName (relForeignTable r) <> "_" <> show depth) aggAlias = qiName (relTable r) <> "_" <> fromMaybe relName relAlias <> "_" <> show depth in case r of - Relationship{relCardinality=M2M _} -> - rPlan{from=relForeignTable r, relToParent=Just r, relAggAlias=aggAlias, relJoinConds=getJoinConditions Nothing fromAlias r} + Relationship{relCardinality=M2M _} -> -- m2m does internal implicit joins that don't need aliasing + rPlan{from=relForeignTable r, relToParent=Just r, relAggAlias=aggAlias, relJoinConds=getJoinConditions Nothing parentAlias r} + ComputedRelationship{} -> + rPlan{from=relForeignTable r, relToParent=Just r{relTable=maybe (relTable r) (QualifiedIdentifier mempty) parentAlias}, relAggAlias=aggAlias, fromAlias=newAlias} _ -> - rPlan{from=relForeignTable r, relToParent=Just r, relAggAlias=aggAlias, fromAlias=newAlias, relJoinConds=getJoinConditions newAlias fromAlias r} + rPlan{from=relForeignTable r, relToParent=Just r, relAggAlias=aggAlias, fromAlias=newAlias, relJoinConds=getJoinConditions newAlias parentAlias r} ) <$> rel origin = if depth == 1 -- Only on depth 1 we check if the root(depth 0) has an alias so the sourceCTEName alias can be found as a relationship - then fromMaybe (qiName parentNodeQi) fromAlias + then fromMaybe (qiName parentNodeQi) parentAlias else qiName parentNodeQi rel = findRel schema allRels origin relName relHint in diff --git a/src/PostgREST/Plan/ReadPlan.hs b/src/PostgREST/Plan/ReadPlan.hs index 6ac789dab..05082328a 100644 --- a/src/PostgREST/Plan/ReadPlan.hs +++ b/src/PostgREST/Plan/ReadPlan.hs @@ -43,9 +43,7 @@ data ReadPlan = ReadPlan , relHint :: Maybe Hint , relJoinType :: Maybe JoinType , depth :: Depth - -- depth is used bc when a self join occurs we - -- need to differentiate the parent from the child tables by having an alias like - -- "table_depth". See http://github.com/PostgREST/postgrest/issues/987. + -- ^ used for aliasing } deriving (Eq) diff --git a/test/spec/Feature/Query/ComputedRelsSpec.hs b/test/spec/Feature/Query/ComputedRelsSpec.hs index 612453d09..ea66ad437 100644 --- a/test/spec/Feature/Query/ComputedRelsSpec.hs +++ b/test/spec/Feature/Query/ComputedRelsSpec.hs @@ -136,3 +136,18 @@ spec = describe "computed relationships" $ do get "/fee?select=*,jsbaz(*,johnsmith(*, fee(*)))" `shouldRespondWith` [json|[]|] { matchHeaders = [matchContentTypeJson] } + + it "creates queries with the right aliasing when following a normal embed" $ do + get "/projects?select=name,clients(name,computed_projects(name))&limit=1" + `shouldRespondWith` + [json| + [{"name":"Windows 7","clients":{"name":"Microsoft","computed_projects":{"name":"Windows 7"}}}] + |] { matchHeaders = [matchContentTypeJson] } + get "/clients?select=name,projects(name,computed_clients(name))&limit=1" + `shouldRespondWith` + [json|[ + {"name":"Microsoft","projects":[ + {"name":"Windows 7","computed_clients":{"name":"Microsoft"}}, + {"name":"Windows 10","computed_clients":{"name":"Microsoft"}} + ]} + ]|] { matchHeaders = [matchContentTypeJson] } diff --git a/test/spec/fixtures/schema.sql b/test/spec/fixtures/schema.sql index 5cb2e22f9..c528bbf15 100644 --- a/test/spec/fixtures/schema.sql +++ b/test/spec/fixtures/schema.sql @@ -2910,3 +2910,11 @@ create trigger ins instead of insert on with_multiple_pks -- issue https://github.com/PostgREST/postgrest/issues/2283 create view self_recursive_view as table projects; create or replace view self_recursive_view as table self_recursive_view; + +CREATE FUNCTION test.computed_clients(test.projects) RETURNS SETOF test.clients ROWS 1 AS $$ + SELECT * FROM test.clients WHERE id = $1.client_id; +$$ LANGUAGE sql STABLE; + +CREATE FUNCTION test.computed_projects(test.clients) RETURNS SETOF test.projects ROWS 1 AS $$ + SELECT * FROM test.projects WHERE client_id = $1.id; +$$ LANGUAGE sql STABLE;