{-# LANGUAGE TypeFamilies, TypeOperators, ConstraintKinds, FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | Convenience facilities for working with nullable columns.
module Database.Selda.Nullable
  ( NonNull, (:?~)
  , nonNull, restrict', (?!)
  , (?==), (?/=), (?>), (?<), (?>=), (?<=), (?+), (?-), (?*), (?/)
  ) where
import Database.Selda
    ( SqlType,
      Same,
      Row,
      Col,
      Selector,
      Coalesce,
      Query,
      restrict,
      SqlOrd,
      (.==),
      (./=),
      (.>),
      (.<),
      (.>=),
      (.<=),
      isNull,
      not_ )
import Database.Selda.Unsafe (cast)
import Database.Selda.Selectors ( Selector(selectorIndex) )
import Database.Selda.Column
    ( UntypedCol(Untyped), Row(Many), Col(One) )
import Unsafe.Coerce ( unsafeCoerce )

-- | Two SQL types which are identical modulo nullability.
type a :?~ b =
  ( NonNull a ~ NonNull b
  , SqlType (NonNull a)
  , SqlType (NonNull b)
  )

type family NonNull a where
  NonNull (Maybe a) = a
  NonNull a         = a

-- | Unconditionally convert a nullable value into a non-nullable one,
--   using the standard SQL null-coalescing behavior.
fromNullable :: SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable :: forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable = forall a b. a -> b
unsafeCoerce

(?==), (?/=) :: (a :?~ b, SqlType a, Same s t) => Col s a -> Col t b -> Col s (Maybe Bool)

(?>), (?<), (?>=), (?<=) :: (a :?~ b, SqlOrd (NonNull a), Same s t)
                         => Col s a -> Col t b -> Col s (Maybe Bool)

(?+), (?-), (?*) :: (a :?~ b, Num (NonNull a), Same s t)
                 => Col s a -> Col t b -> Col s (Maybe (NonNull a))

(?/) :: (a :?~ b, Fractional (Col s (NonNull a)), Same s t)
     => Col s a -> Col t b -> Col s (Maybe (NonNull a))

Col s a
a ?== :: forall a b s t.
(a :?~ b, SqlType a, Same s t) =>
Col s a -> Col t b -> Col s (Maybe Bool)
?== Col t b
b = forall s a b. SqlType b => Col s a -> Col s b
cast forall a b. (a -> b) -> a -> b
$ forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col s a
a forall s t a.
(Same s t, SqlType a) =>
Col s a -> Col t a -> Col s Bool
.== forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col t b
b
Col s a
a ?/= :: forall a b s t.
(a :?~ b, SqlType a, Same s t) =>
Col s a -> Col t b -> Col s (Maybe Bool)
?/= Col t b
b = forall s a b. SqlType b => Col s a -> Col s b
cast forall a b. (a -> b) -> a -> b
$ forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col s a
a forall s t a.
(Same s t, SqlType a) =>
Col s a -> Col t a -> Col s Bool
./= forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col t b
b
Col s a
a ?> :: forall a b s t.
(a :?~ b, SqlOrd (NonNull a), Same s t) =>
Col s a -> Col t b -> Col s (Maybe Bool)
?>  Col t b
b = forall s a b. SqlType b => Col s a -> Col s b
cast forall a b. (a -> b) -> a -> b
$ forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col s a
a forall s t a.
(Same s t, SqlOrd a) =>
Col s a -> Col t a -> Col s Bool
.>  forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col t b
b
Col s a
a ?< :: forall a b s t.
(a :?~ b, SqlOrd (NonNull a), Same s t) =>
Col s a -> Col t b -> Col s (Maybe Bool)
?<  Col t b
b = forall s a b. SqlType b => Col s a -> Col s b
cast forall a b. (a -> b) -> a -> b
$ forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col s a
a forall s t a.
(Same s t, SqlOrd a) =>
Col s a -> Col t a -> Col s Bool
.<  forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col t b
b
Col s a
a ?>= :: forall a b s t.
(a :?~ b, SqlOrd (NonNull a), Same s t) =>
Col s a -> Col t b -> Col s (Maybe Bool)
?>= Col t b
b = forall s a b. SqlType b => Col s a -> Col s b
cast forall a b. (a -> b) -> a -> b
$ forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col s a
a forall s t a.
(Same s t, SqlOrd a) =>
Col s a -> Col t a -> Col s Bool
.>= forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col t b
b
Col s a
a ?<= :: forall a b s t.
(a :?~ b, SqlOrd (NonNull a), Same s t) =>
Col s a -> Col t b -> Col s (Maybe Bool)
?<= Col t b
b = forall s a b. SqlType b => Col s a -> Col s b
cast forall a b. (a -> b) -> a -> b
$ forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col s a
a forall s t a.
(Same s t, SqlOrd a) =>
Col s a -> Col t a -> Col s Bool
.<= forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col t b
b
Col s a
a ?+ :: forall a b s t.
(a :?~ b, Num (NonNull a), Same s t) =>
Col s a -> Col t b -> Col s (Maybe (NonNull a))
?+  Col t b
b = forall s a b. SqlType b => Col s a -> Col s b
cast forall a b. (a -> b) -> a -> b
$ forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col s a
a forall a. Num a => a -> a -> a
+   forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col t b
b
Col s a
a ?- :: forall a b s t.
(a :?~ b, Num (NonNull a), Same s t) =>
Col s a -> Col t b -> Col s (Maybe (NonNull a))
?-  Col t b
b = forall s a b. SqlType b => Col s a -> Col s b
cast forall a b. (a -> b) -> a -> b
$ forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col s a
a forall a. Num a => a -> a -> a
-   forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col t b
b
Col s a
a ?* :: forall a b s t.
(a :?~ b, Num (NonNull a), Same s t) =>
Col s a -> Col t b -> Col s (Maybe (NonNull a))
?*  Col t b
b = forall s a b. SqlType b => Col s a -> Col s b
cast forall a b. (a -> b) -> a -> b
$ forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col s a
a forall a. Num a => a -> a -> a
*   forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col t b
b
Col s a
a ?/ :: forall a b s t.
(a :?~ b, Fractional (Col s (NonNull a)), Same s t) =>
Col s a -> Col t b -> Col s (Maybe (NonNull a))
?/  Col t b
b = forall s a b. SqlType b => Col s a -> Col s b
cast forall a b. (a -> b) -> a -> b
$ forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col s a
a forall a. Fractional a => a -> a -> a
/   forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col t b
b
infixl 4 ?==
infixl 4 ?/=
infixl 4 ?>
infixl 4 ?<
infixl 4 ?>=
infixl 4 ?<=
infixl 6 ?+
infixl 6 ?-
infixl 7 ?*
infixl 7 ?/

-- | Selector indexing, overloaded to work on nullable as well as non-nullable
--   rows.
(?!) :: forall s t a. SqlType a
     => Row s t -> Selector (NonNull t) a -> Col s (Coalesce (Maybe a))
(Many [UntypedCol SQL]
xs) ?! :: forall s t a.
SqlType a =>
Row s t -> Selector (NonNull t) a -> Col s (Coalesce (Maybe a))
?! Selector (NonNull t) a
i = case [UntypedCol SQL]
xs forall a. [a] -> Int -> a
!! (forall t a. Selector t a -> Int
selectorIndex Selector (NonNull t) a
i) of Untyped Exp SQL a
x -> forall {k} (s :: k) a. Exp SQL a -> Col s a
One (forall a b. a -> b
unsafeCoerce Exp SQL a
x)
infixl 9 ?!

-- | Converts a nullable column into a non-nullable one, yielding the empty
--   result set if the column is null.
nonNull :: (Same s t, SqlType a) => Col s (Maybe a) -> Query t (Col t a)
nonNull :: forall s t a.
(Same s t, SqlType a) =>
Col s (Maybe a) -> Query t (Col t a)
nonNull Col s (Maybe a)
x = do
  forall s t. Same s t => Col s Bool -> Query t ()
restrict (forall s. Col s Bool -> Col s Bool
not_ forall a b. (a -> b) -> a -> b
$ forall a s. SqlType a => Col s (Maybe a) -> Col s Bool
isNull Col s (Maybe a)
x)
  forall (m :: * -> *) a. Monad m => a -> m a
return (forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable Col s (Maybe a)
x)

-- | Restrict a query using a nullable expression.
--   Equivalent to @restrict . ifNull false@.
restrict' :: Same s t => Col s (Maybe Bool) -> Query t ()
restrict' :: forall s t. Same s t => Col s (Maybe Bool) -> Query t ()
restrict' = forall s t. Same s t => Col s Bool -> Query t ()
restrict forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a s. SqlType (NonNull a) => Col s a -> Col s (NonNull a)
fromNullable