I was playing around with composite types in Postgres and Npgsql, and noticed interesting behaviour.
I have a UDT in the db created as follows:
CREATE TYPE ids_test AS (first int, second int);
In addition, there is a table ‘test’, which mirrors UDT: two int columns, nothing else.
I also have a corresponding C# class ‘test_ids’ and map it to this UDT. The query is pretty simple:
select *
from test
join (select * from unnest(@idpairs)) as pairs
on pairs.first = test.firstid and pairs.second = test.secondid
The parameter @idpairs
is passed using dynamic params:
var dynamicParameters = new DynamicParameters();
dynamicParameters.Add("@idpairs", ids.Select(_ => new test_ids { first = _.f, second = _.s }).ToList());
var npgsqlConnection1 = new NpgsqlConnection(conStr);
npgsqlConnection1.Open();
npgsqlConnection1.TypeMapper.MapComposite<test_ids>("ids_test");
using (npgsqlConnection1)
{
var res = (await npgsqlConnection1.QueryAsync(query, dynamicParameters)).AsList();
}
Now, here’s the part I don’t understand. When I grab the query from csv log, I have the query and its params:
execute : select *
from test
join (select * from unnest(‘{“(1,1)”,”(2,2)”,”(3,3)”}’)) as pairs
on pairs.first = test.firstid and pairs.second = test.secondidparameters: $1 = ‘{“(1,1)”,”(2,2)”,”(3,3)”}’
However, when I inline the param and try to execute the query, I get the ‘function unnest(unknown) is not unique’ error. Inline query:
select *
from test
join (select * from unnest('{"(1,1)","(2,2)","(3,3)"}')) as pairs
on pairs.first = test.firstid and pairs.second = test.secondid
As far as I understand, the inline query fails because Postgres cannot determine how to interpret the string and requires a cast. However, I don’t get why the query from the app succeeds.
Furthermore, even if I add an explicit cast, the result of unnest does not contain column names. The question is, what does the app do to make this query work correctly?