purescript-postgresql-client/src/Database/PostgreSQL.purs

170 lines
5.7 KiB
Haskell
Raw Normal View History

2016-12-22 18:12:38 +00:00
module Database.PostgreSQL
( POSTGRESQL
, PoolConfiguration
, Pool
, Connection
, class ToSQLRow
, class FromSQLRow
, class ToSQLValue
, class FromSQLValue
, toSQLRow
, fromSQLRow
, toSQLValue
, fromSQLValue
, newPool
, withConnection
, withTransaction
, execute
, query
) where
import Control.Monad.Aff (Aff)
import Control.Monad.Error.Class (catchError, throwError)
import Control.Monad.Except (runExcept)
import Data.Either (Either(..))
import Data.Foreign (Foreign, readArray, readString, toForeign)
import Data.List (List)
import Data.List as List
import Data.Maybe (fromJust, Maybe(..))
import Data.Traversable (traverse)
import Data.Tuple (Tuple)
import Data.Tuple.Nested ((/\), tuple1, tuple2, tuple3, tuple4, tuple5)
import Partial.Unsafe (unsafePartial)
import Prelude
foreign import data POSTGRESQL :: !
type PoolConfiguration =
{ user :: String
, password :: String
, host :: String
, port :: Int
, database :: String
, max :: Int
, idleTimeoutMillis :: Int
}
foreign import data Pool :: *
foreign import data Connection :: *
class ToSQLRow a where
toSQLRow :: a -> Array Foreign
class FromSQLRow a where
fromSQLRow :: Array Foreign -> Maybe a
class ToSQLValue a where
toSQLValue :: a -> Foreign
class FromSQLValue a where
fromSQLValue :: Foreign -> Maybe a
instance toSQLRowUnit :: ToSQLRow Unit where
toSQLRow _ = []
instance toSQLRowTuple1 :: (ToSQLValue a) => ToSQLRow (Tuple a Unit) where
toSQLRow (a /\ _) = [toSQLValue a]
instance toSQLRowTuple2 :: (ToSQLValue a, ToSQLValue b) => ToSQLRow (Tuple a (Tuple b Unit)) where
toSQLRow (a /\ b /\ _) = [toSQLValue a, toSQLValue b]
instance toSQLRowTuple3 :: (ToSQLValue a, ToSQLValue b, ToSQLValue c) => ToSQLRow (Tuple a (Tuple b (Tuple c Unit))) where
toSQLRow (a /\ b /\ c /\ _) = [toSQLValue a, toSQLValue b, toSQLValue c]
instance toSQLRowTuple4 :: (ToSQLValue a, ToSQLValue b, ToSQLValue c, ToSQLValue d) => ToSQLRow (Tuple a (Tuple b (Tuple c (Tuple d Unit)))) where
toSQLRow (a /\ b /\ c /\ d /\ _) = [toSQLValue a, toSQLValue b, toSQLValue c, toSQLValue d]
instance toSQLRowTuple5 :: (ToSQLValue a, ToSQLValue b, ToSQLValue c, ToSQLValue d, ToSQLValue e) => ToSQLRow (Tuple a (Tuple b (Tuple c (Tuple d (Tuple e Unit))))) where
toSQLRow (a /\ b /\ c /\ d /\ e /\ _) = [toSQLValue a, toSQLValue b, toSQLValue c, toSQLValue d, toSQLValue e]
instance fromSQLRowUnit :: FromSQLRow Unit where
fromSQLRow [] = Just unit
fromSQLRow _ = Nothing
instance fromSQLRowTuple1 :: (FromSQLValue a) => FromSQLRow (Tuple a Unit) where
fromSQLRow [a] = tuple1 <$> fromSQLValue a
fromSQLRow _ = Nothing
instance fromSQLRowTuple2 :: (FromSQLValue a, FromSQLValue b) => FromSQLRow (Tuple a (Tuple b Unit)) where
fromSQLRow [a, b] = tuple2 <$> fromSQLValue a <*> fromSQLValue b
fromSQLRow _ = Nothing
instance fromSQLRowTuple3 :: (FromSQLValue a, FromSQLValue b, FromSQLValue c) => FromSQLRow (Tuple a (Tuple b (Tuple c Unit))) where
fromSQLRow [a, b, c] = tuple3 <$> fromSQLValue a <*> fromSQLValue b <*> fromSQLValue c
fromSQLRow _ = Nothing
instance fromSQLRowTuple4 :: (FromSQLValue a, FromSQLValue b, FromSQLValue c, FromSQLValue d) => FromSQLRow (Tuple a (Tuple b (Tuple c (Tuple d Unit)))) where
fromSQLRow [a, b, c, d] = tuple4 <$> fromSQLValue a <*> fromSQLValue b <*> fromSQLValue c <*> fromSQLValue d
fromSQLRow _ = Nothing
instance fromSQLRowTuple5 :: (FromSQLValue a, FromSQLValue b, FromSQLValue c, FromSQLValue d, FromSQLValue e) => FromSQLRow (Tuple a (Tuple b (Tuple c (Tuple d (Tuple e Unit))))) where
fromSQLRow [a, b, c, d, e] = tuple5 <$> fromSQLValue a <*> fromSQLValue b <*> fromSQLValue c <*> fromSQLValue d <*> fromSQLValue e
fromSQLRow _ = Nothing
instance toSQLValueString :: ToSQLValue String where
toSQLValue = toForeign
instance fromSQLValueString :: FromSQLValue String where
fromSQLValue = fromRight <<< runExcept <<< readString
instance fromSQLValueArray :: (FromSQLValue a) => FromSQLValue (Array a) where
fromSQLValue = traverse fromSQLValue <=< fromRight <<< runExcept <<< readArray
instance fromSQLValueList :: (FromSQLValue a) => FromSQLValue (List a) where
fromSQLValue = map List.fromFoldable <<< traverse fromSQLValue <=< fromRight <<< runExcept <<< readArray
foreign import newPool
:: eff
. PoolConfiguration
-> Aff (postgreSQL :: POSTGRESQL | eff) Pool
foreign import withConnection
:: eff a
. Pool
-> (Connection -> Aff (postgreSQL :: POSTGRESQL | eff) a)
-> Aff (postgreSQL :: POSTGRESQL | eff) a
withTransaction
:: eff a
. Connection
-> Aff (postgreSQL :: POSTGRESQL | eff) a
-> Aff (postgreSQL :: POSTGRESQL | eff) a
withTransaction conn action =
execute conn "BEGIN TRANSACTION" unit
*> catchError (Right <$> action) (pure <<< Left) >>= case _ of
Right a -> execute conn "COMMIT TRANSACTION" unit $> a
Left e -> execute conn "ROLLBACK TRANSACTION" unit *> throwError e
execute
:: i eff
. (ToSQLRow i)
=> Connection
-> String
-> i
-> Aff (postgreSQL :: POSTGRESQL | eff) Unit
execute conn sql values =
void $ _query conn sql (toSQLRow values)
query
:: i o eff
. (ToSQLRow i, FromSQLRow o)
=> Connection
-> String
-> i
-> Aff (postgreSQL :: POSTGRESQL | eff) (Array o)
query conn sql values =
_query conn sql (toSQLRow values)
<#> map (unsafePartial fromJust <<< fromSQLRow)
foreign import _query
:: eff
. Connection
-> String
-> Array Foreign
-> Aff (postgreSQL :: POSTGRESQL | eff) (Array (Array Foreign))
fromRight :: a b. Either a b -> Maybe b
fromRight (Left _) = Nothing
fromRight (Right a) = Just a