module Database.TransferDB.Commons where
import Prelude hiding (fail, log)
import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT)
import Control.Monad.Trans.Reader (ReaderT, runReaderT, ask, asks)
import Control.Monad.Trans.Class (lift)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Fail (MonadFail, fail)
import Control.Logging(loggingLogger, LogLevel(LevelError), log)
import Data.Text (Text)
import Data.String (fromString)
import SQL.CLI (SQLHENV, SQLHDBC, sql_handle_env, sql_handle_stmt, sql_null_data, sql_char)
import SQL.CLI.Utils (connect, freeHandle, disconnect, tables, allocHandle, getData, forAllRecords)
import SQL.CLI.ODBC (setupEnv)
import System.IO (hPutStrLn, stderr)
import Foreign.Marshal.Alloc (alloca, allocaBytes)
import Foreign.Storable (peek, poke)
import Foreign.C.String (peekCString)
import Foreign.Ptr (castPtr)
finally :: Monad m => m a -> ReaderT r (MaybeT m) b -> ReaderT r (MaybeT m) b
finally afterRun run = do
env <- ask
lift $ MaybeT $ runMaybeT (runReaderT run env) >>= (\ result -> afterRun >> return result)
finally' :: (MonadIO m, MonadFail m) => IO a -> MaybeT IO b -> m b
finally' afterRun run = do
result <- liftIO $ runMaybeT run >>= (\result -> afterRun >> return result)
maybe (fail "action failed in finally'") return result
faillog :: (MonadIO m, MonadFail m) => String -> m a
faillog = faillogS $ fromString ""
faillogS :: (MonadIO m, MonadFail m) => Text -> String -> m a
faillogS source msg = (liftIO $ loggingLogger LevelError source msg) >> fail msg
withConnection :: (MonadIO m)
=> String
-> String
-> String
-> (SQLHENV -> SQLHDBC -> (ReaderT r (MaybeT m) a))
-> ReaderT r (MaybeT m) a
withConnection d u p f = do
liftIO $ log $ fromString $ "connect to " ++ d
henv <- setupEnv
let freeEnvHandle = do
liftIO $ log $ fromString "free environment handle"
liftIO $ freeHandle sql_handle_env henv
in finally freeEnvHandle $ do
hdbc <- connect henv d u p
let freeHDBC = do
liftIO $ log $ fromString $ "disconnect from " ++ d
liftIO $ disconnect hdbc
in finally freeHDBC $ f henv hdbc
withConnection' :: (MonadIO m, HasDBInfo r) => (SQLHENV -> SQLHDBC -> (ReaderT r (MaybeT m) a)) -> ReaderT r (MaybeT m) a
withConnection' f = do
dbi <- asks extractDBInfo
let d = dbi_Datasource dbi
u = dbi_User dbi
p = dbi_Password dbi
withConnection d u p f
withEnvConnection :: (MonadIO m)
=> SQLHENV
-> String
-> String
-> String
-> (SQLHENV -> SQLHDBC -> (ReaderT r (MaybeT m) a))
-> ReaderT r (MaybeT m) a
withEnvConnection henv d u p f = do
liftIO $ log $ fromString $ "withEnvConnection' using environment handle " ++ (show henv)
hdbc <- connect henv d u p
let freeHDBC = do
liftIO $ log $ fromString $ "disconnect from " ++ d
liftIO $ disconnect hdbc
in finally freeHDBC $ f henv hdbc
withEnvConnection' :: (MonadIO m, HasDBInfo r) => SQLHENV -> (SQLHENV -> SQLHDBC -> (ReaderT r (MaybeT m) a)) -> ReaderT r (MaybeT m) a
withEnvConnection' henv f = do
dbi <- asks extractDBInfo
let d = dbi_Datasource dbi
u = dbi_User dbi
p = dbi_Password dbi
withEnvConnection henv d u p f
connect' :: (MonadIO m, MonadFail m, HasDBInfo r) => SQLHENV -> ReaderT r m SQLHDBC
connect' henv = do
dbi <- asks extractDBInfo
let d = dbi_Datasource dbi
u = dbi_User dbi
p = dbi_Password dbi
connect henv d u p
data ProgramOptions = ProgramOptions {
po_Source :: DBInfo,
po_Dest :: DBInfo
}
data DBInfo = DBInfo {
dbi_Datasource :: String,
dbi_User :: String,
dbi_Password :: String,
dbi_Schema :: String
}
class HasDBInfo a where
extractDBInfo :: a -> DBInfo
instance HasDBInfo DBInfo where
extractDBInfo = id
instance HasDBInfo ProgramOptions where
extractDBInfo = po_Source
forAllTables :: (MonadFail m, MonadIO m, HasDBInfo r) => SQLHDBC -> a -> (a -> String -> ReaderT r (MaybeT m) a) -> ReaderT r (MaybeT m) a
forAllTables hdbc arg f = do
tables_stmt <- allocHandle sql_handle_stmt hdbc
liftIO $ log $ fromString $ "forAllTables allocated tables statement: " ++ (show tables_stmt)
schema <- asks $ dbi_Schema.extractDBInfo
finally (liftIO $ freeHandle sql_handle_stmt tables_stmt) $ do
result <- liftIO $ runMaybeT $ tables tables_stmt Nothing (Just schema) Nothing (Just "TABLE")
let readTableName = liftIO $ allocaBytes 255
(\ p_tableName ->
alloca
(\ p_tableName_ind -> do
poke p_tableName_ind 0
_ <- getData tables_stmt 3 sql_char p_tableName 255 p_tableName_ind
tableName_ind <- liftIO $ peek p_tableName_ind
tableName <- if tableName_ind == sql_null_data
then return Nothing
else (liftIO . peekCString . castPtr) p_tableName >>= (return.Just)
return tableName))
withTableName arg' = do
tableName <- readTableName
maybe (return arg') (f arg') tableName
result <- forAllRecords tables_stmt withTableName arg
return result