tomjaguarpaw / haskell-opaleye

Other
602 stars 115 forks source link

Allow selecting from relation-valued expressions without strings #567

Closed shane-circuithub closed 1 year ago

shane-circuithub commented 1 year ago

The current relation-valued expressions API is intended to generate SQL like this:

SELECT
  foo AS foo_1,
  bar AS bar_1
FROM
  rel_expr()

However, consider the case of PostgreSQL's unnest function:

=> SELECT * FROM unnest(array[1, 2, 3], array['a', 'b', 'c']);
 unnest | unnest
--------+--------
      1 | a
      2 | b
      3 | c
(3 rows)

When given multiple arrays (which is necessary to zip them together), it returns a relation with multiple columns all named unnest. If we tried to use this with Opaleye, we would get:

SELECT
  unnest AS a,
  unnest AS b
FROM
  unnest(array[1, 2, 3], array['a', 'b, 'c'])

This doesn't work because there's no way to disambiguate between the unnest columns. The way the unnest function is intended to be used is with the following syntax:

SELECT
  *
FROM
  unnest(array[1, 2, 3], array['a', 'b', 'c']) as t(a, b)

But until now there has been no way to generate that syntax with Opaleye.

This PR changes TableAlias in Opaleye.Internal.Print from type TableAlias = String to data TableAlias = TableAlias String (Maybe [SqlColumn]) to allow table aliases that also rename their columns.

The RelExprMaker profunctor is replaced with a simplified RelExprPP (which allows us to drop ViewColumnMaker) that no longer takes a collection of strings, instead it generates a SELECT * and the relation valued expr's columns are renamed using the AS T(a, b, c) syntax with auto-generated column names. This allows it to be used with unnest properly.

tomjaguarpaw commented 1 year ago

Thanks for this. It now needs rebasing. In fact I did that myself in the rel-expr branch of this repo so you can just checkout that and force push over your one.

Can you produce a test that fails on master but this PR fixes?

shane-circuithub commented 1 year ago

@tomjaguarpaw I've rebased, and added a new prior commit with a (failing) test, which, with the subsequent commit (and a slight modification to the test code itself on account of the API change), now passes.

shane-circuithub commented 1 year ago

Hey, any update on this? Are you happy with the test case?

tomjaguarpaw commented 1 year ago

Thanks for the poke. I'll aim to look at this on Sunday.

tomjaguarpaw commented 1 year ago

I released 0.10.0.0, incorporating this: https://hackage.haskell.org/package/opaleye-0.10.0.0

tomjaguarpaw commented 1 year ago

I split the version I merged into smaller commits so that I could see that they were all basically trivial except the payload, and the payload was pretty straightforward. In general I'll merge PRs a lot quicker if they're already in this format, otherwise I have to go through putting them into that format.

Thanks very much for this. Great work as usual!