persistent-0.0.0: Database/Persist.hs
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PackageImports #-}
-- | This defines the API for performing database actions. There are two levels
-- to this API: dealing with fields, and dealing with entities. In SQL, a field
-- corresponds to a column, and should be a single, non-composite value. An
-- entity corresponds to a SQL table. In other words: An entity is a
-- collection of fields.
module Database.Persist
( -- * Fields
PersistValue (..)
, SqlType (..)
, PersistField (..)
-- * Entities
, PersistEntity (..)
) where
import Data.Time (Day, TimeOfDay, UTCTime)
import Data.ByteString.Char8 (ByteString, unpack)
import qualified Data.ByteString.UTF8 as BSU
import Control.Applicative
import Data.Typeable (Typeable)
import Data.Int (Int64)
import Text.Hamlet
import qualified Data.Text as T
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
import "MonadCatchIO-transformers" Control.Monad.CatchIO (MonadCatchIO)
-- | A raw value which can be stored in any backend and can be marshalled to
-- and from a 'PersistField'.
data PersistValue = PersistString String
| PersistByteString ByteString
| PersistInt64 Int64
| PersistDouble Double
| PersistBool Bool
| PersistDay Day
| PersistTimeOfDay TimeOfDay
| PersistUTCTime UTCTime
| PersistNull
deriving (Show, Read, Eq, Typeable)
-- | A SQL data type. Naming attempts to reflect the underlying Haskell
-- datatypes, eg SqlString instead of SqlVarchar. Different SQL databases may
-- have different translations for these types.
data SqlType = SqlString
| SqlInteger
| SqlReal
| SqlBool
| SqlDay
| SqlTime
| SqlDayTime
| SqlBlob
deriving (Show, Read, Eq, Typeable)
-- | A value which can be marshalled to and from a 'PersistValue'.
class PersistField a where
toPersistValue :: a -> PersistValue
fromPersistValue :: PersistValue -> Either String a
sqlType :: a -> SqlType
isNullable :: a -> Bool
isNullable _ = False
instance PersistField String where
toPersistValue = PersistString
fromPersistValue (PersistString s) = Right s
fromPersistValue (PersistByteString bs) = Right $ BSU.toString bs
fromPersistValue (PersistInt64 i) = Right $ show i
fromPersistValue (PersistDouble d) = Right $ show d
fromPersistValue (PersistDay d) = Right $ show d
fromPersistValue (PersistTimeOfDay d) = Right $ show d
fromPersistValue (PersistUTCTime d) = Right $ show d
fromPersistValue PersistNull = Left "Unexpected null"
fromPersistValue (PersistBool b) = Right $ show b
sqlType _ = SqlString
instance PersistField ByteString where
toPersistValue = PersistByteString
fromPersistValue (PersistByteString bs) = Right bs
fromPersistValue x = BSU.fromString <$> fromPersistValue x
sqlType _ = SqlBlob
instance PersistField T.Text where
toPersistValue = PersistString . T.unpack
fromPersistValue = fmap T.pack . fromPersistValue
sqlType _ = SqlString
instance PersistField (Html ()) where
toPersistValue = PersistByteString . S.concat . L.toChunks . renderHtml
fromPersistValue = fmap unsafeByteString . fromPersistValue
sqlType _ = SqlString
instance PersistField Int where
toPersistValue = PersistInt64 . fromIntegral
fromPersistValue (PersistInt64 i) = Right $ fromIntegral i
fromPersistValue x = Left $ "Expected Integer, received: " ++ show x
sqlType _ = SqlInteger
instance PersistField Int64 where
toPersistValue = PersistInt64 . fromIntegral
fromPersistValue (PersistInt64 i) = Right $ fromIntegral i
fromPersistValue x = Left $ "Expected Integer, received: " ++ show x
sqlType _ = SqlInteger
instance PersistField Double where
toPersistValue = PersistDouble
fromPersistValue (PersistDouble d) = Right d
fromPersistValue x = Left $ "Expected Double, received: " ++ show x
sqlType _ = SqlReal
instance PersistField Bool where
toPersistValue = PersistBool
fromPersistValue (PersistBool b) = Right b
fromPersistValue (PersistInt64 i) = Right $ i /= 0
fromPersistValue x = Left $ "Expected Bool, received: " ++ show x
sqlType _ = SqlBool
instance PersistField Day where
toPersistValue = PersistDay
fromPersistValue (PersistDay d) = Right d
fromPersistValue x@(PersistString s) =
case reads s of
(d, _):_ -> Right d
_ -> Left $ "Expected Day, received " ++ show x
fromPersistValue x@(PersistByteString s) =
case reads $ unpack s of
(d, _):_ -> Right d
_ -> Left $ "Expected Day, received " ++ show x
fromPersistValue x = Left $ "Expected Day, received: " ++ show x
sqlType _ = SqlDay
instance PersistField TimeOfDay where
toPersistValue = PersistTimeOfDay
fromPersistValue (PersistTimeOfDay d) = Right d
fromPersistValue x@(PersistString s) =
case reads s of
(d, _):_ -> Right d
_ -> Left $ "Expected TimeOfDay, received " ++ show x
fromPersistValue x@(PersistByteString s) =
case reads $ unpack s of
(d, _):_ -> Right d
_ -> Left $ "Expected TimeOfDay, received " ++ show x
fromPersistValue x = Left $ "Expected TimeOfDay, received: " ++ show x
sqlType _ = SqlTime
instance PersistField UTCTime where
toPersistValue = PersistUTCTime
fromPersistValue (PersistUTCTime d) = Right d
fromPersistValue x@(PersistString s) =
case reads s of
(d, _):_ -> Right d
_ -> Left $ "Expected UTCTime, received " ++ show x
fromPersistValue x@(PersistByteString s) =
case reads $ unpack s of
(d, _):_ -> Right d
_ -> Left $ "Expected UTCTime, received " ++ show x
fromPersistValue x = Left $ "Expected UTCTime, received: " ++ show x
sqlType _ = SqlDayTime
instance PersistField a => PersistField (Maybe a) where
toPersistValue Nothing = PersistNull
toPersistValue (Just a) = toPersistValue a
fromPersistValue PersistNull = Right Nothing
fromPersistValue x = fmap Just $ fromPersistValue x
sqlType _ = sqlType (error "this is the problem" :: a)
isNullable _ = True
-- | A single database entity. For example, if writing a blog application, a
-- blog entry would be an entry, containing fields such as title and content.
class PersistEntity val where
-- | The monad transformer in which actions for this entity must occur. For
-- example, entities declared to work with a sqlite backend would most
-- likely use a ReaderT monad transformer holding onto a database
-- connection.
--
-- By using a monad transformer here, users can allow arbitrary effects to
-- exist in the underlying monad. For example, Yesod applications can embed
-- a Handler monad here. The only restriction on that underlying monad is
-- it must be an instance of 'MonadCatchIO'.
type PersistMonad val :: (* -> *) -> * -> *
-- | The unique identifier associated with this entity. In general, backends also define a type synonym for this, such that \"type MyEntityId = Key MyEntity\".
data Key val
-- | Fields which can be updated using the 'update' and 'updateWhere'
-- functions.
data Update val
-- | Filters which are available for 'select', 'updateWhere' and
-- 'deleteWhere'. Each filter constructor specifies the field being
-- filtered on, the type of comparison applied (equals, not equals, etc)
-- and the argument for the comparison.
data Filter val
-- | How you can sort the results of a 'select'.
data Order val
-- | Unique keys in existence on this entity.
data Unique val
-- | Prepare database for this entity, if necessary. In SQL, this creates
-- values and indices if they don't exist. The first argument is not used,
-- so you can used 'undefined'.
initialize :: MonadCatchIO m => val -> (PersistMonad val) m ()
-- | Create a new record in the database, returning the newly created
-- identifier.
insert :: MonadCatchIO m => val -> (PersistMonad val) m (Key val)
-- | Replace the record in the database with the given key. Result is
-- undefined if such a record does not exist.
replace :: MonadCatchIO m => Key val -> val -> (PersistMonad val) m ()
-- | Update individual fields on a specific record.
update :: MonadCatchIO m => Key val -> [Update val]
-> (PersistMonad val) m ()
-- | Update individual fields on any record matching the given criterion.
updateWhere :: MonadCatchIO m => [Filter val] -> [Update val]
-> (PersistMonad val) m ()
-- | Delete a specific record by identifier. Does nothing if record does
-- not exist.
delete :: MonadCatchIO m => Key val -> (PersistMonad val) m ()
-- | Delete a specific record by unique key. Does nothing if no record
-- matches.
deleteBy :: MonadCatchIO m => Unique val -> (PersistMonad val) m ()
-- | Delete all records matching the given criterion.
deleteWhere :: MonadCatchIO m => [Filter val] -> (PersistMonad val) m ()
-- | Get a record by identifier, if available.
get :: MonadCatchIO m => Key val -> (PersistMonad val) m (Maybe val)
-- | Get a record by unique key, if available. Returns also the identifier.
getBy :: MonadCatchIO m => Unique val
-> (PersistMonad val) m (Maybe (Key val, val))
-- | Get all records matching the given criterion in the specified order.
-- Returns also the identifiers.
select :: MonadCatchIO m => [Filter val] -> [Order val]
-> (PersistMonad val) m [(Key val, val)]