diff --git a/CHANGELOG.md b/CHANGELOG.md index d1f65f6164..09e725cf4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #3558, Add the `admin-server-host` config to set the host for the admin server - @develop7 - #3607, Log to stderr when the JWT secret is less than 32 characters long - @laurenceisla +### Fixed + + - #3093, Nested empty embeds no longer show empty values and are correctly omitted - @laurenceisla + ### Changed - #2052, Dropped support for PostgreSQL 9.6 - @wolfgangwalther diff --git a/src/PostgREST/Plan.hs b/src/PostgREST/Plan.hs index db52d36332..4acedf4264 100644 --- a/src/PostgREST/Plan.hs +++ b/src/PostgREST/Plan.hs @@ -622,7 +622,11 @@ generateRelSelectField (Node ReadPlan{relToParent=Just rel, select, relName, rel where rsSelName = fromMaybe relName relAlias rsEmbedMode = if relIsToOne rel then JsonObject else JsonArray - rsEmptyEmbed = null select && null forest + rsEmptyEmbed = hasOnlyNullEmbed (null select) forest + hasOnlyNullEmbed = foldr checkIfNullEmbed + checkIfNullEmbed :: ReadPlanTree -> Bool -> Bool + checkIfNullEmbed (Node ReadPlan{select=s} f) isNullEmbed = + isNullEmbed && hasOnlyNullEmbed (null s) f generateRelSelectField _ = Nothing generateSpreadSelectFields :: ReadPlan -> [SpreadSelectField] diff --git a/test/spec/Feature/Query/QuerySpec.hs b/test/spec/Feature/Query/QuerySpec.hs index 11ee50e266..696b6d390b 100644 --- a/test/spec/Feature/Query/QuerySpec.hs +++ b/test/spec/Feature/Query/QuerySpec.hs @@ -1207,6 +1207,20 @@ spec = do [json|[{"id":1,"name":"Angela Martin"}]|] { matchHeaders = [matchContentTypeJson] } + it "works on nested relationships" $ do + get "/users?select=*,users_tasks(tasks(projects()))" `shouldRespondWith` + [json| [{"id":1,"name":"Angela Martin"}, {"id":2,"name":"Michael Scott"}, {"id":3,"name":"Dwight Schrute"}]|] + { matchHeaders = [matchContentTypeJson] } + get "/users?select=*,users_tasks!inner(tasks!inner(projects()))&users_tasks.tasks.id=eq.3" `shouldRespondWith` + [json| [{"id":1,"name":"Angela Martin"}]|] + { matchHeaders = [matchContentTypeJson] } + get "/users?select=*,tasks(projects(clients()),users_tasks())" `shouldRespondWith` + [json| [{"id":1,"name":"Angela Martin"}, {"id":2,"name":"Michael Scott"}, {"id":3,"name":"Dwight Schrute"}]|] + { matchHeaders = [matchContentTypeJson] } + get "/users?select=*,tasks!inner(projects(clients()),users_tasks(),name)&tasks.id=eq.3" `shouldRespondWith` + [json| [{"id":1,"name":"Angela Martin","tasks":[{"name": "Design w10"}]}]|] + { matchHeaders = [matchContentTypeJson] } + context "empty root select" $ it "gives all columns" $ do get "/projects?select=" `shouldRespondWith` @@ -1394,4 +1408,3 @@ spec = do get "/infinite_recursion?select=*" `shouldRespondWith` [json|{"code":"42P17","message":"infinite recursion detected in rules for relation \"infinite_recursion\"","details":null,"hint":null}|] { matchStatus = 500 } -