relational-query-HDBC 0.1.0.1 → 0.2.0.0
raw patch · 9 files changed
+327/−190 lines, 9 filesdep +dlistdep +transformersdep ~relational-query
Dependencies added: dlist, transformers
Dependency ranges changed: relational-query
Files
- relational-query-HDBC.cabal +4/−2
- src/Database/HDBC/Query/TH.hs +15/−11
- src/Database/HDBC/Schema/Driver.hs +85/−3
- src/Database/HDBC/Schema/IBMDB2.hs +32/−29
- src/Database/HDBC/Schema/MySQL.hs +45/−28
- src/Database/HDBC/Schema/Oracle.hs +39/−28
- src/Database/HDBC/Schema/PostgreSQL.hs +34/−31
- src/Database/HDBC/Schema/SQLServer.hs +36/−28
- src/Database/HDBC/Schema/SQLite3.hs +37/−30
relational-query-HDBC.cabal view
@@ -1,5 +1,5 @@ name: relational-query-HDBC-version: 0.1.0.1+version: 0.2.0.0 synopsis: HDBC instance of relational-query and typed query interface for HDBC description: This package contains the HDBC instance of relational-query and the typed query interface for HDBC.@@ -43,12 +43,14 @@ build-depends: base <5 , containers+ , transformers , convertible , template-haskell+ , dlist , names-th , persistable-record- , relational-query+ , relational-query >= 0.6.1 , relational-schemas , HDBC >=2 , HDBC-session
src/Database/HDBC/Query/TH.hs view
@@ -28,24 +28,26 @@ import Data.Maybe (listToMaybe, fromMaybe) import qualified Data.Map as Map+import Control.Applicative ((<$>), (<*>)) import Control.Monad (when) import Database.HDBC (IConnection, SqlValue, prepare) import Language.Haskell.TH (Q, runIO, Name, TypeQ, Dec) import Language.Haskell.TH.Name.CamelCase (ConName, varCamelcaseName)-import Language.Haskell.TH.Lib.Extra (reportWarning, reportMessage)+import Language.Haskell.TH.Lib.Extra (reportWarning, reportError, reportMessage) import Database.Record.TH (makeRecordPersistableWithSqlTypeDefault) import qualified Database.Record.TH as Record-import Database.Relational.Query (Relation, Config, defaultConfig, relationalQuerySQL)+import Database.Relational.Query (Relation, Config, verboseAsCompilerWarning, defaultConfig, relationalQuerySQL) import Database.Relational.Query.SQL (QuerySuffix) import qualified Database.Relational.Query.TH as Relational import Database.HDBC.Session (withConnectionIO) import Database.HDBC.Record.Persistable () -import Database.HDBC.Schema.Driver (Driver, getFields, getPrimaryKey)+import Database.HDBC.Schema.Driver+ (runLog, newLogChan, takeLogs, Driver, getFields, getPrimaryKey) -- | Generate all persistable templates against defined record like type constructor.@@ -95,15 +97,17 @@ -> [ConName] -- ^ Derivings -> Q [Dec] -- ^ Result declaration defineTableFromDB' connect config drv scm tbl derives = do- let getDBinfo =- withConnectionIO connect- (\conn -> do- (cols, notNullIdxs) <- getFields drv conn scm tbl- primCols <- getPrimaryKey drv conn scm tbl-- return (cols, notNullIdxs, primCols) )+ let getDBinfo = do+ logChan <- newLogChan $ verboseAsCompilerWarning config+ infoP <- withConnectionIO connect+ (\conn ->+ (,)+ <$> getFields drv conn logChan scm tbl+ <*> getPrimaryKey drv conn logChan scm tbl)+ (,) infoP <$> takeLogs logChan - (cols, notNullIdxs, primaryCols) <- runIO getDBinfo+ (((cols, notNullIdxs), primaryCols), logs) <- runIO getDBinfo+ mapM_ (runLog reportWarning reportError) logs when (null primaryCols) . reportWarning $ "getPrimaryKey: Primary key not found for table: " ++ scm ++ "." ++ tbl
src/Database/HDBC/Schema/Driver.hs view
@@ -11,12 +11,24 @@ -- to load database system catalog via HDBC. module Database.HDBC.Schema.Driver ( TypeMap,++ Log, runLog,+ LogChan, newLogChan, takeLogs, putWarning, putError, putVerbose,+ failWith, hoistMaybe, maybeIO,+ Driver(Driver, typeMap, getFieldsWithMap, getPrimaryKey), emptyDriver, getFields ) where import Database.HDBC (IConnection)+import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef)+import Data.Monoid (mempty, (<>))+import Data.DList (DList, toList)+import Control.Applicative ((<$>), pure, (<*>))+import Control.Monad (MonadPlus, mzero)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Maybe (MaybeT (..)) import Language.Haskell.TH (TypeQ) @@ -24,6 +36,73 @@ -- Type name string depends on specification of DBMS system catalogs. type TypeMap = [(String, TypeQ)] +-- | Log string type for compile time.+data Log+ = Warning String+ | Error String++-- | Folding operation of 'Log' type.+runLog :: (String -> t) -> (String -> t) -> Log -> t+runLog wf ef = d where+ d (Warning m) = wf m+ d (Error m) = ef m++-- | Channel to store compile-time warning messages.+data LogChan =+ LogChan+ { chan :: IORef (DList Log)+ , verboseAsWarning :: Bool+ }++-- | Build and return a new instance of 'LogChan'.+newLogChan :: Bool -> IO LogChan+newLogChan v =+ LogChan <$> newIORef mempty <*> pure v++-- | Take all logs list from channel.+takeLogs :: LogChan -> IO [Log]+takeLogs lchan = do+ xs <- readIORef $ chan lchan+ writeIORef (chan lchan) mempty+ return $ toList xs++putLog :: LogChan -> Log -> IO ()+putLog lchan m = chan lchan `modifyIORef` (<> pure m)++-- | Push a warning string into 'LogChan'.+putWarning :: LogChan -> String -> IO ()+putWarning lchan = putLog lchan . Warning++-- | Push an error string into 'LogChan'.+putError :: LogChan -> String -> IO ()+putError lchan = putLog lchan . Warning++-- | Push an error string into 'LogChan' and return failed context.+failWith :: LogChan -> String -> MaybeT IO a+failWith lchan m = do+ lift $ putError lchan m+ mzero++hoistM :: MonadPlus m => Maybe a -> m a+hoistM = maybe mzero return++-- | Hoist from 'Maybe' context into 'MaybeT'.+hoistMaybe :: Monad m => Maybe a -> MaybeT m a+hoistMaybe = hoistM++maybeT :: Functor f => b -> (a -> b) -> MaybeT f a -> f b+maybeT zero f = (maybe zero f <$>) . runMaybeT++-- | Run 'MaybeT' with default value.+maybeIO :: b -> (a -> b) -> MaybeT IO a -> IO b+maybeIO = maybeT++-- | Put verbose compile-time message as warning when 'verboseAsWarning'.+putVerbose :: LogChan -> String -> IO ()+putVerbose lchan+ | verboseAsWarning lchan = putWarning lchan . ("info: " ++)+ | otherwise = const $ pure ()+ -- | Interface type to load database system catalog via HDBC. data Driver conn = Driver@@ -33,12 +112,15 @@ -- | Get column name and Haskell type pairs and not-null columns index. , getFieldsWithMap :: TypeMap -- Custom type mapping -> conn -- Connection to query system catalog+ -> LogChan -> String -- Schema name string -> String -- Table name string- -> IO ([(String, TypeQ)], [Int]) -- Action to get column name and Haskell type pairs and not-null columns index.+ -> IO ([(String, TypeQ)], [Int]) {- Action to get column name and Haskell type pairs+ and not-null columns index. -} -- | Get primary key column name. , getPrimaryKey :: conn -- Connection to query system catalog+ -> LogChan -> String -- Schema name string -> String -- Table name string -> IO [String] -- Action to get column names of primary key@@ -46,8 +128,8 @@ -- | Empty definition of 'Driver' emptyDriver :: IConnection conn => Driver conn-emptyDriver = Driver [] (\_ _ _ _ -> return ([],[])) (\_ _ _ -> return [])+emptyDriver = Driver [] (\_ _ _ _ _ -> return ([],[])) (\_ _ _ _ -> return []) -- | Helper function to call 'getFieldsWithMap' using 'typeMap' of 'Driver'.-getFields :: IConnection conn => Driver conn -> conn -> String -> String -> IO ([(String, TypeQ)], [Int])+getFields :: IConnection conn => Driver conn -> conn -> LogChan -> String -> String -> IO ([(String, TypeQ)], [Int]) getFields drv = getFieldsWithMap drv (typeMap drv)
src/Database/HDBC/Schema/IBMDB2.hs view
@@ -24,12 +24,13 @@ import qualified Data.List as List import Data.Char (toUpper) import Data.Map (fromList)-import Control.Monad (when)+import Control.Applicative ((<$>), (<|>))+import Control.Monad (guard)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Maybe (MaybeT) import Database.HDBC (IConnection, SqlValue) -import Language.Haskell.TH.Lib.Extra (reportMessage)- import Database.HDBC.Record.Query (runQuery') import Database.HDBC.Record.Persistable () @@ -41,7 +42,8 @@ import qualified Database.Relational.Schema.DB2Syscat.Columns as Columns import Database.HDBC.Schema.Driver- (TypeMap, Driver, getFieldsWithMap, getPrimaryKey, emptyDriver)+ (TypeMap, LogChan, putVerbose, failWith, maybeIO, hoistMaybe,+ Driver, getFieldsWithMap, getPrimaryKey, emptyDriver) -- Specify type constructor and data constructor from same table name.@@ -51,48 +53,49 @@ logPrefix :: String -> String logPrefix = ("IBMDB2: " ++) -putLog :: String -> IO ()-putLog = reportMessage . logPrefix+putLog :: LogChan -> String -> IO ()+putLog lchan = putVerbose lchan . logPrefix -compileErrorIO :: String -> IO a-compileErrorIO = fail . logPrefix+compileError :: LogChan -> String -> MaybeT IO a+compileError lchan = failWith lchan . logPrefix getPrimaryKey' :: IConnection conn- => conn- -> String- -> String- -> IO [String]-getPrimaryKey' conn scm' tbl' = do+ => conn+ -> LogChan+ -> String+ -> String+ -> IO [String]+getPrimaryKey' conn lchan scm' tbl' = do let tbl = map toUpper tbl' scm = map toUpper scm' primCols <- runQuery' conn primaryKeyQuerySQL (scm, tbl)- let primaryKeyCols = normalizeColumn `fmap` primCols- putLog $ "getPrimaryKey: primary key = " ++ show primaryKeyCols+ let primaryKeyCols = normalizeColumn <$> primCols+ putLog lchan $ "getPrimaryKey: primary key = " ++ show primaryKeyCols return primaryKeyCols getColumns' :: IConnection conn- => TypeMap- -> conn- -> String- -> String- -> IO ([(String, TypeQ)], [Int])-getColumns' tmap conn scm' tbl' = do+ => TypeMap+ -> conn+ -> LogChan+ -> String+ -> String+ -> IO ([(String, TypeQ)], [Int])+getColumns' tmap conn lchan scm' tbl' = maybeIO ([], []) id $ do let tbl = map toUpper tbl' scm = map toUpper scm' - cols <- runQuery' conn columnsQuerySQL (scm, tbl)- when (null cols) . compileErrorIO- $ "getFields: No columns found: schema = " ++ scm ++ ", table = " ++ tbl+ cols <- lift $ runQuery' conn columnsQuerySQL (scm, tbl)+ guard (not $ null cols) <|>+ compileError lchan ("getFields: No columns found: schema = " ++ scm ++ ", table = " ++ tbl) let notNullIdxs = map fst . filter (notNull . snd) . zip [0..] $ cols- putLog+ lift . putLog lchan $ "getFields: num of columns = " ++ show (List.length cols) ++ ", not null columns = " ++ show notNullIdxs- let getType' col = case getType (fromList tmap) col of- Nothing -> compileErrorIO- $ "Type mapping is not defined against DB2 type: " ++ Columns.typename col- Just p -> return p+ let getType' col =+ hoistMaybe (getType (fromList tmap) col) <|>+ compileError lchan ("Type mapping is not defined against DB2 type: " ++ Columns.typename col) types <- mapM getType' cols return (types, notNullIdxs)
src/Database/HDBC/Schema/MySQL.hs view
@@ -1,6 +1,15 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE MultiParamTypeClasses #-}++-- |+-- Module : Database.HDBC.Schema.MySQL+-- Copyright : 2013 Sho KURODA+-- License : BSD3+--+-- Maintainer : krdlab@gmail.com+-- Stability : experimental+-- Portability : unknown module Database.HDBC.Schema.MySQL ( driverMySQL@@ -9,7 +18,10 @@ import Prelude hiding (length) import Language.Haskell.TH (TypeQ)-import Language.Haskell.TH.Lib.Extra (reportMessage)+import Control.Applicative ((<$>), (<|>))+import Control.Monad (guard)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Maybe (MaybeT) import qualified Data.List as List import Data.Map (fromList) @@ -17,6 +29,11 @@ import Database.HDBC.Record.Query (runQuery') import Database.HDBC.Record.Persistable () import Database.HDBC.Schema.Driver ( TypeMap+ , LogChan+ , putVerbose+ , failWith+ , maybeIO+ , hoistMaybe , Driver , getFieldsWithMap , getPrimaryKey@@ -38,52 +55,52 @@ logPrefix :: String -> String logPrefix = ("MySQL: " ++) -putLog :: String -> IO ()-putLog = reportMessage . logPrefix+putLog :: LogChan -> String -> IO ()+putLog lchan = putVerbose lchan . logPrefix -compileErrorIO :: String -> IO a-compileErrorIO = fail . logPrefix+compileError :: LogChan -> String -> MaybeT IO a+compileError lchan = failWith lchan . logPrefix getPrimaryKey' :: IConnection conn => conn+ -> LogChan -> String -> String -> IO [String]-getPrimaryKey' conn scm tbl = do+getPrimaryKey' conn lchan scm tbl = do primCols <- runQuery' conn primaryKeyQuerySQL (scm, tbl)- let primaryKeyCols = normalizeColumn `fmap` primCols- putLog $ "getPrimaryKey: primary key = " ++ show primaryKeyCols+ let primaryKeyCols = normalizeColumn <$> primCols+ putLog lchan $ "getPrimaryKey: primary key = " ++ show primaryKeyCols return primaryKeyCols -getFields' :: IConnection conn- => TypeMap- -> conn- -> String- -> String- -> IO ([(String, TypeQ)], [Int])-getFields' tmap conn scm tbl = do- cols <- runQuery' conn columnsQuerySQL (scm, tbl)- case cols of- [] -> compileErrorIO- $ "getFields: No columns found: schema = " ++ scm- ++ ", table = " ++ tbl- _ -> return ()+getColumns' :: IConnection conn+ => TypeMap+ -> conn+ -> LogChan+ -> String+ -> String+ -> IO ([(String, TypeQ)], [Int])+getColumns' tmap conn lchan scm tbl = maybeIO ([], []) id $ do+ cols <- lift $ runQuery' conn columnsQuerySQL (scm, tbl)+ guard (not $ null cols) <|>+ compileError lchan+ ("getFields: No columns found: schema = " ++ scm+ ++ ", table = " ++ tbl) let notNullIdxs = map fst . filter (notNull . snd) . zip [0..] $ cols- putLog+ lift . putLog lchan $ "getFields: num of columns = " ++ show (List.length cols) ++ ", not null columns = " ++ show notNullIdxs types <- mapM getType' cols return (types, notNullIdxs) where getType' col =- case getType (fromList tmap) col of- Nothing -> compileErrorIO- $ "Type mapping is not defined against MySQL type: "- ++ Columns.dataType col- Just p -> return p+ hoistMaybe (getType (fromList tmap) col) <|>+ compileError lchan+ ("Type mapping is not defined against MySQL type: "+ ++ Columns.dataType col) -- | Driver implementation driverMySQL :: IConnection conn => Driver conn driverMySQL =- emptyDriver { getFieldsWithMap = getFields' }+ emptyDriver { getFieldsWithMap = getColumns' } { getPrimaryKey = getPrimaryKey' }
src/Database/HDBC/Schema/Oracle.hs view
@@ -1,23 +1,34 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE TemplateHaskell, MultiParamTypeClasses #-} +-- |+-- Module : Database.HDBC.Schema.MySQL+-- Copyright : 2013 Shohei Yasutake+-- License : BSD3+--+-- Maintainer : amutake.s@gmail.com+-- Stability : experimental+-- Portability : unknown module Database.HDBC.Schema.Oracle ( driverOracle ) where -import Control.Applicative ((<$>))+import Control.Applicative ((<$>), (<|>))+import Control.Monad (guard)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Maybe (MaybeT) import Data.Char (toUpper) import Data.Map (fromList) import Data.Maybe (catMaybes) import Language.Haskell.TH (TypeQ)-import Language.Haskell.TH.Lib.Extra (reportMessage) import Database.HDBC (IConnection, SqlValue) import Database.HDBC.Record.Query (runQuery') import Database.HDBC.Record.Persistable () import Database.Record.TH (makeRecordPersistableWithSqlTypeDefaultFromDefined) import Database.HDBC.Schema.Driver- ( TypeMap, Driver, getFieldsWithMap, getPrimaryKey, emptyDriver+ ( TypeMap, LogChan, putVerbose, failWith, maybeIO, hoistMaybe,+ Driver, getFieldsWithMap, getPrimaryKey, emptyDriver ) import Database.Relational.Schema.Oracle@@ -34,53 +45,53 @@ logPrefix :: String -> String logPrefix = ("Oracle: " ++) -putLog :: String -> IO ()-putLog = reportMessage . logPrefix+putLog :: LogChan -> String -> IO ()+putLog lchan = putVerbose lchan . logPrefix -compileErrorIO :: String -> IO a-compileErrorIO = fail . logPrefix+compileError :: LogChan -> String -> MaybeT IO a+compileError lchan = failWith lchan . logPrefix getPrimaryKey' :: IConnection conn => conn+ -> LogChan -> String -- ^ owner name -> String -- ^ table name -> IO [String] -- ^ primary key names-getPrimaryKey' conn owner' tbl' = do+getPrimaryKey' conn lchan owner' tbl' = do let owner = map toUpper owner' tbl = map toUpper tbl' prims <- map normalizeColumn . catMaybes <$> runQuery' conn primaryKeyQuerySQL (owner, tbl)- putLog $ "getPrimaryKey: keys = " ++ show prims+ putLog lchan $ "getPrimaryKey: keys = " ++ show prims return prims -getFields' :: IConnection conn- => TypeMap- -> conn- -> String- -> String- -> IO ([(String, TypeQ)], [Int])-getFields' tmap conn owner' tbl' = do+getColumns' :: IConnection conn+ => TypeMap+ -> conn+ -> LogChan+ -> String+ -> String+ -> IO ([(String, TypeQ)], [Int])+getColumns' tmap conn lchan owner' tbl' = maybeIO ([], []) id $ do let owner = map toUpper owner' tbl = map toUpper tbl'- cols <- runQuery' conn columnsQuerySQL (owner, tbl)- case cols of- [] -> compileErrorIO $- "getFields: No columns found: owner = " ++ owner ++ ", table = " ++ tbl- _ -> return ()+ cols <- lift $ runQuery' conn columnsQuerySQL (owner, tbl)+ guard (not $ null cols) <|>+ compileError lchan+ ("getFields: No columns found: owner = " ++ owner ++ ", table = " ++ tbl) let notNullIdxs = map fst . filter (notNull . snd) . zip [0..] $ cols- putLog $+ lift . putLog lchan $ "getFields: num of columns = " ++ show (length cols) ++ ", not null columns = " ++ show notNullIdxs- let getType' col = case getType (fromList tmap) col of- Nothing -> compileErrorIO $- "Type mapping is not defined against Oracle DB type: " ++- show (Cols.dataType col)- Just p -> return p+ let getType' col =+ hoistMaybe (getType (fromList tmap) col) <|>+ compileError lchan+ ("Type mapping is not defined against Oracle DB type: " ++ show (Cols.dataType col)) types <- mapM getType' cols return (types, notNullIdxs) -- | Driver for Oracle DB driverOracle :: IConnection conn => Driver conn driverOracle =- emptyDriver { getFieldsWithMap = getFields' }+ emptyDriver { getFieldsWithMap = getColumns' } { getPrimaryKey = getPrimaryKey' }
src/Database/HDBC/Schema/PostgreSQL.hs view
@@ -21,12 +21,13 @@ import Data.Char (toLower) import Data.Map (fromList)-import Control.Monad (when)+import Control.Applicative ((<$>), (<|>))+import Control.Monad (guard)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Maybe (MaybeT) import Database.HDBC (IConnection, SqlValue) -import Language.Haskell.TH.Lib.Extra (reportMessage)- import Database.HDBC.Record.Query (runQuery') import Database.HDBC.Record.Persistable () @@ -40,7 +41,8 @@ import qualified Database.Relational.Schema.PgCatalog.PgType as Type import Database.HDBC.Schema.Driver- (TypeMap, Driver, getFieldsWithMap, getPrimaryKey, emptyDriver)+ (TypeMap, LogChan, putVerbose, failWith, maybeIO, hoistMaybe,+ Driver, getFieldsWithMap, getPrimaryKey, emptyDriver) $(makeRecordPersistableWithSqlTypeDefaultFromDefined@@ -52,55 +54,56 @@ logPrefix :: String -> String logPrefix = ("PostgreSQL: " ++) -putLog :: String -> IO ()-putLog = reportMessage . logPrefix+putLog :: LogChan -> String -> IO ()+putLog lchan = putVerbose lchan . logPrefix -compileErrorIO :: String -> IO a-compileErrorIO = fail . logPrefix+compileError :: LogChan -> String -> MaybeT IO a+compileError lchan = failWith lchan . logPrefix getPrimaryKey' :: IConnection conn- => conn- -> String- -> String- -> IO [String]-getPrimaryKey' conn scm' tbl' = do+ => conn+ -> LogChan+ -> String+ -> String+ -> IO [String]+getPrimaryKey' conn lchan scm' tbl' = do let scm = map toLower scm' tbl = map toLower tbl' mayKeyLen <- runQuery' conn primaryKeyLengthQuerySQL (scm, tbl) case mayKeyLen of [] -> do- putLog "getPrimaryKey: Primary key not found."+ putLog lchan "getPrimaryKey: Primary key not found." return [] [keyLen] -> do primCols <- runQuery' conn (primaryKeyQuerySQL keyLen) (scm, tbl)- let primaryKeyCols = normalizeColumn `fmap` primCols- putLog $ "getPrimaryKey: primary key = " ++ show primaryKeyCols+ let primaryKeyCols = normalizeColumn <$> primCols+ putLog lchan $ "getPrimaryKey: primary key = " ++ show primaryKeyCols return primaryKeyCols _:_:_ -> do- putLog "getPrimaryKey: Fail to detect primary key. Something wrong."+ putLog lchan "getPrimaryKey: Fail to detect primary key. Something wrong." return [] getColumns' :: IConnection conn- => TypeMap- -> conn- -> String- -> String- -> IO ([(String, TypeQ)], [Int])-getColumns' tmap conn scm' tbl' = do+ => TypeMap+ -> conn+ -> LogChan+ -> String+ -> String+ -> IO ([(String, TypeQ)], [Int])+getColumns' tmap conn lchan scm' tbl' = maybeIO ([], []) id $ do let scm = map toLower scm' tbl = map toLower tbl'- cols <- runQuery' conn columnQuerySQL (scm, tbl)- when (null cols) . compileErrorIO- $ "getFields: No columns found: schema = " ++ scm ++ ", table = " ++ tbl+ cols <- lift $ runQuery' conn columnQuerySQL (scm, tbl)+ guard (not $ null cols) <|>+ compileError lchan ("getFields: No columns found: schema = " ++ scm ++ ", table = " ++ tbl) let notNullIdxs = map fst . filter (notNull . snd) . zip [0..] $ cols- putLog+ lift . putLog lchan $ "getFields: num of columns = " ++ show (length cols) ++ ", not null columns = " ++ show notNullIdxs- let getType' col = case getType (fromList tmap) col of- Nothing -> compileErrorIO- $ "Type mapping is not defined against PostgreSQL type: " ++ Type.typname (snd col)- Just p -> return p+ let getType' col =+ hoistMaybe (getType (fromList tmap) col) <|>+ compileError lchan ("Type mapping is not defined against PostgreSQL type: " ++ Type.typname (snd col)) types <- mapM getType' cols return (types, notNullIdxs)
src/Database/HDBC/Schema/SQLServer.hs view
@@ -17,20 +17,26 @@ import qualified Database.Relational.Schema.SQLServerSyscat.Columns as Columns import qualified Database.Relational.Schema.SQLServerSyscat.Types as Types +import Control.Applicative ((<$>), (<|>))+import Control.Monad (guard)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Maybe (MaybeT) import Data.Map (fromList) import Data.Maybe (catMaybes) import Database.HDBC (IConnection, SqlValue) import Database.HDBC.Record.Query (runQuery') import Database.HDBC.Record.Persistable ()-import Database.HDBC.Schema.Driver (TypeMap, Driver, getFieldsWithMap, getPrimaryKey, emptyDriver)+import Database.HDBC.Schema.Driver+ (TypeMap, LogChan, putVerbose, failWith, maybeIO,+ Driver, hoistMaybe, getFieldsWithMap, getPrimaryKey, emptyDriver) import Database.Record.TH (makeRecordPersistableWithSqlTypeDefaultFromDefined) import Database.Relational.Schema.SQLServer (columnTypeQuerySQL, getType, normalizeColumn, notNull, primaryKeyQuerySQL) import Database.Relational.Schema.SQLServerSyscat.Columns (Columns) import Database.Relational.Schema.SQLServerSyscat.Types (Types) import Language.Haskell.TH (TypeQ)-import Language.Haskell.TH.Lib.Extra (reportMessage) + $(makeRecordPersistableWithSqlTypeDefaultFromDefined [t| SqlValue |] ''Columns) @@ -40,49 +46,51 @@ logPrefix :: String -> String logPrefix = ("SQLServer: " ++) -putLog :: String -> IO ()-putLog = reportMessage . logPrefix+putLog :: LogChan -> String -> IO ()+putLog lchan = putVerbose lchan . logPrefix -compileErrorIO :: String -> IO a-compileErrorIO = fail . logPrefix+compileError :: LogChan -> String -> MaybeT IO a+compileError lchan = failWith lchan . logPrefix getPrimaryKey' :: IConnection conn => conn+ -> LogChan -> String -> String -> IO [String]-getPrimaryKey' conn scm tbl = do- prims <- catMaybes `fmap` runQuery' conn primaryKeyQuerySQL (scm,tbl)+getPrimaryKey' conn lchan scm tbl = do+ prims <- catMaybes <$> runQuery' conn primaryKeyQuerySQL (scm,tbl) let primColumns = map normalizeColumn prims- putLog $ "getPrimaryKey: keys=" ++ show primColumns+ putLog lchan $ "getPrimaryKey: keys=" ++ show primColumns return primColumns -getFields' :: IConnection conn- => TypeMap- -> conn- -> String- -> String- -> IO ([(String, TypeQ)], [Int])-getFields' tmap conn scm tbl = do- rows <- runQuery' conn columnTypeQuerySQL (scm, tbl)- case rows of- [] -> compileErrorIO- $ "getFields: No columns found: schema = " ++ scm ++ ", table = " ++ tbl- _ -> return ()+getColumns' :: IConnection conn+ => TypeMap+ -> conn+ -> LogChan+ -> String+ -> String+ -> IO ([(String, TypeQ)], [Int])+getColumns' tmap conn lchan scm tbl = maybeIO ([], []) id $ do+ rows <- lift $ runQuery' conn columnTypeQuerySQL (scm, tbl)+ guard (not $ null rows) <|>+ compileError lchan+ ("getFields: No columns found: schema = " ++ scm ++ ", table = " ++ tbl) let columnId ((cols,_),_) = Columns.columnId cols - 1 let notNullIdxs = map (fromIntegral . columnId) . filter notNull $ rows- putLog+ lift . putLog lchan $ "getFields: num of columns = " ++ show (length rows) ++ ", not null columns = " ++ show notNullIdxs- let getType' rec@((_,typs),typScms) = case getType (fromList tmap) rec of- Nothing -> compileErrorIO- $ "Type mapping is not defined against SQLServer type: "- ++ typScms ++ "." ++ Types.name typs- Just p -> return p+ let getType' rec'@((_,typs),typScms) =+ hoistMaybe (getType (fromList tmap) rec') <|>+ compileError lchan+ ("Type mapping is not defined against SQLServer type: "+ ++ typScms ++ "." ++ Types.name typs) types <- mapM getType' rows return (types, notNullIdxs) +-- | Driver implementation driverSQLServer :: IConnection conn => Driver conn driverSQLServer =- emptyDriver { getFieldsWithMap = getFields' }+ emptyDriver { getFieldsWithMap = getColumns' } { getPrimaryKey = getPrimaryKey' }
src/Database/HDBC/Schema/SQLite3.hs view
@@ -18,12 +18,18 @@ import qualified Database.Relational.Schema.SQLite3Syscat.IndexList as IndexList import qualified Database.Relational.Schema.SQLite3Syscat.TableInfo as TableInfo +import Control.Applicative ((<|>))+import Control.Monad (guard)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Maybe (MaybeT) import Data.List (isPrefixOf, sort, sortBy) import Data.Map (fromList) import Database.HDBC (IConnection, SqlValue) import Database.HDBC.Record.Query (runQuery') import Database.HDBC.Record.Persistable ()-import Database.HDBC.Schema.Driver (TypeMap, Driver, getFieldsWithMap, getPrimaryKey, emptyDriver)+import Database.HDBC.Schema.Driver+ (TypeMap, LogChan, putVerbose, failWith, maybeIO,+ Driver, hoistMaybe, getFieldsWithMap, getPrimaryKey, emptyDriver) import Database.Record.TH (makeRecordPersistableWithSqlTypeDefaultFromDefined) import Database.Relational.Schema.SQLite3 (getType, indexInfoQuerySQL, indexListQuerySQL, normalizeColumn, normalizeType, notNull, tableInfoQuerySQL)@@ -31,7 +37,6 @@ import Database.Relational.Schema.SQLite3Syscat.IndexList (IndexList) import Database.Relational.Schema.SQLite3Syscat.TableInfo (TableInfo) import Language.Haskell.TH (TypeQ)-import Language.Haskell.TH.Lib.Extra (reportMessage) $(makeRecordPersistableWithSqlTypeDefaultFromDefined [t| SqlValue |] ''TableInfo)@@ -45,23 +50,24 @@ logPrefix :: String -> String logPrefix = ("SQLite3: " ++) -putLog :: String -> IO ()-putLog = reportMessage . logPrefix+putLog :: LogChan -> String -> IO ()+putLog lchan = putVerbose lchan . logPrefix -compileErrorIO :: String -> IO a-compileErrorIO = fail . logPrefix+compileError :: LogChan -> String -> MaybeT IO a+compileError lchan = failWith lchan . logPrefix getPrimaryKey' :: IConnection conn => conn+ -> LogChan -> String -> String -> IO [String]-getPrimaryKey' conn scm tbl = do+getPrimaryKey' conn lchan scm tbl = do tblinfo <- runQuery' conn (tableInfoQuerySQL scm tbl) ()- let primColumns = map (normalizeColumn . TableInfo.name)- . filter ((1 ==) . TableInfo.pk) $ tblinfo+ let primColumns = [ normalizeColumn $ TableInfo.name ti+ | ti <- tblinfo, TableInfo.pk ti == 1 ] if length primColumns <= 1 then do- putLog $ "getPrimaryKey: key=" ++ show primColumns+ putLog lchan $ "getPrimaryKey: key=" ++ show primColumns return primColumns else do idxlist <- runQuery' conn (indexListQuerySQL scm tbl) ()@@ -73,35 +79,36 @@ let idxInfo = concat . take 1 . filter isPrimaryKey $ idxInfos let comp x y = compare (IndexInfo.seqno x) (IndexInfo.seqno y) let primColumns' = map IndexInfo.name . sortBy comp $ idxInfo- putLog $ "getPrimaryKey: keys=" ++ show primColumns'+ putLog lchan $ "getPrimaryKey: keys=" ++ show primColumns' return primColumns' -getFields' :: IConnection conn- => TypeMap- -> conn- -> String- -> String- -> IO ([(String, TypeQ)], [Int])-getFields' tmap conn scm tbl = do- rows <- runQuery' conn (tableInfoQuerySQL scm tbl) ()- case rows of- [] -> compileErrorIO- $ "getFields: No columns found: schema = " ++ scm ++ ", table = " ++ tbl- _ -> return ()+getColumns' :: IConnection conn+ => TypeMap+ -> conn+ -> LogChan+ -> String+ -> String+ -> IO ([(String, TypeQ)], [Int])+getColumns' tmap conn lchan scm tbl = maybeIO ([], []) id $ do+ rows <- lift $ runQuery' conn (tableInfoQuerySQL scm tbl) ()+ guard (not $ null rows) <|>+ compileError lchan+ ("getFields: No columns found: schema = " ++ scm ++ ", table = " ++ tbl) let columnId = TableInfo.cid let notNullIdxs = map (fromIntegral . columnId) . filter notNull $ rows- putLog+ lift . putLog lchan $ "getFields: num of columns = " ++ show (length rows) ++ ", not null columns = " ++ show notNullIdxs- let getType' ti = case getType (fromList tmap) ti of- Nothing -> compileErrorIO- $ "Type mapping is not defined against SQLite3 type: "- ++ normalizeType (TableInfo.ctype ti)- Just p -> return p+ let getType' ti =+ hoistMaybe (getType (fromList tmap) ti) <|>+ compileError lchan+ ("Type mapping is not defined against SQLite3 type: "+ ++ normalizeType (TableInfo.ctype ti)) types <- mapM getType' rows return (types, notNullIdxs) +-- | Driver implementation driverSQLite3 :: IConnection conn => Driver conn driverSQLite3 =- emptyDriver { getFieldsWithMap = getFields' }+ emptyDriver { getFieldsWithMap = getColumns' } { getPrimaryKey = getPrimaryKey' }