hasql-postgres
hasql-postgres copied to clipboard
Export more
At the moment you can't write instances for CxValue
or CxRow
for anything, since neither the StmtParam
nor the ResultValue
constructors are exported. (I need CxValue
instances for non-homogenous data, which I can get by using the JSON Value instance, but that is quite a roundabout method. Tuple instances for CxValue
would also work I guess, but still in a kind of roundabout way -- I'd be immediately repackaging them anyway)
I'm gonna need some motivating example, because I don't understand the use case so far.
I suspect that this is closely related to https://github.com/nikita-volkov/hasql-postgres/issues/4. I've provided an extensive explanation of why such these classes are not exported in that discussion.
I want StmtParam
and ResultValue
exported, so we can write our own CxRow
and CxValue
instances. I suppose you'd also need more of Mapping
exported for that to be possible (and convenient).
It's extremely cumbersome to work with many-element tuples, so I (and I expect everybody else...) repackage results from queries into custom types. The overhead is unnecessary, and the tuple to record code is very precarious. It's easier to be reason about clear code.
hasql-postgres
doesn't (and can't) support any queries that use array_agg
on "heterogeneous" (... homogenously heterogenous? :) I mean things that don't have the same type for all fields - ie, pretty well any table you'd actually use) data.
CREATE TABLE tag(id INT PRIMARY KEY, name TEXT);
CREATE TABLE post (id INT PRIMARY KEY, ... lots of stuff ...);
CREATE TABLE post_to_tag(post INT REFERENCES post(id), tag INT REFERENCES tag(id));
Here's a query that we can't do with these tables, with hasql-postgres
:
SELECT post.*, array_agg(tag.*) AS tags
FROM post
JOIN post_to_tag ON post_to_tag.post = post.id
JOIN tag ON post_to_tag.tag = tag.id
GROUP BY post.id;
It would be possible with tuple instances for CxValue
(I don't know whether you'd want to use a Mapping
instance behind the scenes), or with a custom instance for the Haskell-side Tag
type.
Looking into it, what it (tuple instances for Mapping (=> CxValue) -- not the argument for whether users should be able to write their own CxRow/CxValue instances) seems to boil down to is supporting composite types, which I think Postgres creates out of rows you give to array_agg
. A much simpler but maybe less compelling example is just
CREATE TABLE thing(foo int, bar text);
SELECT array_agg(thing.*) FROM thing;
Might be worth opening a new issue for that?
The way I understand it, your issue can be separated in three:
- Request for support of custom mappings to rows
- Request for support of custom mappings to Postgresql's types
- Request for support of Postgresql's "row" types. As I understand
array_arg
revolves around them.
Concerning the support for "row" types, I confirm, this should be implemented. However it will require some work in the "postgresql-binary" package. Unfortunately I'm not gonna have time to do this any time soon. Contribution is welcome though. The way I see it, "row" types should be mapped to tuples as well.
As for the custom mappings, the discussion in https://github.com/nikita-volkov/hasql-postgres/issues/4 is indeed closely related. Long story short, I find that types of result rows only make sense in the context of the executed query. They pertain to the query, not to a Postgres table or any custom model in Haskell. In one query you may be getting only the dates and titles of your posts, in another one - all the fields you have in that table, in a third one you'll be joining it with another table - are you gonna create a custom type for each such case? This is one of the reasons why I believe that tuples are the most consistent way to access the results of queries, and I intend them to be used as intermediate results, which the user than converts to some normalised model. Other reasons I explain in the aforementioned discussion.
My grumble is that tuple mangling code is just very cumbersome, as well as feeling unnecessary. For my application, I actually am making custom types for pretty much every query I'm using in outside-the-backend code. Dealing with these custom types is nicer than 10+ element tuples. I just compose these types together for large joins since I don't really have any cases where I want only some columns from whichever tables, but not others.
As is probably apparent from the content of my posts here, I don't know head nor tail of postgres internals... Maybe a look at hpqtypes
, which seems to support composite types (I'm not 100% on this, since at least in postgresql-simple
, they refer to "composite types" as to mean the concatenation of row columns), will be enlightening.
My grumble is that tuple mangling code is just very cumbersome, as well as feeling unnecessary. For my application, I actually am making custom types for pretty much every query I'm using in outside-the-backend code. Dealing with these custom types is nicer than 10+ element tuples. I just compose these types together for large joins since I don't really have any cases where I want only some columns from whichever tables, but not others.
Can you provide a few examples? It's just that I haven't met any such problems in my experience, but I don't want to judge subjectively.
I don't know head nor tail of postgres internals
Well, neither did I before I implemented that library ) Basically it's about translation of the "libpqtypes" library. Here is a post, which in part provides an introduction to how it can be approach.
I think as per #16, Generic
instances for CxRow
- ie: :*:
, :.:
, M1
and K1
- is a good compromise. Users can't shoot themselves in the foot so long as the Generic
instance is correct, but they also avoid any tuple-related foot incidents.
Thank you for your contribution, Mike. I'll check out the code on the weekend.