rel8 icon indicating copy to clipboard operation
rel8 copied to clipboard

Support for the `ANY` operator?

Open intractable opened this issue 2 years ago • 7 comments

Thanks for the fantastic library, I have been enjoying using it to adapt a bunch of code from handrolled SQL via postgresql-simple.

I have a SQL query that does something like WHERE a = ANY(as) where as is an array (uuid[] in this case) and a is an element, but I've not been able to figure out how to represent that in rel8 (or if it's possible at present).

E.g.,

let a :: Expr a = ... 
row@MyRow{as} <- someQuery -- as :: Expr (Vector a)
as <$ where_ (a `in_` as)

I obviously can't do that, as the types are wrong for in_, so I think I either need some way of writing a ==. any_ as (ideally that lowers to actual use of ANY, I suppose), or I need to do the equivalent thing via some other means.

Any insight is appreciated; I'm likely missing something obvious.

intractable avatar Aug 25 '22 18:08 intractable

As per https://github.com/circuithub/rel8/issues/188#issuecomment-1172500747 I think you might be ok with

any_ :: Expr [a] -> Expr a
any_ = dbFunction "any"

Then you would write

where_ $ uuid ==. any_ uuids

ocharles avatar Aug 25 '22 18:08 ocharles

any_ :: Expr [a] -> Expr a
any_ = function "any"

Unfortunately, that doesn't seem to work (and I'm not familiar enough with the instances yet to grok what it's complaining about yet), at least not in 1.3.1.0:

    • No instance for (DBType
                         (rel8-1.3.1.0:Rel8.Schema.Null.Unnullify'
                            (rel8-1.3.1.0:Rel8.Schema.Null.IsMaybe a) a))
        arising from a use of ‘function’
    • In the expression: function "any"
      In an equation for ‘any_’: any_ = function "any"

I'll give it a shot on 1.4.0.0 just in case.

intractable avatar Aug 25 '22 19:08 intractable

Try sticking a DBType a => at the start?

ocharles avatar Aug 26 '22 07:08 ocharles

That worked, but it looks like the generated SQL is invalid; I believe because how the cast is being applied? An expression like u ==. any_ us renders as

((CAST(E'94692290-05a2-2f1f-9e26-f31cea05c7c2' AS uuid)) = (CAST(ANY(\"us1_15\") AS uuid)))

and the captured error complains about the use of ANY:

ResultError (ServerError "42601" "syntax error at or near \"ANY\"" Nothing Nothing)

intractable avatar Aug 26 '22 13:08 intractable

Ah, that's no good. We may have to add this in then, thanks for testing!

ocharles avatar Aug 30 '22 07:08 ocharles

I'm probably not familiar enough with the codebase to contribute much yet, but if you're inclined to sketch what you think is needed at a high level, I'd be happy to take a stab at it. Maybe an annotation of some kind for constructs like this which force the cast to be omitted?

intractable avatar Aug 30 '22 12:08 intractable

For what it's worth @intractable, here's a snippet from CircuitHub's codebase that provides a function elem :: Sql DBEq a => Expr a -> Expr [a] -> Expr Bool which like it would do what you're looking for:

(<@) :: Sql DBEq a => Expr [a] -> Expr [a] -> Expr Bool
(<@) = binaryOperator "<@"


elem :: Sql DBEq a => Expr a -> Expr [a] -> Expr Bool
elem = (<@) . (id $*) . listTable . pure

We should probably move this into Rel8 proper at some point.

shane-circuithub avatar Oct 17 '22 13:10 shane-circuithub

@shane-circuithub Somehow I had missed this, thanks very much for the example!

intractable avatar Jun 22 '23 19:06 intractable