squeal-postgresql-0.9.2.0: src/Squeal/PostgreSQL/Expression/Null.hs
{-|
Module: Squeal.PostgreSQL.Expression.Null
Description: null expressions and handlers
Copyright: (c) Eitan Chatav, 2019
Maintainer: eitan@morphism.tech
Stability: experimental
null expressions and handlers
-}
{-# LANGUAGE
DataKinds
, KindSignatures
, OverloadedStrings
, RankNTypes
, TypeFamilies
, TypeOperators
#-}
module Squeal.PostgreSQL.Expression.Null
( -- * Null
null_
, just_
, unsafeNotNull
, monoNotNull
, coalesce
, fromNull
, isNull
, isNotNull
, matchNull
, nullIf
, CombineNullity
-- deprecated
, notNull
) where
import Squeal.PostgreSQL.Expression
import Squeal.PostgreSQL.Expression.Logic
import Squeal.PostgreSQL.Render
import Squeal.PostgreSQL.Type.Schema
-- $setup
-- >>> import Squeal.PostgreSQL
-- | analagous to `Nothing`
--
-- >>> printSQL null_
-- NULL
null_ :: Expr ('Null ty)
null_ = UnsafeExpression "NULL"
-- | analagous to `Just`
--
-- >>> printSQL $ just_ true
-- TRUE
just_ :: 'NotNull ty --> 'Null ty
just_ = UnsafeExpression . renderSQL
-- | analagous to `Just`
{-# DEPRECATED notNull "use just_ instead" #-}
notNull :: 'NotNull ty --> 'Null ty
notNull = UnsafeExpression . renderSQL
-- | Analagous to `Data.Maybe.fromJust` inverse to `notNull`,
-- useful when you know an `Expression` is `NotNull`,
-- because, for instance, you've filtered out @NULL@
-- values in a column.
unsafeNotNull :: 'Null ty --> 'NotNull ty
unsafeNotNull = UnsafeExpression . renderSQL
-- | Some expressions are null polymorphic which may raise
-- inference issues. Use `monoNotNull` to fix their
-- nullity as `NotNull`.
monoNotNull
:: (forall null. Expression grp lat with db params from (null ty))
-- ^ null polymorphic
-> Expression grp lat with db params from ('NotNull ty)
monoNotNull x = x
-- | return the leftmost value which is not NULL
--
-- >>> printSQL $ coalesce [null_, true] false
-- COALESCE(NULL, TRUE, FALSE)
coalesce :: FunctionVar ('Null ty) (null ty) (null ty)
coalesce nullxs notNullx = UnsafeExpression $
"COALESCE" <> parenthesized (commaSeparated
((renderSQL <$> nullxs) <> [renderSQL notNullx]))
-- | analagous to `Data.Maybe.fromMaybe` using @COALESCE@
--
-- >>> printSQL $ fromNull true null_
-- COALESCE(NULL, TRUE)
fromNull
:: Expression grp lat with db params from ('NotNull ty)
-- ^ what to convert @NULL@ to
-> Expression grp lat with db params from ('Null ty)
-> Expression grp lat with db params from ('NotNull ty)
fromNull notNullx nullx = coalesce [nullx] notNullx
-- | >>> printSQL $ null_ & isNull
-- NULL IS NULL
isNull :: 'Null ty --> null 'PGbool
isNull x = UnsafeExpression $ renderSQL x <+> "IS NULL"
-- | >>> printSQL $ null_ & isNotNull
-- NULL IS NOT NULL
isNotNull :: 'Null ty --> null 'PGbool
isNotNull x = UnsafeExpression $ renderSQL x <+> "IS NOT NULL"
-- | analagous to `maybe` using @IS NULL@
--
-- >>> printSQL $ matchNull true not_ null_
-- CASE WHEN NULL IS NULL THEN TRUE ELSE (NOT NULL) END
matchNull
:: Expression grp lat with db params from (nullty)
-- ^ what to convert @NULL@ to
-> ( Expression grp lat with db params from ('NotNull ty)
-> Expression grp lat with db params from (nullty) )
-- ^ function to perform when @NULL@ is absent
-> Expression grp lat with db params from ('Null ty)
-> Expression grp lat with db params from (nullty)
matchNull y f x = ifThenElse (isNull x) y
(f (UnsafeExpression (renderSQL x)))
{-| right inverse to `fromNull`, if its arguments are equal then
`nullIf` gives @NULL@.
>>> :set -XTypeApplications
>>> printSQL (nullIf (false *: param @1))
NULLIF(FALSE, ($1 :: bool))
-}
nullIf :: '[ 'NotNull ty, 'NotNull ty] ---> 'Null ty
nullIf = unsafeFunctionN "NULLIF"
{-| Make the return type of the type family `NotNull` if both arguments are,
or `Null` otherwise.
-}
type family CombineNullity
(lhs :: PGType -> NullType) (rhs :: PGType -> NullType) :: PGType -> NullType where
CombineNullity 'NotNull 'NotNull = 'NotNull
CombineNullity _ _ = 'Null