fix: Avoid casting to table type when select= and media type handler are used

Previously using a generic mimetype handler failed when any kind of select= was given, because
we tried to cast the select-result to the original table type. With this change, this cast is
only applied when select=* is given implicitly or explicitly. This is the only case where this
makes sense, because this guarantees that correct columns are selected in the correct order for
this cast to succeed.

Resolves #3160
This commit is contained in:
Wolfgang Walther
2024-02-15 19:01:12 +01:00
parent 2466f4e738
commit 00fbe9ff3e
10 changed files with 131 additions and 43 deletions
@@ -230,6 +230,76 @@ spec = describe "custom media types" $ do
simpleHeaders r `shouldContain` [("Content-Type", "text/csv; charset=utf-8")]
simpleHeaders r `shouldContain` [("Content-Disposition", "attachment; filename=\"lines.csv\"")]
-- https://github.com/PostgREST/postgrest/issues/3160
context "using select query parameter" $ do
it "without select" $ do
request methodGet "/projects?id=in.(1,2)" (acceptHdrs "pg/outfunc") ""
`shouldRespondWith`
[str|(1,"Windows 7",1)
|(2,"Windows 10",1)
|]
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "pg/outfunc"]
}
it "with fewer columns selected" $ do
request methodGet "/projects?id=in.(1,2)&select=id,name" (acceptHdrs "pg/outfunc") ""
`shouldRespondWith`
[str|(1,"Windows 7")
|(2,"Windows 10")
|]
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "pg/outfunc"]
}
it "with columns in different order" $ do
request methodGet "/projects?id=in.(1,2)&select=name,id,client_id" (acceptHdrs "pg/outfunc") ""
`shouldRespondWith`
[str|("Windows 7",1,1)
|("Windows 10",2,1)
|]
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "pg/outfunc"]
}
it "with computed columns" $ do
request methodGet "/items?id=in.(1,2)&select=id,always_true" (acceptHdrs "pg/outfunc") ""
`shouldRespondWith`
[str|(1,t)
|(2,t)
|]
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "pg/outfunc"]
}
-- TODO: Embeddings should not return JSON. Arrays of record would be much better.
it "with embedding" $ do
request methodGet "/projects?id=in.(1,2)&select=*,clients(id)" (acceptHdrs "pg/outfunc") ""
`shouldRespondWith`
[str|(1,"Windows 7",1,"{""id"": 1}")
|(2,"Windows 10",1,"{""id"": 1}")
|]
{ matchStatus = 200
, matchHeaders = ["Content-Type" <:> "pg/outfunc"]
}
it "will fail for specific aggregate with fewer columns" $ do
request methodGet "/lines?select=id" (acceptHdrs "application/vnd.twkb") ""
`shouldRespondWith` 406
it "will fail for specific aggregate with more columns" $ do
request methodGet "/lines?select=id,name,geom,id" (acceptHdrs "application/vnd.twkb") ""
`shouldRespondWith` 406
it "will fail for specific aggregate with columns in different order" $ do
request methodGet "/lines?select=name,id,geom" (acceptHdrs "application/vnd.twkb") ""
`shouldRespondWith` 406
-- This is just because it would be hard to detect this case, so we better error in this case, too.
it "will fail for specific aggregate with columns in same order" $ do
request methodGet "/lines?select=id,name,geom" (acceptHdrs "application/vnd.twkb") ""
`shouldRespondWith` 406
context "any media type" $ do
context "on functions" $ do
it "returns application/json for */* if not explicitly set" $ do
+15 -2
View File
@@ -3550,8 +3550,8 @@ returns "application/vnd.geo2+json" as $$
select (jsonb_build_object('type', 'FeatureCollection', 'hello', 'world'))::"application/vnd.geo2+json";
$$ language sql;
drop aggregate if exists test.geo2json_agg(anyelement);
create aggregate test.geo2json_agg(anyelement) (
drop aggregate if exists test.geo2json_agg_any(anyelement);
create aggregate test.geo2json_agg_any(anyelement) (
initcond = '[]'
, stype = "application/vnd.geo2+json"
, sfunc = geo2json_trans
@@ -3755,3 +3755,16 @@ create aggregate test.some_agg (some_numbers) (
create view bad_subquery as
select * from projects where id = (select id from projects);
-- custom generic mimetype
create domain "pg/outfunc" as text;
create function test.outfunc_trans (state text, next anyelement)
returns "pg/outfunc" as $$
select (state || next::text || E'\n')::"pg/outfunc";
$$ language sql;
create aggregate test.outfunc_agg (anyelement) (
initcond = ''
, stype = "pg/outfunc"
, sfunc = outfunc_trans
);