module Database.Dpi.Sql(
getLanguage
, setupLanguage
, execute
, queryAsRes
, queryByPage
, Name
, SqlParam
) where
import Database.Dpi
import Database.Dpi.Field
import Control.Monad (void, when)
import Control.Monad.IO.Class (MonadIO (..))
import Data.Acquire (Acquire, mkAcquire, with)
import Data.ByteString (ByteString)
import Data.ByteString.Char8 (unpack)
import Data.Conduit
import qualified Data.Conduit.List as CL
import Data.Maybe
import Data.Monoid ((<>))
import System.Environment
getLanguage :: OracleConfig -> IO ByteString
getLanguage conf = withContext $
\cxt -> withConnection cxt conf return $
\conn -> do
((v:_):_) <- queryByPage conn "SELECT USERENV ('language') FROM DUAL" [] (0,1)
Just s :: Maybe ByteString <- fromDataField v
return s
setupLanguage :: OracleConfig -> IO ()
setupLanguage conf = do
nl <- lookupEnv "NLS_LANG"
when (isNothing nl) $ getLanguage conf >>= setEnv "NLS_LANG" . unpack
type Name = ByteString
type SqlParam = (Name, IO DataValue)
execute :: PtrConn -> SQL -> [SqlParam] -> IO Int
execute conn sql ps = do
st <- prepareStatement conn False sql
bindValue st ps
_ <- executeStatement st ModeExecDefault
fromIntegral <$> getRowCount st
{-# INLINE bindValue #-}
bindValue :: PtrStmt -> [SqlParam] -> IO ()
bindValue = mapM_ . bd
where
bd st (name,value) = value >>= bindValueByName st name
queryAsRes :: FromDataFields a => PtrConn -> SQL -> [SqlParam] -> Acquire (ConduitT () a IO ())
queryAsRes conn sql ps = do
let {-# INLINE pst #-}
pst = do
st <- prepareStatement conn False sql
bindValue st ps
r <- executeStatement st ModeExecDefault
return (st,r)
{-# INLINE meg #-}
meg info value = DataField{..}
(st,r) <- mkAcquire pst (void . releaseStatement . fst)
let {-# INLINE pull #-}
pull = do
mayC <- liftIO $ fetch st
case mayC of
Nothing -> return ()
(Just _) -> do
vs <- liftIO $ mapM (getQueryValue st) [1..r]
qs <- liftIO $ mapM (getQueryInfo st) [1..r]
a <- liftIO $ fromDataFields' $ zipWith meg qs vs
yield a
pull
return pull
queryByPage :: FromDataFields a => PtrConn -> SQL -> [SqlParam] -> Page -> IO [a]
queryByPage conn sql ps (offset,limit) = do
let sql' = sql <> " OFFSET " <> show offset <> " ROWS FETCH NEXT " <> show limit <> " ROWS ONLY"
with (queryAsRes conn sql' ps) (\a -> runConduit $ a .| CL.fold (flip (:)) [])