{-# LANGUAGE GADTs, TypeFamilies, ExistentialQuantification, StandaloneDeriving, TypeSynonymInstances, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, OverlappingInstances, ScopedTypeVariables, GeneralizedNewtypeDeriving, UndecidableInstances, EmptyDataDecls #-} -- | This module defines the functions and datatypes used throughout the framework. -- Most of them are for internal use module Database.Groundhog.Core ( -- * Main types PersistEntity(..) , PersistValue(..) , PersistField(..) , Key(..) -- * Constructing expressions -- $exprDoc , Cond(..) , Update(..) , (=.), (&&.), (||.), (==.), (/=.), (<.), (<=.), (>.), (>=.) , wrapPrim , toArith , Expression(..) , Primitive(..) , HasOrder , Numeric , NeverNull , Arith(..) , Expr(..) , Order(..) -- * Type description , DbType(..) , NamedType , namedType , getName , getType , EntityDef(..) , ConstructorDef(..) , Constructor(..) , Constraint -- * Migration , SingleMigration , NamedMigrations , Migration -- * Database , PersistBackend(..) , RowPopper , DbPersist(..) , runDbPersist ) where import Control.Applicative(Applicative) import Control.Monad(liftM, liftM2, liftM3, liftM4, liftM5) import Control.Monad.Trans.Class(MonadTrans(..)) import Control.Monad.IO.Class(MonadIO(..)) import Control.Monad.IO.Control (MonadControlIO) import Control.Monad.Trans.Reader(ReaderT, runReaderT) import Control.Monad.Trans.State(StateT) import Data.Bits(bitSize) import Data.ByteString.Char8 (ByteString, unpack) import Data.Enumerator(Enumerator) import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Encoding.Error as T import Data.Int (Int8, Int16, Int32, Int64) import Data.Word (Word8, Word16, Word32, Word64) import Data.Map(Map) import Data.Time(Day, TimeOfDay, UTCTime) import Unsafe.Coerce(unsafeCoerce) -- | Only instances of this class can be persisted in a database class PersistField v => PersistEntity v where -- | This type is used for typesafe manipulation of separate fields of datatype v. -- Each constructor in 'Fields' corresponds to its field in a datatype v. -- It is parametrised by constructor phantom type and field value type. data Fields v :: * -> * -> * -- | Returns a complete description of the type entityDef :: v -> EntityDef -- | Marshalls value to a list of 'PersistValue' ready for insert to a database toPersistValues :: PersistBackend m => v -> m [PersistValue] -- | Constructs the value from the list of 'PersistValue' fromPersistValues :: PersistBackend m => [PersistValue] -> m v -- | Returns constructor number and a list of constraint names and corresponding field names with their values getConstraints :: v -> (Int, [(String, [(String, PersistValue)])]) -- Show (Fields v c a) constraint would be nicer, but free c & a params don't allow this showField :: Fields v c a -> String eqField :: Fields v c a -> Fields v c a -> Bool instance PersistEntity v => Show (Fields v c a) where show = showField instance PersistEntity v => Eq (Fields v c a) where (==) = eqField -- | A unique identifier of a value stored in a database data PersistEntity v => Key v = Key Int64 deriving Show data Any type family MoreSpecific a b type instance MoreSpecific Any a = a type instance MoreSpecific a Any = a type instance MoreSpecific a a = a type instance MoreSpecific Any Any = Any class TypesCastV x y z | x y -> z -- instance TypesCastV x x x would not work. For example, it does not match TypesCastV (Type a1) (Type a2) z instance (x ~ y, MoreSpecific x y ~ z) => TypesCastV x y z instance TypesCastV Any x x instance TypesCastV x Any x instance TypesCastV Any Any Any class TypesEqualC x y instance TypesEqualC x x instance TypesEqualC Any x instance TypesEqualC x Any instance TypesEqualC Any Any class TypesCastC x y z | x y -> z instance (TypesEqualC x y, MoreSpecific x y ~ z) => TypesCastC x y z class (Expression a, Expression b) => TypeCast a b v c | a b -> v, a b -> c instance (Expression a, Expression b, TypesCastV (FuncV a) (FuncV b) v, TypesCastC (FuncC a) (FuncC b) c) => TypeCast a b v c --instance (Expression a, Expression b, FuncV a ~ Any, FuncC a ~ Any, FuncV b ~ v, FuncC b ~ c) => TypeCast a b v c --instance (FuncV a ~ v, FuncC a ~ c) => TypeCast a Any v c --instance TypeCast Any Any Any Any -- $exprDoc -- The expressions are used in conditions and right part of Update statement. -- Despite the wordy types of the comparison functions, they are simple to use. -- Type of the compared polymorphic values like numbers or Nothing must be supplied manually. Example: -- -- @ -- StringField ==. \"abc\" &&. NumberField >. (0 :: Int) ||. MaybeField ==. (Nothing :: Maybe String) ||. MaybeField ==. Just \"def\" -- @ -- -- | Represents condition for a query. data Cond v c = And (Cond v c) (Cond v c) | Or (Cond v c) (Cond v c) | Not (Cond v c) | forall a.(HasOrder a, PersistField a) => Lesser (Expr v c a) (Expr v c a) | forall a.(HasOrder a, PersistField a) => Greater (Expr v c a) (Expr v c a) | forall a.(PersistField a) => Equals (Expr v c a) (Expr v c a) | forall a.(PersistField a) => NotEquals (Expr v c a) (Expr v c a) -- | Lookup will be performed only in table for the specified constructor c. To fetch value by key without constructor limitation use 'get' | KeyIs (Key v) data Update v c = forall a.Update (Fields v c a) (Expr v c a) --deriving instance (Show (Fields c a)) => Show (Update c) -- | Defines sort order of a result-set data Order v c = forall a.HasOrder a => Asc (Fields v c a) | forall a.HasOrder a => Desc (Fields v c a) -- TODO: UGLY: we use unsafeCoerce to cast phantom types Any and Any to more specific type if possible. The safety is assured by TypeEqual and TypeEqualC classes. I hope it will work w/o woes of segfaults -- | Update field infixr 3 =. (=.) :: ( Expression a , TypesCastV v (FuncV a) v , TypesCastC c (FuncC a) c) => Fields v c (FuncA a) -> a -> Update v c f =. a = Update f (unsafeCoerceExpr $ wrap a) -- | Boolean \"and\" operator. (&&.) :: (TypesCastV v1 v2 v3, TypesCastC c1 c2 c3) => Cond v1 c1 -> Cond v2 c2 -> Cond v3 c3 -- | Boolean \"or\" operator. (||.) :: (TypesCastV v1 v2 v3, TypesCastC c1 c2 c3) => Cond v1 c1 -> Cond v2 c2 -> Cond v3 c3 infixr 3 &&. a &&. b = And (unsafeCoerce a) (unsafeCoerce b) infixr 2 ||. a ||. b = Or (unsafeCoerce a) (unsafeCoerce b) unsafeCoerceExpr :: Expr v1 c1 a -> Expr v2 c2 a unsafeCoerceExpr = unsafeCoerce (==.), (/=.) :: ( TypeCast a b v c , FuncA a ~ FuncA b , PersistField (FuncA a)) => a -> b -> Cond v c (<.), (<=.), (>.), (>=.) :: ( TypeCast a b v c , FuncA a ~ FuncA b , PersistField (FuncA a) , HasOrder (FuncA a)) => a -> b -> Cond v c infix 4 ==., <., <=., >., >=. a ==. b = Equals (unsafeCoerceExpr $ wrap a) (unsafeCoerceExpr $ wrap b) a /=. b = NotEquals (unsafeCoerceExpr $ wrap a) (unsafeCoerceExpr $ wrap b) a <. b = Lesser (unsafeCoerceExpr $ wrap a) (unsafeCoerceExpr $ wrap b) a <=. b = Not $ a >. b a >. b = Greater (unsafeCoerceExpr $ wrap a) (unsafeCoerceExpr $ wrap b) a >=. b = Not $ a <. b newtype Monad m => DbPersist conn m a = DbPersist { unDbPersist :: ReaderT conn m a } deriving (Monad, MonadIO, Functor, Applicative, MonadControlIO, MonadTrans) runDbPersist :: Monad m => DbPersist conn m a -> conn -> m a runDbPersist = runReaderT.unDbPersist class Monad m => PersistBackend m where -- | Insert a new record to a database and return its 'Key' insert :: PersistEntity v => v -> m (Key v) -- | Try to insert a record and return Right newkey. If there is a constraint violation, Left oldkey is returned -- , where oldkey is an identifier of the record with the same constraint values. Note that if several constraints are violated, a key of an arbitrary matching record is returned. insertBy :: PersistEntity v => v -> m (Either (Key v) (Key v)) -- | Replace a record with the given key. Result is undefined if the record does not exist. replace :: PersistEntity v => Key v -> v -> m () -- | Return a list of all records selectEnum :: (PersistEntity v, Constructor c) => Cond v c -> [Order v c] -> Int -- ^ limit -> Int -- ^ offset -> Enumerator (Key v, v) m a -- | Get all records. Order is undefined selectAllEnum :: PersistEntity v => Enumerator (Key v, v) m a -- | Return a list of the records satisfying the condition select :: (PersistEntity v, Constructor c) => Cond v c -> [Order v c] -> Int -- ^ limit -> Int -- ^ offset -> m [(Key v, v)] -- | Return a list of all records. Order is undefined selectAll :: PersistEntity v => m [(Key v, v)] -- | Fetch an entity from a database get :: PersistEntity v => Key v -> m (Maybe v) -- | Update the records satisfying the condition update :: (PersistEntity v, Constructor c) => [Update v c] -> Cond v c -> m () -- | Remove the records satisfying the condition delete :: (PersistEntity v, Constructor c) => Cond v c -> m () -- | Remove the record with given key. No-op if the record does not exist deleteByKey :: PersistEntity v => Key v -> m () -- | Count total number of records satisfying the condition count :: (PersistEntity v, Constructor c) => Cond v c -> m Int -- | Count total number of records with all constructors countAll :: PersistEntity v => v -> m Int -- | Check database schema and create migrations for the entity and the entities it contains migrate :: PersistEntity v => v -> Migration m -- | Execute raw query executeRaw :: Bool -- ^ keep in cache -> String -- ^ query -> [PersistValue] -- ^ positional parameters -> m () -- | Execute raw query with results queryRaw :: Bool -- ^ keep in cache -> String -- ^ query -> [PersistValue] -- ^ positional parameters -> (RowPopper m -> m a) -- ^ results processing function -> m a -- TODO: we need to supply names of the tables or other info insertTuple :: NamedType -> [PersistValue] -> m Int64 getTuple :: NamedType -> Int64 -> m [PersistValue] insertList :: PersistField a => [a] -> m Int64 getList :: PersistField a => Int64 -> m [a] type RowPopper m = m (Maybe [PersistValue]) type Migration m = StateT NamedMigrations m () -- | Datatype names and corresponding migrations type NamedMigrations = Map String SingleMigration -- | Either error messages or migration queries with safety flags type SingleMigration = Either [String] [(Bool, String)] -- | Describes an ADT. data EntityDef = EntityDef { -- | Emtity name entityName :: String -- | Named types of the instantiated polymorphic type parameters , typeParams :: [NamedType] -- | List of entity constructors definitions , constructors :: [ConstructorDef] } deriving (Show, Eq) -- | Describes an entity constructor data ConstructorDef = ConstructorDef { -- | Number of the constructor in the ADT constrNum :: Int -- | Constructor name , constrName :: String -- | Parameter names with their named type , constrParams :: [(String, NamedType)] -- | Uniqueness constraints on the constructor fiels , constrConstrs :: [Constraint] } deriving (Show, Eq) -- | Phantom constructors are made instances of this class. This class should be used only by Template Haskell codegen class Constructor a where -- returning ConstructorDef seems more logical, but it would require the value datatype -- it can be supplied either as a part of constructor type, eg instance Constructor (MyDataConstructor (MyData a)) which requires -XFlexibleInstances -- or as a separate type, eg instance Constructor MyDataConstructor (MyData a) which requires -XMultiParamTypeClasses -- the phantoms are primarily used to get the constructor name. So to keep user code cleaner we return only the name and number, which can be later used to get ConstructorDef from the EntityDef phantomConstrName :: a -> String phantomConstrNum :: a -> Int -- | Constraint name and list of the field names that form a unique combination. -- Only fields of 'Primitive' types can be used in a constraint type Constraint = (String, [String]) -- | A DB data type. Naming attempts to reflect the underlying Haskell -- datatypes, eg DbString instead of DbVarchar. Different databases may -- have different translations for these types. data DbType = DbString | DbInt32 | DbInt64 | DbReal | DbBool | DbDay | DbTime | DbDayTime | DbBlob -- ByteString -- More complex types | DbMaybe NamedType | DbList NamedType | DbTuple Int [NamedType] | DbEntity EntityDef deriving Show -- TODO: this type can be changed to avoid storing the value itself. For example, ([String, DbType). Restriction: can be used to get DbType and name -- | It is used to store type 'DbType' and persist name of a value data NamedType = forall v.PersistField v => NamedType v namedType :: PersistField v => v -> NamedType namedType = NamedType getName :: NamedType -> String getName (NamedType v) = persistName v getType :: NamedType -> DbType getType (NamedType v) = dbType v instance Show NamedType where show (NamedType v) = show (dbType v) -- rely on the invariant that no two types have the same name instance Eq NamedType where (NamedType v1) == (NamedType v2) = persistName v1 == persistName v2 -- | 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, Eq) -- | Arithmetic expressions which can include fields and literals data Arith v c a = Plus (Arith v c a) (Arith v c a) | Minus (Arith v c a) (Arith v c a) | Mult (Arith v c a) (Arith v c a) | Abs (Arith v c a) | ArithField (Fields v c a) | Lit Int64 deriving instance Eq (Fields v c a) => Eq (Arith v c a) deriving instance Show (Fields v c a) => Show (Arith v c a) instance (Eq (Fields v c a), Show (Fields v c a), Numeric a) => Num (Arith v c a) where a + b = Plus a b a - b = Minus a b a * b = Mult a b abs = Abs signum = error "no signum" fromInteger = Lit . fromInteger -- | Convert field to an arithmetic value toArith :: Fields v c a -> Arith v c a toArith = ArithField -- | Constraint for use in arithmetic expressions. 'Num' is not used to explicitly include only types supported by the library . -- TODO: consider replacement with 'Num' class Numeric a -- | The same goals as for 'Numeric'. Certain types like String which have order in Haskell may not have it in DB class HasOrder a -- | Types which when converted to 'PersistValue' are never NULL. -- Consider the type @Maybe (Maybe a)@. Now Nothing is stored as NULL, so we cannot distinguish between Just Nothing and Nothing which is a problem. -- The purpose of this class is to ban the inner Maybe's. -- Maybe this class can be removed when support for inner Maybe's appears. class NeverNull a -- | Datatypes which can be converted directly to 'PersistValue' class Primitive a where toPrim :: a -> PersistValue fromPrim :: PersistValue -> a -- | Used to uniformly represent fields, literals and arithmetic expressions. -- A value should be convertec to 'Expr' for usage in expressions data Expr v c a where ExprPrim :: Primitive a => a -> Expr v c a ExprField :: PersistEntity v => Fields v c a -> Expr v c a ExprArith :: PersistEntity v => Arith v c a -> Expr v c a -- we need this field for Key and Maybe mostly ExprPlain :: Primitive a => a -> Expr v c (FuncA a) -- I wish wrap could return Expr with both fixed and polymorphic v&c. Any is used to emulate polymorphic types. -- | Instances of this type can be converted to 'Expr' class Expression a where type FuncV a; type FuncC a; type FuncA a wrap :: a -> Expr (FuncV a) (FuncC a) (FuncA a) -- | By default during converting values of certain types to 'Expr', the types can be changed. For example, @'Key' a@ is transformed into @a@. -- It is convenient because the fields usually contain reference to a certain datatype, not its 'Key'. -- But sometimes when automatic transformation gets in the way function 'wrapPrim' will help. Use it when a field in a datatype has type @(Key a)@ or @Maybe (Key a)@. Example: -- -- @ --data Example = Example {entity1 :: Maybe Smth, entity2 :: Key Smth} --Entity1Field ==. Just k &&. Entity2Field ==. wrapPrim k -- @ wrapPrim :: Primitive a => a -> Expr Any Any a -- We cannot create different Expression instances for (Fields v c a) and (Fields v c (Key a)) -- so that Func (Fields v c a) = a and Func (Fields v c (Key a)) = a -- because of the type families overlap restrictions. Neither we can create different instances for Key a wrapPrim = ExprPrim class PersistField a where -- | Return name of the type. If it is polymorhic, the names of parameter types are separated with \"$\" symbol persistName :: a -> String -- | Convert a value into something which can be stored in a database column. -- Note that for complex datatypes it may insert them to return identifier toPersistValue :: PersistBackend m => a -> m PersistValue -- | Constructs a value from a 'PersistValue'. For complex datatypes it may query the database fromPersistValue :: PersistBackend m => PersistValue -> m a -- | Description of value type dbType :: a -> DbType ---- INSTANCES instance Numeric Int instance Numeric Int8 instance Numeric Int16 instance Numeric Int32 instance Numeric Int64 instance Numeric Word8 instance Numeric Word16 instance Numeric Word32 instance Numeric Word64 instance Numeric Double instance HasOrder Int instance HasOrder Int8 instance HasOrder Int16 instance HasOrder Int32 instance HasOrder Int64 instance HasOrder Word8 instance HasOrder Word16 instance HasOrder Word32 instance HasOrder Word64 instance HasOrder Double instance HasOrder Bool instance HasOrder Day instance HasOrder TimeOfDay instance HasOrder UTCTime instance Primitive String where toPrim = PersistString fromPrim (PersistString s) = s fromPrim (PersistByteString bs) = T.unpack $ T.decodeUtf8With T.lenientDecode bs fromPrim (PersistInt64 i) = show i fromPrim (PersistDouble d) = show d fromPrim (PersistDay d) = show d fromPrim (PersistTimeOfDay d) = show d fromPrim (PersistUTCTime d) = show d fromPrim (PersistBool b) = show b fromPrim PersistNull = error "Unexpected null" instance Primitive T.Text where toPrim = PersistString . T.unpack fromPrim (PersistByteString bs) = T.decodeUtf8With T.lenientDecode bs fromPrim x = T.pack $ fromPrim x instance Primitive ByteString where toPrim = PersistByteString fromPrim (PersistByteString a) = a fromPrim x = T.encodeUtf8 . T.pack $ fromPrim x instance Primitive Int where toPrim = PersistInt64 . fromIntegral fromPrim (PersistInt64 a) = fromIntegral a fromPrim x = error $ "Expected Integer, received: " ++ show x instance Primitive Int8 where toPrim = PersistInt64 . fromIntegral fromPrim (PersistInt64 a) = fromIntegral a fromPrim x = error $ "Expected Integer, received: " ++ show x instance Primitive Int16 where toPrim = PersistInt64 . fromIntegral fromPrim (PersistInt64 a) = fromIntegral a fromPrim x = error $ "Expected Integer, received: " ++ show x instance Primitive Int32 where toPrim = PersistInt64 . fromIntegral fromPrim (PersistInt64 a) = fromIntegral a fromPrim x = error $ "Expected Integer, received: " ++ show x instance Primitive Int64 where toPrim = PersistInt64 fromPrim (PersistInt64 a) = a fromPrim x = error $ "Expected Integer, received: " ++ show x instance Primitive Word8 where toPrim = PersistInt64 . fromIntegral fromPrim (PersistInt64 a) = fromIntegral a fromPrim x = error $ "Expected Integer, received: " ++ show x instance Primitive Word16 where toPrim = PersistInt64 . fromIntegral fromPrim (PersistInt64 a) = fromIntegral a fromPrim x = error $ "Expected Integer, received: " ++ show x instance Primitive Word32 where toPrim = PersistInt64 . fromIntegral fromPrim (PersistInt64 a) = fromIntegral a fromPrim x = error $ "Expected Integer, received: " ++ show x instance Primitive Word64 where toPrim = PersistInt64 . fromIntegral fromPrim (PersistInt64 a) = fromIntegral a fromPrim x = error $ "Expected Integer, received: " ++ show x instance Primitive Double where toPrim = PersistDouble fromPrim (PersistDouble a) = a fromPrim x = error $ "Expected Double, received: " ++ show x instance Primitive Bool where toPrim = PersistBool fromPrim (PersistBool a) = a fromPrim (PersistInt64 i) = i /= 0 fromPrim x = error $ "Expected Bool, received: " ++ show x instance Primitive Day where toPrim = PersistDay fromPrim (PersistDay a) = a fromPrim x = readHelper x ("Expected Day, received: " ++ show x) instance Primitive TimeOfDay where toPrim = PersistTimeOfDay fromPrim (PersistTimeOfDay a) = a fromPrim x = readHelper x ("Expected TimeOfDay, received: " ++ show x) instance Primitive UTCTime where toPrim = PersistUTCTime fromPrim (PersistUTCTime a) = a fromPrim x = readHelper x ("Expected UTCTime, received: " ++ show x) instance Primitive (Key a) where toPrim (Key a) = PersistInt64 a fromPrim (PersistInt64 a) = Key a fromPrim x = error $ "Expected Integer(entity key), received: " ++ show x instance (Primitive a, NeverNull a) => Primitive (Maybe a) where toPrim = maybe PersistNull toPrim fromPrim PersistNull = Nothing fromPrim x = Just $ fromPrim x instance NeverNull String instance NeverNull T.Text instance NeverNull ByteString instance NeverNull Int instance NeverNull Int64 instance NeverNull Double instance NeverNull Bool instance NeverNull Day instance NeverNull TimeOfDay instance NeverNull UTCTime instance NeverNull (Key a) instance NeverNull [a] instance NeverNull (a, b) instance NeverNull (a, b, c) instance NeverNull (a, b, c, d) instance NeverNull (a, b, c, d, e) instance PersistEntity a => NeverNull a instance Expression (Expr v c a) where type FuncV (Expr v c a) = v type FuncC (Expr v c a) = c type FuncA (Expr v c a) = a wrap = id instance PersistEntity v => Expression (Fields v c a) where type FuncV (Fields v c a) = v type FuncC (Fields v c a) = c type FuncA (Fields v c a) = a wrap = ExprField instance PersistEntity v => Expression (Arith v c a) where type FuncV (Arith v c a) = v type FuncC (Arith v c a) = c type FuncA (Arith v c a) = a wrap = ExprArith instance (Expression a, Primitive a, NeverNull a) => Expression (Maybe a) where type FuncV (Maybe a) = Any type FuncC (Maybe a) = Any type FuncA (Maybe a) = (Maybe (FuncA a)) wrap = ExprPlain instance Expression (Key a) where type FuncV (Key a) = Any; type FuncC (Key a) = Any; type FuncA (Key a) = a wrap = ExprPlain instance Expression Int where type FuncV Int = Any; type FuncC Int = Any; type FuncA Int = Int wrap = ExprPrim instance Expression Int8 where type FuncV Int8 = Any; type FuncC Int8 = Any; type FuncA Int8 = Int8 wrap = ExprPrim instance Expression Int16 where type FuncV Int16 = Any; type FuncC Int16 = Any; type FuncA Int16 = Int16 wrap = ExprPrim instance Expression Int32 where type FuncV Int32 = Any; type FuncC Int32 = Any; type FuncA Int32 = Int32 wrap = ExprPrim instance Expression Int64 where type FuncV Int64 = Any; type FuncC Int64 = Any; type FuncA Int64 = Int64 wrap = ExprPrim instance Expression Word8 where type FuncV Word8 = Any; type FuncC Word8 = Any; type FuncA Word8 = Word8 wrap = ExprPrim instance Expression Word16 where type FuncV Word16 = Any; type FuncC Word16 = Any; type FuncA Word16 = Word16 wrap = ExprPrim instance Expression Word32 where type FuncV Word32 = Any; type FuncC Word32 = Any; type FuncA Word32 = Word32 wrap = ExprPrim instance Expression Word64 where type FuncV Word64 = Any; type FuncC Word64 = Any; type FuncA Word64 = Word64 wrap = ExprPrim instance Expression String where type FuncV String = Any; type FuncC String = Any; type FuncA String = String wrap = ExprPrim instance Expression ByteString where type FuncV ByteString = Any; type FuncC ByteString = Any; type FuncA ByteString = ByteString wrap = ExprPrim instance Expression T.Text where type FuncV T.Text = Any; type FuncC T.Text = Any; type FuncA T.Text = T.Text wrap = ExprPrim instance Expression Bool where type FuncV Bool = Any; type FuncC Bool = Any; type FuncA Bool = Bool wrap = ExprPrim readHelper :: Read a => PersistValue -> String -> a readHelper s errMessage = case s of PersistString str -> readHelper' str PersistByteString str -> readHelper' (unpack str) _ -> error errMessage where readHelper' str = case reads str of (a, _):_ -> a _ -> error errMessage instance PersistField ByteString where persistName _ = "ByteString" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType _ = DbBlob instance PersistField String where persistName _ = "String" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType _ = DbString instance PersistField T.Text where persistName _ = "Text" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType _ = DbString instance PersistField Int where persistName _ = "Int" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType a = if bitSize a == 32 then DbInt32 else DbInt64 instance PersistField Int8 where persistName _ = "Int8" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType _ = DbInt64 instance PersistField Int16 where persistName _ = "Int16" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType _ = DbInt64 instance PersistField Int32 where persistName _ = "Int32" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType _ = DbInt64 instance PersistField Int64 where persistName _ = "Int64" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType _ = DbInt64 instance PersistField Word8 where persistName _ = "Word8" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType _ = DbInt64 instance PersistField Word16 where persistName _ = "Word16" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType _ = DbInt64 instance PersistField Word32 where persistName _ = "Word32" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType _ = DbInt64 instance PersistField Word64 where persistName _ = "Word64" toPersistValue = return . toPrim fromPersistValue = return . fromPrim dbType _ = DbInt64 instance PersistField Double where persistName _ = "Double" toPersistValue = return . PersistDouble fromPersistValue = return . fromPrim dbType _ = DbReal instance PersistField Bool where persistName _ = "Bool" toPersistValue = return . PersistBool fromPersistValue = return . fromPrim dbType _ = DbBool instance PersistField Day where persistName _ = "Day" toPersistValue = return . PersistDay fromPersistValue = return . fromPrim dbType _ = DbDay instance PersistField TimeOfDay where persistName _ = "TimeOfDay" toPersistValue = return . PersistTimeOfDay fromPersistValue = return . fromPrim dbType _ = DbTime instance PersistField UTCTime where persistName _ = "UTCTime" toPersistValue = return . PersistUTCTime fromPersistValue = return . fromPrim dbType _ = DbDayTime instance (PersistField a, NeverNull a) => PersistField (Maybe a) where persistName (_ :: Maybe a) = "Maybe$" ++ persistName (undefined :: a) toPersistValue = maybe (return PersistNull) toPersistValue fromPersistValue PersistNull = return Nothing fromPersistValue x = liftM Just $ fromPersistValue x dbType (_ :: Maybe a) = DbMaybe $ namedType (undefined :: a) instance (PersistEntity a) => PersistField (Key a) where persistName (_ :: Key a) = "Key$" ++ persistName (undefined :: a) toPersistValue (Key a) = return $ PersistInt64 a fromPersistValue = return . fromPrim dbType (_ :: Key a) = DbEntity $ entityDef (undefined :: a) instance (PersistField a) => PersistField [a] where persistName (_ :: [a]) = "List$$" ++ persistName (undefined :: a) toPersistValue l = insertList l >>= toPersistValue fromPersistValue k = getList (fromPrim k) dbType (_ :: [a]) = DbList $ namedType (undefined :: a) instance (PersistField a, PersistField b) => PersistField (a, b) where persistName (_ :: (a, b)) = "Tuple2$$" ++ persistName (undefined :: a) ++ "$" ++ persistName (undefined :: b) toPersistValue x@(a, b) = do vals <- sequence [toPersistValue a, toPersistValue b] liftM PersistInt64 $ insertTuple (namedType x) vals fromPersistValue (PersistInt64 key) = do [a, b] <- getTuple (namedType (undefined :: (a, b))) key liftM2 (,) (fromPersistValue a) (fromPersistValue b) fromPersistValue x = fail $ "Expected Integer(tuple key), received: " ++ show x dbType (_ :: (a, b)) = DbTuple 2 [namedType (undefined :: a), namedType (undefined :: b)] instance (PersistField a, PersistField b, PersistField c) => PersistField (a, b, c) where persistName (_ :: (a, b, c)) = "Tuple3$$" ++ persistName (undefined :: a) ++ "$" ++ persistName (undefined :: b) ++ "$" ++ persistName (undefined :: c) toPersistValue x@(a, b, c) = do vals <- sequence [toPersistValue a, toPersistValue b, toPersistValue c] liftM PersistInt64 $ insertTuple (namedType x) vals fromPersistValue (PersistInt64 key) = do [a, b, c] <- getTuple (namedType (undefined :: (a, b, c))) key liftM3 (,,) (fromPersistValue a) (fromPersistValue b) (fromPersistValue c) fromPersistValue x = fail $ "Expected Integer(tuple key), received: " ++ show x dbType (_ :: (a, b, c)) = DbTuple 3 [namedType (undefined :: a), namedType (undefined :: b), namedType (undefined :: c)] instance (PersistField a, PersistField b, PersistField c, PersistField d) => PersistField (a, b, c, d) where persistName (_ :: (a, b, c, d)) = "Tuple4$$" ++ persistName (undefined :: a) ++ "$" ++ persistName (undefined :: b) ++ "$" ++ persistName (undefined :: c) ++ "$" ++ persistName (undefined :: d) toPersistValue x@(a, b, c, d) = do vals <- sequence [toPersistValue a, toPersistValue b, toPersistValue c, toPersistValue d] liftM PersistInt64 $ insertTuple (namedType x) vals fromPersistValue (PersistInt64 key) = do [a, b, c, d] <- getTuple (namedType (undefined :: (a, b, c, d))) key liftM4 (,,,) (fromPersistValue a) (fromPersistValue b) (fromPersistValue c) (fromPersistValue d) fromPersistValue x = fail $ "Expected Integer(tuple key), received: " ++ show x dbType (_ :: (a, b, c, d)) = DbTuple 4 [namedType (undefined :: a), namedType (undefined :: b), namedType (undefined :: c), namedType (undefined :: d)] instance (PersistField a, PersistField b, PersistField c, PersistField d, PersistField e) => PersistField (a, b, c, d, e) where persistName (_ :: (a, b, c, d, e)) = "Tuple5$$" ++ persistName (undefined :: a) ++ "$" ++ persistName (undefined :: b) ++ "$" ++ persistName (undefined :: c) ++ "$" ++ persistName (undefined :: d) ++ "$" ++ persistName (undefined :: e) toPersistValue x@(a, b, c, d, e) = do vals <- sequence [toPersistValue a, toPersistValue b, toPersistValue c, toPersistValue d, toPersistValue e] liftM PersistInt64 $ insertTuple (namedType x) vals fromPersistValue (PersistInt64 key) = do [a, b, c, d, e] <- getTuple (namedType (undefined :: (a, b, c, d, e))) key liftM5 (,,,,) (fromPersistValue a) (fromPersistValue b) (fromPersistValue c) (fromPersistValue d) (fromPersistValue e) fromPersistValue x = fail $ "Expected Integer(tuple key), received: " ++ show x dbType (_ :: (a, b, c, d, e)) = DbTuple 5 [namedType (undefined :: a), namedType (undefined :: b), namedType (undefined :: c), namedType (undefined :: d), namedType (undefined :: e)]