module Database.SQLite3.Direct (
open,
close,
errcode,
errmsg,
setTrace,
getAutoCommit,
setSharedCacheEnabled,
exec,
execWithCallback,
ExecCallback,
prepare,
getStatementDatabase,
step,
reset,
finalize,
clearBindings,
statementSql,
bindParameterCount,
bindParameterName,
bindParameterIndex,
columnCount,
columnName,
bindInt64,
bindDouble,
bindText,
bindBlob,
bindZeroBlob,
bindNull,
columnType,
columnInt64,
columnDouble,
columnText,
columnBlob,
setLoadExtensionEnabled,
lastInsertRowId,
changes,
totalChanges,
createFunction,
createAggregate,
deleteFunction,
funcArgCount,
funcArgType,
funcArgInt64,
funcArgDouble,
funcArgText,
funcArgBlob,
funcResultInt64,
funcResultDouble,
funcResultText,
funcResultBlob,
funcResultZeroBlob,
funcResultNull,
getFuncContextDatabase,
createCollation,
deleteCollation,
interrupt,
blobOpen,
blobClose,
blobReopen,
blobBytes,
blobRead,
blobReadBuf,
blobWrite,
backupInit,
backupFinish,
backupStep,
backupRemaining,
backupPagecount,
Database(..),
Statement(..),
ColumnType(..),
FuncContext(..),
FuncArgs(..),
Blob(..),
Backup(..),
StepResult(..),
BackupStepResult(..),
Error(..),
Utf8(..),
ParamIndex(..),
ColumnIndex(..),
ColumnCount,
ArgCount(..),
ArgIndex,
) where
import Database.SQLite3.Bindings
import qualified Data.ByteString as BS
import qualified Data.ByteString.Unsafe as BSU
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Control.Applicative ((<$>))
import Control.Exception as E
import Control.Monad (join, unless)
import Data.ByteString (ByteString)
import Data.IORef
import Data.Monoid
import Data.String (IsString(..))
import Data.Text.Encoding.Error (lenientDecode)
import Foreign
import Foreign.C
import qualified System.IO.Unsafe as IOU
newtype Database = Database (Ptr CDatabase)
deriving (Eq, Show)
newtype Statement = Statement (Ptr CStatement)
deriving (Eq, Show)
data StepResult
= Row
| Done
deriving (Eq, Show)
data BackupStepResult
= BackupOK
| BackupDone
deriving (Eq, Show)
newtype Utf8 = Utf8 ByteString
deriving (Eq, Ord)
instance Show Utf8 where
show (Utf8 s) = (show . T.decodeUtf8With lenientDecode) s
instance IsString Utf8 where
fromString = Utf8 . T.encodeUtf8 . T.pack
instance Monoid Utf8 where
mempty = Utf8 BS.empty
mappend (Utf8 a) (Utf8 b) = Utf8 (BS.append a b)
mconcat = Utf8 . BS.concat . map (\(Utf8 s) -> s)
packUtf8 :: a -> (Utf8 -> a) -> CString -> IO a
packUtf8 n f cstr | cstr == nullPtr = return n
| otherwise = f . Utf8 <$> BS.packCString cstr
packCStringLen :: CString -> CNumBytes -> IO ByteString
packCStringLen cstr len =
BS.packCStringLen (cstr, fromIntegral len)
packUtf8Array :: IO a -> (Utf8 -> IO a) -> Int -> Ptr CString -> IO [a]
packUtf8Array onNull onUtf8 count base =
peekArray count base >>= mapM (join . packUtf8 onNull onUtf8)
unsafeUseAsCStringLenNoNull :: ByteString -> (CString -> CNumBytes -> IO a) -> IO a
unsafeUseAsCStringLenNoNull bs cb
| BS.null bs = cb (intPtrToPtr 1) 0
| otherwise = BSU.unsafeUseAsCStringLen bs $ \(ptr, len) ->
cb ptr (fromIntegral len)
wrapNullablePtr :: (Ptr a -> b) -> Ptr a -> Maybe b
wrapNullablePtr f ptr | ptr == nullPtr = Nothing
| otherwise = Just (f ptr)
type Result a = Either Error a
toResult :: a -> CError -> Result a
toResult a (CError 0) = Right a
toResult _ code = Left $ decodeError code
toResultM :: Monad m => m a -> CError -> m (Result a)
toResultM m (CError 0) = m >>= return . Right
toResultM _ code = return $ Left $ decodeError code
toStepResult :: CError -> Result StepResult
toStepResult code =
case decodeError code of
ErrorRow -> Right Row
ErrorDone -> Right Done
err -> Left err
toBackupStepResult :: CError -> Result BackupStepResult
toBackupStepResult code =
case decodeError code of
ErrorOK -> Right BackupOK
ErrorDone -> Right BackupDone
err -> Left err
newtype FuncContext = FuncContext (Ptr CContext)
deriving (Eq, Show)
data FuncArgs = FuncArgs CArgCount (Ptr (Ptr CValue))
data Blob = Blob Database (Ptr CBlob)
deriving (Eq, Show)
data Backup = Backup Database (Ptr CBackup)
deriving (Eq, Show)
open :: Utf8 -> IO (Either (Error, Utf8) Database)
open (Utf8 path) =
BS.useAsCString path $ \path' ->
alloca $ \database -> do
rc <- c_sqlite3_open path' database
db <- Database <$> peek database
case toResult () rc of
Left err -> do
msg <- errmsg db
_ <- close db
return $ Left (err, msg)
Right () ->
if db == Database nullPtr
then fail "sqlite3_open unexpectedly returned NULL"
else return $ Right db
close :: Database -> IO (Either Error ())
close (Database db) =
toResult () <$> c_sqlite3_close db
interrupt :: Database -> IO ()
interrupt (Database db) =
c_sqlite3_interrupt db
errcode :: Database -> IO Error
errcode (Database db) =
decodeError <$> c_sqlite3_errcode db
errmsg :: Database -> IO Utf8
errmsg (Database db) =
c_sqlite3_errmsg db >>= packUtf8 (Utf8 BS.empty) id
exec :: Database -> Utf8 -> IO (Either (Error, Utf8) ())
exec (Database db) (Utf8 sql) =
BS.useAsCString sql $ \sql' ->
alloca $ \msgPtrOut -> do
rc <- c_sqlite3_exec db sql' nullFunPtr nullPtr msgPtrOut
case toResult () rc of
Left err -> do
msgPtr <- peek msgPtrOut
msg <- packUtf8 (Utf8 BS.empty) id msgPtr
c_sqlite3_free msgPtr
return $ Left (err, msg)
Right () -> return $ Right ()
execWithCallback :: Database -> Utf8 -> ExecCallback -> IO (Either (Error, Utf8) ())
execWithCallback (Database db) (Utf8 sql) cb = do
abortReason <- newIORef Nothing :: IO (IORef (Maybe SomeException))
cbCache <- newIORef Nothing :: IO (IORef (Maybe ([Maybe Utf8] -> IO ())))
let getCallback cCount cNames = do
m <- readIORef cbCache
case m of
Nothing -> do
names <- packUtf8Array (fail "execWithCallback: NULL column name")
return
(fromIntegral cCount) cNames
let !cb' = cb (fromFFI cCount) names
writeIORef cbCache $ Just cb'
return cb'
Just cb' -> return cb'
let onExceptionAbort io =
(io >> return 0) `E.catch` \ex -> do
writeIORef abortReason $ Just ex
return 1
let cExecCallback _ctx cCount cValues cNames =
onExceptionAbort $ do
cb' <- getCallback cCount cNames
values <- packUtf8Array (return Nothing)
(return . Just)
(fromIntegral cCount) cValues
cb' values
BS.useAsCString sql $ \sql' ->
alloca $ \msgPtrOut ->
bracket (mkCExecCallback cExecCallback) freeHaskellFunPtr $
\pExecCallback -> do
let returnError err = do
msgPtr <- peek msgPtrOut
msg <- packUtf8 (Utf8 BS.empty) id msgPtr
c_sqlite3_free msgPtr
return $ Left (err, msg)
rc <- c_sqlite3_exec db sql' pExecCallback nullPtr msgPtrOut
case toResult () rc of
Left ErrorAbort -> do
m <- readIORef abortReason
case m of
Nothing -> returnError ErrorAbort
Just ex -> throwIO ex
Left err -> returnError err
Right () -> return $ Right ()
type ExecCallback
= ColumnCount
-> [Utf8]
-> [Maybe Utf8]
-> IO ()
setTrace :: Database -> Maybe (Utf8 -> IO ()) -> IO ()
setTrace (Database db) logger =
case logger of
Nothing -> do
_ <- c_sqlite3_trace db nullFunPtr nullPtr
return ()
Just output -> do
cb <- mkCTraceCallback $ \_ctx cStr -> do
msg <- packUtf8 (Utf8 BS.empty) id cStr
output msg
_ <- c_sqlite3_trace db cb nullPtr
return ()
getAutoCommit :: Database -> IO Bool
getAutoCommit (Database db) =
(/= 0) <$> c_sqlite3_get_autocommit db
setSharedCacheEnabled :: Bool -> IO (Either Error ())
setSharedCacheEnabled val =
toResult () <$> c_sqlite3_enable_shared_cache
(if val then 1 else 0)
prepare :: Database -> Utf8 -> IO (Either Error (Maybe Statement))
prepare (Database db) (Utf8 sql) =
BS.useAsCString sql $ \sql' ->
alloca $ \statement ->
c_sqlite3_prepare_v2 db sql' (1) statement nullPtr >>=
toResultM (wrapNullablePtr Statement <$> peek statement)
getStatementDatabase :: Statement -> IO Database
getStatementDatabase (Statement stmt) = do
db <- c_sqlite3_db_handle stmt
if db == nullPtr
then fail $ "sqlite3_db_handle(" ++ show stmt ++ ") returned NULL"
else return (Database db)
step :: Statement -> IO (Either Error StepResult)
step (Statement stmt) =
toStepResult <$> c_sqlite3_step stmt
reset :: Statement -> IO (Either Error ())
reset (Statement stmt) =
toResult () <$> c_sqlite3_reset stmt
finalize :: Statement -> IO (Either Error ())
finalize (Statement stmt) =
toResult () <$> c_sqlite3_finalize stmt
statementSql :: Statement -> IO (Maybe Utf8)
statementSql (Statement stmt) =
c_sqlite3_sql stmt >>= packUtf8 Nothing Just
clearBindings :: Statement -> IO ()
clearBindings (Statement stmt) = do
_ <- c_sqlite3_clear_bindings stmt
return ()
bindParameterCount :: Statement -> IO ParamIndex
bindParameterCount (Statement stmt) =
fromFFI <$> c_sqlite3_bind_parameter_count stmt
bindParameterName :: Statement -> ParamIndex -> IO (Maybe Utf8)
bindParameterName (Statement stmt) idx =
c_sqlite3_bind_parameter_name stmt (toFFI idx) >>=
packUtf8 Nothing Just
bindParameterIndex :: Statement -> Utf8 -> IO (Maybe ParamIndex)
bindParameterIndex (Statement stmt) (Utf8 name) =
BS.useAsCString name $ \name' -> do
idx <- fromFFI <$> c_sqlite3_bind_parameter_index stmt name'
return $ if idx == 0 then Nothing else Just idx
columnCount :: Statement -> IO ColumnCount
columnCount (Statement stmt) =
fromFFI <$> c_sqlite3_column_count stmt
columnName :: Statement -> ColumnIndex -> IO (Maybe Utf8)
columnName (Statement stmt) idx =
c_sqlite3_column_name stmt (toFFI idx) >>=
packUtf8 Nothing Just
bindInt64 :: Statement -> ParamIndex -> Int64 -> IO (Either Error ())
bindInt64 (Statement stmt) idx value =
toResult () <$> c_sqlite3_bind_int64 stmt (toFFI idx) value
bindDouble :: Statement -> ParamIndex -> Double -> IO (Either Error ())
bindDouble (Statement stmt) idx value =
toResult () <$> c_sqlite3_bind_double stmt (toFFI idx) value
bindText :: Statement -> ParamIndex -> Utf8 -> IO (Either Error ())
bindText (Statement stmt) idx (Utf8 value) =
unsafeUseAsCStringLenNoNull value $ \ptr len ->
toResult () <$>
c_sqlite3_bind_text stmt (toFFI idx) ptr len c_SQLITE_TRANSIENT
bindBlob :: Statement -> ParamIndex -> ByteString -> IO (Either Error ())
bindBlob (Statement stmt) idx value =
unsafeUseAsCStringLenNoNull value $ \ptr len ->
toResult () <$>
c_sqlite3_bind_blob stmt (toFFI idx) ptr len c_SQLITE_TRANSIENT
bindZeroBlob :: Statement -> ParamIndex -> Int -> IO (Either Error ())
bindZeroBlob (Statement stmt) idx len =
toResult () <$>
c_sqlite3_bind_zeroblob stmt (toFFI idx) (fromIntegral len)
bindNull :: Statement -> ParamIndex -> IO (Either Error ())
bindNull (Statement stmt) idx =
toResult () <$> c_sqlite3_bind_null stmt (toFFI idx)
columnType :: Statement -> ColumnIndex -> IO ColumnType
columnType (Statement stmt) idx =
decodeColumnType <$> c_sqlite3_column_type stmt (toFFI idx)
columnInt64 :: Statement -> ColumnIndex -> IO Int64
columnInt64 (Statement stmt) idx =
c_sqlite3_column_int64 stmt (toFFI idx)
columnDouble :: Statement -> ColumnIndex -> IO Double
columnDouble (Statement stmt) idx =
c_sqlite3_column_double stmt (toFFI idx)
columnText :: Statement -> ColumnIndex -> IO Utf8
columnText (Statement stmt) idx = do
ptr <- c_sqlite3_column_text stmt (toFFI idx)
len <- c_sqlite3_column_bytes stmt (toFFI idx)
Utf8 <$> packCStringLen ptr len
columnBlob :: Statement -> ColumnIndex -> IO ByteString
columnBlob (Statement stmt) idx = do
ptr <- c_sqlite3_column_blob stmt (toFFI idx)
len <- c_sqlite3_column_bytes stmt (toFFI idx)
packCStringLen ptr len
lastInsertRowId :: Database -> IO Int64
lastInsertRowId (Database db) =
c_sqlite3_last_insert_rowid db
changes :: Database -> IO Int
changes (Database db) =
fromIntegral <$> c_sqlite3_changes db
totalChanges :: Database -> IO Int
totalChanges (Database db) =
fromIntegral <$> c_sqlite3_total_changes db
data CFuncPtrs = CFuncPtrs (FunPtr CFunc) (FunPtr CFunc) (FunPtr CFuncFinal)
destroyCFuncPtrs :: FunPtr (CFuncDestroy ())
destroyCFuncPtrs = IOU.unsafePerformIO $ mkCFuncDestroy destroy
where
destroy p = do
let p' = castPtrToStablePtr p
CFuncPtrs p1 p2 p3 <- deRefStablePtr p'
unless (p1 == nullFunPtr) $ freeHaskellFunPtr p1
unless (p2 == nullFunPtr) $ freeHaskellFunPtr p2
unless (p3 == nullFunPtr) $ freeHaskellFunPtr p3
freeStablePtr p'
createFunction
:: Database
-> Utf8
-> Maybe ArgCount
-> Bool
-> (FuncContext -> FuncArgs -> IO ())
-> IO (Either Error ())
createFunction (Database db) (Utf8 name) nArgs isDet fun = mask_ $ do
funPtr <- mkCFunc fun'
u <- newStablePtr $ CFuncPtrs funPtr nullFunPtr nullFunPtr
BS.useAsCString name $ \namePtr ->
toResult () <$>
c_sqlite3_create_function_v2
db namePtr (maybeArgCount nArgs) flags (castStablePtrToPtr u)
funPtr nullFunPtr nullFunPtr destroyCFuncPtrs
where
flags = if isDet then c_SQLITE_DETERMINISTIC else 0
fun' ctx nArgs' cvals =
catchAsResultError ctx $
fun (FuncContext ctx) (FuncArgs nArgs' cvals)
createAggregate
:: Database
-> Utf8
-> Maybe ArgCount
-> a
-> (FuncContext -> FuncArgs -> a -> IO a)
-> (FuncContext -> a -> IO ())
-> IO (Either Error ())
createAggregate (Database db) (Utf8 name) nArgs initSt xStep xFinal = mask_ $ do
stepPtr <- mkCFunc xStep'
finalPtr <- mkCFuncFinal xFinal'
u <- newStablePtr $ CFuncPtrs nullFunPtr stepPtr finalPtr
BS.useAsCString name $ \namePtr ->
toResult () <$>
c_sqlite3_create_function_v2
db namePtr (maybeArgCount nArgs) 0 (castStablePtrToPtr u)
nullFunPtr stepPtr finalPtr destroyCFuncPtrs
where
xStep' ctx nArgs' cvals =
catchAsResultError ctx $ do
aggCtx <- getAggregateContext ctx
aggStPtr <- peek aggCtx
aggStRef <-
if castStablePtrToPtr aggStPtr /= nullPtr then
deRefStablePtr aggStPtr
else do
aggStRef <- newIORef initSt
aggStPtr' <- newStablePtr aggStRef
poke aggCtx aggStPtr'
return aggStRef
aggSt <- readIORef aggStRef
aggSt' <- xStep (FuncContext ctx) (FuncArgs nArgs' cvals) aggSt
writeIORef aggStRef aggSt'
xFinal' ctx = do
aggCtx <- getAggregateContext ctx
aggStPtr <- peek aggCtx
if castStablePtrToPtr aggStPtr == nullPtr then
catchAsResultError ctx $
xFinal (FuncContext ctx) initSt
else do
catchAsResultError ctx $ do
aggStRef <- deRefStablePtr aggStPtr
aggSt <- readIORef aggStRef
xFinal (FuncContext ctx) aggSt
freeStablePtr aggStPtr
getAggregateContext ctx =
c_sqlite3_aggregate_context ctx stPtrSize
stPtrSize = fromIntegral $ sizeOf (undefined :: StablePtr ())
catchAsResultError :: Ptr CContext -> IO () -> IO ()
catchAsResultError ctx action = E.catch action $ \exn -> do
let msg = show (exn :: SomeException)
withCAStringLen msg $ \(ptr, len) ->
c_sqlite3_result_error ctx ptr (fromIntegral len)
deleteFunction :: Database -> Utf8 -> Maybe ArgCount -> IO (Either Error ())
deleteFunction (Database db) (Utf8 name) nArgs =
BS.useAsCString name $ \namePtr ->
toResult () <$>
c_sqlite3_create_function_v2
db namePtr (maybeArgCount nArgs) 0 nullPtr
nullFunPtr nullFunPtr nullFunPtr nullFunPtr
maybeArgCount :: Maybe ArgCount -> CArgCount
maybeArgCount (Just n) = toFFI n
maybeArgCount Nothing = 1
funcArgCount :: FuncArgs -> ArgCount
funcArgCount (FuncArgs nArgs _) = fromIntegral nArgs
funcArgType :: FuncArgs -> ArgIndex -> IO ColumnType
funcArgType =
extractFuncArg NullColumn (fmap decodeColumnType . c_sqlite3_value_type)
funcArgInt64 :: FuncArgs -> ArgIndex -> IO Int64
funcArgInt64 = extractFuncArg 0 c_sqlite3_value_int64
funcArgDouble :: FuncArgs -> ArgIndex -> IO Double
funcArgDouble = extractFuncArg 0 c_sqlite3_value_double
funcArgText :: FuncArgs -> ArgIndex -> IO Utf8
funcArgText = extractFuncArg mempty $ \cval -> do
ptr <- c_sqlite3_value_text cval
len <- c_sqlite3_value_bytes cval
Utf8 <$> packCStringLen ptr len
funcArgBlob :: FuncArgs -> ArgIndex -> IO ByteString
funcArgBlob = extractFuncArg mempty $ \cval -> do
ptr <- c_sqlite3_value_blob cval
len <- c_sqlite3_value_bytes cval
packCStringLen ptr len
extractFuncArg :: a -> (Ptr CValue -> IO a) -> FuncArgs -> ArgIndex -> IO a
extractFuncArg defVal extract (FuncArgs nArgs p) idx
| 0 <= idx && idx < fromIntegral nArgs = do
cval <- peekElemOff p (fromIntegral idx)
extract cval
| otherwise = return defVal
funcResultInt64 :: FuncContext -> Int64 -> IO ()
funcResultInt64 (FuncContext ctx) value =
c_sqlite3_result_int64 ctx value
funcResultDouble :: FuncContext -> Double -> IO ()
funcResultDouble (FuncContext ctx) value =
c_sqlite3_result_double ctx value
funcResultText :: FuncContext -> Utf8 -> IO ()
funcResultText (FuncContext ctx) (Utf8 value) =
unsafeUseAsCStringLenNoNull value $ \ptr len ->
c_sqlite3_result_text ctx ptr len c_SQLITE_TRANSIENT
funcResultBlob :: FuncContext -> ByteString -> IO ()
funcResultBlob (FuncContext ctx) value =
unsafeUseAsCStringLenNoNull value $ \ptr len ->
c_sqlite3_result_blob ctx ptr len c_SQLITE_TRANSIENT
funcResultZeroBlob :: FuncContext -> Int -> IO ()
funcResultZeroBlob (FuncContext ctx) len =
c_sqlite3_result_zeroblob ctx (fromIntegral len)
funcResultNull :: FuncContext -> IO ()
funcResultNull (FuncContext ctx) =
c_sqlite3_result_null ctx
getFuncContextDatabase :: FuncContext -> IO Database
getFuncContextDatabase (FuncContext ctx) = do
db <- c_sqlite3_context_db_handle ctx
if db == nullPtr
then fail $ "sqlite3_context_db_handle(" ++ show ctx ++ ") returned NULL"
else return (Database db)
destroyCCompare :: CFuncDestroy ()
destroyCCompare ptr = freeHaskellFunPtr ptr'
where
ptr' = castPtrToFunPtr ptr :: FunPtr (CCompare ())
destroyCComparePtr :: FunPtr (CFuncDestroy ())
destroyCComparePtr = IOU.unsafePerformIO $ mkCFuncDestroy destroyCCompare
createCollation
:: Database
-> Utf8
-> (Utf8 -> Utf8 -> Ordering)
-> IO (Either Error ())
createCollation (Database db) (Utf8 name) cmp = mask_ $ do
cmpPtr <- mkCCompare cmp'
let u = castFunPtrToPtr cmpPtr
BS.useAsCString name $ \namePtr ->
toResult () <$> do
r <- c_sqlite3_create_collation_v2
db namePtr c_SQLITE_UTF8 u cmpPtr destroyCComparePtr
unless (r == CError 0) $
destroyCCompare $ castFunPtrToPtr cmpPtr
return r
where
cmp' _ len1 ptr1 len2 ptr2 = handle exnHandler $ do
s1 <- Utf8 <$> packCStringLen ptr1 len1
s2 <- Utf8 <$> packCStringLen ptr2 len2
let c = cmp s1 s2
evaluate (fromIntegral $ fromEnum c 1)
exnHandler (_ :: SomeException) = return (1)
deleteCollation :: Database -> Utf8 -> IO (Either Error ())
deleteCollation (Database db) (Utf8 name) =
BS.useAsCString name $ \namePtr ->
toResult () <$> do
c_sqlite3_create_collation_v2
db namePtr c_SQLITE_UTF8 nullPtr nullFunPtr nullFunPtr
setLoadExtensionEnabled :: Database -> Bool -> IO (Either Error ())
setLoadExtensionEnabled (Database db) enabled = do
toResult () <$> c_sqlite3_enable_load_extension db enabled
blobOpen
:: Database
-> Utf8
-> Utf8
-> Utf8
-> Int64
-> Bool
-> IO (Either Error Blob)
blobOpen (Database db) (Utf8 zDb) (Utf8 zTable) (Utf8 zColumn) rowid rw =
BS.useAsCString zDb $ \ptrDb ->
BS.useAsCString zTable $ \ptrTable ->
BS.useAsCString zColumn $ \ptrColumn ->
alloca $ \ptrBlob -> do
c_sqlite3_blob_open db ptrDb ptrTable ptrColumn rowid flags ptrBlob
>>= toResultM (Blob (Database db) <$> peek ptrBlob)
where
flags = if rw then 1 else 0
blobClose :: Blob -> IO (Either Error ())
blobClose (Blob _ blob) =
toResult () <$> c_sqlite3_blob_close blob
blobReopen :: Blob -> Int64 -> IO (Either Error ())
blobReopen (Blob _ blob) rowid =
toResult () <$> c_sqlite3_blob_reopen blob rowid
blobBytes :: Blob -> IO Int
blobBytes (Blob _ blob) =
fromIntegral <$> c_sqlite3_blob_bytes blob
blobRead
:: Blob
-> Int
-> Int
-> IO (Either Error ByteString)
blobRead blob len offset =
mask $ \restore -> do
buf <- mallocBytes len
r <- restore (blobReadBuf blob buf len offset)
`onException` (free buf)
case r of
Left err -> free buf >> return (Left err)
Right () -> do
bs <- BSU.unsafePackCStringFinalizer buf len (free buf)
return (Right bs)
blobReadBuf :: Blob -> Ptr a -> Int -> Int -> IO (Either Error ())
blobReadBuf (Blob _ blob) buf len offset =
toResult () <$>
c_sqlite3_blob_read blob buf (fromIntegral len) (fromIntegral offset)
blobWrite
:: Blob
-> ByteString
-> Int
-> IO (Either Error ())
blobWrite (Blob _ blob) bs offset =
BSU.unsafeUseAsCStringLen bs $ \(buf, len) ->
toResult () <$>
c_sqlite3_blob_write blob buf (fromIntegral len) (fromIntegral offset)
backupInit
:: Database
-> Utf8
-> Database
-> Utf8
-> IO (Either Error Backup)
backupInit (Database dstDb) (Utf8 dstName) (Database srcDb) (Utf8 srcName) =
BS.useAsCString dstName $ \dstName' ->
BS.useAsCString srcName $ \srcName' -> do
r <- c_sqlite3_backup_init dstDb dstName' srcDb srcName'
if r == nullPtr
then Left <$> errcode (Database dstDb)
else return (Right (Backup (Database dstDb) r))
backupFinish :: Backup -> IO (Either Error ())
backupFinish (Backup _ backup) =
toResult () <$>
c_sqlite3_backup_finish backup
backupStep :: Backup -> Int -> IO (Either Error BackupStepResult)
backupStep (Backup _ backup) pages =
toBackupStepResult <$>
c_sqlite3_backup_step backup (fromIntegral pages)
backupRemaining :: Backup -> IO Int
backupRemaining (Backup _ backup) =
fromIntegral <$> c_sqlite3_backup_remaining backup
backupPagecount :: Backup -> IO Int
backupPagecount (Backup _ backup) =
fromIntegral <$> c_sqlite3_backup_pagecount backup