Open parsonsmatt opened 3 years ago
I'm not sure that this is really what you want. Joins and where's aren't really peers. Ergonomics would be better focused on the from expression language. You have correctly acknowledge the issue with a monadic from language (without requiring all subqueries to be lateral). I wonder if From could admit a monad safely? At the very least a limited FromBuilder could be made that did but not sure how it would look.
Yeah, what I'm imagining is something like: Swap WriterT SideData
to StateT SideData
. Then I can write modifyLastJoin :: (FromClause -> SqlQuery FromClause) -> SqlQuery ()
. which would let me slap a LeftOuterJoin
on.
Something like:
leftOuterJoin k = do
lastJoin <- popLastJoin
from $ lastJoin `LeftOuterJoin` table @t `on` (\(_ :& t) -> k t)
popLastJoin :: SqlQuery FromClause
popLastJoin = do
lift $ state $ \sd ->
let (old, last) = unsnoc $ sdFromClause sd
in (last, sd { sdFromClause = old })
It's true that having multiple from
with a SubQuery
allows you to produce a LATERAL
query illegally, but that's already true if you're passing SqlExpr (Entity e)
as inputs to functions.
I wonder if LinearTypes
could be used here. I doubt it, but it'd be neat. Something like, "this value must not refer to anything not introduced in it's own scope." ST s
comes to mind as a related trick, but probably not worth the effort.
It's true that having multiple from with a SubQuery allows you to produce a LATERAL query illegally, but that's already true if you're passing SqlExpr (Entity e) as inputs to functions.
This is probably somethign that would require an indexed monad to fix. Alternatively we can make it always just lateral cross join if there is already a from clause.
I wonder if LinearTypes could be used here. I doubt it, but it'd be neat. Something like, "this value must not refer to anything not introduced in it's own scope." ST s comes to mind as a related trick, but probably not worth the effort.
Beam uses the ST trick to prevent this and it is miserable to use, I prefer discouraging use of from
twice.
I am not opposed to changing the writer to a state (it would cleanup the internals of subqueries) but what you are proposing leaves ambiguous references to the results of lastJoin potentially hanging since they will be returned again by leftOuterJoin
We can write
innerJoin
relatively easily:Well, okay, it's a
CROSS JOIN
with aWHERE
. Usage looks nice.With a bigass query, and lots of tables, the
on
lambda becomes a bit cumbersome. Factoring this out helps a lot.But...
Well, it's a hack - it only works because
FROM a, b WHERE a.id = b.a
is equivalent toFROM a INNER JOIN b ON a.id = b.a
. We can't do left joins, and it also re-opens the problem forLATERAL
if we allow subqueries.If we had access to the
[FromClause]
in theSideData
record, we could easily do this.except, well, we'd need to overwrite the final clause.
Even getting the final clause isn't possible with
WriterT
. It's fine withStateT
.Except, well, fuck, all the
Experimental
stuff is doingFromRaw
and building the bytestrings directly. So modifying them after the fact isn't really a thing.Actually maybe it's fine. Anyway I need to explore this a bit more.