{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} module Database.PostgreSQL.ORM.SqlType (SqlType(..), getTypeOid) where import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L import Data.Int import Data.Monoid import qualified Data.Text as ST import qualified Data.Text.Lazy as LT import Data.Time import Data.Typeable import qualified Data.Vector as V import Database.PostgreSQL.Simple import Database.PostgreSQL.Simple.FromField import Database.PostgreSQL.Simple.Time import Database.PostgreSQL.Simple.ToField import Database.PostgreSQL.Simple.TypeInfo.Static import Database.PostgreSQL.Simple.Types import Database.PostgreSQL.ORM.Model newtype ExtractTypeOid = ExtractTypeOid Oid instance FromField ExtractTypeOid where fromField f _ = return $ ExtractTypeOid $ typeOid f -- | Retreive the 'Oid' corresponding to a type. You can subsequently -- use the 'Oid' to call 'getTypeInfo' for more information on the -- type. getTypeOid :: Connection -> S.ByteString -> IO Oid getTypeOid c tname = do [Only (ExtractTypeOid ti)] <- query_ c $ Query $ "SELECT NULL :: " <> tname return ti -- | The class of Haskell types that can be converted to and from a -- particular SQL type. For most instances, you only need to define -- 'sqlBaseType'. class (ToField a, FromField a) => SqlType a where sqlBaseType :: a -> S.ByteString -- ^ The name of the SQL type corresponding to Haskell type @a@, -- when a value of @a@ can be null. This is the SQL type to and -- from which a @'Maybe' a@ will be converted (where 'Nothing' -- corresponds to the SQL value null). sqlType :: a -> S.ByteString -- ^ The name of the SQL type corresponding to Haskell type @a@, -- when @a@ is not wrapped in 'Maybe' and hence cannot be null. If -- @sqlType@ is unspecified, the default is to append \"@NOT NULL@\" -- to 'sqlBaseType'. {-# INLINE sqlType #-} sqlType _ = (sqlBaseType (undefined :: a)) <> " NOT NULL" #define TYPE(hs, sql) \ instance SqlType (hs) where sqlBaseType _ = typname (sql) TYPE(Bool, bool) TYPE(Double, float8) TYPE(Float, float4) TYPE(Int16, int2) TYPE(Int32, int4) TYPE(Int64, int8) TYPE(S.ByteString, text) TYPE(L.ByteString, text) TYPE(ST.Text, text) TYPE(LT.Text, text) TYPE(Oid,oid) TYPE(LocalTime, timestamp) TYPE(ZonedTime, timestamptz) TYPE(TimeOfDay, time) TYPE(UTCTime, timestamptz) TYPE(Day, date) TYPE(Date, date) TYPE(ZonedTimestamp, timestamptz) TYPE(UTCTimestamp, timestamptz) TYPE(LocalTimestamp, timestamp) TYPE(String, text) TYPE(Binary S.ByteString, bytea) TYPE(Binary L.ByteString, bytea) #undef TYPE instance SqlType DBKey where sqlType _ = "bigserial UNIQUE NOT NULL PRIMARY KEY" sqlBaseType _ = error "DBKey should not be wrapped in type" instance (SqlType a) => SqlType (Maybe a) where sqlType _ = sqlBaseType (undefined :: a) sqlBaseType _ = error "Table field Maybe should not be wrapped in other type" instance (Typeable a, SqlType a) => SqlType (V.Vector a) where sqlBaseType _ = sqlBaseType (undefined :: a) <> "[]" instance (Model a) => SqlType (DBRef a) where sqlBaseType (DBRef k) = sqlBaseType k <> ref where t = modelInfo :: ModelInfo a Just orig = modelOrigTable (modelIdentifiers :: ModelIdentifiers a) ref = S.concat [ " REFERENCES ", quoteIdent orig, "(" , quoteIdent (modelColumns t !! modelPrimaryColumn t), ")" ] instance (Model a) => SqlType (DBRefUnique a) where sqlBaseType (DBRef k) = sqlBaseType k <> ref where t = modelInfo :: ModelInfo a Just orig = modelOrigTable (modelIdentifiers :: ModelIdentifiers a) ref = S.concat [ " UNIQUE REFERENCES ", quoteIdent orig , "(" , quoteIdent (modelColumns t !! modelPrimaryColumn t), ")" ]