{-# LANGUAGE TypeApplications #-}
module Database.PostgreSQL.PQTypes.Composite (
    Composite(..)
  , unComposite
  , CompositeRow
  , CompositeFromSQL(..)
  , CompositeToSQL(..)
  ) where

import Data.Kind (Type)
import Foreign.Ptr
import qualified Control.Exception as E

import Database.PostgreSQL.PQTypes.Format
import Database.PostgreSQL.PQTypes.FromRow
import Database.PostgreSQL.PQTypes.FromSQL
import Database.PostgreSQL.PQTypes.Internal.C.Interface
import Database.PostgreSQL.PQTypes.Internal.C.Types
import Database.PostgreSQL.PQTypes.Internal.Utils
import Database.PostgreSQL.PQTypes.ToRow
import Database.PostgreSQL.PQTypes.ToSQL

-- | Wrapper for (de)serializing composite types.
newtype Composite a = Composite a
  deriving (Composite a -> Composite a -> Bool
forall a. Eq a => Composite a -> Composite a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Composite a -> Composite a -> Bool
$c/= :: forall a. Eq a => Composite a -> Composite a -> Bool
== :: Composite a -> Composite a -> Bool
$c== :: forall a. Eq a => Composite a -> Composite a -> Bool
Eq, forall a b. a -> Composite b -> Composite a
forall a b. (a -> b) -> Composite a -> Composite b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Composite b -> Composite a
$c<$ :: forall a b. a -> Composite b -> Composite a
fmap :: forall a b. (a -> b) -> Composite a -> Composite b
$cfmap :: forall a b. (a -> b) -> Composite a -> Composite b
Functor, Composite a -> Composite a -> Bool
Composite a -> Composite a -> Ordering
Composite a -> Composite a -> Composite a
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall {a}. Ord a => Eq (Composite a)
forall a. Ord a => Composite a -> Composite a -> Bool
forall a. Ord a => Composite a -> Composite a -> Ordering
forall a. Ord a => Composite a -> Composite a -> Composite a
min :: Composite a -> Composite a -> Composite a
$cmin :: forall a. Ord a => Composite a -> Composite a -> Composite a
max :: Composite a -> Composite a -> Composite a
$cmax :: forall a. Ord a => Composite a -> Composite a -> Composite a
>= :: Composite a -> Composite a -> Bool
$c>= :: forall a. Ord a => Composite a -> Composite a -> Bool
> :: Composite a -> Composite a -> Bool
$c> :: forall a. Ord a => Composite a -> Composite a -> Bool
<= :: Composite a -> Composite a -> Bool
$c<= :: forall a. Ord a => Composite a -> Composite a -> Bool
< :: Composite a -> Composite a -> Bool
$c< :: forall a. Ord a => Composite a -> Composite a -> Bool
compare :: Composite a -> Composite a -> Ordering
$ccompare :: forall a. Ord a => Composite a -> Composite a -> Ordering
Ord, Int -> Composite a -> ShowS
forall a. Show a => Int -> Composite a -> ShowS
forall a. Show a => [Composite a] -> ShowS
forall a. Show a => Composite a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Composite a] -> ShowS
$cshowList :: forall a. Show a => [Composite a] -> ShowS
show :: Composite a -> String
$cshow :: forall a. Show a => Composite a -> String
showsPrec :: Int -> Composite a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Composite a -> ShowS
Show)

-- | Extract underlying value.
unComposite :: Composite a -> a
unComposite :: forall a. Composite a -> a
unComposite (Composite a
a) = a
a

-- | Type function which maps composite type to its intermediate
-- representation as a tuple (row) of Haskell types that correspond
-- to PostgreSQL types in composite type definition.
--
-- As an example, consider the type defined as (a INTEGER, b DATE).
-- Then its CompositeRow instance could be (Maybe Int32, Maybe Day),
-- (Maybe Int32, Day), (Int32, Maybe Day) or (Int32, Day).
type family CompositeRow t :: Type

-- | Class which represents \"from SQL to composite\" transformation.
class (PQFormat t, FromRow (CompositeRow t)) => CompositeFromSQL t where
  -- | Convert composite row to destination type.
  toComposite :: CompositeRow t -> t

-- | Class which represents \"from composite to SQL\" transformation.
class (PQFormat t, ToRow (CompositeRow t)) => CompositeToSQL t where
  -- | Convert composite type to its intermediate representation.
  fromComposite :: t -> CompositeRow t

instance PQFormat t => PQFormat (Composite t) where
  pqFormat :: ByteString
pqFormat = forall t. PQFormat t => ByteString
pqFormat @t

instance CompositeFromSQL t => FromSQL (Composite t) where
  type PQBase (Composite t) = Ptr PGresult
  fromSQL :: Maybe (PQBase (Composite t)) -> IO (Composite t)
fromSQL Maybe (PQBase (Composite t))
Nothing = forall a. IO a
unexpectedNULL
  fromSQL (Just PQBase (Composite t)
res) = forall a. a -> Composite a
Composite
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a b. IO a -> IO b -> IO a
E.finally (forall t. CompositeFromSQL t => CompositeRow t -> t
toComposite forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall row. FromRow row => Ptr PGresult -> CInt -> CInt -> IO row
fromRow' PQBase (Composite t)
res CInt
0 CInt
0) (Ptr PGresult -> IO ()
c_PQclear PQBase (Composite t)
res)

instance CompositeToSQL t => ToSQL (Composite t) where
  type PQDest (Composite t) = PGparam
  toSQL :: forall r.
Composite t
-> ParamAllocator -> (Ptr (PQDest (Composite t)) -> IO r) -> IO r
toSQL (Composite t
comp) pa :: ParamAllocator
pa@(ParamAllocator forall r. (Ptr PGparam -> IO r) -> IO r
allocParam) Ptr (PQDest (Composite t)) -> IO r
conv =
    forall r. (Ptr PGparam -> IO r) -> IO r
allocParam forall a b. (a -> b) -> a -> b
$ \Ptr PGparam
param -> do
      forall row.
ToRow row =>
row -> ParamAllocator -> Ptr PGparam -> IO ()
toRow' (forall t. CompositeToSQL t => t -> CompositeRow t
fromComposite t
comp) ParamAllocator
pa Ptr PGparam
param
      Ptr (PQDest (Composite t)) -> IO r
conv Ptr PGparam
param