lsm-tree-1.0.0.0: src-rocksdb/RocksDB.hs
{-# LANGUAGE CPP #-}
module RocksDB (
-- * Options
Options,
withOptions,
optionsSetCreateIfMissing,
optionsSetMaxOpenFiles,
optionsSetCompression,
optionsIncreaseParallelism,
optionsOptimizeLevelStyleCompaction,
-- * Read Options
ReadOptions,
withReadOptions,
-- * Write options
WriteOptions,
withWriteOptions,
writeOptionsDisableWAL,
-- * DB operations
RocksDB,
withRocksDB,
put,
get,
multiGet,
delete,
write,
checkpoint,
-- * Write batch
WriteBatch,
withWriteBatch,
writeBatchPut,
writeBatchDelete,
-- * Block based table options
BlockTableOptions,
withBlockTableOptions,
blockBasedOptionsSetFilterPolicy,
optionsSetBlockBasedTableFactory,
-- * Filter policy
FilterPolicy,
withFilterPolicyBloom,
) where
import Control.Exception (bracket)
import Control.Monad (forM)
import Data.ByteString (ByteString)
import Data.ByteString.Unsafe (unsafePackMallocCStringLen,
unsafeUseAsCStringLen)
import Data.Foldable.WithIndex (ifor_)
import Data.Word (Word64)
import Foreign.C.String (peekCString, withCString)
import Foreign.C.Types (CInt, CSize)
import Foreign.Marshal.Alloc (alloca, free)
import Foreign.Marshal.Array (allocaArray)
import Foreign.Marshal.Utils (withMany)
import Foreign.Ptr (Ptr, nullPtr)
import Foreign.Storable (peek, peekElemOff, poke, pokeElemOff)
import RocksDB.FFI
#if MIN_VERSION_base(4,18,0)
import Foreign.C.ConstPtr (ConstPtr (..))
#endif
-------------------------------------------------------------------------------
-- constptr
-------------------------------------------------------------------------------
#if MIN_VERSION_base(4,18,0)
constPtr :: Ptr a -> ConstPtr a
constPtr = ConstPtr
#else
constPtr :: Ptr a -> Ptr a
constPtr = id
#endif
-------------------------------------------------------------------------------
-- error
-------------------------------------------------------------------------------
withErrPtr :: (ErrPtr -> IO r) -> IO r
withErrPtr kont = alloca $ \ptr -> do
poke ptr nullPtr
x <- kont ptr
ptr' <- peek ptr
if ptr' == nullPtr
then pure ()
else free ptr'
pure x
assertErrPtr :: String -> ErrPtr -> IO ()
assertErrPtr fun ptr = do
ptr' <- peek ptr
if ptr' == nullPtr
then pure ()
else do
msg <- peekCString ptr'
fail $ fun ++ ": " ++ msg
-------------------------------------------------------------------------------
-- options
-------------------------------------------------------------------------------
newtype Options = Options (Ptr OPTIONS)
withOptions :: (Options -> IO r) -> IO r
withOptions kont = bracket
rocksdb_options_create
rocksdb_options_destroy
(\ptr -> kont (Options ptr))
optionsSetCreateIfMissing :: Options -> Bool -> IO ()
optionsSetCreateIfMissing (Options ptr) v =
rocksdb_options_set_create_if_missing ptr (if v then 1 else 0)
optionsIncreaseParallelism :: Options -> Int -> IO ()
optionsIncreaseParallelism (Options ptr) v =
rocksdb_options_increase_parallelism ptr (intToCInt v)
optionsSetMaxOpenFiles :: Options -> Int -> IO ()
optionsSetMaxOpenFiles (Options ptr) v =
rocksdb_options_set_max_open_files ptr (intToCInt v)
optionsOptimizeLevelStyleCompaction :: Options -> Word64 -> IO ()
optionsOptimizeLevelStyleCompaction (Options ptr) v =
rocksdb_options_optimize_level_style_compaction ptr v
optionsSetCompression :: Options -> Int -> IO ()
optionsSetCompression (Options ptr) v =
rocksdb_options_set_compression ptr (intToCInt v)
-------------------------------------------------------------------------------
-- read options
-------------------------------------------------------------------------------
newtype ReadOptions = ReadOptions (Ptr READOPTIONS)
withReadOptions :: (ReadOptions -> IO r) -> IO r
withReadOptions kont = bracket
rocksdb_readoptions_create
rocksdb_readoptions_destroy
(\ptr -> kont (ReadOptions ptr))
-------------------------------------------------------------------------------
-- write options
-------------------------------------------------------------------------------
newtype WriteOptions = WriteOptions (Ptr WRITEOPTIONS)
withWriteOptions :: (WriteOptions -> IO r) -> IO r
withWriteOptions kont = bracket
rocksdb_writeoptions_create
rocksdb_writeoptions_destroy
(\ptr -> kont (WriteOptions ptr))
writeOptionsDisableWAL :: WriteOptions -> Bool {- ^ Disable -} -> IO ()
writeOptionsDisableWAL (WriteOptions opts) disable =
rocksdb_writeoptions_disable_WAL opts (if disable then 1 else 0)
-------------------------------------------------------------------------------
-- db operations
-------------------------------------------------------------------------------
data RocksDB = RocksDB (Ptr DB) ErrPtr
withRocksDB :: Options -> FilePath -> (RocksDB -> IO r) -> IO r
withRocksDB (Options opt) path kont =
withCString path $ \path' ->
withErrPtr $ \errptr ->
bracket (rocksdb_open' path' errptr) rocksdb_close $ \ptr ->
kont (RocksDB ptr errptr)
where
rocksdb_open' path' errptr = do
ptr <- rocksdb_open opt path' errptr
assertErrPtr "rocksdb_open" errptr
pure ptr
put :: RocksDB -> WriteOptions -> ByteString -> ByteString -> IO ()
put (RocksDB dbPtr errPtr) (WriteOptions opts) key val =
unsafeUseAsCStringLen key $ \(kp, kl) ->
unsafeUseAsCStringLen val $ \(vp, vl) -> do
rocksdb_put dbPtr opts kp (intToCSize kl) vp (intToCSize vl) errPtr
assertErrPtr "rocksdb_put" errPtr
get :: RocksDB -> ReadOptions -> ByteString -> IO (Maybe ByteString)
get (RocksDB dbPtr errPtr) (ReadOptions opts) key =
unsafeUseAsCStringLen key $ \(kp, kl) ->
alloca $ \vlPtr -> do
vp <- rocksdb_get dbPtr opts kp (intToCSize kl) vlPtr errPtr
assertErrPtr "rocksdb_get" errPtr -- TODO: may leak
vl <- peek vlPtr
if vp == nullPtr
then pure Nothing
else Just <$> unsafePackMallocCStringLen (vp, csizeToInt vl)
multiGet :: RocksDB -> ReadOptions -> [ByteString] -> IO [Maybe ByteString]
multiGet (RocksDB db _errPtr) (ReadOptions opts) keys =
allocaArray n $ \kps ->
allocaArray n $ \kls ->
allocaArray n $ \vps ->
allocaArray n $ \vls ->
allocaArray n $ \errs ->
withMany unsafeUseAsCStringLen keys $ \keys' -> do
-- populate keys
ifor_ keys' $ \i (kp, kl) -> do
pokeElemOff kps i (constPtr kp)
pokeElemOff kls i (intToCSize kl)
-- multi get
rocksdb_multi_get db opts (intToCSize n) kps kls vps vls errs
-- read keys
forM [ 0 .. n - 1 ] $ \i -> do
vp <- peekElemOff vps i
vl <- peekElemOff vls i
-- TODO: we don't check errs here. we should.
if vp == nullPtr
then pure Nothing
else Just <$> unsafePackMallocCStringLen (vp, csizeToInt vl)
where
n = length keys
delete :: RocksDB -> WriteOptions -> ByteString -> IO ()
delete (RocksDB dbPtr errPtr) (WriteOptions opts) key =
unsafeUseAsCStringLen key $ \(kp, kl) -> do
rocksdb_delete dbPtr opts kp (intToCSize kl) errPtr
assertErrPtr "rocksdb_delete" errPtr
write :: RocksDB -> WriteOptions -> WriteBatch -> IO ()
write (RocksDB dbPtr errPtr) (WriteOptions opts) (WriteBatch batch) = do
rocksdb_write dbPtr opts batch errPtr
assertErrPtr "rocksdb_write" errPtr
checkpoint :: RocksDB -> FilePath -> IO ()
checkpoint (RocksDB dbPtr errPtr) path =
withCString path $ \path' ->
bracket rocksdb_checkpoint_object_create' rocksdb_checkpoint_object_destroy $ \cp -> do
rocksdb_checkpoint_create cp path' 0 errPtr
assertErrPtr "rocksdb_checkpoint_create" errPtr
where
rocksdb_checkpoint_object_create' = do
cp <- rocksdb_checkpoint_object_create dbPtr errPtr
assertErrPtr "rocksdb_checkpoint_object_create" errPtr
pure cp
-------------------------------------------------------------------------------
-- write batch
-------------------------------------------------------------------------------
newtype WriteBatch = WriteBatch (Ptr WRITEBATCH)
withWriteBatch :: (WriteBatch -> IO r) -> IO r
withWriteBatch kont = bracket
rocksdb_writebatch_create
rocksdb_writebatch_destroy
(\ptr -> kont (WriteBatch ptr))
writeBatchPut :: WriteBatch -> ByteString -> ByteString -> IO ()
writeBatchPut (WriteBatch batch) key val =
unsafeUseAsCStringLen key $ \(kp, kl) ->
unsafeUseAsCStringLen val $ \(vp, vl) ->
rocksdb_writebatch_put batch kp (intToCSize kl) vp (intToCSize vl)
writeBatchDelete :: WriteBatch -> ByteString -> IO ()
writeBatchDelete (WriteBatch batch) key =
unsafeUseAsCStringLen key $ \(kp, kl) ->
rocksdb_writebatch_delete batch kp (intToCSize kl)
-------------------------------------------------------------------------------
-- block based table options
-------------------------------------------------------------------------------
newtype BlockTableOptions = BlockTableOptions (Ptr BLOCKTABLEOPTIONS)
withBlockTableOptions :: (BlockTableOptions -> IO r) -> IO r
withBlockTableOptions kont = bracket
rocksdb_block_based_options_create
rocksdb_block_based_options_destroy
(\ptr -> kont (BlockTableOptions ptr))
blockBasedOptionsSetFilterPolicy :: BlockTableOptions -> FilterPolicy -> IO ()
blockBasedOptionsSetFilterPolicy (BlockTableOptions opts) (FilterPolicy policy) =
rocksdb_block_based_options_set_filter_policy opts policy
optionsSetBlockBasedTableFactory :: Options -> BlockTableOptions -> IO ()
optionsSetBlockBasedTableFactory (Options opts) (BlockTableOptions ptr) =
rocksdb_options_set_block_based_table_factory opts ptr
-------------------------------------------------------------------------------
-- filter policy
-------------------------------------------------------------------------------
newtype FilterPolicy = FilterPolicy (Ptr FILTERPOLICY)
withFilterPolicyBloom :: Int -> (FilterPolicy -> IO r) -> IO r
withFilterPolicyBloom bits_per_key kont = bracket
(rocksdb_filterpolicy_create_bloom (intToCInt bits_per_key))
(\_ -> pure ()) -- rocksdb_filterpolicy_destroy, causes segfault if we free it.
(\ptr -> kont (FilterPolicy ptr))
-------------------------------------------------------------------------------
-- utils
-------------------------------------------------------------------------------
intToCSize :: Int -> CSize
intToCSize = fromIntegral
csizeToInt :: CSize -> Int
csizeToInt = fromIntegral
intToCInt :: Int -> CInt
intToCInt = fromIntegral