{-# language FlexibleContexts #-}
{-# language GADTs #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language ViewPatterns #-}

{-# options_ghc -fno-warn-redundant-constraints #-}

module Rel8.Expr.Eq
  ( (==.), (/=.)
  , (==?), (/=?)
  , in_
  )
where

-- base
import Data.Foldable ( toList )
import Data.List.NonEmpty ( nonEmpty )
import Prelude

-- opaleye
import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye

-- rel8
import Rel8.Expr ( Expr )
import Rel8.Expr.Bool ( (&&.), (||.), false, or_, coalesce )
import Rel8.Expr.Null ( isNull, unsafeLiftOpNull )
import Rel8.Expr.Opaleye ( fromPrimExpr, toPrimExpr, zipPrimExprsWith )
import Rel8.Schema.Null ( Nullity( NotNull, Null ), Sql, nullable )
import Rel8.Type.Eq ( DBEq )


eq :: DBEq a => Expr a -> Expr a -> Expr Bool
eq :: Expr a -> Expr a -> Expr Bool
eq = (PrimExpr -> PrimExpr -> PrimExpr) -> Expr a -> Expr a -> Expr Bool
forall a b c.
(PrimExpr -> PrimExpr -> PrimExpr) -> Expr a -> Expr b -> Expr c
zipPrimExprsWith (BinOp -> PrimExpr -> PrimExpr -> PrimExpr
Opaleye.BinExpr BinOp
(Opaleye.:==))


ne :: DBEq a => Expr a -> Expr a -> Expr Bool
ne :: Expr a -> Expr a -> Expr Bool
ne = (PrimExpr -> PrimExpr -> PrimExpr) -> Expr a -> Expr a -> Expr Bool
forall a b c.
(PrimExpr -> PrimExpr -> PrimExpr) -> Expr a -> Expr b -> Expr c
zipPrimExprsWith (BinOp -> PrimExpr -> PrimExpr -> PrimExpr
Opaleye.BinExpr BinOp
(Opaleye.:<>))


-- | Compare two expressions for equality. 
--
-- This corresponds to the SQL @IS NOT DISTINCT FROM@ operator, and will equate
-- @null@ values as @true@. This differs from @=@ which would return @null@.
-- This operator matches Haskell's '==' operator. For an operator identical to
-- SQL @=@, see '==?'.
(==.) :: forall a. Sql DBEq a => Expr a -> Expr a -> Expr Bool
==. :: Expr a -> Expr a -> Expr Bool
(==.) = case Nullable a => Nullity a
forall a. Nullable a => Nullity a
nullable @a of
  Nullity a
Null -> \Expr a
ma Expr a
mb -> Expr (Maybe (Unnullify' (IsMaybe a) a)) -> Expr Bool
forall a. Expr (Maybe a) -> Expr Bool
isNull Expr a
Expr (Maybe (Unnullify' (IsMaybe a) a))
ma Expr Bool -> Expr Bool -> Expr Bool
&&. Expr (Maybe (Unnullify' (IsMaybe a) a)) -> Expr Bool
forall a. Expr (Maybe a) -> Expr Bool
isNull Expr a
Expr (Maybe (Unnullify' (IsMaybe a) a))
mb Expr Bool -> Expr Bool -> Expr Bool
||. Expr a
Expr (Maybe (Unnullify' (IsMaybe a) a))
ma Expr (Maybe (Unnullify' (IsMaybe a) a))
-> Expr (Maybe (Unnullify' (IsMaybe a) a)) -> Expr Bool
forall a. DBEq a => Expr (Maybe a) -> Expr (Maybe a) -> Expr Bool
==? Expr a
Expr (Maybe (Unnullify' (IsMaybe a) a))
mb
  Nullity a
NotNull -> Expr a -> Expr a -> Expr Bool
forall a. DBEq a => Expr a -> Expr a -> Expr Bool
eq
infix 4 ==.
{-# INLINABLE (==.) #-}


-- | Test if two expressions are different (not equal).
--
-- This corresponds to the SQL @IS DISTINCT FROM@ operator, and will return
-- @false@ when comparing two @null@ values. This differs from ordinary @=@
-- which would return @null@. This operator is closer to Haskell's '=='
-- operator. For an operator identical to SQL @=@, see '/=?'.
(/=.) :: forall a. Sql DBEq a => Expr a -> Expr a -> Expr Bool
/=. :: Expr a -> Expr a -> Expr Bool
(/=.) = case Nullable a => Nullity a
forall a. Nullable a => Nullity a
nullable @a of
  Nullity a
Null -> \Expr a
ma Expr a
mb -> Expr (Maybe (Unnullify' (IsMaybe a) a)) -> Expr Bool
forall a. Expr (Maybe a) -> Expr Bool
isNull Expr a
Expr (Maybe (Unnullify' (IsMaybe a) a))
ma Expr Bool -> Expr Bool -> Expr Bool
forall a. DBEq a => Expr a -> Expr a -> Expr Bool
`ne` Expr (Maybe (Unnullify' (IsMaybe a) a)) -> Expr Bool
forall a. Expr (Maybe a) -> Expr Bool
isNull Expr a
Expr (Maybe (Unnullify' (IsMaybe a) a))
mb Expr Bool -> Expr Bool -> Expr Bool
||. Expr a
Expr (Maybe (Unnullify' (IsMaybe a) a))
ma Expr (Maybe (Unnullify' (IsMaybe a) a))
-> Expr (Maybe (Unnullify' (IsMaybe a) a)) -> Expr Bool
forall a. DBEq a => Expr (Maybe a) -> Expr (Maybe a) -> Expr Bool
/=? Expr a
Expr (Maybe (Unnullify' (IsMaybe a) a))
mb
  Nullity a
NotNull -> Expr a -> Expr a -> Expr Bool
forall a. DBEq a => Expr a -> Expr a -> Expr Bool
ne
infix 4 /=.
{-# INLINABLE (/=.) #-}


-- | Test if two expressions are equal. This operator is usually the best
-- choice when forming join conditions, as PostgreSQL has a much harder time
-- optimizing a join that has multiple 'True' conditions.
--
-- This corresponds to the SQL @=@ operator, though it will always return a
-- 'Bool'.
(==?) :: DBEq a => Expr (Maybe a) -> Expr (Maybe a) -> Expr Bool
Expr (Maybe a)
a ==? :: Expr (Maybe a) -> Expr (Maybe a) -> Expr Bool
==? Expr (Maybe a)
b = Expr (Maybe Bool) -> Expr Bool
coalesce (Expr (Maybe Bool) -> Expr Bool) -> Expr (Maybe Bool) -> Expr Bool
forall a b. (a -> b) -> a -> b
$ (Expr a -> Expr a -> Expr Bool)
-> Expr (Maybe a) -> Expr (Maybe a) -> Expr (Maybe Bool)
forall c a b.
NotNull c =>
(Expr a -> Expr b -> Expr c)
-> Expr (Maybe a) -> Expr (Maybe b) -> Expr (Maybe c)
unsafeLiftOpNull Expr a -> Expr a -> Expr Bool
forall a. DBEq a => Expr a -> Expr a -> Expr Bool
eq Expr (Maybe a)
a Expr (Maybe a)
b
infix 4 ==?


-- | Test if two expressions are different. 
--
-- This corresponds to the SQL @<>@ operator, though it will always return a
-- 'Bool'.
(/=?) :: DBEq a => Expr (Maybe a) -> Expr (Maybe a) -> Expr Bool
Expr (Maybe a)
a /=? :: Expr (Maybe a) -> Expr (Maybe a) -> Expr Bool
/=? Expr (Maybe a)
b = Expr (Maybe Bool) -> Expr Bool
coalesce (Expr (Maybe Bool) -> Expr Bool) -> Expr (Maybe Bool) -> Expr Bool
forall a b. (a -> b) -> a -> b
$ (Expr a -> Expr a -> Expr Bool)
-> Expr (Maybe a) -> Expr (Maybe a) -> Expr (Maybe Bool)
forall c a b.
NotNull c =>
(Expr a -> Expr b -> Expr c)
-> Expr (Maybe a) -> Expr (Maybe b) -> Expr (Maybe c)
unsafeLiftOpNull Expr a -> Expr a -> Expr Bool
forall a. DBEq a => Expr a -> Expr a -> Expr Bool
ne Expr (Maybe a)
a Expr (Maybe a)
b
infix 4 /=?


-- | Like the SQL @IN@ operator, but implemented by folding over a list with
-- '==.' and '||.'.
in_ :: forall a f. (Sql DBEq a, Foldable f)
  => Expr a -> f (Expr a) -> Expr Bool
in_ :: Expr a -> f (Expr a) -> Expr Bool
in_ Expr a
a (f (Expr a) -> [Expr a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList -> [Expr a]
as) = case Nullable a => Nullity a
forall a. Nullable a => Nullity a
nullable @a of
  Nullity a
Null -> [Expr Bool] -> Expr Bool
forall (f :: * -> *). Foldable f => f (Expr Bool) -> Expr Bool
or_ ([Expr Bool] -> Expr Bool) -> [Expr Bool] -> Expr Bool
forall a b. (a -> b) -> a -> b
$ (Expr a -> Expr Bool) -> [Expr a] -> [Expr Bool]
forall a b. (a -> b) -> [a] -> [b]
map (Expr a
a Expr a -> Expr a -> Expr Bool
forall a. Sql DBEq a => Expr a -> Expr a -> Expr Bool
==.) [Expr a]
as
  Nullity a
NotNull -> case [Expr a] -> Maybe (NonEmpty (Expr a))
forall a. [a] -> Maybe (NonEmpty a)
nonEmpty [Expr a]
as of
     Maybe (NonEmpty (Expr a))
Nothing -> Expr Bool
false
     Just NonEmpty (Expr a)
xs ->
       PrimExpr -> Expr Bool
forall a. PrimExpr -> Expr a
fromPrimExpr (PrimExpr -> Expr Bool) -> PrimExpr -> Expr Bool
forall a b. (a -> b) -> a -> b
$
         BinOp -> PrimExpr -> PrimExpr -> PrimExpr
Opaleye.BinExpr BinOp
Opaleye.OpIn
           (Expr a -> PrimExpr
forall a. Expr a -> PrimExpr
toPrimExpr Expr a
a)
           (NonEmpty PrimExpr -> PrimExpr
Opaleye.ListExpr (Expr a -> PrimExpr
forall a. Expr a -> PrimExpr
toPrimExpr (Expr a -> PrimExpr) -> NonEmpty (Expr a) -> NonEmpty PrimExpr
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty (Expr a)
xs))