rust_rocksdb/
db.rs

1// Copyright 2020 Tyler Neely
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15
16use std::cell::RefCell;
17use std::collections::{BTreeMap, HashMap};
18use std::ffi::{CStr, CString};
19use std::fmt;
20use std::fs;
21use std::iter;
22use std::path::Path;
23use std::path::PathBuf;
24use std::ptr;
25use std::slice;
26use std::str;
27use std::sync::Arc;
28use std::time::Duration;
29
30use crate::column_family::ColumnFamilyTtl;
31use crate::ffi_util::CSlice;
32use crate::{
33    column_family::AsColumnFamilyRef,
34    column_family::BoundColumnFamily,
35    column_family::UnboundColumnFamily,
36    db_options::{ImportColumnFamilyOptions, OptionsMustOutliveDB},
37    ffi,
38    ffi_util::{from_cstr, opt_bytes_to_ptr, raw_data, to_cpath, CStrLike},
39    ColumnFamily, ColumnFamilyDescriptor, CompactOptions, DBIteratorWithThreadMode,
40    DBPinnableSlice, DBRawIteratorWithThreadMode, DBWALIterator, Direction, Error, FlushOptions,
41    IngestExternalFileOptions, IteratorMode, Options, ReadOptions, SnapshotWithThreadMode,
42    WaitForCompactOptions, WriteBatch, WriteBatchWithIndex, WriteOptions,
43    DEFAULT_COLUMN_FAMILY_NAME,
44};
45use rust_librocksdb_sys::{
46    rocksdb_livefile_destroy, rocksdb_livefile_t, rocksdb_livefiles_destroy, rocksdb_livefiles_t,
47};
48
49use libc::{self, c_char, c_int, c_uchar, c_void, size_t};
50use parking_lot::RwLock;
51
52// Default options are kept per-thread to avoid re-allocating on every call while
53// also preventing cross-thread sharing. Some RocksDB option wrappers hold
54// pointers into internal buffers and are not safe to share across threads.
55// Using thread_local allows cheap reuse in the common "default options" path
56// without synchronization overhead. Callers who need non-defaults must pass
57// explicit options.
58thread_local! { static DEFAULT_READ_OPTS: ReadOptions = ReadOptions::default(); }
59thread_local! { static DEFAULT_WRITE_OPTS: WriteOptions = WriteOptions::default(); }
60thread_local! { static DEFAULT_FLUSH_OPTS: FlushOptions = FlushOptions::default(); }
61// Thread-local ReadOptions for hot prefix probes; preconfigured for prefix scans.
62thread_local! { static PREFIX_READ_OPTS: RefCell<ReadOptions> = RefCell::new({ let mut o = ReadOptions::default(); o.set_prefix_same_as_start(true); o }); }
63
64/// A range of keys, `start_key` is included, but not `end_key`.
65///
66/// You should make sure `end_key` is not less than `start_key`.
67pub struct Range<'a> {
68    start_key: &'a [u8],
69    end_key: &'a [u8],
70}
71
72impl<'a> Range<'a> {
73    pub fn new(start_key: &'a [u8], end_key: &'a [u8]) -> Range<'a> {
74        Range { start_key, end_key }
75    }
76}
77
78/// A reusable prefix probe that avoids per-call iterator creation/destruction.
79///
80/// Use this when performing many prefix existence checks in a tight loop.
81pub struct PrefixProber<'a, D: DBAccess> {
82    raw: DBRawIteratorWithThreadMode<'a, D>,
83}
84
85impl<D: DBAccess> PrefixProber<'_, D> {
86    /// Returns true if any key exists with the given prefix.
87    /// This performs a seek to the prefix and checks the current key.
88    pub fn exists(&mut self, prefix: &[u8]) -> Result<bool, Error> {
89        self.raw.seek(prefix);
90        if self.raw.valid() {
91            if let Some(k) = self.raw.key() {
92                return Ok(k.starts_with(prefix));
93            }
94        }
95        self.raw.status()?;
96        Ok(false)
97    }
98}
99
100/// Marker trait to specify single or multi threaded column family alternations for
101/// [`DBWithThreadMode<T>`]
102///
103/// This arrangement makes differences in self mutability and return type in
104/// some of `DBWithThreadMode` methods.
105///
106/// While being a marker trait to be generic over `DBWithThreadMode`, this trait
107/// also has a minimum set of not-encapsulated internal methods between
108/// [`SingleThreaded`] and [`MultiThreaded`].  These methods aren't expected to be
109/// called and defined externally.
110pub trait ThreadMode {
111    /// Internal implementation for storing column family handles
112    fn new_cf_map_internal(
113        cf_map: BTreeMap<String, *mut ffi::rocksdb_column_family_handle_t>,
114    ) -> Self;
115    /// Internal implementation for dropping column family handles
116    fn drop_all_cfs_internal(&mut self);
117}
118
119/// Actual marker type for the marker trait `ThreadMode`, which holds
120/// a collection of column families without synchronization primitive, providing
121/// no overhead for the single-threaded column family alternations. The other
122/// mode is [`MultiThreaded`].
123///
124/// See [`DB`] for more details, including performance implications for each mode
125pub struct SingleThreaded {
126    pub(crate) cfs: HashMap<String, ColumnFamily>,
127}
128
129/// Actual marker type for the marker trait `ThreadMode`, which holds
130/// a collection of column families wrapped in a RwLock to be mutated
131/// concurrently. The other mode is [`SingleThreaded`].
132///
133/// See [`DB`] for more details, including performance implications for each mode
134pub struct MultiThreaded {
135    pub(crate) cfs: RwLock<HashMap<String, Arc<UnboundColumnFamily>>>,
136}
137
138impl ThreadMode for SingleThreaded {
139    fn new_cf_map_internal(
140        cfs: BTreeMap<String, *mut ffi::rocksdb_column_family_handle_t>,
141    ) -> Self {
142        Self {
143            cfs: cfs
144                .into_iter()
145                .map(|(n, c)| (n, ColumnFamily { inner: c }))
146                .collect(),
147        }
148    }
149
150    fn drop_all_cfs_internal(&mut self) {
151        // Cause all ColumnFamily objects to be Drop::drop()-ed.
152        self.cfs.clear();
153    }
154}
155
156impl ThreadMode for MultiThreaded {
157    fn new_cf_map_internal(
158        cfs: BTreeMap<String, *mut ffi::rocksdb_column_family_handle_t>,
159    ) -> Self {
160        Self {
161            cfs: RwLock::new(
162                cfs.into_iter()
163                    .map(|(n, c)| (n, Arc::new(UnboundColumnFamily { inner: c })))
164                    .collect(),
165            ),
166        }
167    }
168
169    fn drop_all_cfs_internal(&mut self) {
170        // Cause all UnboundColumnFamily objects to be Drop::drop()-ed.
171        self.cfs.write().clear();
172    }
173}
174
175/// Get underlying `rocksdb_t`.
176pub trait DBInner {
177    fn inner(&self) -> *mut ffi::rocksdb_t;
178}
179
180/// A helper type to implement some common methods for [`DBWithThreadMode`]
181/// and [`OptimisticTransactionDB`].
182///
183/// [`OptimisticTransactionDB`]: crate::OptimisticTransactionDB
184pub struct DBCommon<T: ThreadMode, D: DBInner> {
185    pub(crate) inner: D,
186    cfs: T, // Column families are held differently depending on thread mode
187    path: PathBuf,
188    _outlive: Vec<OptionsMustOutliveDB>,
189}
190
191/// Minimal set of DB-related methods, intended to be generic over
192/// `DBWithThreadMode<T>`. Mainly used internally
193pub trait DBAccess {
194    unsafe fn create_snapshot(&self) -> *const ffi::rocksdb_snapshot_t;
195
196    unsafe fn release_snapshot(&self, snapshot: *const ffi::rocksdb_snapshot_t);
197
198    unsafe fn create_iterator(&self, readopts: &ReadOptions) -> *mut ffi::rocksdb_iterator_t;
199
200    unsafe fn create_iterator_cf(
201        &self,
202        cf_handle: *mut ffi::rocksdb_column_family_handle_t,
203        readopts: &ReadOptions,
204    ) -> *mut ffi::rocksdb_iterator_t;
205
206    fn get_opt<K: AsRef<[u8]>>(
207        &self,
208        key: K,
209        readopts: &ReadOptions,
210    ) -> Result<Option<Vec<u8>>, Error>;
211
212    fn get_cf_opt<K: AsRef<[u8]>>(
213        &self,
214        cf: &impl AsColumnFamilyRef,
215        key: K,
216        readopts: &ReadOptions,
217    ) -> Result<Option<Vec<u8>>, Error>;
218
219    fn get_pinned_opt<K: AsRef<[u8]>>(
220        &'_ self,
221        key: K,
222        readopts: &ReadOptions,
223    ) -> Result<Option<DBPinnableSlice<'_>>, Error>;
224
225    fn get_pinned_cf_opt<K: AsRef<[u8]>>(
226        &'_ self,
227        cf: &impl AsColumnFamilyRef,
228        key: K,
229        readopts: &ReadOptions,
230    ) -> Result<Option<DBPinnableSlice<'_>>, Error>;
231
232    fn multi_get_opt<K, I>(
233        &self,
234        keys: I,
235        readopts: &ReadOptions,
236    ) -> Vec<Result<Option<Vec<u8>>, Error>>
237    where
238        K: AsRef<[u8]>,
239        I: IntoIterator<Item = K>;
240
241    fn multi_get_cf_opt<'b, K, I, W>(
242        &self,
243        keys_cf: I,
244        readopts: &ReadOptions,
245    ) -> Vec<Result<Option<Vec<u8>>, Error>>
246    where
247        K: AsRef<[u8]>,
248        I: IntoIterator<Item = (&'b W, K)>,
249        W: AsColumnFamilyRef + 'b;
250}
251
252impl<T: ThreadMode, D: DBInner> DBAccess for DBCommon<T, D> {
253    unsafe fn create_snapshot(&self) -> *const ffi::rocksdb_snapshot_t {
254        unsafe { ffi::rocksdb_create_snapshot(self.inner.inner()) }
255    }
256
257    unsafe fn release_snapshot(&self, snapshot: *const ffi::rocksdb_snapshot_t) {
258        unsafe {
259            ffi::rocksdb_release_snapshot(self.inner.inner(), snapshot);
260        }
261    }
262
263    unsafe fn create_iterator(&self, readopts: &ReadOptions) -> *mut ffi::rocksdb_iterator_t {
264        unsafe { ffi::rocksdb_create_iterator(self.inner.inner(), readopts.inner) }
265    }
266
267    unsafe fn create_iterator_cf(
268        &self,
269        cf_handle: *mut ffi::rocksdb_column_family_handle_t,
270        readopts: &ReadOptions,
271    ) -> *mut ffi::rocksdb_iterator_t {
272        unsafe { ffi::rocksdb_create_iterator_cf(self.inner.inner(), readopts.inner, cf_handle) }
273    }
274
275    fn get_opt<K: AsRef<[u8]>>(
276        &self,
277        key: K,
278        readopts: &ReadOptions,
279    ) -> Result<Option<Vec<u8>>, Error> {
280        self.get_opt(key, readopts)
281    }
282
283    fn get_cf_opt<K: AsRef<[u8]>>(
284        &self,
285        cf: &impl AsColumnFamilyRef,
286        key: K,
287        readopts: &ReadOptions,
288    ) -> Result<Option<Vec<u8>>, Error> {
289        self.get_cf_opt(cf, key, readopts)
290    }
291
292    fn get_pinned_opt<K: AsRef<[u8]>>(
293        &'_ self,
294        key: K,
295        readopts: &ReadOptions,
296    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
297        self.get_pinned_opt(key, readopts)
298    }
299
300    fn get_pinned_cf_opt<K: AsRef<[u8]>>(
301        &'_ self,
302        cf: &impl AsColumnFamilyRef,
303        key: K,
304        readopts: &ReadOptions,
305    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
306        self.get_pinned_cf_opt(cf, key, readopts)
307    }
308
309    fn multi_get_opt<K, Iter>(
310        &self,
311        keys: Iter,
312        readopts: &ReadOptions,
313    ) -> Vec<Result<Option<Vec<u8>>, Error>>
314    where
315        K: AsRef<[u8]>,
316        Iter: IntoIterator<Item = K>,
317    {
318        self.multi_get_opt(keys, readopts)
319    }
320
321    fn multi_get_cf_opt<'b, K, Iter, W>(
322        &self,
323        keys_cf: Iter,
324        readopts: &ReadOptions,
325    ) -> Vec<Result<Option<Vec<u8>>, Error>>
326    where
327        K: AsRef<[u8]>,
328        Iter: IntoIterator<Item = (&'b W, K)>,
329        W: AsColumnFamilyRef + 'b,
330    {
331        self.multi_get_cf_opt(keys_cf, readopts)
332    }
333}
334
335pub struct DBWithThreadModeInner {
336    inner: *mut ffi::rocksdb_t,
337}
338
339impl DBInner for DBWithThreadModeInner {
340    fn inner(&self) -> *mut ffi::rocksdb_t {
341        self.inner
342    }
343}
344
345impl Drop for DBWithThreadModeInner {
346    fn drop(&mut self) {
347        unsafe {
348            ffi::rocksdb_close(self.inner);
349        }
350    }
351}
352
353/// A type alias to RocksDB database.
354///
355/// See crate level documentation for a simple usage example.
356/// See [`DBCommon`] for full list of methods.
357pub type DBWithThreadMode<T> = DBCommon<T, DBWithThreadModeInner>;
358
359/// A type alias to DB instance type with the single-threaded column family
360/// creations/deletions
361///
362/// # Compatibility and multi-threaded mode
363///
364/// Previously, [`DB`] was defined as a direct `struct`. Now, it's type-aliased for
365/// compatibility. Use `DBCommon<MultiThreaded>` for multi-threaded
366/// column family alternations.
367///
368/// # Limited performance implication for single-threaded mode
369///
370/// Even with [`SingleThreaded`], almost all of RocksDB operations is
371/// multi-threaded unless the underlying RocksDB instance is
372/// specifically configured otherwise. `SingleThreaded` only forces
373/// serialization of column family alternations by requiring `&mut self` of DB
374/// instance due to its wrapper implementation details.
375///
376/// # Multi-threaded mode
377///
378/// [`MultiThreaded`] can be appropriate for the situation of multi-threaded
379/// workload including multi-threaded column family alternations, costing the
380/// RwLock overhead inside `DB`.
381#[cfg(not(feature = "multi-threaded-cf"))]
382pub type DB = DBWithThreadMode<SingleThreaded>;
383
384#[cfg(feature = "multi-threaded-cf")]
385pub type DB = DBWithThreadMode<MultiThreaded>;
386
387// Safety note: auto-implementing Send on most db-related types is prevented by the inner FFI
388// pointer. In most cases, however, this pointer is Send-safe because it is never aliased and
389// rocksdb internally does not rely on thread-local information for its user-exposed types.
390unsafe impl<T: ThreadMode + Send, I: DBInner> Send for DBCommon<T, I> {}
391
392// Sync is similarly safe for many types because they do not expose interior mutability, and their
393// use within the rocksdb library is generally behind a const reference
394unsafe impl<T: ThreadMode, I: DBInner> Sync for DBCommon<T, I> {}
395
396// Specifies whether open DB for read only.
397enum AccessType<'a> {
398    ReadWrite,
399    ReadOnly { error_if_log_file_exist: bool },
400    Secondary { secondary_path: &'a Path },
401    WithTTL { ttl: Duration },
402}
403
404/// Methods of `DBWithThreadMode`.
405impl<T: ThreadMode> DBWithThreadMode<T> {
406    /// Opens a database with default options.
407    pub fn open_default<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
408        let mut opts = Options::default();
409        opts.create_if_missing(true);
410        Self::open(&opts, path)
411    }
412
413    /// Opens the database with the specified options.
414    pub fn open<P: AsRef<Path>>(opts: &Options, path: P) -> Result<Self, Error> {
415        Self::open_cf(opts, path, None::<&str>)
416    }
417
418    /// Opens the database for read only with the specified options.
419    pub fn open_for_read_only<P: AsRef<Path>>(
420        opts: &Options,
421        path: P,
422        error_if_log_file_exist: bool,
423    ) -> Result<Self, Error> {
424        Self::open_cf_for_read_only(opts, path, None::<&str>, error_if_log_file_exist)
425    }
426
427    /// Opens the database as a secondary.
428    pub fn open_as_secondary<P: AsRef<Path>>(
429        opts: &Options,
430        primary_path: P,
431        secondary_path: P,
432    ) -> Result<Self, Error> {
433        Self::open_cf_as_secondary(opts, primary_path, secondary_path, None::<&str>)
434    }
435
436    /// Opens the database with a Time to Live compaction filter.
437    ///
438    /// This applies the given `ttl` to all column families created without an explicit TTL.
439    /// See [`DB::open_cf_descriptors_with_ttl`] for more control over individual column family TTLs.
440    pub fn open_with_ttl<P: AsRef<Path>>(
441        opts: &Options,
442        path: P,
443        ttl: Duration,
444    ) -> Result<Self, Error> {
445        Self::open_cf_descriptors_with_ttl(opts, path, std::iter::empty(), ttl)
446    }
447
448    /// Opens the database with a Time to Live compaction filter and column family names.
449    ///
450    /// Column families opened using this function will be created with default `Options`.
451    pub fn open_cf_with_ttl<P, I, N>(
452        opts: &Options,
453        path: P,
454        cfs: I,
455        ttl: Duration,
456    ) -> Result<Self, Error>
457    where
458        P: AsRef<Path>,
459        I: IntoIterator<Item = N>,
460        N: AsRef<str>,
461    {
462        let cfs = cfs
463            .into_iter()
464            .map(|name| ColumnFamilyDescriptor::new(name.as_ref(), Options::default()));
465
466        Self::open_cf_descriptors_with_ttl(opts, path, cfs, ttl)
467    }
468
469    /// Opens a database with the given database with a Time to Live compaction filter and
470    /// column family descriptors.
471    ///
472    /// Applies the provided `ttl` as the default TTL for all column families.
473    /// Column families will inherit this TTL by default, unless their descriptor explicitly
474    /// sets a different TTL using [`ColumnFamilyTtl::Duration`] or opts out using [`ColumnFamilyTtl::Disabled`].
475    ///
476    /// *NOTE*: The `default` column family is opened with `Options::default()` unless
477    /// explicitly configured within the `cfs` iterator.
478    /// To customize the `default` column family's options, include a `ColumnFamilyDescriptor`
479    /// with the name "default" in the `cfs` iterator.
480    ///
481    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
482    pub fn open_cf_descriptors_with_ttl<P, I>(
483        opts: &Options,
484        path: P,
485        cfs: I,
486        ttl: Duration,
487    ) -> Result<Self, Error>
488    where
489        P: AsRef<Path>,
490        I: IntoIterator<Item = ColumnFamilyDescriptor>,
491    {
492        Self::open_cf_descriptors_internal(opts, path, cfs, &AccessType::WithTTL { ttl })
493    }
494
495    /// Opens a database with the given database options and column family names.
496    ///
497    /// Column families opened using this function will be created with default `Options`.
498    pub fn open_cf<P, I, N>(opts: &Options, path: P, cfs: I) -> Result<Self, Error>
499    where
500        P: AsRef<Path>,
501        I: IntoIterator<Item = N>,
502        N: AsRef<str>,
503    {
504        let cfs = cfs
505            .into_iter()
506            .map(|name| ColumnFamilyDescriptor::new(name.as_ref(), Options::default()));
507
508        Self::open_cf_descriptors_internal(opts, path, cfs, &AccessType::ReadWrite)
509    }
510
511    /// Opens a database with the given database options and column family names.
512    ///
513    /// Column families opened using given `Options`.
514    pub fn open_cf_with_opts<P, I, N>(opts: &Options, path: P, cfs: I) -> Result<Self, Error>
515    where
516        P: AsRef<Path>,
517        I: IntoIterator<Item = (N, Options)>,
518        N: AsRef<str>,
519    {
520        let cfs = cfs
521            .into_iter()
522            .map(|(name, opts)| ColumnFamilyDescriptor::new(name.as_ref(), opts));
523
524        Self::open_cf_descriptors(opts, path, cfs)
525    }
526
527    /// Opens a database for read only with the given database options and column family names.
528    /// *NOTE*: `default` column family is opened with `Options::default()`.
529    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
530    pub fn open_cf_for_read_only<P, I, N>(
531        opts: &Options,
532        path: P,
533        cfs: I,
534        error_if_log_file_exist: bool,
535    ) -> Result<Self, Error>
536    where
537        P: AsRef<Path>,
538        I: IntoIterator<Item = N>,
539        N: AsRef<str>,
540    {
541        let cfs = cfs
542            .into_iter()
543            .map(|name| ColumnFamilyDescriptor::new(name.as_ref(), Options::default()));
544
545        Self::open_cf_descriptors_internal(
546            opts,
547            path,
548            cfs,
549            &AccessType::ReadOnly {
550                error_if_log_file_exist,
551            },
552        )
553    }
554
555    /// Opens a database for read only with the given database options and column family names.
556    /// *NOTE*: `default` column family is opened with `Options::default()`.
557    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
558    pub fn open_cf_with_opts_for_read_only<P, I, N>(
559        db_opts: &Options,
560        path: P,
561        cfs: I,
562        error_if_log_file_exist: bool,
563    ) -> Result<Self, Error>
564    where
565        P: AsRef<Path>,
566        I: IntoIterator<Item = (N, Options)>,
567        N: AsRef<str>,
568    {
569        let cfs = cfs
570            .into_iter()
571            .map(|(name, cf_opts)| ColumnFamilyDescriptor::new(name.as_ref(), cf_opts));
572
573        Self::open_cf_descriptors_internal(
574            db_opts,
575            path,
576            cfs,
577            &AccessType::ReadOnly {
578                error_if_log_file_exist,
579            },
580        )
581    }
582
583    /// Opens a database for ready only with the given database options and
584    /// column family descriptors.
585    /// *NOTE*: `default` column family is opened with `Options::default()`.
586    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
587    pub fn open_cf_descriptors_read_only<P, I>(
588        opts: &Options,
589        path: P,
590        cfs: I,
591        error_if_log_file_exist: bool,
592    ) -> Result<Self, Error>
593    where
594        P: AsRef<Path>,
595        I: IntoIterator<Item = ColumnFamilyDescriptor>,
596    {
597        Self::open_cf_descriptors_internal(
598            opts,
599            path,
600            cfs,
601            &AccessType::ReadOnly {
602                error_if_log_file_exist,
603            },
604        )
605    }
606
607    /// Opens the database as a secondary with the given database options and column family names.
608    /// *NOTE*: `default` column family is opened with `Options::default()`.
609    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
610    pub fn open_cf_as_secondary<P, I, N>(
611        opts: &Options,
612        primary_path: P,
613        secondary_path: P,
614        cfs: I,
615    ) -> Result<Self, Error>
616    where
617        P: AsRef<Path>,
618        I: IntoIterator<Item = N>,
619        N: AsRef<str>,
620    {
621        let cfs = cfs
622            .into_iter()
623            .map(|name| ColumnFamilyDescriptor::new(name.as_ref(), Options::default()));
624
625        Self::open_cf_descriptors_internal(
626            opts,
627            primary_path,
628            cfs,
629            &AccessType::Secondary {
630                secondary_path: secondary_path.as_ref(),
631            },
632        )
633    }
634
635    /// Opens the database as a secondary with the given database options and
636    /// column family descriptors.
637    /// *NOTE*: `default` column family is opened with `Options::default()`.
638    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
639    pub fn open_cf_descriptors_as_secondary<P, I>(
640        opts: &Options,
641        path: P,
642        secondary_path: P,
643        cfs: I,
644    ) -> Result<Self, Error>
645    where
646        P: AsRef<Path>,
647        I: IntoIterator<Item = ColumnFamilyDescriptor>,
648    {
649        Self::open_cf_descriptors_internal(
650            opts,
651            path,
652            cfs,
653            &AccessType::Secondary {
654                secondary_path: secondary_path.as_ref(),
655            },
656        )
657    }
658
659    /// Opens a database with the given database options and column family descriptors.
660    /// *NOTE*: `default` column family is opened with `Options::default()`.
661    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
662    pub fn open_cf_descriptors<P, I>(opts: &Options, path: P, cfs: I) -> Result<Self, Error>
663    where
664        P: AsRef<Path>,
665        I: IntoIterator<Item = ColumnFamilyDescriptor>,
666    {
667        Self::open_cf_descriptors_internal(opts, path, cfs, &AccessType::ReadWrite)
668    }
669
670    /// Internal implementation for opening RocksDB.
671    fn open_cf_descriptors_internal<P, I>(
672        opts: &Options,
673        path: P,
674        cfs: I,
675        access_type: &AccessType,
676    ) -> Result<Self, Error>
677    where
678        P: AsRef<Path>,
679        I: IntoIterator<Item = ColumnFamilyDescriptor>,
680    {
681        let cfs: Vec<_> = cfs.into_iter().collect();
682        let outlive = iter::once(opts.outlive.clone())
683            .chain(cfs.iter().map(|cf| cf.options.outlive.clone()))
684            .collect();
685
686        let cpath = to_cpath(&path)?;
687
688        if let Err(e) = fs::create_dir_all(&path) {
689            return Err(Error::new(format!(
690                "Failed to create RocksDB directory: `{e:?}`."
691            )));
692        }
693
694        let db: *mut ffi::rocksdb_t;
695        let mut cf_map = BTreeMap::new();
696
697        if cfs.is_empty() {
698            db = Self::open_raw(opts, &cpath, access_type)?;
699        } else {
700            let mut cfs_v = cfs;
701            // Always open the default column family.
702            if !cfs_v.iter().any(|cf| cf.name == DEFAULT_COLUMN_FAMILY_NAME) {
703                cfs_v.push(ColumnFamilyDescriptor {
704                    name: String::from(DEFAULT_COLUMN_FAMILY_NAME),
705                    options: Options::default(),
706                    ttl: ColumnFamilyTtl::SameAsDb,
707                });
708            }
709            // We need to store our CStrings in an intermediate vector
710            // so that their pointers remain valid.
711            let c_cfs: Vec<CString> = cfs_v
712                .iter()
713                .map(|cf| CString::new(cf.name.as_bytes()).unwrap())
714                .collect();
715
716            let cfnames: Vec<_> = c_cfs.iter().map(|cf| cf.as_ptr()).collect();
717
718            // These handles will be populated by DB.
719            let mut cfhandles: Vec<_> = cfs_v.iter().map(|_| ptr::null_mut()).collect();
720
721            let cfopts: Vec<_> = cfs_v
722                .iter()
723                .map(|cf| cf.options.inner.cast_const())
724                .collect();
725
726            db = Self::open_cf_raw(
727                opts,
728                &cpath,
729                &cfs_v,
730                &cfnames,
731                &cfopts,
732                &mut cfhandles,
733                access_type,
734            )?;
735            for handle in &cfhandles {
736                if handle.is_null() {
737                    return Err(Error::new(
738                        "Received null column family handle from DB.".to_owned(),
739                    ));
740                }
741            }
742
743            for (cf_desc, inner) in cfs_v.iter().zip(cfhandles) {
744                cf_map.insert(cf_desc.name.clone(), inner);
745            }
746        }
747
748        if db.is_null() {
749            return Err(Error::new("Could not initialize database.".to_owned()));
750        }
751
752        Ok(Self {
753            inner: DBWithThreadModeInner { inner: db },
754            path: path.as_ref().to_path_buf(),
755            cfs: T::new_cf_map_internal(cf_map),
756            _outlive: outlive,
757        })
758    }
759
760    fn open_raw(
761        opts: &Options,
762        cpath: &CString,
763        access_type: &AccessType,
764    ) -> Result<*mut ffi::rocksdb_t, Error> {
765        let db = unsafe {
766            match *access_type {
767                AccessType::ReadOnly {
768                    error_if_log_file_exist,
769                } => ffi_try!(ffi::rocksdb_open_for_read_only(
770                    opts.inner,
771                    cpath.as_ptr(),
772                    c_uchar::from(error_if_log_file_exist),
773                )),
774                AccessType::ReadWrite => {
775                    ffi_try!(ffi::rocksdb_open(opts.inner, cpath.as_ptr()))
776                }
777                AccessType::Secondary { secondary_path } => {
778                    ffi_try!(ffi::rocksdb_open_as_secondary(
779                        opts.inner,
780                        cpath.as_ptr(),
781                        to_cpath(secondary_path)?.as_ptr(),
782                    ))
783                }
784                AccessType::WithTTL { ttl } => ffi_try!(ffi::rocksdb_open_with_ttl(
785                    opts.inner,
786                    cpath.as_ptr(),
787                    ttl.as_secs() as c_int,
788                )),
789            }
790        };
791        Ok(db)
792    }
793
794    #[allow(clippy::pedantic)]
795    fn open_cf_raw(
796        opts: &Options,
797        cpath: &CString,
798        cfs_v: &[ColumnFamilyDescriptor],
799        cfnames: &[*const c_char],
800        cfopts: &[*const ffi::rocksdb_options_t],
801        cfhandles: &mut [*mut ffi::rocksdb_column_family_handle_t],
802        access_type: &AccessType,
803    ) -> Result<*mut ffi::rocksdb_t, Error> {
804        let db = unsafe {
805            match *access_type {
806                AccessType::ReadOnly {
807                    error_if_log_file_exist,
808                } => ffi_try!(ffi::rocksdb_open_for_read_only_column_families(
809                    opts.inner,
810                    cpath.as_ptr(),
811                    cfs_v.len() as c_int,
812                    cfnames.as_ptr(),
813                    cfopts.as_ptr(),
814                    cfhandles.as_mut_ptr(),
815                    c_uchar::from(error_if_log_file_exist),
816                )),
817                AccessType::ReadWrite => ffi_try!(ffi::rocksdb_open_column_families(
818                    opts.inner,
819                    cpath.as_ptr(),
820                    cfs_v.len() as c_int,
821                    cfnames.as_ptr(),
822                    cfopts.as_ptr(),
823                    cfhandles.as_mut_ptr(),
824                )),
825                AccessType::Secondary { secondary_path } => {
826                    ffi_try!(ffi::rocksdb_open_as_secondary_column_families(
827                        opts.inner,
828                        cpath.as_ptr(),
829                        to_cpath(secondary_path)?.as_ptr(),
830                        cfs_v.len() as c_int,
831                        cfnames.as_ptr(),
832                        cfopts.as_ptr(),
833                        cfhandles.as_mut_ptr(),
834                    ))
835                }
836                AccessType::WithTTL { ttl } => {
837                    let ttls: Vec<_> = cfs_v
838                        .iter()
839                        .map(|cf| match cf.ttl {
840                            ColumnFamilyTtl::Disabled => i32::MAX,
841                            ColumnFamilyTtl::Duration(duration) => duration.as_secs() as i32,
842                            ColumnFamilyTtl::SameAsDb => ttl.as_secs() as i32,
843                        })
844                        .collect();
845
846                    ffi_try!(ffi::rocksdb_open_column_families_with_ttl(
847                        opts.inner,
848                        cpath.as_ptr(),
849                        cfs_v.len() as c_int,
850                        cfnames.as_ptr(),
851                        cfopts.as_ptr(),
852                        cfhandles.as_mut_ptr(),
853                        ttls.as_ptr(),
854                    ))
855                }
856            }
857        };
858        Ok(db)
859    }
860
861    /// Removes the database entries in the range `["from", "to")` using given write options.
862    pub fn delete_range_cf_opt<K: AsRef<[u8]>>(
863        &self,
864        cf: &impl AsColumnFamilyRef,
865        from: K,
866        to: K,
867        writeopts: &WriteOptions,
868    ) -> Result<(), Error> {
869        let from = from.as_ref();
870        let to = to.as_ref();
871
872        unsafe {
873            ffi_try!(ffi::rocksdb_delete_range_cf(
874                self.inner.inner(),
875                writeopts.inner,
876                cf.inner(),
877                from.as_ptr() as *const c_char,
878                from.len() as size_t,
879                to.as_ptr() as *const c_char,
880                to.len() as size_t,
881            ));
882            Ok(())
883        }
884    }
885
886    /// Removes the database entries in the range `["from", "to")` using default write options.
887    pub fn delete_range_cf<K: AsRef<[u8]>>(
888        &self,
889        cf: &impl AsColumnFamilyRef,
890        from: K,
891        to: K,
892    ) -> Result<(), Error> {
893        DEFAULT_WRITE_OPTS.with(|opts| self.delete_range_cf_opt(cf, from, to, opts))
894    }
895
896    pub fn write_opt(&self, batch: &WriteBatch, writeopts: &WriteOptions) -> Result<(), Error> {
897        unsafe {
898            ffi_try!(ffi::rocksdb_write(
899                self.inner.inner(),
900                writeopts.inner,
901                batch.inner
902            ));
903        }
904        Ok(())
905    }
906
907    pub fn write(&self, batch: &WriteBatch) -> Result<(), Error> {
908        DEFAULT_WRITE_OPTS.with(|opts| self.write_opt(batch, opts))
909    }
910
911    pub fn write_without_wal(&self, batch: &WriteBatch) -> Result<(), Error> {
912        let mut wo = WriteOptions::new();
913        wo.disable_wal(true);
914        self.write_opt(batch, &wo)
915    }
916
917    pub fn write_wbwi(&self, wbwi: &WriteBatchWithIndex) -> Result<(), Error> {
918        DEFAULT_WRITE_OPTS.with(|opts| self.write_wbwi_opt(wbwi, opts))
919    }
920
921    pub fn write_wbwi_opt(
922        &self,
923        wbwi: &WriteBatchWithIndex,
924        writeopts: &WriteOptions,
925    ) -> Result<(), Error> {
926        unsafe {
927            ffi_try!(ffi::rocksdb_write_writebatch_wi(
928                self.inner.inner(),
929                writeopts.inner,
930                wbwi.inner
931            ));
932
933            Ok(())
934        }
935    }
936
937    /// Suspend deleting obsolete files. Compactions will continue to occur,
938    /// but no obsolete files will be deleted. To resume file deletions, each
939    /// call to disable_file_deletions() must be matched by a subsequent call to
940    /// enable_file_deletions(). For more details, see enable_file_deletions().
941    pub fn disable_file_deletions(&self) -> Result<(), Error> {
942        unsafe {
943            ffi_try!(ffi::rocksdb_disable_file_deletions(self.inner.inner()));
944        }
945        Ok(())
946    }
947
948    /// Resume deleting obsolete files, following up on `disable_file_deletions()`.
949    ///
950    /// File deletions disabling and enabling is not controlled by a binary flag,
951    /// instead it's represented as a counter to allow different callers to
952    /// independently disable file deletion. Disabling file deletion can be
953    /// critical for operations like making a backup. So the counter implementation
954    /// makes the file deletion disabled as long as there is one caller requesting
955    /// so, and only when every caller agrees to re-enable file deletion, it will
956    /// be enabled. Two threads can call this method concurrently without
957    /// synchronization -- i.e., file deletions will be enabled only after both
958    /// threads call enable_file_deletions()
959    pub fn enable_file_deletions(&self) -> Result<(), Error> {
960        unsafe {
961            ffi_try!(ffi::rocksdb_enable_file_deletions(self.inner.inner()));
962        }
963        Ok(())
964    }
965}
966
967/// Common methods of `DBWithThreadMode` and `OptimisticTransactionDB`.
968impl<T: ThreadMode, D: DBInner> DBCommon<T, D> {
969    pub(crate) fn new(inner: D, cfs: T, path: PathBuf, outlive: Vec<OptionsMustOutliveDB>) -> Self {
970        Self {
971            inner,
972            cfs,
973            path,
974            _outlive: outlive,
975        }
976    }
977
978    pub fn list_cf<P: AsRef<Path>>(opts: &Options, path: P) -> Result<Vec<String>, Error> {
979        let cpath = to_cpath(path)?;
980        let mut length = 0;
981
982        unsafe {
983            let ptr = ffi_try!(ffi::rocksdb_list_column_families(
984                opts.inner,
985                cpath.as_ptr(),
986                &mut length,
987            ));
988
989            let vec = slice::from_raw_parts(ptr, length)
990                .iter()
991                .map(|ptr| CStr::from_ptr(*ptr).to_string_lossy().into_owned())
992                .collect();
993            ffi::rocksdb_list_column_families_destroy(ptr, length);
994            Ok(vec)
995        }
996    }
997
998    pub fn destroy<P: AsRef<Path>>(opts: &Options, path: P) -> Result<(), Error> {
999        let cpath = to_cpath(path)?;
1000        unsafe {
1001            ffi_try!(ffi::rocksdb_destroy_db(opts.inner, cpath.as_ptr()));
1002        }
1003        Ok(())
1004    }
1005
1006    pub fn repair<P: AsRef<Path>>(opts: &Options, path: P) -> Result<(), Error> {
1007        let cpath = to_cpath(path)?;
1008        unsafe {
1009            ffi_try!(ffi::rocksdb_repair_db(opts.inner, cpath.as_ptr()));
1010        }
1011        Ok(())
1012    }
1013
1014    pub fn path(&self) -> &Path {
1015        self.path.as_path()
1016    }
1017
1018    /// Flushes the WAL buffer. If `sync` is set to `true`, also syncs
1019    /// the data to disk.
1020    pub fn flush_wal(&self, sync: bool) -> Result<(), Error> {
1021        unsafe {
1022            ffi_try!(ffi::rocksdb_flush_wal(
1023                self.inner.inner(),
1024                c_uchar::from(sync)
1025            ));
1026        }
1027        Ok(())
1028    }
1029
1030    /// Flushes database memtables to SST files on the disk.
1031    pub fn flush_opt(&self, flushopts: &FlushOptions) -> Result<(), Error> {
1032        unsafe {
1033            ffi_try!(ffi::rocksdb_flush(self.inner.inner(), flushopts.inner));
1034        }
1035        Ok(())
1036    }
1037
1038    /// Flushes database memtables to SST files on the disk using default options.
1039    pub fn flush(&self) -> Result<(), Error> {
1040        self.flush_opt(&FlushOptions::default())
1041    }
1042
1043    /// Flushes database memtables to SST files on the disk for a given column family.
1044    pub fn flush_cf_opt(
1045        &self,
1046        cf: &impl AsColumnFamilyRef,
1047        flushopts: &FlushOptions,
1048    ) -> Result<(), Error> {
1049        unsafe {
1050            ffi_try!(ffi::rocksdb_flush_cf(
1051                self.inner.inner(),
1052                flushopts.inner,
1053                cf.inner()
1054            ));
1055        }
1056        Ok(())
1057    }
1058
1059    /// Flushes multiple column families.
1060    ///
1061    /// If atomic flush is not enabled, it is equivalent to calling flush_cf multiple times.
1062    /// If atomic flush is enabled, it will flush all column families specified in `cfs` up to the latest sequence
1063    /// number at the time when flush is requested.
1064    pub fn flush_cfs_opt(
1065        &self,
1066        cfs: &[&impl AsColumnFamilyRef],
1067        opts: &FlushOptions,
1068    ) -> Result<(), Error> {
1069        let mut cfs = cfs.iter().map(|cf| cf.inner()).collect::<Vec<_>>();
1070        unsafe {
1071            ffi_try!(ffi::rocksdb_flush_cfs(
1072                self.inner.inner(),
1073                opts.inner,
1074                cfs.as_mut_ptr(),
1075                cfs.len() as libc::c_int,
1076            ));
1077        }
1078        Ok(())
1079    }
1080
1081    /// Flushes database memtables to SST files on the disk for a given column family using default
1082    /// options.
1083    pub fn flush_cf(&self, cf: &impl AsColumnFamilyRef) -> Result<(), Error> {
1084        DEFAULT_FLUSH_OPTS.with(|opts| self.flush_cf_opt(cf, opts))
1085    }
1086
1087    /// Return the bytes associated with a key value with read options. If you only intend to use
1088    /// the vector returned temporarily, consider using [`get_pinned_opt`](#method.get_pinned_opt)
1089    /// to avoid unnecessary memory copy.
1090    pub fn get_opt<K: AsRef<[u8]>>(
1091        &self,
1092        key: K,
1093        readopts: &ReadOptions,
1094    ) -> Result<Option<Vec<u8>>, Error> {
1095        self.get_pinned_opt(key, readopts)
1096            .map(|x| x.map(|v| v.as_ref().to_vec()))
1097    }
1098
1099    /// Return the bytes associated with a key value. If you only intend to use the vector returned
1100    /// temporarily, consider using [`get_pinned`](#method.get_pinned) to avoid unnecessary memory
1101    /// copy.
1102    pub fn get<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<Vec<u8>>, Error> {
1103        DEFAULT_READ_OPTS.with(|opts| self.get_opt(key.as_ref(), opts))
1104    }
1105
1106    /// Return the bytes associated with a key value and the given column family with read options.
1107    /// If you only intend to use the vector returned temporarily, consider using
1108    /// [`get_pinned_cf_opt`](#method.get_pinned_cf_opt) to avoid unnecessary memory.
1109    pub fn get_cf_opt<K: AsRef<[u8]>>(
1110        &self,
1111        cf: &impl AsColumnFamilyRef,
1112        key: K,
1113        readopts: &ReadOptions,
1114    ) -> Result<Option<Vec<u8>>, Error> {
1115        self.get_pinned_cf_opt(cf, key, readopts)
1116            .map(|x| x.map(|v| v.as_ref().to_vec()))
1117    }
1118
1119    /// Return the bytes associated with a key value and the given column family. If you only
1120    /// intend to use the vector returned temporarily, consider using
1121    /// [`get_pinned_cf`](#method.get_pinned_cf) to avoid unnecessary memory.
1122    pub fn get_cf<K: AsRef<[u8]>>(
1123        &self,
1124        cf: &impl AsColumnFamilyRef,
1125        key: K,
1126    ) -> Result<Option<Vec<u8>>, Error> {
1127        DEFAULT_READ_OPTS.with(|opts| self.get_cf_opt(cf, key.as_ref(), opts))
1128    }
1129
1130    /// Return the value associated with a key using RocksDB's PinnableSlice
1131    /// so as to avoid unnecessary memory copy.
1132    pub fn get_pinned_opt<K: AsRef<[u8]>>(
1133        &'_ self,
1134        key: K,
1135        readopts: &ReadOptions,
1136    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
1137        if readopts.inner.is_null() {
1138            return Err(Error::new(
1139                "Unable to create RocksDB read options. This is a fairly trivial call, and its \
1140                 failure may be indicative of a mis-compiled or mis-loaded RocksDB library."
1141                    .to_owned(),
1142            ));
1143        }
1144
1145        let key = key.as_ref();
1146        unsafe {
1147            let val = ffi_try!(ffi::rocksdb_get_pinned(
1148                self.inner.inner(),
1149                readopts.inner,
1150                key.as_ptr() as *const c_char,
1151                key.len() as size_t,
1152            ));
1153            if val.is_null() {
1154                Ok(None)
1155            } else {
1156                Ok(Some(DBPinnableSlice::from_c(val)))
1157            }
1158        }
1159    }
1160
1161    /// Return the value associated with a key using RocksDB's PinnableSlice
1162    /// so as to avoid unnecessary memory copy. Similar to get_pinned_opt but
1163    /// leverages default options.
1164    pub fn get_pinned<K: AsRef<[u8]>>(
1165        &'_ self,
1166        key: K,
1167    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
1168        DEFAULT_READ_OPTS.with(|opts| self.get_pinned_opt(key, opts))
1169    }
1170
1171    /// Return the value associated with a key using RocksDB's PinnableSlice
1172    /// so as to avoid unnecessary memory copy. Similar to get_pinned_opt but
1173    /// allows specifying ColumnFamily
1174    pub fn get_pinned_cf_opt<K: AsRef<[u8]>>(
1175        &'_ self,
1176        cf: &impl AsColumnFamilyRef,
1177        key: K,
1178        readopts: &ReadOptions,
1179    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
1180        if readopts.inner.is_null() {
1181            return Err(Error::new(
1182                "Unable to create RocksDB read options. This is a fairly trivial call, and its \
1183                 failure may be indicative of a mis-compiled or mis-loaded RocksDB library."
1184                    .to_owned(),
1185            ));
1186        }
1187
1188        let key = key.as_ref();
1189        unsafe {
1190            let val = ffi_try!(ffi::rocksdb_get_pinned_cf(
1191                self.inner.inner(),
1192                readopts.inner,
1193                cf.inner(),
1194                key.as_ptr() as *const c_char,
1195                key.len() as size_t,
1196            ));
1197            if val.is_null() {
1198                Ok(None)
1199            } else {
1200                Ok(Some(DBPinnableSlice::from_c(val)))
1201            }
1202        }
1203    }
1204
1205    /// Return the value associated with a key using RocksDB's PinnableSlice
1206    /// so as to avoid unnecessary memory copy. Similar to get_pinned_cf_opt but
1207    /// leverages default options.
1208    pub fn get_pinned_cf<K: AsRef<[u8]>>(
1209        &'_ self,
1210        cf: &impl AsColumnFamilyRef,
1211        key: K,
1212    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
1213        DEFAULT_READ_OPTS.with(|opts| self.get_pinned_cf_opt(cf, key, opts))
1214    }
1215
1216    /// Return the values associated with the given keys.
1217    pub fn multi_get<K, I>(&self, keys: I) -> Vec<Result<Option<Vec<u8>>, Error>>
1218    where
1219        K: AsRef<[u8]>,
1220        I: IntoIterator<Item = K>,
1221    {
1222        DEFAULT_READ_OPTS.with(|opts| self.multi_get_opt(keys, opts))
1223    }
1224
1225    /// Return the values associated with the given keys using read options.
1226    pub fn multi_get_opt<K, I>(
1227        &self,
1228        keys: I,
1229        readopts: &ReadOptions,
1230    ) -> Vec<Result<Option<Vec<u8>>, Error>>
1231    where
1232        K: AsRef<[u8]>,
1233        I: IntoIterator<Item = K>,
1234    {
1235        let owned_keys: Vec<K> = keys.into_iter().collect();
1236        let keys_sizes: Vec<usize> = owned_keys.iter().map(|k| k.as_ref().len()).collect();
1237        let ptr_keys: Vec<*const c_char> = owned_keys
1238            .iter()
1239            .map(|k| k.as_ref().as_ptr() as *const c_char)
1240            .collect();
1241
1242        let mut values: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
1243        let mut values_sizes: Vec<usize> = Vec::with_capacity(ptr_keys.len());
1244        let mut errors: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
1245        unsafe {
1246            ffi::rocksdb_multi_get(
1247                self.inner.inner(),
1248                readopts.inner,
1249                ptr_keys.len(),
1250                ptr_keys.as_ptr(),
1251                keys_sizes.as_ptr(),
1252                values.as_mut_ptr(),
1253                values_sizes.as_mut_ptr(),
1254                errors.as_mut_ptr(),
1255            );
1256        }
1257
1258        unsafe {
1259            values.set_len(ptr_keys.len());
1260            values_sizes.set_len(ptr_keys.len());
1261            errors.set_len(ptr_keys.len());
1262        }
1263
1264        convert_values(values, values_sizes, errors)
1265    }
1266
1267    /// Returns pinned values associated with the given keys using default read options.
1268    ///
1269    /// This iterates `get_pinned_opt` for each key and returns zero-copy
1270    /// `DBPinnableSlice` values when present, avoiding value copies.
1271    pub fn multi_get_pinned<K, I>(
1272        &'_ self,
1273        keys: I,
1274    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1275    where
1276        K: AsRef<[u8]>,
1277        I: IntoIterator<Item = K>,
1278    {
1279        DEFAULT_READ_OPTS.with(|opts| self.multi_get_pinned_opt(keys, opts))
1280    }
1281
1282    /// Returns pinned values associated with the given keys using the provided read options.
1283    ///
1284    /// This iterates `get_pinned_opt` for each key and returns zero-copy
1285    /// `DBPinnableSlice` values when present, avoiding value copies.
1286    pub fn multi_get_pinned_opt<K, I>(
1287        &'_ self,
1288        keys: I,
1289        readopts: &ReadOptions,
1290    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1291    where
1292        K: AsRef<[u8]>,
1293        I: IntoIterator<Item = K>,
1294    {
1295        keys.into_iter()
1296            .map(|k| self.get_pinned_opt(k, readopts))
1297            .collect()
1298    }
1299
1300    /// Returns pinned values associated with the given keys and column families
1301    /// using default read options.
1302    pub fn multi_get_pinned_cf<'a, 'b: 'a, K, I, W>(
1303        &'a self,
1304        keys: I,
1305    ) -> Vec<Result<Option<DBPinnableSlice<'a>>, Error>>
1306    where
1307        K: AsRef<[u8]>,
1308        I: IntoIterator<Item = (&'b W, K)>,
1309        W: 'b + AsColumnFamilyRef,
1310    {
1311        DEFAULT_READ_OPTS.with(|opts| self.multi_get_pinned_cf_opt(keys, opts))
1312    }
1313
1314    /// Returns pinned values associated with the given keys and column families
1315    /// using the provided read options.
1316    pub fn multi_get_pinned_cf_opt<'a, 'b: 'a, K, I, W>(
1317        &'a self,
1318        keys: I,
1319        readopts: &ReadOptions,
1320    ) -> Vec<Result<Option<DBPinnableSlice<'a>>, Error>>
1321    where
1322        K: AsRef<[u8]>,
1323        I: IntoIterator<Item = (&'b W, K)>,
1324        W: 'b + AsColumnFamilyRef,
1325    {
1326        keys.into_iter()
1327            .map(|(cf, k)| self.get_pinned_cf_opt(cf, k, readopts))
1328            .collect()
1329    }
1330
1331    /// Return the values associated with the given keys and column families.
1332    pub fn multi_get_cf<'a, 'b: 'a, K, I, W>(
1333        &'a self,
1334        keys: I,
1335    ) -> Vec<Result<Option<Vec<u8>>, Error>>
1336    where
1337        K: AsRef<[u8]>,
1338        I: IntoIterator<Item = (&'b W, K)>,
1339        W: 'b + AsColumnFamilyRef,
1340    {
1341        DEFAULT_READ_OPTS.with(|opts| self.multi_get_cf_opt(keys, opts))
1342    }
1343
1344    /// Return the values associated with the given keys and column families using read options.
1345    pub fn multi_get_cf_opt<'a, 'b: 'a, K, I, W>(
1346        &'a self,
1347        keys: I,
1348        readopts: &ReadOptions,
1349    ) -> Vec<Result<Option<Vec<u8>>, Error>>
1350    where
1351        K: AsRef<[u8]>,
1352        I: IntoIterator<Item = (&'b W, K)>,
1353        W: 'b + AsColumnFamilyRef,
1354    {
1355        let cfs_and_owned_keys: Vec<(&'b W, K)> = keys.into_iter().collect();
1356        let keys_sizes: Vec<usize> = cfs_and_owned_keys
1357            .iter()
1358            .map(|(_, k)| k.as_ref().len())
1359            .collect();
1360        let ptr_keys: Vec<*const c_char> = cfs_and_owned_keys
1361            .iter()
1362            .map(|(_, k)| k.as_ref().as_ptr() as *const c_char)
1363            .collect();
1364        let ptr_cfs: Vec<*const ffi::rocksdb_column_family_handle_t> = cfs_and_owned_keys
1365            .iter()
1366            .map(|(c, _)| c.inner().cast_const())
1367            .collect();
1368        let mut values: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
1369        let mut values_sizes: Vec<usize> = Vec::with_capacity(ptr_keys.len());
1370        let mut errors: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
1371        unsafe {
1372            ffi::rocksdb_multi_get_cf(
1373                self.inner.inner(),
1374                readopts.inner,
1375                ptr_cfs.as_ptr(),
1376                ptr_keys.len(),
1377                ptr_keys.as_ptr(),
1378                keys_sizes.as_ptr(),
1379                values.as_mut_ptr(),
1380                values_sizes.as_mut_ptr(),
1381                errors.as_mut_ptr(),
1382            );
1383        }
1384
1385        unsafe {
1386            values.set_len(ptr_keys.len());
1387            values_sizes.set_len(ptr_keys.len());
1388            errors.set_len(ptr_keys.len());
1389        }
1390
1391        convert_values(values, values_sizes, errors)
1392    }
1393
1394    /// Return the values associated with the given keys and the specified column family
1395    /// where internally the read requests are processed in batch if block-based table
1396    /// SST format is used.  It is a more optimized version of multi_get_cf.
1397    pub fn batched_multi_get_cf<'a, K, I>(
1398        &'_ self,
1399        cf: &impl AsColumnFamilyRef,
1400        keys: I,
1401        sorted_input: bool,
1402    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1403    where
1404        K: AsRef<[u8]> + 'a + ?Sized,
1405        I: IntoIterator<Item = &'a K>,
1406    {
1407        DEFAULT_READ_OPTS.with(|opts| self.batched_multi_get_cf_opt(cf, keys, sorted_input, opts))
1408    }
1409
1410    /// Return the values associated with the given keys and the specified column family
1411    /// where internally the read requests are processed in batch if block-based table
1412    /// SST format is used. It is a more optimized version of multi_get_cf_opt.
1413    pub fn batched_multi_get_cf_opt<'a, K, I>(
1414        &'_ self,
1415        cf: &impl AsColumnFamilyRef,
1416        keys: I,
1417        sorted_input: bool,
1418        readopts: &ReadOptions,
1419    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1420    where
1421        K: AsRef<[u8]> + 'a + ?Sized,
1422        I: IntoIterator<Item = &'a K>,
1423    {
1424        let (ptr_keys, keys_sizes): (Vec<_>, Vec<_>) = keys
1425            .into_iter()
1426            .map(|k| {
1427                let k = k.as_ref();
1428                (k.as_ptr() as *const c_char, k.len())
1429            })
1430            .unzip();
1431
1432        let mut pinned_values = vec![ptr::null_mut(); ptr_keys.len()];
1433        let mut errors = vec![ptr::null_mut(); ptr_keys.len()];
1434
1435        unsafe {
1436            ffi::rocksdb_batched_multi_get_cf(
1437                self.inner.inner(),
1438                readopts.inner,
1439                cf.inner(),
1440                ptr_keys.len(),
1441                ptr_keys.as_ptr(),
1442                keys_sizes.as_ptr(),
1443                pinned_values.as_mut_ptr(),
1444                errors.as_mut_ptr(),
1445                sorted_input,
1446            );
1447            pinned_values
1448                .into_iter()
1449                .zip(errors)
1450                .map(|(v, e)| {
1451                    if e.is_null() {
1452                        if v.is_null() {
1453                            Ok(None)
1454                        } else {
1455                            Ok(Some(DBPinnableSlice::from_c(v)))
1456                        }
1457                    } else {
1458                        Err(Error::new(crate::ffi_util::error_message(e)))
1459                    }
1460                })
1461                .collect()
1462        }
1463    }
1464
1465    /// Returns `false` if the given key definitely doesn't exist in the database, otherwise returns
1466    /// `true`. This function uses default `ReadOptions`.
1467    pub fn key_may_exist<K: AsRef<[u8]>>(&self, key: K) -> bool {
1468        DEFAULT_READ_OPTS.with(|opts| self.key_may_exist_opt(key, opts))
1469    }
1470
1471    /// Returns `false` if the given key definitely doesn't exist in the database, otherwise returns
1472    /// `true`.
1473    pub fn key_may_exist_opt<K: AsRef<[u8]>>(&self, key: K, readopts: &ReadOptions) -> bool {
1474        let key = key.as_ref();
1475        unsafe {
1476            0 != ffi::rocksdb_key_may_exist(
1477                self.inner.inner(),
1478                readopts.inner,
1479                key.as_ptr() as *const c_char,
1480                key.len() as size_t,
1481                ptr::null_mut(), /*value*/
1482                ptr::null_mut(), /*val_len*/
1483                ptr::null(),     /*timestamp*/
1484                0,               /*timestamp_len*/
1485                ptr::null_mut(), /*value_found*/
1486            )
1487        }
1488    }
1489
1490    /// Returns `false` if the given key definitely doesn't exist in the specified column family,
1491    /// otherwise returns `true`. This function uses default `ReadOptions`.
1492    pub fn key_may_exist_cf<K: AsRef<[u8]>>(&self, cf: &impl AsColumnFamilyRef, key: K) -> bool {
1493        DEFAULT_READ_OPTS.with(|opts| self.key_may_exist_cf_opt(cf, key, opts))
1494    }
1495
1496    /// Returns `false` if the given key definitely doesn't exist in the specified column family,
1497    /// otherwise returns `true`.
1498    pub fn key_may_exist_cf_opt<K: AsRef<[u8]>>(
1499        &self,
1500        cf: &impl AsColumnFamilyRef,
1501        key: K,
1502        readopts: &ReadOptions,
1503    ) -> bool {
1504        let key = key.as_ref();
1505        0 != unsafe {
1506            ffi::rocksdb_key_may_exist_cf(
1507                self.inner.inner(),
1508                readopts.inner,
1509                cf.inner(),
1510                key.as_ptr() as *const c_char,
1511                key.len() as size_t,
1512                ptr::null_mut(), /*value*/
1513                ptr::null_mut(), /*val_len*/
1514                ptr::null(),     /*timestamp*/
1515                0,               /*timestamp_len*/
1516                ptr::null_mut(), /*value_found*/
1517            )
1518        }
1519    }
1520
1521    /// If the key definitely does not exist in the database, then this method
1522    /// returns `(false, None)`, else `(true, None)` if it may.
1523    /// If the key is found in memory, then it returns `(true, Some<CSlice>)`.
1524    ///
1525    /// This check is potentially lighter-weight than calling `get()`. One way
1526    /// to make this lighter weight is to avoid doing any IOs.
1527    pub fn key_may_exist_cf_opt_value<K: AsRef<[u8]>>(
1528        &self,
1529        cf: &impl AsColumnFamilyRef,
1530        key: K,
1531        readopts: &ReadOptions,
1532    ) -> (bool, Option<CSlice>) {
1533        let key = key.as_ref();
1534        let mut val: *mut c_char = ptr::null_mut();
1535        let mut val_len: usize = 0;
1536        let mut value_found: c_uchar = 0;
1537        let may_exists = 0
1538            != unsafe {
1539                ffi::rocksdb_key_may_exist_cf(
1540                    self.inner.inner(),
1541                    readopts.inner,
1542                    cf.inner(),
1543                    key.as_ptr() as *const c_char,
1544                    key.len() as size_t,
1545                    &mut val,         /*value*/
1546                    &mut val_len,     /*val_len*/
1547                    ptr::null(),      /*timestamp*/
1548                    0,                /*timestamp_len*/
1549                    &mut value_found, /*value_found*/
1550                )
1551            };
1552        // The value is only allocated (using malloc) and returned if it is found and
1553        // value_found isn't NULL. In that case the user is responsible for freeing it.
1554        if may_exists && value_found != 0 {
1555            (
1556                may_exists,
1557                Some(unsafe { CSlice::from_raw_parts(val, val_len) }),
1558            )
1559        } else {
1560            (may_exists, None)
1561        }
1562    }
1563
1564    fn create_inner_cf_handle(
1565        &self,
1566        name: impl CStrLike,
1567        opts: &Options,
1568    ) -> Result<*mut ffi::rocksdb_column_family_handle_t, Error> {
1569        let cf_name = name.bake().map_err(|err| {
1570            Error::new(format!(
1571                "Failed to convert path to CString when creating cf: {err}"
1572            ))
1573        })?;
1574        Ok(unsafe {
1575            ffi_try!(ffi::rocksdb_create_column_family(
1576                self.inner.inner(),
1577                opts.inner,
1578                cf_name.as_ptr(),
1579            ))
1580        })
1581    }
1582
1583    pub fn iterator<'a: 'b, 'b>(
1584        &'a self,
1585        mode: IteratorMode,
1586    ) -> DBIteratorWithThreadMode<'b, Self> {
1587        let readopts = ReadOptions::default();
1588        self.iterator_opt(mode, readopts)
1589    }
1590
1591    pub fn iterator_opt<'a: 'b, 'b>(
1592        &'a self,
1593        mode: IteratorMode,
1594        readopts: ReadOptions,
1595    ) -> DBIteratorWithThreadMode<'b, Self> {
1596        DBIteratorWithThreadMode::new(self, readopts, mode)
1597    }
1598
1599    /// Opens an iterator using the provided ReadOptions.
1600    /// This is used when you want to iterate over a specific ColumnFamily with a modified ReadOptions
1601    pub fn iterator_cf_opt<'a: 'b, 'b>(
1602        &'a self,
1603        cf_handle: &impl AsColumnFamilyRef,
1604        readopts: ReadOptions,
1605        mode: IteratorMode,
1606    ) -> DBIteratorWithThreadMode<'b, Self> {
1607        DBIteratorWithThreadMode::new_cf(self, cf_handle.inner(), readopts, mode)
1608    }
1609
1610    /// Opens an iterator with `set_total_order_seek` enabled.
1611    /// This must be used to iterate across prefixes when `set_memtable_factory` has been called
1612    /// with a Hash-based implementation.
1613    pub fn full_iterator<'a: 'b, 'b>(
1614        &'a self,
1615        mode: IteratorMode,
1616    ) -> DBIteratorWithThreadMode<'b, Self> {
1617        let mut opts = ReadOptions::default();
1618        opts.set_total_order_seek(true);
1619        DBIteratorWithThreadMode::new(self, opts, mode)
1620    }
1621
1622    pub fn prefix_iterator<'a: 'b, 'b, P: AsRef<[u8]>>(
1623        &'a self,
1624        prefix: P,
1625    ) -> DBIteratorWithThreadMode<'b, Self> {
1626        let mut opts = ReadOptions::default();
1627        opts.set_prefix_same_as_start(true);
1628        DBIteratorWithThreadMode::new(
1629            self,
1630            opts,
1631            IteratorMode::From(prefix.as_ref(), Direction::Forward),
1632        )
1633    }
1634
1635    pub fn iterator_cf<'a: 'b, 'b>(
1636        &'a self,
1637        cf_handle: &impl AsColumnFamilyRef,
1638        mode: IteratorMode,
1639    ) -> DBIteratorWithThreadMode<'b, Self> {
1640        let opts = ReadOptions::default();
1641        DBIteratorWithThreadMode::new_cf(self, cf_handle.inner(), opts, mode)
1642    }
1643
1644    pub fn full_iterator_cf<'a: 'b, 'b>(
1645        &'a self,
1646        cf_handle: &impl AsColumnFamilyRef,
1647        mode: IteratorMode,
1648    ) -> DBIteratorWithThreadMode<'b, Self> {
1649        let mut opts = ReadOptions::default();
1650        opts.set_total_order_seek(true);
1651        DBIteratorWithThreadMode::new_cf(self, cf_handle.inner(), opts, mode)
1652    }
1653
1654    pub fn prefix_iterator_cf<'a, P: AsRef<[u8]>>(
1655        &'a self,
1656        cf_handle: &impl AsColumnFamilyRef,
1657        prefix: P,
1658    ) -> DBIteratorWithThreadMode<'a, Self> {
1659        let mut opts = ReadOptions::default();
1660        opts.set_prefix_same_as_start(true);
1661        DBIteratorWithThreadMode::<'a, Self>::new_cf(
1662            self,
1663            cf_handle.inner(),
1664            opts,
1665            IteratorMode::From(prefix.as_ref(), Direction::Forward),
1666        )
1667    }
1668
1669    /// Returns `true` if there exists at least one key with the given prefix
1670    /// in the default column family using default read options.
1671    ///
1672    /// When to use: prefer this for one-shot checks. It enables
1673    /// `prefix_same_as_start(true)` and bounds the iterator to the
1674    /// prefix via `PrefixRange`, minimizing stray IO per call.
1675    pub fn prefix_exists<P: AsRef<[u8]>>(&self, prefix: P) -> Result<bool, Error> {
1676        let p = prefix.as_ref();
1677        PREFIX_READ_OPTS.with(|rc| {
1678            let mut opts = rc.borrow_mut();
1679            opts.set_iterate_range(crate::PrefixRange(p));
1680            self.prefix_exists_opt(p, &opts)
1681        })
1682    }
1683
1684    /// Returns `true` if there exists at least one key with the given prefix
1685    /// in the default column family using the provided read options.
1686    pub fn prefix_exists_opt<P: AsRef<[u8]>>(
1687        &self,
1688        prefix: P,
1689        readopts: &ReadOptions,
1690    ) -> Result<bool, Error> {
1691        let prefix = prefix.as_ref();
1692        let iter = unsafe { self.create_iterator(readopts) };
1693        let res = unsafe {
1694            ffi::rocksdb_iter_seek(
1695                iter,
1696                prefix.as_ptr() as *const c_char,
1697                prefix.len() as size_t,
1698            );
1699            if ffi::rocksdb_iter_valid(iter) != 0 {
1700                let mut key_len: size_t = 0;
1701                let key_ptr = ffi::rocksdb_iter_key(iter, &mut key_len);
1702                let key = slice::from_raw_parts(key_ptr as *const u8, key_len as usize);
1703                Ok(key.starts_with(prefix))
1704            } else if let Err(e) = (|| {
1705                // Check status to differentiate end-of-range vs error
1706                ffi_try!(ffi::rocksdb_iter_get_error(iter));
1707                Ok::<(), Error>(())
1708            })() {
1709                Err(e)
1710            } else {
1711                Ok(false)
1712            }
1713        };
1714        unsafe { ffi::rocksdb_iter_destroy(iter) };
1715        res
1716    }
1717
1718    /// Creates a reusable prefix prober over the default column family using
1719    /// read options optimized for prefix probes.
1720    ///
1721    /// When to use: prefer this in hot loops with many checks per second. It
1722    /// reuses a raw iterator to avoid per-call allocation/FFI overhead. If you
1723    /// need custom tuning (e.g. async IO, readahead, cache-only), use
1724    /// `prefix_prober_with_opts`.
1725    pub fn prefix_prober(&self) -> PrefixProber<'_, Self> {
1726        let mut opts = ReadOptions::default();
1727        opts.set_prefix_same_as_start(true);
1728        PrefixProber {
1729            raw: DBRawIteratorWithThreadMode::new(self, opts),
1730        }
1731    }
1732
1733    /// Creates a reusable prefix prober over the default column family using
1734    /// the provided read options (owned).
1735    ///
1736    /// When to use: advanced tuning for heavy workloads. Callers can set
1737    /// `set_async_io(true)`, `set_readahead_size`, `set_read_tier`, etc. Note:
1738    /// the prober owns `ReadOptions` to keep internal buffers alive.
1739    pub fn prefix_prober_with_opts(&self, readopts: ReadOptions) -> PrefixProber<'_, Self> {
1740        PrefixProber {
1741            raw: DBRawIteratorWithThreadMode::new(self, readopts),
1742        }
1743    }
1744
1745    /// Creates a reusable prefix prober over the specified column family using
1746    /// read options optimized for prefix probes.
1747    pub fn prefix_prober_cf(&self, cf_handle: &impl AsColumnFamilyRef) -> PrefixProber<'_, Self> {
1748        let mut opts = ReadOptions::default();
1749        opts.set_prefix_same_as_start(true);
1750        PrefixProber {
1751            raw: DBRawIteratorWithThreadMode::new_cf(self, cf_handle.inner(), opts),
1752        }
1753    }
1754
1755    /// Creates a reusable prefix prober over the specified column family using
1756    /// the provided read options (owned).
1757    ///
1758    /// When to use: advanced tuning for heavy workloads on a specific CF.
1759    pub fn prefix_prober_cf_with_opts(
1760        &self,
1761        cf_handle: &impl AsColumnFamilyRef,
1762        readopts: ReadOptions,
1763    ) -> PrefixProber<'_, Self> {
1764        PrefixProber {
1765            raw: DBRawIteratorWithThreadMode::new_cf(self, cf_handle.inner(), readopts),
1766        }
1767    }
1768
1769    /// Returns `true` if there exists at least one key with the given prefix
1770    /// in the specified column family using default read options.
1771    ///
1772    /// When to use: one-shot checks on a CF. Enables
1773    /// `prefix_same_as_start(true)` and bounds the iterator via `PrefixRange`.
1774    pub fn prefix_exists_cf<P: AsRef<[u8]>>(
1775        &self,
1776        cf_handle: &impl AsColumnFamilyRef,
1777        prefix: P,
1778    ) -> Result<bool, Error> {
1779        let p = prefix.as_ref();
1780        PREFIX_READ_OPTS.with(|rc| {
1781            let mut opts = rc.borrow_mut();
1782            opts.set_iterate_range(crate::PrefixRange(p));
1783            self.prefix_exists_cf_opt(cf_handle, p, &opts)
1784        })
1785    }
1786
1787    /// Returns `true` if there exists at least one key with the given prefix
1788    /// in the specified column family using the provided read options.
1789    pub fn prefix_exists_cf_opt<P: AsRef<[u8]>>(
1790        &self,
1791        cf_handle: &impl AsColumnFamilyRef,
1792        prefix: P,
1793        readopts: &ReadOptions,
1794    ) -> Result<bool, Error> {
1795        let prefix = prefix.as_ref();
1796        let iter = unsafe { self.create_iterator_cf(cf_handle.inner(), readopts) };
1797        let res = unsafe {
1798            ffi::rocksdb_iter_seek(
1799                iter,
1800                prefix.as_ptr() as *const c_char,
1801                prefix.len() as size_t,
1802            );
1803            if ffi::rocksdb_iter_valid(iter) != 0 {
1804                let mut key_len: size_t = 0;
1805                let key_ptr = ffi::rocksdb_iter_key(iter, &mut key_len);
1806                let key = slice::from_raw_parts(key_ptr as *const u8, key_len as usize);
1807                Ok(key.starts_with(prefix))
1808            } else if let Err(e) = (|| {
1809                ffi_try!(ffi::rocksdb_iter_get_error(iter));
1810                Ok::<(), Error>(())
1811            })() {
1812                Err(e)
1813            } else {
1814                Ok(false)
1815            }
1816        };
1817        unsafe { ffi::rocksdb_iter_destroy(iter) };
1818        res
1819    }
1820
1821    /// Opens a raw iterator over the database, using the default read options
1822    pub fn raw_iterator<'a: 'b, 'b>(&'a self) -> DBRawIteratorWithThreadMode<'b, Self> {
1823        let opts = ReadOptions::default();
1824        DBRawIteratorWithThreadMode::new(self, opts)
1825    }
1826
1827    /// Opens a raw iterator over the given column family, using the default read options
1828    pub fn raw_iterator_cf<'a: 'b, 'b>(
1829        &'a self,
1830        cf_handle: &impl AsColumnFamilyRef,
1831    ) -> DBRawIteratorWithThreadMode<'b, Self> {
1832        let opts = ReadOptions::default();
1833        DBRawIteratorWithThreadMode::new_cf(self, cf_handle.inner(), opts)
1834    }
1835
1836    /// Opens a raw iterator over the database, using the given read options
1837    pub fn raw_iterator_opt<'a: 'b, 'b>(
1838        &'a self,
1839        readopts: ReadOptions,
1840    ) -> DBRawIteratorWithThreadMode<'b, Self> {
1841        DBRawIteratorWithThreadMode::new(self, readopts)
1842    }
1843
1844    /// Opens a raw iterator over the given column family, using the given read options
1845    pub fn raw_iterator_cf_opt<'a: 'b, 'b>(
1846        &'a self,
1847        cf_handle: &impl AsColumnFamilyRef,
1848        readopts: ReadOptions,
1849    ) -> DBRawIteratorWithThreadMode<'b, Self> {
1850        DBRawIteratorWithThreadMode::new_cf(self, cf_handle.inner(), readopts)
1851    }
1852
1853    pub fn snapshot(&'_ self) -> SnapshotWithThreadMode<'_, Self> {
1854        SnapshotWithThreadMode::<Self>::new(self)
1855    }
1856
1857    pub fn put_opt<K, V>(&self, key: K, value: V, writeopts: &WriteOptions) -> Result<(), Error>
1858    where
1859        K: AsRef<[u8]>,
1860        V: AsRef<[u8]>,
1861    {
1862        let key = key.as_ref();
1863        let value = value.as_ref();
1864
1865        unsafe {
1866            ffi_try!(ffi::rocksdb_put(
1867                self.inner.inner(),
1868                writeopts.inner,
1869                key.as_ptr() as *const c_char,
1870                key.len() as size_t,
1871                value.as_ptr() as *const c_char,
1872                value.len() as size_t,
1873            ));
1874            Ok(())
1875        }
1876    }
1877
1878    pub fn put_cf_opt<K, V>(
1879        &self,
1880        cf: &impl AsColumnFamilyRef,
1881        key: K,
1882        value: V,
1883        writeopts: &WriteOptions,
1884    ) -> Result<(), Error>
1885    where
1886        K: AsRef<[u8]>,
1887        V: AsRef<[u8]>,
1888    {
1889        let key = key.as_ref();
1890        let value = value.as_ref();
1891
1892        unsafe {
1893            ffi_try!(ffi::rocksdb_put_cf(
1894                self.inner.inner(),
1895                writeopts.inner,
1896                cf.inner(),
1897                key.as_ptr() as *const c_char,
1898                key.len() as size_t,
1899                value.as_ptr() as *const c_char,
1900                value.len() as size_t,
1901            ));
1902            Ok(())
1903        }
1904    }
1905
1906    /// Set the database entry for "key" to "value" with WriteOptions.
1907    /// If "key" already exists, it will coexist with previous entry.
1908    /// `Get` with a timestamp ts specified in ReadOptions will return
1909    /// the most recent key/value whose timestamp is smaller than or equal to ts.
1910    /// Takes an additional argument `ts` as the timestamp.
1911    /// Note: the DB must be opened with user defined timestamp enabled.
1912    pub fn put_with_ts_opt<K, V, S>(
1913        &self,
1914        key: K,
1915        ts: S,
1916        value: V,
1917        writeopts: &WriteOptions,
1918    ) -> Result<(), Error>
1919    where
1920        K: AsRef<[u8]>,
1921        V: AsRef<[u8]>,
1922        S: AsRef<[u8]>,
1923    {
1924        let key = key.as_ref();
1925        let value = value.as_ref();
1926        let ts = ts.as_ref();
1927        unsafe {
1928            ffi_try!(ffi::rocksdb_put_with_ts(
1929                self.inner.inner(),
1930                writeopts.inner,
1931                key.as_ptr() as *const c_char,
1932                key.len() as size_t,
1933                ts.as_ptr() as *const c_char,
1934                ts.len() as size_t,
1935                value.as_ptr() as *const c_char,
1936                value.len() as size_t,
1937            ));
1938            Ok(())
1939        }
1940    }
1941
1942    /// Put with timestamp in a specific column family with WriteOptions.
1943    /// If "key" already exists, it will coexist with previous entry.
1944    /// `Get` with a timestamp ts specified in ReadOptions will return
1945    /// the most recent key/value whose timestamp is smaller than or equal to ts.
1946    /// Takes an additional argument `ts` as the timestamp.
1947    /// Note: the DB must be opened with user defined timestamp enabled.
1948    pub fn put_cf_with_ts_opt<K, V, S>(
1949        &self,
1950        cf: &impl AsColumnFamilyRef,
1951        key: K,
1952        ts: S,
1953        value: V,
1954        writeopts: &WriteOptions,
1955    ) -> Result<(), Error>
1956    where
1957        K: AsRef<[u8]>,
1958        V: AsRef<[u8]>,
1959        S: AsRef<[u8]>,
1960    {
1961        let key = key.as_ref();
1962        let value = value.as_ref();
1963        let ts = ts.as_ref();
1964        unsafe {
1965            ffi_try!(ffi::rocksdb_put_cf_with_ts(
1966                self.inner.inner(),
1967                writeopts.inner,
1968                cf.inner(),
1969                key.as_ptr() as *const c_char,
1970                key.len() as size_t,
1971                ts.as_ptr() as *const c_char,
1972                ts.len() as size_t,
1973                value.as_ptr() as *const c_char,
1974                value.len() as size_t,
1975            ));
1976            Ok(())
1977        }
1978    }
1979
1980    pub fn merge_opt<K, V>(&self, key: K, value: V, writeopts: &WriteOptions) -> Result<(), Error>
1981    where
1982        K: AsRef<[u8]>,
1983        V: AsRef<[u8]>,
1984    {
1985        let key = key.as_ref();
1986        let value = value.as_ref();
1987
1988        unsafe {
1989            ffi_try!(ffi::rocksdb_merge(
1990                self.inner.inner(),
1991                writeopts.inner,
1992                key.as_ptr() as *const c_char,
1993                key.len() as size_t,
1994                value.as_ptr() as *const c_char,
1995                value.len() as size_t,
1996            ));
1997            Ok(())
1998        }
1999    }
2000
2001    pub fn merge_cf_opt<K, V>(
2002        &self,
2003        cf: &impl AsColumnFamilyRef,
2004        key: K,
2005        value: V,
2006        writeopts: &WriteOptions,
2007    ) -> Result<(), Error>
2008    where
2009        K: AsRef<[u8]>,
2010        V: AsRef<[u8]>,
2011    {
2012        let key = key.as_ref();
2013        let value = value.as_ref();
2014
2015        unsafe {
2016            ffi_try!(ffi::rocksdb_merge_cf(
2017                self.inner.inner(),
2018                writeopts.inner,
2019                cf.inner(),
2020                key.as_ptr() as *const c_char,
2021                key.len() as size_t,
2022                value.as_ptr() as *const c_char,
2023                value.len() as size_t,
2024            ));
2025            Ok(())
2026        }
2027    }
2028
2029    pub fn delete_opt<K: AsRef<[u8]>>(
2030        &self,
2031        key: K,
2032        writeopts: &WriteOptions,
2033    ) -> Result<(), Error> {
2034        let key = key.as_ref();
2035
2036        unsafe {
2037            ffi_try!(ffi::rocksdb_delete(
2038                self.inner.inner(),
2039                writeopts.inner,
2040                key.as_ptr() as *const c_char,
2041                key.len() as size_t,
2042            ));
2043            Ok(())
2044        }
2045    }
2046
2047    pub fn delete_cf_opt<K: AsRef<[u8]>>(
2048        &self,
2049        cf: &impl AsColumnFamilyRef,
2050        key: K,
2051        writeopts: &WriteOptions,
2052    ) -> Result<(), Error> {
2053        let key = key.as_ref();
2054
2055        unsafe {
2056            ffi_try!(ffi::rocksdb_delete_cf(
2057                self.inner.inner(),
2058                writeopts.inner,
2059                cf.inner(),
2060                key.as_ptr() as *const c_char,
2061                key.len() as size_t,
2062            ));
2063            Ok(())
2064        }
2065    }
2066
2067    /// Remove the database entry (if any) for "key" with WriteOptions.
2068    /// Takes an additional argument `ts` as the timestamp.
2069    /// Note: the DB must be opened with user defined timestamp enabled.
2070    pub fn delete_with_ts_opt<K, S>(
2071        &self,
2072        key: K,
2073        ts: S,
2074        writeopts: &WriteOptions,
2075    ) -> Result<(), Error>
2076    where
2077        K: AsRef<[u8]>,
2078        S: AsRef<[u8]>,
2079    {
2080        let key = key.as_ref();
2081        let ts = ts.as_ref();
2082        unsafe {
2083            ffi_try!(ffi::rocksdb_delete_with_ts(
2084                self.inner.inner(),
2085                writeopts.inner,
2086                key.as_ptr() as *const c_char,
2087                key.len() as size_t,
2088                ts.as_ptr() as *const c_char,
2089                ts.len() as size_t,
2090            ));
2091            Ok(())
2092        }
2093    }
2094
2095    /// Delete with timestamp in a specific column family with WriteOptions.
2096    /// Takes an additional argument `ts` as the timestamp.
2097    /// Note: the DB must be opened with user defined timestamp enabled.
2098    pub fn delete_cf_with_ts_opt<K, S>(
2099        &self,
2100        cf: &impl AsColumnFamilyRef,
2101        key: K,
2102        ts: S,
2103        writeopts: &WriteOptions,
2104    ) -> Result<(), Error>
2105    where
2106        K: AsRef<[u8]>,
2107        S: AsRef<[u8]>,
2108    {
2109        let key = key.as_ref();
2110        let ts = ts.as_ref();
2111        unsafe {
2112            ffi_try!(ffi::rocksdb_delete_cf_with_ts(
2113                self.inner.inner(),
2114                writeopts.inner,
2115                cf.inner(),
2116                key.as_ptr() as *const c_char,
2117                key.len() as size_t,
2118                ts.as_ptr() as *const c_char,
2119                ts.len() as size_t,
2120            ));
2121            Ok(())
2122        }
2123    }
2124
2125    pub fn put<K, V>(&self, key: K, value: V) -> Result<(), Error>
2126    where
2127        K: AsRef<[u8]>,
2128        V: AsRef<[u8]>,
2129    {
2130        DEFAULT_WRITE_OPTS.with(|opts| self.put_opt(key, value, opts))
2131    }
2132
2133    pub fn put_cf<K, V>(&self, cf: &impl AsColumnFamilyRef, key: K, value: V) -> Result<(), Error>
2134    where
2135        K: AsRef<[u8]>,
2136        V: AsRef<[u8]>,
2137    {
2138        DEFAULT_WRITE_OPTS.with(|opts| self.put_cf_opt(cf, key, value, opts))
2139    }
2140
2141    /// Set the database entry for "key" to "value".
2142    /// If "key" already exists, it will coexist with previous entry.
2143    /// `Get` with a timestamp ts specified in ReadOptions will return
2144    /// the most recent key/value whose timestamp is smaller than or equal to ts.
2145    /// Takes an additional argument `ts` as the timestamp.
2146    /// Note: the DB must be opened with user defined timestamp enabled.
2147    pub fn put_with_ts<K, V, S>(&self, key: K, ts: S, value: V) -> Result<(), Error>
2148    where
2149        K: AsRef<[u8]>,
2150        V: AsRef<[u8]>,
2151        S: AsRef<[u8]>,
2152    {
2153        DEFAULT_WRITE_OPTS
2154            .with(|opts| self.put_with_ts_opt(key.as_ref(), ts.as_ref(), value.as_ref(), opts))
2155    }
2156
2157    /// Put with timestamp in a specific column family.
2158    /// If "key" already exists, it will coexist with previous entry.
2159    /// `Get` with a timestamp ts specified in ReadOptions will return
2160    /// the most recent key/value whose timestamp is smaller than or equal to ts.
2161    /// Takes an additional argument `ts` as the timestamp.
2162    /// Note: the DB must be opened with user defined timestamp enabled.
2163    pub fn put_cf_with_ts<K, V, S>(
2164        &self,
2165        cf: &impl AsColumnFamilyRef,
2166        key: K,
2167        ts: S,
2168        value: V,
2169    ) -> Result<(), Error>
2170    where
2171        K: AsRef<[u8]>,
2172        V: AsRef<[u8]>,
2173        S: AsRef<[u8]>,
2174    {
2175        DEFAULT_WRITE_OPTS.with(|opts| {
2176            self.put_cf_with_ts_opt(cf, key.as_ref(), ts.as_ref(), value.as_ref(), opts)
2177        })
2178    }
2179
2180    pub fn merge<K, V>(&self, key: K, value: V) -> Result<(), Error>
2181    where
2182        K: AsRef<[u8]>,
2183        V: AsRef<[u8]>,
2184    {
2185        DEFAULT_WRITE_OPTS.with(|opts| self.merge_opt(key, value, opts))
2186    }
2187
2188    pub fn merge_cf<K, V>(&self, cf: &impl AsColumnFamilyRef, key: K, value: V) -> Result<(), Error>
2189    where
2190        K: AsRef<[u8]>,
2191        V: AsRef<[u8]>,
2192    {
2193        DEFAULT_WRITE_OPTS.with(|opts| self.merge_cf_opt(cf, key, value, opts))
2194    }
2195
2196    pub fn delete<K: AsRef<[u8]>>(&self, key: K) -> Result<(), Error> {
2197        DEFAULT_WRITE_OPTS.with(|opts| self.delete_opt(key, opts))
2198    }
2199
2200    pub fn delete_cf<K: AsRef<[u8]>>(
2201        &self,
2202        cf: &impl AsColumnFamilyRef,
2203        key: K,
2204    ) -> Result<(), Error> {
2205        DEFAULT_WRITE_OPTS.with(|opts| self.delete_cf_opt(cf, key, opts))
2206    }
2207
2208    /// Remove the database entry (if any) for "key".
2209    /// Takes an additional argument `ts` as the timestamp.
2210    /// Note: the DB must be opened with user defined timestamp enabled.
2211    pub fn delete_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
2212        &self,
2213        key: K,
2214        ts: S,
2215    ) -> Result<(), Error> {
2216        DEFAULT_WRITE_OPTS.with(|opts| self.delete_with_ts_opt(key, ts, opts))
2217    }
2218
2219    /// Delete with timestamp in a specific column family.
2220    /// Takes an additional argument `ts` as the timestamp.
2221    /// Note: the DB must be opened with user defined timestamp enabled.
2222    pub fn delete_cf_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
2223        &self,
2224        cf: &impl AsColumnFamilyRef,
2225        key: K,
2226        ts: S,
2227    ) -> Result<(), Error> {
2228        DEFAULT_WRITE_OPTS.with(|opts| self.delete_cf_with_ts_opt(cf, key, ts, opts))
2229    }
2230
2231    /// Remove the database entry for "key" with WriteOptions.
2232    ///
2233    /// Requires that the key exists and was not overwritten. Returns OK on success,
2234    /// and a non-OK status on error. It is not an error if "key" did not exist in the database.
2235    ///
2236    /// If a key is overwritten (by calling Put() multiple times), then the result
2237    /// of calling SingleDelete() on this key is undefined. SingleDelete() only
2238    /// behaves correctly if there has been only one Put() for this key since the
2239    /// previous call to SingleDelete() for this key.
2240    ///
2241    /// This feature is currently an experimental performance optimization
2242    /// for a very specific workload. It is up to the caller to ensure that
2243    /// SingleDelete is only used for a key that is not deleted using Delete() or
2244    /// written using Merge(). Mixing SingleDelete operations with Deletes and
2245    /// Merges can result in undefined behavior.
2246    ///
2247    /// Note: consider setting options.sync = true.
2248    ///
2249    /// For more information, see <https://github.com/facebook/rocksdb/wiki/Single-Delete>
2250    pub fn single_delete_opt<K: AsRef<[u8]>>(
2251        &self,
2252        key: K,
2253        writeopts: &WriteOptions,
2254    ) -> Result<(), Error> {
2255        let key = key.as_ref();
2256
2257        unsafe {
2258            ffi_try!(ffi::rocksdb_singledelete(
2259                self.inner.inner(),
2260                writeopts.inner,
2261                key.as_ptr() as *const c_char,
2262                key.len() as size_t,
2263            ));
2264            Ok(())
2265        }
2266    }
2267
2268    /// Remove the database entry for "key" from a specific column family with WriteOptions.
2269    ///
2270    /// See single_delete_opt() for detailed behavior and restrictions.
2271    pub fn single_delete_cf_opt<K: AsRef<[u8]>>(
2272        &self,
2273        cf: &impl AsColumnFamilyRef,
2274        key: K,
2275        writeopts: &WriteOptions,
2276    ) -> Result<(), Error> {
2277        let key = key.as_ref();
2278
2279        unsafe {
2280            ffi_try!(ffi::rocksdb_singledelete_cf(
2281                self.inner.inner(),
2282                writeopts.inner,
2283                cf.inner(),
2284                key.as_ptr() as *const c_char,
2285                key.len() as size_t,
2286            ));
2287            Ok(())
2288        }
2289    }
2290
2291    /// Remove the database entry for "key" with WriteOptions.
2292    ///
2293    /// Takes an additional argument `ts` as the timestamp.
2294    /// Note: the DB must be opened with user defined timestamp enabled.
2295    ///
2296    /// See single_delete_opt() for detailed behavior and restrictions.
2297    pub fn single_delete_with_ts_opt<K, S>(
2298        &self,
2299        key: K,
2300        ts: S,
2301        writeopts: &WriteOptions,
2302    ) -> Result<(), Error>
2303    where
2304        K: AsRef<[u8]>,
2305        S: AsRef<[u8]>,
2306    {
2307        let key = key.as_ref();
2308        let ts = ts.as_ref();
2309        unsafe {
2310            ffi_try!(ffi::rocksdb_singledelete_with_ts(
2311                self.inner.inner(),
2312                writeopts.inner,
2313                key.as_ptr() as *const c_char,
2314                key.len() as size_t,
2315                ts.as_ptr() as *const c_char,
2316                ts.len() as size_t,
2317            ));
2318            Ok(())
2319        }
2320    }
2321
2322    /// Remove the database entry for "key" from a specific column family with WriteOptions.
2323    ///
2324    /// Takes an additional argument `ts` as the timestamp.
2325    /// Note: the DB must be opened with user defined timestamp enabled.
2326    ///
2327    /// See single_delete_opt() for detailed behavior and restrictions.
2328    pub fn single_delete_cf_with_ts_opt<K, S>(
2329        &self,
2330        cf: &impl AsColumnFamilyRef,
2331        key: K,
2332        ts: S,
2333        writeopts: &WriteOptions,
2334    ) -> Result<(), Error>
2335    where
2336        K: AsRef<[u8]>,
2337        S: AsRef<[u8]>,
2338    {
2339        let key = key.as_ref();
2340        let ts = ts.as_ref();
2341        unsafe {
2342            ffi_try!(ffi::rocksdb_singledelete_cf_with_ts(
2343                self.inner.inner(),
2344                writeopts.inner,
2345                cf.inner(),
2346                key.as_ptr() as *const c_char,
2347                key.len() as size_t,
2348                ts.as_ptr() as *const c_char,
2349                ts.len() as size_t,
2350            ));
2351            Ok(())
2352        }
2353    }
2354
2355    /// Remove the database entry for "key".
2356    ///
2357    /// See single_delete_opt() for detailed behavior and restrictions.
2358    pub fn single_delete<K: AsRef<[u8]>>(&self, key: K) -> Result<(), Error> {
2359        DEFAULT_WRITE_OPTS.with(|opts| self.single_delete_opt(key, opts))
2360    }
2361
2362    /// Remove the database entry for "key" from a specific column family.
2363    ///
2364    /// See single_delete_opt() for detailed behavior and restrictions.
2365    pub fn single_delete_cf<K: AsRef<[u8]>>(
2366        &self,
2367        cf: &impl AsColumnFamilyRef,
2368        key: K,
2369    ) -> Result<(), Error> {
2370        DEFAULT_WRITE_OPTS.with(|opts| self.single_delete_cf_opt(cf, key, opts))
2371    }
2372
2373    /// Remove the database entry for "key".
2374    ///
2375    /// Takes an additional argument `ts` as the timestamp.
2376    /// Note: the DB must be opened with user defined timestamp enabled.
2377    ///
2378    /// See single_delete_opt() for detailed behavior and restrictions.
2379    pub fn single_delete_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
2380        &self,
2381        key: K,
2382        ts: S,
2383    ) -> Result<(), Error> {
2384        DEFAULT_WRITE_OPTS.with(|opts| self.single_delete_with_ts_opt(key, ts, opts))
2385    }
2386
2387    /// Remove the database entry for "key" from a specific column family.
2388    ///
2389    /// Takes an additional argument `ts` as the timestamp.
2390    /// Note: the DB must be opened with user defined timestamp enabled.
2391    ///
2392    /// See single_delete_opt() for detailed behavior and restrictions.
2393    pub fn single_delete_cf_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
2394        &self,
2395        cf: &impl AsColumnFamilyRef,
2396        key: K,
2397        ts: S,
2398    ) -> Result<(), Error> {
2399        DEFAULT_WRITE_OPTS.with(|opts| self.single_delete_cf_with_ts_opt(cf, key, ts, opts))
2400    }
2401
2402    /// Runs a manual compaction on the Range of keys given. This is not likely to be needed for typical usage.
2403    pub fn compact_range<S: AsRef<[u8]>, E: AsRef<[u8]>>(&self, start: Option<S>, end: Option<E>) {
2404        unsafe {
2405            let start = start.as_ref().map(AsRef::as_ref);
2406            let end = end.as_ref().map(AsRef::as_ref);
2407
2408            ffi::rocksdb_compact_range(
2409                self.inner.inner(),
2410                opt_bytes_to_ptr(start),
2411                start.map_or(0, <[u8]>::len) as size_t,
2412                opt_bytes_to_ptr(end),
2413                end.map_or(0, <[u8]>::len) as size_t,
2414            );
2415        }
2416    }
2417
2418    /// Same as `compact_range` but with custom options.
2419    pub fn compact_range_opt<S: AsRef<[u8]>, E: AsRef<[u8]>>(
2420        &self,
2421        start: Option<S>,
2422        end: Option<E>,
2423        opts: &CompactOptions,
2424    ) {
2425        unsafe {
2426            let start = start.as_ref().map(AsRef::as_ref);
2427            let end = end.as_ref().map(AsRef::as_ref);
2428
2429            ffi::rocksdb_compact_range_opt(
2430                self.inner.inner(),
2431                opts.inner,
2432                opt_bytes_to_ptr(start),
2433                start.map_or(0, <[u8]>::len) as size_t,
2434                opt_bytes_to_ptr(end),
2435                end.map_or(0, <[u8]>::len) as size_t,
2436            );
2437        }
2438    }
2439
2440    /// Runs a manual compaction on the Range of keys given on the
2441    /// given column family. This is not likely to be needed for typical usage.
2442    pub fn compact_range_cf<S: AsRef<[u8]>, E: AsRef<[u8]>>(
2443        &self,
2444        cf: &impl AsColumnFamilyRef,
2445        start: Option<S>,
2446        end: Option<E>,
2447    ) {
2448        unsafe {
2449            let start = start.as_ref().map(AsRef::as_ref);
2450            let end = end.as_ref().map(AsRef::as_ref);
2451
2452            ffi::rocksdb_compact_range_cf(
2453                self.inner.inner(),
2454                cf.inner(),
2455                opt_bytes_to_ptr(start),
2456                start.map_or(0, <[u8]>::len) as size_t,
2457                opt_bytes_to_ptr(end),
2458                end.map_or(0, <[u8]>::len) as size_t,
2459            );
2460        }
2461    }
2462
2463    /// Same as `compact_range_cf` but with custom options.
2464    pub fn compact_range_cf_opt<S: AsRef<[u8]>, E: AsRef<[u8]>>(
2465        &self,
2466        cf: &impl AsColumnFamilyRef,
2467        start: Option<S>,
2468        end: Option<E>,
2469        opts: &CompactOptions,
2470    ) {
2471        unsafe {
2472            let start = start.as_ref().map(AsRef::as_ref);
2473            let end = end.as_ref().map(AsRef::as_ref);
2474
2475            ffi::rocksdb_compact_range_cf_opt(
2476                self.inner.inner(),
2477                cf.inner(),
2478                opts.inner,
2479                opt_bytes_to_ptr(start),
2480                start.map_or(0, <[u8]>::len) as size_t,
2481                opt_bytes_to_ptr(end),
2482                end.map_or(0, <[u8]>::len) as size_t,
2483            );
2484        }
2485    }
2486
2487    /// Wait for all flush and compactions jobs to finish. Jobs to wait include the
2488    /// unscheduled (queued, but not scheduled yet).
2489    ///
2490    /// NOTE: This may also never return if there's sufficient ongoing writes that
2491    /// keeps flush and compaction going without stopping. The user would have to
2492    /// cease all the writes to DB to make this eventually return in a stable
2493    /// state. The user may also use timeout option in WaitForCompactOptions to
2494    /// make this stop waiting and return when timeout expires.
2495    pub fn wait_for_compact(&self, opts: &WaitForCompactOptions) -> Result<(), Error> {
2496        unsafe {
2497            ffi_try!(ffi::rocksdb_wait_for_compact(
2498                self.inner.inner(),
2499                opts.inner
2500            ));
2501        }
2502        Ok(())
2503    }
2504
2505    pub fn set_options(&self, opts: &[(&str, &str)]) -> Result<(), Error> {
2506        let copts = convert_options(opts)?;
2507        let cnames: Vec<*const c_char> = copts.iter().map(|opt| opt.0.as_ptr()).collect();
2508        let cvalues: Vec<*const c_char> = copts.iter().map(|opt| opt.1.as_ptr()).collect();
2509        let count = opts.len() as i32;
2510        unsafe {
2511            ffi_try!(ffi::rocksdb_set_options(
2512                self.inner.inner(),
2513                count,
2514                cnames.as_ptr(),
2515                cvalues.as_ptr(),
2516            ));
2517        }
2518        Ok(())
2519    }
2520
2521    pub fn set_options_cf(
2522        &self,
2523        cf: &impl AsColumnFamilyRef,
2524        opts: &[(&str, &str)],
2525    ) -> Result<(), Error> {
2526        let copts = convert_options(opts)?;
2527        let cnames: Vec<*const c_char> = copts.iter().map(|opt| opt.0.as_ptr()).collect();
2528        let cvalues: Vec<*const c_char> = copts.iter().map(|opt| opt.1.as_ptr()).collect();
2529        let count = opts.len() as i32;
2530        unsafe {
2531            ffi_try!(ffi::rocksdb_set_options_cf(
2532                self.inner.inner(),
2533                cf.inner(),
2534                count,
2535                cnames.as_ptr(),
2536                cvalues.as_ptr(),
2537            ));
2538        }
2539        Ok(())
2540    }
2541
2542    /// Implementation for property_value et al methods.
2543    ///
2544    /// `name` is the name of the property.  It will be converted into a CString
2545    /// and passed to `get_property` as argument.  `get_property` reads the
2546    /// specified property and either returns NULL or a pointer to a C allocated
2547    /// string; this method takes ownership of that string and will free it at
2548    /// the end. That string is parsed using `parse` callback which produces
2549    /// the returned result.
2550    fn property_value_impl<R>(
2551        name: impl CStrLike,
2552        get_property: impl FnOnce(*const c_char) -> *mut c_char,
2553        parse: impl FnOnce(&str) -> Result<R, Error>,
2554    ) -> Result<Option<R>, Error> {
2555        let value = match name.bake() {
2556            Ok(prop_name) => get_property(prop_name.as_ptr()),
2557            Err(e) => {
2558                return Err(Error::new(format!(
2559                    "Failed to convert property name to CString: {e}"
2560                )));
2561            }
2562        };
2563        if value.is_null() {
2564            return Ok(None);
2565        }
2566        let result = match unsafe { CStr::from_ptr(value) }.to_str() {
2567            Ok(s) => parse(s).map(|value| Some(value)),
2568            Err(e) => Err(Error::new(format!(
2569                "Failed to convert property value to string: {e}"
2570            ))),
2571        };
2572        unsafe {
2573            ffi::rocksdb_free(value as *mut c_void);
2574        }
2575        result
2576    }
2577
2578    /// Retrieves a RocksDB property by name.
2579    ///
2580    /// Full list of properties could be find
2581    /// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L428-L634).
2582    pub fn property_value(&self, name: impl CStrLike) -> Result<Option<String>, Error> {
2583        Self::property_value_impl(
2584            name,
2585            |prop_name| unsafe { ffi::rocksdb_property_value(self.inner.inner(), prop_name) },
2586            |str_value| Ok(str_value.to_owned()),
2587        )
2588    }
2589
2590    /// Retrieves a RocksDB property by name, for a specific column family.
2591    ///
2592    /// Full list of properties could be find
2593    /// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L428-L634).
2594    pub fn property_value_cf(
2595        &self,
2596        cf: &impl AsColumnFamilyRef,
2597        name: impl CStrLike,
2598    ) -> Result<Option<String>, Error> {
2599        Self::property_value_impl(
2600            name,
2601            |prop_name| unsafe {
2602                ffi::rocksdb_property_value_cf(self.inner.inner(), cf.inner(), prop_name)
2603            },
2604            |str_value| Ok(str_value.to_owned()),
2605        )
2606    }
2607
2608    fn parse_property_int_value(value: &str) -> Result<u64, Error> {
2609        value.parse::<u64>().map_err(|err| {
2610            Error::new(format!(
2611                "Failed to convert property value {value} to int: {err}"
2612            ))
2613        })
2614    }
2615
2616    /// Retrieves a RocksDB property and casts it to an integer.
2617    ///
2618    /// Full list of properties that return int values could be find
2619    /// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689).
2620    pub fn property_int_value(&self, name: impl CStrLike) -> Result<Option<u64>, Error> {
2621        Self::property_value_impl(
2622            name,
2623            |prop_name| unsafe { ffi::rocksdb_property_value(self.inner.inner(), prop_name) },
2624            Self::parse_property_int_value,
2625        )
2626    }
2627
2628    /// Retrieves a RocksDB property for a specific column family and casts it to an integer.
2629    ///
2630    /// Full list of properties that return int values could be find
2631    /// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689).
2632    pub fn property_int_value_cf(
2633        &self,
2634        cf: &impl AsColumnFamilyRef,
2635        name: impl CStrLike,
2636    ) -> Result<Option<u64>, Error> {
2637        Self::property_value_impl(
2638            name,
2639            |prop_name| unsafe {
2640                ffi::rocksdb_property_value_cf(self.inner.inner(), cf.inner(), prop_name)
2641            },
2642            Self::parse_property_int_value,
2643        )
2644    }
2645
2646    /// The sequence number of the most recent transaction.
2647    pub fn latest_sequence_number(&self) -> u64 {
2648        unsafe { ffi::rocksdb_get_latest_sequence_number(self.inner.inner()) }
2649    }
2650
2651    /// Return the approximate file system space used by keys in each ranges.
2652    ///
2653    /// Note that the returned sizes measure file system space usage, so
2654    /// if the user data compresses by a factor of ten, the returned
2655    /// sizes will be one-tenth the size of the corresponding user data size.
2656    ///
2657    /// Due to lack of abi, only data flushed to disk is taken into account.
2658    pub fn get_approximate_sizes(&self, ranges: &[Range]) -> Vec<u64> {
2659        self.get_approximate_sizes_cfopt(None::<&ColumnFamily>, ranges)
2660    }
2661
2662    pub fn get_approximate_sizes_cf(
2663        &self,
2664        cf: &impl AsColumnFamilyRef,
2665        ranges: &[Range],
2666    ) -> Vec<u64> {
2667        self.get_approximate_sizes_cfopt(Some(cf), ranges)
2668    }
2669
2670    fn get_approximate_sizes_cfopt(
2671        &self,
2672        cf: Option<&impl AsColumnFamilyRef>,
2673        ranges: &[Range],
2674    ) -> Vec<u64> {
2675        let start_keys: Vec<*const c_char> = ranges
2676            .iter()
2677            .map(|x| x.start_key.as_ptr() as *const c_char)
2678            .collect();
2679        let start_key_lens: Vec<_> = ranges.iter().map(|x| x.start_key.len()).collect();
2680        let end_keys: Vec<*const c_char> = ranges
2681            .iter()
2682            .map(|x| x.end_key.as_ptr() as *const c_char)
2683            .collect();
2684        let end_key_lens: Vec<_> = ranges.iter().map(|x| x.end_key.len()).collect();
2685        let mut sizes: Vec<u64> = vec![0; ranges.len()];
2686        let (n, start_key_ptr, start_key_len_ptr, end_key_ptr, end_key_len_ptr, size_ptr) = (
2687            ranges.len() as i32,
2688            start_keys.as_ptr(),
2689            start_key_lens.as_ptr(),
2690            end_keys.as_ptr(),
2691            end_key_lens.as_ptr(),
2692            sizes.as_mut_ptr(),
2693        );
2694        let mut err: *mut c_char = ptr::null_mut();
2695        match cf {
2696            None => unsafe {
2697                ffi::rocksdb_approximate_sizes(
2698                    self.inner.inner(),
2699                    n,
2700                    start_key_ptr,
2701                    start_key_len_ptr,
2702                    end_key_ptr,
2703                    end_key_len_ptr,
2704                    size_ptr,
2705                    &mut err,
2706                );
2707            },
2708            Some(cf) => unsafe {
2709                ffi::rocksdb_approximate_sizes_cf(
2710                    self.inner.inner(),
2711                    cf.inner(),
2712                    n,
2713                    start_key_ptr,
2714                    start_key_len_ptr,
2715                    end_key_ptr,
2716                    end_key_len_ptr,
2717                    size_ptr,
2718                    &mut err,
2719                );
2720            },
2721        }
2722        sizes
2723    }
2724
2725    /// Iterate over batches of write operations since a given sequence.
2726    ///
2727    /// Produce an iterator that will provide the batches of write operations
2728    /// that have occurred since the given sequence (see
2729    /// `latest_sequence_number()`). Use the provided iterator to retrieve each
2730    /// (`u64`, `WriteBatch`) tuple, and then gather the individual puts and
2731    /// deletes using the `WriteBatch::iterate()` function.
2732    ///
2733    /// Calling `get_updates_since()` with a sequence number that is out of
2734    /// bounds will return an error.
2735    pub fn get_updates_since(&self, seq_number: u64) -> Result<DBWALIterator, Error> {
2736        unsafe {
2737            // rocksdb_wal_readoptions_t does not appear to have any functions
2738            // for creating and destroying it; fortunately we can pass a nullptr
2739            // here to get the default behavior
2740            let opts: *const ffi::rocksdb_wal_readoptions_t = ptr::null();
2741            let iter = ffi_try!(ffi::rocksdb_get_updates_since(
2742                self.inner.inner(),
2743                seq_number,
2744                opts
2745            ));
2746            Ok(DBWALIterator {
2747                inner: iter,
2748                start_seq_number: seq_number,
2749            })
2750        }
2751    }
2752
2753    /// Tries to catch up with the primary by reading as much as possible from the
2754    /// log files.
2755    pub fn try_catch_up_with_primary(&self) -> Result<(), Error> {
2756        unsafe {
2757            ffi_try!(ffi::rocksdb_try_catch_up_with_primary(self.inner.inner()));
2758        }
2759        Ok(())
2760    }
2761
2762    /// Loads a list of external SST files created with SstFileWriter into the DB with default opts
2763    pub fn ingest_external_file<P: AsRef<Path>>(&self, paths: Vec<P>) -> Result<(), Error> {
2764        let opts = IngestExternalFileOptions::default();
2765        self.ingest_external_file_opts(&opts, paths)
2766    }
2767
2768    /// Loads a list of external SST files created with SstFileWriter into the DB
2769    pub fn ingest_external_file_opts<P: AsRef<Path>>(
2770        &self,
2771        opts: &IngestExternalFileOptions,
2772        paths: Vec<P>,
2773    ) -> Result<(), Error> {
2774        let paths_v: Vec<CString> = paths.iter().map(to_cpath).collect::<Result<Vec<_>, _>>()?;
2775        let cpaths: Vec<_> = paths_v.iter().map(|path| path.as_ptr()).collect();
2776
2777        self.ingest_external_file_raw(opts, &paths_v, &cpaths)
2778    }
2779
2780    /// Loads a list of external SST files created with SstFileWriter into the DB for given Column Family
2781    /// with default opts
2782    pub fn ingest_external_file_cf<P: AsRef<Path>>(
2783        &self,
2784        cf: &impl AsColumnFamilyRef,
2785        paths: Vec<P>,
2786    ) -> Result<(), Error> {
2787        let opts = IngestExternalFileOptions::default();
2788        self.ingest_external_file_cf_opts(cf, &opts, paths)
2789    }
2790
2791    /// Loads a list of external SST files created with SstFileWriter into the DB for given Column Family
2792    pub fn ingest_external_file_cf_opts<P: AsRef<Path>>(
2793        &self,
2794        cf: &impl AsColumnFamilyRef,
2795        opts: &IngestExternalFileOptions,
2796        paths: Vec<P>,
2797    ) -> Result<(), Error> {
2798        let paths_v: Vec<CString> = paths.iter().map(to_cpath).collect::<Result<Vec<_>, _>>()?;
2799        let cpaths: Vec<_> = paths_v.iter().map(|path| path.as_ptr()).collect();
2800
2801        self.ingest_external_file_raw_cf(cf, opts, &paths_v, &cpaths)
2802    }
2803
2804    fn ingest_external_file_raw(
2805        &self,
2806        opts: &IngestExternalFileOptions,
2807        paths_v: &[CString],
2808        cpaths: &[*const c_char],
2809    ) -> Result<(), Error> {
2810        unsafe {
2811            ffi_try!(ffi::rocksdb_ingest_external_file(
2812                self.inner.inner(),
2813                cpaths.as_ptr(),
2814                paths_v.len(),
2815                opts.inner.cast_const()
2816            ));
2817            Ok(())
2818        }
2819    }
2820
2821    fn ingest_external_file_raw_cf(
2822        &self,
2823        cf: &impl AsColumnFamilyRef,
2824        opts: &IngestExternalFileOptions,
2825        paths_v: &[CString],
2826        cpaths: &[*const c_char],
2827    ) -> Result<(), Error> {
2828        unsafe {
2829            ffi_try!(ffi::rocksdb_ingest_external_file_cf(
2830                self.inner.inner(),
2831                cf.inner(),
2832                cpaths.as_ptr(),
2833                paths_v.len(),
2834                opts.inner.cast_const()
2835            ));
2836            Ok(())
2837        }
2838    }
2839
2840    /// Obtains the LSM-tree meta data of the default column family of the DB
2841    pub fn get_column_family_metadata(&self) -> ColumnFamilyMetaData {
2842        unsafe {
2843            let ptr = ffi::rocksdb_get_column_family_metadata(self.inner.inner());
2844
2845            let metadata = ColumnFamilyMetaData {
2846                size: ffi::rocksdb_column_family_metadata_get_size(ptr),
2847                name: from_cstr(ffi::rocksdb_column_family_metadata_get_name(ptr)),
2848                file_count: ffi::rocksdb_column_family_metadata_get_file_count(ptr),
2849            };
2850
2851            // destroy
2852            ffi::rocksdb_column_family_metadata_destroy(ptr);
2853
2854            // return
2855            metadata
2856        }
2857    }
2858
2859    /// Obtains the LSM-tree meta data of the specified column family of the DB
2860    pub fn get_column_family_metadata_cf(
2861        &self,
2862        cf: &impl AsColumnFamilyRef,
2863    ) -> ColumnFamilyMetaData {
2864        unsafe {
2865            let ptr = ffi::rocksdb_get_column_family_metadata_cf(self.inner.inner(), cf.inner());
2866
2867            let metadata = ColumnFamilyMetaData {
2868                size: ffi::rocksdb_column_family_metadata_get_size(ptr),
2869                name: from_cstr(ffi::rocksdb_column_family_metadata_get_name(ptr)),
2870                file_count: ffi::rocksdb_column_family_metadata_get_file_count(ptr),
2871            };
2872
2873            // destroy
2874            ffi::rocksdb_column_family_metadata_destroy(ptr);
2875
2876            // return
2877            metadata
2878        }
2879    }
2880
2881    /// Returns a list of all table files with their level, start key
2882    /// and end key
2883    pub fn live_files(&self) -> Result<Vec<LiveFile>, Error> {
2884        unsafe {
2885            let livefiles_ptr = ffi::rocksdb_livefiles(self.inner.inner());
2886            if livefiles_ptr.is_null() {
2887                Err(Error::new("Could not get live files".to_owned()))
2888            } else {
2889                let files = LiveFile::from_rocksdb_livefiles_ptr(livefiles_ptr);
2890
2891                // destroy livefiles metadata(s)
2892                ffi::rocksdb_livefiles_destroy(livefiles_ptr);
2893
2894                // return
2895                Ok(files)
2896            }
2897        }
2898    }
2899
2900    /// Delete sst files whose keys are entirely in the given range.
2901    ///
2902    /// Could leave some keys in the range which are in files which are not
2903    /// entirely in the range.
2904    ///
2905    /// Note: L0 files are left regardless of whether they're in the range.
2906    ///
2907    /// SnapshotWithThreadModes before the delete might not see the data in the given range.
2908    pub fn delete_file_in_range<K: AsRef<[u8]>>(&self, from: K, to: K) -> Result<(), Error> {
2909        let from = from.as_ref();
2910        let to = to.as_ref();
2911        unsafe {
2912            ffi_try!(ffi::rocksdb_delete_file_in_range(
2913                self.inner.inner(),
2914                from.as_ptr() as *const c_char,
2915                from.len() as size_t,
2916                to.as_ptr() as *const c_char,
2917                to.len() as size_t,
2918            ));
2919            Ok(())
2920        }
2921    }
2922
2923    /// Same as `delete_file_in_range` but only for specific column family
2924    pub fn delete_file_in_range_cf<K: AsRef<[u8]>>(
2925        &self,
2926        cf: &impl AsColumnFamilyRef,
2927        from: K,
2928        to: K,
2929    ) -> Result<(), Error> {
2930        let from = from.as_ref();
2931        let to = to.as_ref();
2932        unsafe {
2933            ffi_try!(ffi::rocksdb_delete_file_in_range_cf(
2934                self.inner.inner(),
2935                cf.inner(),
2936                from.as_ptr() as *const c_char,
2937                from.len() as size_t,
2938                to.as_ptr() as *const c_char,
2939                to.len() as size_t,
2940            ));
2941            Ok(())
2942        }
2943    }
2944
2945    /// Request stopping background work, if wait is true wait until it's done.
2946    pub fn cancel_all_background_work(&self, wait: bool) {
2947        unsafe {
2948            ffi::rocksdb_cancel_all_background_work(self.inner.inner(), c_uchar::from(wait));
2949        }
2950    }
2951
2952    fn drop_column_family<C>(
2953        &self,
2954        cf_inner: *mut ffi::rocksdb_column_family_handle_t,
2955        cf: C,
2956    ) -> Result<(), Error> {
2957        unsafe {
2958            // first mark the column family as dropped
2959            ffi_try!(ffi::rocksdb_drop_column_family(
2960                self.inner.inner(),
2961                cf_inner
2962            ));
2963        }
2964        // then finally reclaim any resources (mem, files) by destroying the only single column
2965        // family handle by drop()-ing it
2966        drop(cf);
2967        Ok(())
2968    }
2969
2970    /// Increase the full_history_ts of column family. The new ts_low value should
2971    /// be newer than current full_history_ts value.
2972    /// If another thread updates full_history_ts_low concurrently to a higher
2973    /// timestamp than the requested ts_low, a try again error will be returned.
2974    pub fn increase_full_history_ts_low<S: AsRef<[u8]>>(
2975        &self,
2976        cf: &impl AsColumnFamilyRef,
2977        ts: S,
2978    ) -> Result<(), Error> {
2979        let ts = ts.as_ref();
2980        unsafe {
2981            ffi_try!(ffi::rocksdb_increase_full_history_ts_low(
2982                self.inner.inner(),
2983                cf.inner(),
2984                ts.as_ptr() as *const c_char,
2985                ts.len() as size_t,
2986            ));
2987            Ok(())
2988        }
2989    }
2990
2991    /// Get current full_history_ts value.
2992    pub fn get_full_history_ts_low(&self, cf: &impl AsColumnFamilyRef) -> Result<Vec<u8>, Error> {
2993        unsafe {
2994            let mut ts_lowlen = 0;
2995            let ts = ffi_try!(ffi::rocksdb_get_full_history_ts_low(
2996                self.inner.inner(),
2997                cf.inner(),
2998                &mut ts_lowlen,
2999            ));
3000
3001            if ts.is_null() {
3002                Err(Error::new("Could not get full_history_ts_low".to_owned()))
3003            } else {
3004                let mut vec = vec![0; ts_lowlen];
3005                ptr::copy_nonoverlapping(ts as *mut u8, vec.as_mut_ptr(), ts_lowlen);
3006                ffi::rocksdb_free(ts as *mut c_void);
3007                Ok(vec)
3008            }
3009        }
3010    }
3011
3012    /// Returns the DB identity. This is typically ASCII bytes, but that is not guaranteed.
3013    pub fn get_db_identity(&self) -> Result<Vec<u8>, Error> {
3014        unsafe {
3015            let mut length: usize = 0;
3016            let identity_ptr = ffi::rocksdb_get_db_identity(self.inner.inner(), &mut length);
3017            let identity_vec = raw_data(identity_ptr, length);
3018            ffi::rocksdb_free(identity_ptr as *mut c_void);
3019            // In RocksDB: get_db_identity copies a std::string so it should not fail, but
3020            // the API allows it to be overridden, so it might
3021            identity_vec.ok_or_else(|| Error::new("get_db_identity returned NULL".to_string()))
3022        }
3023    }
3024}
3025
3026impl<I: DBInner> DBCommon<SingleThreaded, I> {
3027    /// Creates column family with given name and options
3028    pub fn create_cf<N: AsRef<str>>(&mut self, name: N, opts: &Options) -> Result<(), Error> {
3029        let inner = self.create_inner_cf_handle(name.as_ref(), opts)?;
3030        self.cfs
3031            .cfs
3032            .insert(name.as_ref().to_string(), ColumnFamily { inner });
3033        Ok(())
3034    }
3035
3036    #[doc = include_str!("db_create_column_family_with_import.md")]
3037    pub fn create_column_family_with_import<N: AsRef<str>>(
3038        &mut self,
3039        options: &Options,
3040        column_family_name: N,
3041        import_options: &ImportColumnFamilyOptions,
3042        metadata: &ExportImportFilesMetaData,
3043    ) -> Result<(), Error> {
3044        let name = column_family_name.as_ref();
3045        let c_name = CString::new(name).map_err(|err| {
3046            Error::new(format!(
3047                "Failed to convert name to CString while importing column family: {err}"
3048            ))
3049        })?;
3050        let inner = unsafe {
3051            ffi_try!(ffi::rocksdb_create_column_family_with_import(
3052                self.inner.inner(),
3053                options.inner,
3054                c_name.as_ptr(),
3055                import_options.inner,
3056                metadata.inner
3057            ))
3058        };
3059        self.cfs
3060            .cfs
3061            .insert(column_family_name.as_ref().into(), ColumnFamily { inner });
3062        Ok(())
3063    }
3064
3065    /// Drops the column family with the given name
3066    pub fn drop_cf(&mut self, name: &str) -> Result<(), Error> {
3067        match self.cfs.cfs.remove(name) {
3068            Some(cf) => self.drop_column_family(cf.inner, cf),
3069            _ => Err(Error::new(format!("Invalid column family: {name}"))),
3070        }
3071    }
3072
3073    /// Returns the underlying column family handle
3074    pub fn cf_handle(&self, name: &str) -> Option<&ColumnFamily> {
3075        self.cfs.cfs.get(name)
3076    }
3077
3078    /// Returns the list of column families currently open.
3079    ///
3080    /// The order of names is unspecified and may vary between calls.
3081    pub fn cf_names(&self) -> Vec<String> {
3082        self.cfs.cfs.keys().cloned().collect()
3083    }
3084}
3085
3086impl<I: DBInner> DBCommon<MultiThreaded, I> {
3087    /// Creates column family with given name and options
3088    pub fn create_cf<N: AsRef<str>>(&self, name: N, opts: &Options) -> Result<(), Error> {
3089        // Note that we acquire the cfs lock before inserting: otherwise we might race
3090        // another caller who observed the handle as missing.
3091        let mut cfs = self.cfs.cfs.write();
3092        let inner = self.create_inner_cf_handle(name.as_ref(), opts)?;
3093        cfs.insert(
3094            name.as_ref().to_string(),
3095            Arc::new(UnboundColumnFamily { inner }),
3096        );
3097        Ok(())
3098    }
3099
3100    #[doc = include_str!("db_create_column_family_with_import.md")]
3101    pub fn create_column_family_with_import<N: AsRef<str>>(
3102        &self,
3103        options: &Options,
3104        column_family_name: N,
3105        import_options: &ImportColumnFamilyOptions,
3106        metadata: &ExportImportFilesMetaData,
3107    ) -> Result<(), Error> {
3108        // Acquire CF lock upfront, before creating the CF, to avoid a race with concurrent creators
3109        let mut cfs = self.cfs.cfs.write();
3110        let name = column_family_name.as_ref();
3111        let c_name = CString::new(name).map_err(|err| {
3112            Error::new(format!(
3113                "Failed to convert name to CString while importing column family: {err}"
3114            ))
3115        })?;
3116        let inner = unsafe {
3117            ffi_try!(ffi::rocksdb_create_column_family_with_import(
3118                self.inner.inner(),
3119                options.inner,
3120                c_name.as_ptr(),
3121                import_options.inner,
3122                metadata.inner
3123            ))
3124        };
3125        cfs.insert(
3126            column_family_name.as_ref().to_string(),
3127            Arc::new(UnboundColumnFamily { inner }),
3128        );
3129        Ok(())
3130    }
3131
3132    /// Drops the column family with the given name by internally locking the inner column
3133    /// family map. This avoids needing `&mut self` reference
3134    pub fn drop_cf(&self, name: &str) -> Result<(), Error> {
3135        match self.cfs.cfs.write().remove(name) {
3136            Some(cf) => self.drop_column_family(cf.inner, cf),
3137            _ => Err(Error::new(format!("Invalid column family: {name}"))),
3138        }
3139    }
3140
3141    /// Returns the underlying column family handle
3142    pub fn cf_handle(&'_ self, name: &str) -> Option<Arc<BoundColumnFamily<'_>>> {
3143        self.cfs
3144            .cfs
3145            .read()
3146            .get(name)
3147            .cloned()
3148            .map(UnboundColumnFamily::bound_column_family)
3149    }
3150
3151    /// Returns the list of column families currently open.
3152    ///
3153    /// The order of names is unspecified and may vary between calls.
3154    pub fn cf_names(&self) -> Vec<String> {
3155        self.cfs.cfs.read().keys().cloned().collect()
3156    }
3157}
3158
3159impl<T: ThreadMode, I: DBInner> Drop for DBCommon<T, I> {
3160    fn drop(&mut self) {
3161        self.cfs.drop_all_cfs_internal();
3162    }
3163}
3164
3165impl<T: ThreadMode, I: DBInner> fmt::Debug for DBCommon<T, I> {
3166    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3167        write!(f, "RocksDB {{ path: {} }}", self.path().display())
3168    }
3169}
3170
3171/// The metadata that describes a column family.
3172#[derive(Debug, Clone)]
3173pub struct ColumnFamilyMetaData {
3174    // The size of this column family in bytes, which is equal to the sum of
3175    // the file size of its "levels".
3176    pub size: u64,
3177    // The name of the column family.
3178    pub name: String,
3179    // The number of files in this column family.
3180    pub file_count: usize,
3181}
3182
3183/// The metadata that describes a SST file
3184#[derive(Debug, Clone)]
3185pub struct LiveFile {
3186    /// Name of the column family the file belongs to
3187    pub column_family_name: String,
3188    /// Name of the file
3189    pub name: String,
3190    /// The directory containing the file, without a trailing '/'. This could be
3191    /// a DB path, wal_dir, etc.
3192    pub directory: String,
3193    /// Size of the file
3194    pub size: usize,
3195    /// Level at which this file resides
3196    pub level: i32,
3197    /// Smallest user defined key in the file
3198    pub start_key: Option<Vec<u8>>,
3199    /// Largest user defined key in the file
3200    pub end_key: Option<Vec<u8>>,
3201    pub smallest_seqno: u64,
3202    pub largest_seqno: u64,
3203    /// Number of entries/alive keys in the file
3204    pub num_entries: u64,
3205    /// Number of deletions/tomb key(s) in the file
3206    pub num_deletions: u64,
3207}
3208
3209impl LiveFile {
3210    /// Create a `Vec<LiveFile>` from a `rocksdb_livefiles_t` pointer
3211    pub(crate) fn from_rocksdb_livefiles_ptr(
3212        files: *const ffi::rocksdb_livefiles_t,
3213    ) -> Vec<LiveFile> {
3214        unsafe {
3215            let n = ffi::rocksdb_livefiles_count(files);
3216
3217            let mut livefiles = Vec::with_capacity(n as usize);
3218            let mut key_size: usize = 0;
3219
3220            for i in 0..n {
3221                let column_family_name =
3222                    from_cstr(ffi::rocksdb_livefiles_column_family_name(files, i));
3223                let name = from_cstr(ffi::rocksdb_livefiles_name(files, i));
3224                let directory = from_cstr(ffi::rocksdb_livefiles_directory(files, i));
3225                let size = ffi::rocksdb_livefiles_size(files, i);
3226                let level = ffi::rocksdb_livefiles_level(files, i);
3227
3228                // get smallest key inside file
3229                let smallest_key = ffi::rocksdb_livefiles_smallestkey(files, i, &mut key_size);
3230                let smallest_key = raw_data(smallest_key, key_size);
3231
3232                // get largest key inside file
3233                let largest_key = ffi::rocksdb_livefiles_largestkey(files, i, &mut key_size);
3234                let largest_key = raw_data(largest_key, key_size);
3235
3236                livefiles.push(LiveFile {
3237                    column_family_name,
3238                    name,
3239                    directory,
3240                    size,
3241                    level,
3242                    start_key: smallest_key,
3243                    end_key: largest_key,
3244                    largest_seqno: ffi::rocksdb_livefiles_largest_seqno(files, i),
3245                    smallest_seqno: ffi::rocksdb_livefiles_smallest_seqno(files, i),
3246                    num_entries: ffi::rocksdb_livefiles_entries(files, i),
3247                    num_deletions: ffi::rocksdb_livefiles_deletions(files, i),
3248                });
3249            }
3250
3251            livefiles
3252        }
3253    }
3254}
3255
3256struct LiveFileGuard(*mut rocksdb_livefile_t);
3257
3258impl LiveFileGuard {
3259    fn into_raw(mut self) -> *mut rocksdb_livefile_t {
3260        let ptr = self.0;
3261        self.0 = ptr::null_mut();
3262        ptr
3263    }
3264}
3265
3266impl Drop for LiveFileGuard {
3267    fn drop(&mut self) {
3268        if !self.0.is_null() {
3269            unsafe {
3270                rocksdb_livefile_destroy(self.0);
3271            }
3272        }
3273    }
3274}
3275
3276struct LiveFilesGuard(*mut rocksdb_livefiles_t);
3277
3278impl LiveFilesGuard {
3279    fn into_raw(mut self) -> *mut rocksdb_livefiles_t {
3280        let ptr = self.0;
3281        self.0 = ptr::null_mut();
3282        ptr
3283    }
3284}
3285
3286impl Drop for LiveFilesGuard {
3287    fn drop(&mut self) {
3288        if !self.0.is_null() {
3289            unsafe {
3290                rocksdb_livefiles_destroy(self.0);
3291            }
3292        }
3293    }
3294}
3295
3296/// Metadata returned as output from [`Checkpoint::export_column_family`][export_column_family] and
3297/// used as input to [`DB::create_column_family_with_import`].
3298///
3299/// [export_column_family]: crate::checkpoint::Checkpoint::export_column_family
3300#[derive(Debug)]
3301pub struct ExportImportFilesMetaData {
3302    pub(crate) inner: *mut ffi::rocksdb_export_import_files_metadata_t,
3303}
3304
3305impl ExportImportFilesMetaData {
3306    pub fn get_db_comparator_name(&self) -> String {
3307        unsafe {
3308            let c_name =
3309                ffi::rocksdb_export_import_files_metadata_get_db_comparator_name(self.inner);
3310            let name = from_cstr(c_name);
3311            ffi::rocksdb_free(c_name as *mut c_void);
3312            name
3313        }
3314    }
3315
3316    pub fn set_db_comparator_name(&mut self, name: &str) {
3317        let c_name = CString::new(name.as_bytes()).unwrap();
3318        unsafe {
3319            ffi::rocksdb_export_import_files_metadata_set_db_comparator_name(
3320                self.inner,
3321                c_name.as_ptr(),
3322            );
3323        };
3324    }
3325
3326    pub fn get_files(&self) -> Vec<LiveFile> {
3327        unsafe {
3328            let livefiles_ptr = ffi::rocksdb_export_import_files_metadata_get_files(self.inner);
3329            let files = LiveFile::from_rocksdb_livefiles_ptr(livefiles_ptr);
3330            ffi::rocksdb_livefiles_destroy(livefiles_ptr);
3331            files
3332        }
3333    }
3334
3335    pub fn set_files(&mut self, files: &[LiveFile]) -> Result<(), Error> {
3336        // Use a non-null empty pointer for zero-length keys
3337        static EMPTY: [u8; 0] = [];
3338        let empty_ptr = EMPTY.as_ptr() as *const libc::c_char;
3339
3340        unsafe {
3341            let live_files = LiveFilesGuard(ffi::rocksdb_livefiles_create());
3342
3343            for file in files {
3344                let live_file = LiveFileGuard(ffi::rocksdb_livefile_create());
3345                ffi::rocksdb_livefile_set_level(live_file.0, file.level);
3346
3347                // SAFETY: C strings are copied inside the FFI layer so do not need to be kept alive
3348                let c_cf_name = CString::new(file.column_family_name.as_str()).map_err(|err| {
3349                    Error::new(format!("Unable to convert column family to CString: {err}"))
3350                })?;
3351                ffi::rocksdb_livefile_set_column_family_name(live_file.0, c_cf_name.as_ptr());
3352
3353                let c_name = CString::new(file.name.as_str()).map_err(|err| {
3354                    Error::new(format!("Unable to convert file name to CString: {err}"))
3355                })?;
3356                ffi::rocksdb_livefile_set_name(live_file.0, c_name.as_ptr());
3357
3358                let c_directory = CString::new(file.directory.as_str()).map_err(|err| {
3359                    Error::new(format!("Unable to convert directory to CString: {err}"))
3360                })?;
3361                ffi::rocksdb_livefile_set_directory(live_file.0, c_directory.as_ptr());
3362
3363                ffi::rocksdb_livefile_set_size(live_file.0, file.size);
3364
3365                let (start_key_ptr, start_key_len) = match &file.start_key {
3366                    None => (empty_ptr, 0),
3367                    Some(key) => (key.as_ptr() as *const libc::c_char, key.len()),
3368                };
3369                ffi::rocksdb_livefile_set_smallest_key(live_file.0, start_key_ptr, start_key_len);
3370
3371                let (largest_key_ptr, largest_key_len) = match &file.end_key {
3372                    None => (empty_ptr, 0),
3373                    Some(key) => (key.as_ptr() as *const libc::c_char, key.len()),
3374                };
3375                ffi::rocksdb_livefile_set_largest_key(
3376                    live_file.0,
3377                    largest_key_ptr,
3378                    largest_key_len,
3379                );
3380                ffi::rocksdb_livefile_set_smallest_seqno(live_file.0, file.smallest_seqno);
3381                ffi::rocksdb_livefile_set_largest_seqno(live_file.0, file.largest_seqno);
3382                ffi::rocksdb_livefile_set_num_entries(live_file.0, file.num_entries);
3383                ffi::rocksdb_livefile_set_num_deletions(live_file.0, file.num_deletions);
3384
3385                // moves ownership of live_files into live_file
3386                ffi::rocksdb_livefiles_add(live_files.0, live_file.into_raw());
3387            }
3388
3389            // moves ownership of live_files into inner
3390            ffi::rocksdb_export_import_files_metadata_set_files(self.inner, live_files.into_raw());
3391            Ok(())
3392        }
3393    }
3394}
3395
3396impl Default for ExportImportFilesMetaData {
3397    fn default() -> Self {
3398        let inner = unsafe { ffi::rocksdb_export_import_files_metadata_create() };
3399        assert!(
3400            !inner.is_null(),
3401            "Could not create rocksdb_export_import_files_metadata_t"
3402        );
3403
3404        Self { inner }
3405    }
3406}
3407
3408impl Drop for ExportImportFilesMetaData {
3409    fn drop(&mut self) {
3410        unsafe {
3411            ffi::rocksdb_export_import_files_metadata_destroy(self.inner);
3412        }
3413    }
3414}
3415
3416unsafe impl Send for ExportImportFilesMetaData {}
3417unsafe impl Sync for ExportImportFilesMetaData {}
3418
3419fn convert_options(opts: &[(&str, &str)]) -> Result<Vec<(CString, CString)>, Error> {
3420    opts.iter()
3421        .map(|(name, value)| {
3422            let cname = match CString::new(name.as_bytes()) {
3423                Ok(cname) => cname,
3424                Err(e) => return Err(Error::new(format!("Invalid option name `{e}`"))),
3425            };
3426            let cvalue = match CString::new(value.as_bytes()) {
3427                Ok(cvalue) => cvalue,
3428                Err(e) => return Err(Error::new(format!("Invalid option value: `{e}`"))),
3429            };
3430            Ok((cname, cvalue))
3431        })
3432        .collect()
3433}
3434
3435pub(crate) fn convert_values(
3436    values: Vec<*mut c_char>,
3437    values_sizes: Vec<usize>,
3438    errors: Vec<*mut c_char>,
3439) -> Vec<Result<Option<Vec<u8>>, Error>> {
3440    values
3441        .into_iter()
3442        .zip(values_sizes)
3443        .zip(errors)
3444        .map(|((v, s), e)| {
3445            if e.is_null() {
3446                let value = unsafe { crate::ffi_util::raw_data(v, s) };
3447                unsafe {
3448                    ffi::rocksdb_free(v as *mut c_void);
3449                }
3450                Ok(value)
3451            } else {
3452                Err(Error::new(crate::ffi_util::error_message(e)))
3453            }
3454        })
3455        .collect()
3456}