rust-rocksdb 0.54.0

Rust wrapper for Facebook's RocksDB embeddable database
Documentation
// Copyright 2020 Lucjan Suski
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//`

use crate::{Error, Options, env_options::EnvOptions, ffi, ffi_util::to_cpath};

use libc::{self, c_char, size_t};
use std::{ffi::CString, marker::PhantomData, path::Path};

/// SstFileWriter is used to create sst files that can be added to database later
/// All keys in files generated by SstFileWriter will have sequence number = 0.
pub struct SstFileWriter<'a> {
    pub(crate) inner: *mut ffi::rocksdb_sstfilewriter_t,
    // `Options`, and any `EnvOptions` passed to `create_with_env_options`, have
    // to stay alive for as long as the writer does. Both appear behind `'a` in
    // the constructors, so this pins them for the writer's lifetime.
    phantom: PhantomData<&'a Options>,
}

unsafe impl Send for SstFileWriter<'_> {}
unsafe impl Sync for SstFileWriter<'_> {}

impl<'a> SstFileWriter<'a> {
    /// Initializes SstFileWriter with given DB options.
    ///
    /// Writes go through a default [`EnvOptions`]. Use
    /// [`create_with_env_options`](Self::create_with_env_options) to control
    /// how the file is written, for example to enable direct IO or to rate
    /// limit the writes.
    pub fn create(opts: &'a Options) -> Self {
        // A default `EnvOptions` is all scalars, and C++ copies the struct into
        // the writer, so this temporary does not need to outlive the writer.
        // That is not true once a rate limiter is set, which is why
        // `create_with_env_options` borrows for `'a` instead.
        let writer = Self::create_raw(opts, &EnvOptions::default());

        Self {
            inner: writer,
            phantom: PhantomData,
        }
    }

    /// Initializes SstFileWriter with given DB options and file IO options.
    ///
    /// `env_opts` is borrowed for as long as the writer lives. C++ copies the
    /// `EnvOptions` struct into the writer, but a rate limiter installed with
    /// [`EnvOptions::set_ratelimiter`] is copied as a plain pointer while the
    /// reference keeping it alive stays behind in `env_opts`, so dropping the
    /// options first would leave the writer pointing at freed memory.
    pub fn create_with_env_options(opts: &'a Options, env_opts: &'a EnvOptions) -> Self {
        let writer = Self::create_raw(opts, env_opts);

        Self {
            inner: writer,
            phantom: PhantomData,
        }
    }

    fn create_raw(opts: &Options, env_opts: &EnvOptions) -> *mut ffi::rocksdb_sstfilewriter_t {
        unsafe { ffi::rocksdb_sstfilewriter_create(env_opts.as_ptr(), opts.inner) }
    }

    /// Prepare SstFileWriter to write into file located at "file_path".
    ///
    /// Takes `&mut self` because the underlying `rocksdb::SstFileWriter::Open`
    /// mutates the writer (it installs a new `WritableFileWriter` and table
    /// builder) and is not thread safe. With `&self` and the `Sync` impl above,
    /// two threads could call this concurrently on the same writer from safe
    /// code and race on those fields.
    pub fn open<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
        let cpath = to_cpath(&path)?;
        self.open_raw(&cpath)
    }

    fn open_raw(&mut self, cpath: &CString) -> Result<(), Error> {
        unsafe {
            ffi_try!(ffi::rocksdb_sstfilewriter_open(
                self.inner,
                cpath.as_ptr() as *const _
            ));

            Ok(())
        }
    }

    /// Finalize writing to sst file and close file.
    pub fn finish(&mut self) -> Result<(), Error> {
        unsafe {
            ffi_try!(ffi::rocksdb_sstfilewriter_finish(self.inner,));
            Ok(())
        }
    }

    /// returns the current file size
    pub fn file_size(&self) -> u64 {
        let mut file_size: u64 = 0;
        unsafe {
            ffi::rocksdb_sstfilewriter_file_size(self.inner, &raw mut file_size);
        }
        file_size
    }

    /// Adds a Put key with value to currently opened file
    /// REQUIRES: key is after any previously added key according to comparator.
    pub fn put<K, V>(&mut self, key: K, value: V) -> Result<(), Error>
    where
        K: AsRef<[u8]>,
        V: AsRef<[u8]>,
    {
        let key = key.as_ref();
        let value = value.as_ref();
        unsafe {
            ffi_try!(ffi::rocksdb_sstfilewriter_put(
                self.inner,
                key.as_ptr() as *const c_char,
                key.len() as size_t,
                value.as_ptr() as *const c_char,
                value.len() as size_t,
            ));
            Ok(())
        }
    }

    /// Adds a Put key with value to currently opened file
    /// REQUIRES: key is after any previously added key according to comparator.
    pub fn put_with_ts<K, V, S>(&mut self, key: K, ts: S, value: V) -> Result<(), Error>
    where
        K: AsRef<[u8]>,
        V: AsRef<[u8]>,
        S: AsRef<[u8]>,
    {
        let key = key.as_ref();
        let value = value.as_ref();
        let ts = ts.as_ref();
        unsafe {
            ffi_try!(ffi::rocksdb_sstfilewriter_put_with_ts(
                self.inner,
                key.as_ptr() as *const c_char,
                key.len() as size_t,
                ts.as_ptr() as *const c_char,
                ts.len() as size_t,
                value.as_ptr() as *const c_char,
                value.len() as size_t,
            ));
            Ok(())
        }
    }

    /// Adds a Merge key with value to currently opened file
    /// REQUIRES: key is after any previously added key according to comparator.
    pub fn merge<K, V>(&mut self, key: K, value: V) -> Result<(), Error>
    where
        K: AsRef<[u8]>,
        V: AsRef<[u8]>,
    {
        let key = key.as_ref();
        let value = value.as_ref();

        unsafe {
            ffi_try!(ffi::rocksdb_sstfilewriter_merge(
                self.inner,
                key.as_ptr() as *const c_char,
                key.len() as size_t,
                value.as_ptr() as *const c_char,
                value.len() as size_t,
            ));
            Ok(())
        }
    }

    /// Adds a deletion key to currently opened file
    /// REQUIRES: key is after any previously added key according to comparator.
    pub fn delete<K: AsRef<[u8]>>(&mut self, key: K) -> Result<(), Error> {
        let key = key.as_ref();

        unsafe {
            ffi_try!(ffi::rocksdb_sstfilewriter_delete(
                self.inner,
                key.as_ptr() as *const c_char,
                key.len() as size_t,
            ));
            Ok(())
        }
    }

    /// Adds a range deletion tombstone to the currently opened file.
    /// Unlike point entries, range tombstones may be added out of order.
    /// REQUIRES: `begin_key` <= `end_key` according to the comparator.
    /// REQUIRES: comparator is not timestamp-aware.
    pub fn delete_range<K: AsRef<[u8]>>(&mut self, begin_key: K, end_key: K) -> Result<(), Error> {
        let begin_key = begin_key.as_ref();
        let end_key = end_key.as_ref();

        unsafe {
            ffi_try!(ffi::rocksdb_sstfilewriter_delete_range(
                self.inner,
                begin_key.as_ptr() as *const c_char,
                begin_key.len() as size_t,
                end_key.as_ptr() as *const c_char,
                end_key.len() as size_t,
            ));
            Ok(())
        }
    }

    /// Adds a deletion key to currently opened file
    /// REQUIRES: key is after any previously added key according to comparator.
    pub fn delete_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
        &mut self,
        key: K,
        ts: S,
    ) -> Result<(), Error> {
        let key = key.as_ref();
        let ts = ts.as_ref();
        unsafe {
            ffi_try!(ffi::rocksdb_sstfilewriter_delete_with_ts(
                self.inner,
                key.as_ptr() as *const c_char,
                key.len() as size_t,
                ts.as_ptr() as *const c_char,
                ts.len() as size_t,
            ));
            Ok(())
        }
    }
}

impl Drop for SstFileWriter<'_> {
    fn drop(&mut self) {
        unsafe {
            ffi::rocksdb_sstfilewriter_destroy(self.inner);
        }
    }
}