Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SQL IS binary operator #358

Open
FPtje opened this issue Apr 17, 2023 · 7 comments
Open

Support SQL IS binary operator #358

FPtje opened this issue Apr 17, 2023 · 7 comments

Comments

@FPtje
Copy link

FPtje commented Apr 17, 2023

I found myself manually creating the following definition:

is :: Esq.SqlExpr (Esq.Value a) -> Esq.SqlExpr (Esq.Value a) -> Esq.SqlExpr (Esq.Value Bool)
is = EsqInternal.unsafeSqlBinOp "IS"

Equivalently, one can imagine the following operator:

is_not :: Esq.SqlExpr (Esq.Value a) -> Esq.SqlExpr (Esq.Value a) -> Esq.SqlExpr (Esq.Value Bool)
is_not = EsqInternal.unsafeSqlBinOp "IS NOT"

(although this one can be done with the not_ (is ...) function)

The IS binary operator is most commonly seen in IS NULL and IS NOT NULL. esqueleto indeed has this in the isNothing function, but they can also be used for other values, e.g. foo IS true. The reason to use IS over = here is because they have different behaviors regarding NULL.

Here are the truth tables for x = y and x IS y for booleans:

x = y true false NULL
true true false NULL
false false true NULL
NULL NULL NULL NULL
x IS y true false NULL
true true false false
false false true false
NULL false false true

I didn't make a pull request because this function might be up to debate. Arguably, one might say that with a correct Persistent table definition, you statically know whether a column is NULL or not, so the IS operator may not technically be needed. On the other hand, there might be other expressions where NULL sneaks in to give funny behavior. What do you think?


Also, apologies if this issue is duplicate. It's kind of hard to search for a two-letter operator 😅

@belevy
Copy link
Collaborator

belevy commented Apr 18, 2023

Is IS supported like this in all databases? If so, is there ever a reason to use =? Does IS perform differently? Honestly it feels like .== should just use IS if this is actually supported in all 3 of our supported DBs

@belevy
Copy link
Collaborator

belevy commented Apr 18, 2023

looking this up, is can only be used for trivalued booleans (true, false, null)

@parsonsmatt
Copy link
Collaborator

So the proper type here is more like SqlExpr (Value (Maybe Bool)) -> SqlExpr (Value (Maybe Bool)) -> SqlExpr (Value (Maybe Bool))?

@Vlix
Copy link
Contributor

Vlix commented Dec 27, 2024

Should be is :: SqlExpr (Value (Maybe Bool)) -> SqlExpr (Value (Maybe Bool)) -> SqlExpr (Value Bool), no?

@parsonsmatt
Copy link
Collaborator

Yes, good catch- thanks!

@FPtje
Copy link
Author

FPtje commented Dec 27, 2024

I hadn't realized that the use is limited to true/false/null. The signature looks good to me 👍

@Vlix
Copy link
Contributor

Vlix commented Dec 27, 2024

Yeah, it's limited to Bool and Maybe a, so I'd personally probably want the API to be more like:

-- Boolean operator
is :: SqlExpr (Value (Maybe Bool)) -> SqlExpr (Value (Maybe Bool)) -> SqlExpr (Value Bool)
-- Synonym for `isNothing`
isNull :: SqlExpr (Value (Maybe a)) -> SqlExpr (Value Bool)
-- Synonym for `not_ . isNothing`
isNotNull :: SqlExpr (Value (Maybe a)) -> SqlExpr (Value Bool)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants