Skip to main content

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    ColumnFamily, ColumnFamilyDescriptor, CompactOptions, DBIteratorWithThreadMode,
34    DBPinnableBatch, DBPinnableSlice, DBRawIteratorWithThreadMode, DBWALIterator,
35    DEFAULT_COLUMN_FAMILY_NAME, Direction, Error, FlushOptions, IngestExternalFileOptions,
36    IteratorMode, Options, ReadOptions, SnapshotWithThreadMode, WaitForCompactOptions, WriteBatch,
37    WriteBatchWithIndex, WriteOptions,
38    column_family::{AsColumnFamilyRef, BoundColumnFamily, UnboundColumnFamily},
39    db_options::{ImportColumnFamilyOptions, OptionsMustOutliveDB},
40    ffi,
41    ffi_util::{
42        CStrLike, convert_rocksdb_error, from_cstr_and_free, from_cstr_without_free,
43        opt_bytes_to_ptr, raw_data, to_cpath,
44    },
45};
46use rust_librocksdb_sys::{
47    rocksdb_livefile_destroy, rocksdb_livefile_t, rocksdb_livefiles_destroy, rocksdb_livefiles_t,
48};
49
50use libc::{self, c_char, c_int, c_uchar, c_void, size_t};
51use parking_lot::RwLock;
52
53// Default options are kept per-thread to avoid re-allocating on every call while
54// also preventing cross-thread sharing. Some RocksDB option wrappers hold
55// pointers into internal buffers and are not safe to share across threads.
56// Using thread_local allows cheap reuse in the common "default options" path
57// without synchronization overhead. Callers who need non-defaults must pass
58// explicit options.
59thread_local! { static DEFAULT_READ_OPTS: ReadOptions = ReadOptions::default(); }
60thread_local! { static DEFAULT_WRITE_OPTS: WriteOptions = WriteOptions::default(); }
61thread_local! { static DEFAULT_FLUSH_OPTS: FlushOptions = FlushOptions::default(); }
62// Thread-local ReadOptions for hot prefix probes; preconfigured for prefix scans.
63thread_local! { static PREFIX_READ_OPTS: RefCell<ReadOptions> = RefCell::new({ let mut o = ReadOptions::default(); o.set_prefix_same_as_start(true); o }); }
64
65/// Runs `f` with `ReadOptions` bounded to `prefix` and `prefix_same_as_start`
66/// enabled, reusing a thread-local instance when it is available.
67///
68/// The borrow is held across an FFI call that can synchronously re-enter Rust
69/// through a user-supplied comparator or merge operator. If that callback probes
70/// another prefix on the same thread, a plain `borrow_mut` would panic with
71/// `BorrowMutError` — and because the callback runs inside an `extern "C"` frame
72/// the panic aborts the process. Falling back to fresh options on contention
73/// costs an allocation in that rare re-entrant case and keeps the fast path
74/// allocation-free.
75fn with_prefix_read_opts<R>(prefix: &[u8], f: impl FnOnce(&ReadOptions) -> R) -> R {
76    PREFIX_READ_OPTS.with(|rc| {
77        if let Ok(mut opts) = rc.try_borrow_mut() {
78            opts.set_prefix_range_in_place(prefix);
79            f(&opts)
80        } else {
81            let mut opts = ReadOptions::default();
82            opts.set_prefix_same_as_start(true);
83            opts.set_prefix_range_in_place(prefix);
84            f(&opts)
85        }
86    })
87}
88
89/// A range of keys, `start_key` is included, but not `end_key`.
90///
91/// You should make sure `end_key` is not less than `start_key`.
92pub struct Range<'a> {
93    start_key: &'a [u8],
94    end_key: &'a [u8],
95}
96
97impl<'a> Range<'a> {
98    pub fn new(start_key: &'a [u8], end_key: &'a [u8]) -> Range<'a> {
99        Range { start_key, end_key }
100    }
101}
102
103/// Result of a [`get_into_buffer`](DBCommon::get_into_buffer) operation.
104///
105/// This enum represents the outcome of attempting to read a value directly
106/// into a caller-provided buffer, avoiding memory allocation. This is the most
107/// efficient way to read values when you have a pre-allocated buffer available.
108///
109/// # Performance
110///
111/// Using `get_into_buffer` with a reusable buffer can significantly reduce
112/// allocation overhead in hot paths compared to [`get`](DBCommon::get) or even
113/// [`get_pinned`](DBCommon::get_pinned):
114///
115/// - [`get`](DBCommon::get): Allocates a new `Vec<u8>` for each call
116/// - [`get_pinned`](DBCommon::get_pinned): Pins memory in RocksDB's block cache
117/// - `get_into_buffer`: Zero allocation when buffer is large enough
118///
119/// # Example
120///
121/// ```
122/// use rust_rocksdb::{DB, GetIntoBufferResult};
123///
124/// # let tempdir = tempfile::Builder::new().prefix("ex").tempdir().unwrap();
125/// let db = DB::open_default(tempdir.path()).unwrap();
126/// db.put(b"key", b"value").unwrap();
127///
128/// let mut buffer = [0u8; 1024];
129/// match db.get_into_buffer(b"key", &mut buffer).unwrap() {
130///     GetIntoBufferResult::Found(len) => {
131///         println!("Value: {:?}", &buffer[..len]);
132///     }
133///     GetIntoBufferResult::NotFound => {
134///         println!("Key not found");
135///     }
136///     GetIntoBufferResult::BufferTooSmall(needed) => {
137///         println!("Need a buffer of at least {} bytes", needed);
138///     }
139/// }
140/// ```
141#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
142pub enum GetIntoBufferResult {
143    /// The key was not found in the database.
144    NotFound,
145    /// The value was found and successfully copied into the buffer.
146    /// The `usize` contains the actual size of the value (number of bytes written).
147    Found(usize),
148    /// The value was found but the provided buffer was too small to hold it.
149    /// The `usize` contains the actual size of the value, allowing the caller
150    /// to allocate a larger buffer and retry.
151    ///
152    /// Note: When this variant is returned, no data is written to the buffer.
153    BufferTooSmall(usize),
154}
155
156impl GetIntoBufferResult {
157    /// Returns `true` if the key was found (regardless of buffer size).
158    #[inline]
159    pub fn is_found(&self) -> bool {
160        matches!(self, Self::Found(_) | Self::BufferTooSmall(_))
161    }
162
163    /// Returns `true` if the key was not found.
164    #[inline]
165    pub fn is_not_found(&self) -> bool {
166        matches!(self, Self::NotFound)
167    }
168
169    /// Returns the value size if the key was found, `None` otherwise.
170    #[inline]
171    pub fn value_size(&self) -> Option<usize> {
172        match self {
173            Self::Found(size) | Self::BufferTooSmall(size) => Some(*size),
174            Self::NotFound => None,
175        }
176    }
177}
178
179/// A reusable prefix probe that avoids per-call iterator creation/destruction.
180///
181/// Use this when performing many prefix existence checks in a tight loop.
182pub struct PrefixProber<'a, D: DBAccess> {
183    raw: DBRawIteratorWithThreadMode<'a, D>,
184}
185
186impl<D: DBAccess> PrefixProber<'_, D> {
187    /// Returns true if any key exists with the given prefix.
188    /// This performs a seek to the prefix and checks the current key.
189    pub fn exists(&mut self, prefix: &[u8]) -> Result<bool, Error> {
190        self.raw.seek(prefix);
191        if self.raw.valid()
192            && let Some(k) = self.raw.key()
193        {
194            return Ok(k.starts_with(prefix));
195        }
196        self.raw.status()?;
197        Ok(false)
198    }
199}
200
201/// Marker trait to specify single or multi threaded column family alternations for
202/// [`DBWithThreadMode<T>`]
203///
204/// This arrangement makes differences in self mutability and return type in
205/// some of `DBWithThreadMode` methods.
206///
207/// While being a marker trait to be generic over `DBWithThreadMode`, this trait
208/// also has a minimum set of not-encapsulated internal methods between
209/// [`SingleThreaded`] and [`MultiThreaded`].  These methods aren't expected to be
210/// called and defined externally.
211pub trait ThreadMode {
212    /// Internal implementation for storing column family handles
213    fn new_cf_map_internal(
214        cf_map: BTreeMap<String, *mut ffi::rocksdb_column_family_handle_t>,
215    ) -> Self;
216    /// Internal implementation for dropping column family handles
217    fn drop_all_cfs_internal(&mut self);
218}
219
220/// Actual marker type for the marker trait `ThreadMode`, which holds
221/// a collection of column families without synchronization primitive, providing
222/// no overhead for the single-threaded column family alternations. The other
223/// mode is [`MultiThreaded`].
224///
225/// See [`DB`] for more details, including performance implications for each mode
226pub struct SingleThreaded {
227    pub(crate) cfs: HashMap<String, ColumnFamily>,
228}
229
230/// Actual marker type for the marker trait `ThreadMode`, which holds
231/// a collection of column families wrapped in a RwLock to be mutated
232/// concurrently. The other mode is [`SingleThreaded`].
233///
234/// See [`DB`] for more details, including performance implications for each mode
235pub struct MultiThreaded {
236    pub(crate) cfs: RwLock<HashMap<String, Arc<UnboundColumnFamily>>>,
237}
238
239impl ThreadMode for SingleThreaded {
240    fn new_cf_map_internal(
241        cfs: BTreeMap<String, *mut ffi::rocksdb_column_family_handle_t>,
242    ) -> Self {
243        Self {
244            cfs: cfs
245                .into_iter()
246                .map(|(n, c)| (n, ColumnFamily { inner: c }))
247                .collect(),
248        }
249    }
250
251    fn drop_all_cfs_internal(&mut self) {
252        // Cause all ColumnFamily objects to be Drop::drop()-ed.
253        self.cfs.clear();
254    }
255}
256
257impl ThreadMode for MultiThreaded {
258    fn new_cf_map_internal(
259        cfs: BTreeMap<String, *mut ffi::rocksdb_column_family_handle_t>,
260    ) -> Self {
261        Self {
262            cfs: RwLock::new(
263                cfs.into_iter()
264                    .map(|(n, c)| (n, Arc::new(UnboundColumnFamily { inner: c })))
265                    .collect(),
266            ),
267        }
268    }
269
270    fn drop_all_cfs_internal(&mut self) {
271        // Cause all UnboundColumnFamily objects to be Drop::drop()-ed.
272        self.cfs.write().clear();
273    }
274}
275
276/// Get underlying `rocksdb_t`.
277pub trait DBInner {
278    fn inner(&self) -> *mut ffi::rocksdb_t;
279}
280
281/// A helper type to implement some common methods for [`DBWithThreadMode`]
282/// and [`OptimisticTransactionDB`].
283///
284/// [`OptimisticTransactionDB`]: crate::OptimisticTransactionDB
285///
286/// When using [`SingleThreaded`] mode, `create_cf` requires `&mut self`,
287/// preventing multiple immutable references from calling it concurrently:
288///
289/// ```compile_fail,E0596
290/// use rust_rocksdb::{DBWithThreadMode, Options, SingleThreaded};
291///
292/// let db = DBWithThreadMode::<SingleThreaded>::open_default("/path/to/dummy").unwrap();
293/// let db_ref1 = &db;
294/// let db_ref2 = &db;
295/// let opts = Options::default();
296/// db_ref1.create_cf("cf1", &opts).unwrap();
297/// db_ref2.create_cf("cf2", &opts).unwrap();
298/// ```
299///
300/// [`SingleThreaded`]: crate::SingleThreaded
301pub struct DBCommon<T: ThreadMode, D: DBInner> {
302    pub(crate) inner: D,
303    cfs: T, // Column families are held differently depending on thread mode
304    path: PathBuf,
305    _outlive: Vec<OptionsMustOutliveDB>,
306}
307
308/// Minimal set of DB-related methods, intended to be generic over
309/// `DBWithThreadMode<T>`. Mainly used internally
310pub trait DBAccess {
311    unsafe fn create_snapshot(&self) -> *const ffi::rocksdb_snapshot_t;
312
313    unsafe fn release_snapshot(&self, snapshot: *const ffi::rocksdb_snapshot_t);
314
315    unsafe fn create_iterator(&self, readopts: &ReadOptions) -> *mut ffi::rocksdb_iterator_t;
316
317    unsafe fn create_iterator_cf(
318        &self,
319        cf_handle: *mut ffi::rocksdb_column_family_handle_t,
320        readopts: &ReadOptions,
321    ) -> *mut ffi::rocksdb_iterator_t;
322
323    fn get_opt<K: AsRef<[u8]>>(
324        &self,
325        key: K,
326        readopts: &ReadOptions,
327    ) -> Result<Option<Vec<u8>>, Error>;
328
329    fn get_cf_opt<K: AsRef<[u8]>>(
330        &self,
331        cf: &impl AsColumnFamilyRef,
332        key: K,
333        readopts: &ReadOptions,
334    ) -> Result<Option<Vec<u8>>, Error>;
335
336    fn get_pinned_opt<K: AsRef<[u8]>>(
337        &'_ self,
338        key: K,
339        readopts: &ReadOptions,
340    ) -> Result<Option<DBPinnableSlice<'_>>, Error>;
341
342    fn get_pinned_cf_opt<K: AsRef<[u8]>>(
343        &'_ self,
344        cf: &impl AsColumnFamilyRef,
345        key: K,
346        readopts: &ReadOptions,
347    ) -> Result<Option<DBPinnableSlice<'_>>, Error>;
348
349    fn multi_get_opt<K, I>(
350        &self,
351        keys: I,
352        readopts: &ReadOptions,
353    ) -> Vec<Result<Option<Vec<u8>>, Error>>
354    where
355        K: AsRef<[u8]>,
356        I: IntoIterator<Item = K>;
357
358    fn multi_get_cf_opt<'b, K, I, W>(
359        &self,
360        keys_cf: I,
361        readopts: &ReadOptions,
362    ) -> Vec<Result<Option<Vec<u8>>, Error>>
363    where
364        K: AsRef<[u8]>,
365        I: IntoIterator<Item = (&'b W, K)>,
366        W: AsColumnFamilyRef + 'b;
367}
368
369impl<T: ThreadMode, D: DBInner> DBAccess for DBCommon<T, D> {
370    unsafe fn create_snapshot(&self) -> *const ffi::rocksdb_snapshot_t {
371        unsafe { ffi::rocksdb_create_snapshot(self.inner.inner()) }
372    }
373
374    unsafe fn release_snapshot(&self, snapshot: *const ffi::rocksdb_snapshot_t) {
375        unsafe {
376            ffi::rocksdb_release_snapshot(self.inner.inner(), snapshot);
377        }
378    }
379
380    unsafe fn create_iterator(&self, readopts: &ReadOptions) -> *mut ffi::rocksdb_iterator_t {
381        unsafe { ffi::rocksdb_create_iterator(self.inner.inner(), readopts.inner) }
382    }
383
384    unsafe fn create_iterator_cf(
385        &self,
386        cf_handle: *mut ffi::rocksdb_column_family_handle_t,
387        readopts: &ReadOptions,
388    ) -> *mut ffi::rocksdb_iterator_t {
389        unsafe { ffi::rocksdb_create_iterator_cf(self.inner.inner(), readopts.inner, cf_handle) }
390    }
391
392    fn get_opt<K: AsRef<[u8]>>(
393        &self,
394        key: K,
395        readopts: &ReadOptions,
396    ) -> Result<Option<Vec<u8>>, Error> {
397        self.get_opt(key, readopts)
398    }
399
400    fn get_cf_opt<K: AsRef<[u8]>>(
401        &self,
402        cf: &impl AsColumnFamilyRef,
403        key: K,
404        readopts: &ReadOptions,
405    ) -> Result<Option<Vec<u8>>, Error> {
406        self.get_cf_opt(cf, key, readopts)
407    }
408
409    fn get_pinned_opt<K: AsRef<[u8]>>(
410        &'_ self,
411        key: K,
412        readopts: &ReadOptions,
413    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
414        self.get_pinned_opt(key, readopts)
415    }
416
417    fn get_pinned_cf_opt<K: AsRef<[u8]>>(
418        &'_ self,
419        cf: &impl AsColumnFamilyRef,
420        key: K,
421        readopts: &ReadOptions,
422    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
423        self.get_pinned_cf_opt(cf, key, readopts)
424    }
425
426    fn multi_get_opt<K, Iter>(
427        &self,
428        keys: Iter,
429        readopts: &ReadOptions,
430    ) -> Vec<Result<Option<Vec<u8>>, Error>>
431    where
432        K: AsRef<[u8]>,
433        Iter: IntoIterator<Item = K>,
434    {
435        self.multi_get_opt(keys, readopts)
436    }
437
438    fn multi_get_cf_opt<'b, K, Iter, W>(
439        &self,
440        keys_cf: Iter,
441        readopts: &ReadOptions,
442    ) -> Vec<Result<Option<Vec<u8>>, Error>>
443    where
444        K: AsRef<[u8]>,
445        Iter: IntoIterator<Item = (&'b W, K)>,
446        W: AsColumnFamilyRef + 'b,
447    {
448        self.multi_get_cf_opt(keys_cf, readopts)
449    }
450}
451
452pub struct DBWithThreadModeInner {
453    inner: *mut ffi::rocksdb_t,
454}
455
456struct OwnedColumnFamilyHandle {
457    inner: *mut ffi::rocksdb_column_family_handle_t,
458}
459
460struct PinnedMultiGetOutput {
461    values: Vec<*mut ffi::rocksdb_pinnableslice_t>,
462    errors: Vec<*mut c_char>,
463}
464
465/// The result of one `rocksdb_create_iterators` call: the iterator handles
466/// plus the single `ReadOptions` they were all created from, which each
467/// iterator must keep alive.
468struct CreatedIterators {
469    readopts: Arc<ReadOptions>,
470    handles: Vec<*mut ffi::rocksdb_iterator_t>,
471}
472
473impl OwnedColumnFamilyHandle {
474    fn default_for(db: *mut ffi::rocksdb_t) -> Self {
475        Self {
476            inner: unsafe { ffi::rocksdb_get_default_column_family_handle(db) },
477        }
478    }
479}
480
481impl Drop for OwnedColumnFamilyHandle {
482    fn drop(&mut self) {
483        unsafe {
484            ffi::rocksdb_column_family_handle_destroy(self.inner);
485        }
486    }
487}
488
489impl DBInner for DBWithThreadModeInner {
490    #[inline]
491    fn inner(&self) -> *mut ffi::rocksdb_t {
492        self.inner
493    }
494}
495
496impl Drop for DBWithThreadModeInner {
497    fn drop(&mut self) {
498        unsafe {
499            ffi::rocksdb_close(self.inner);
500        }
501    }
502}
503
504/// A type alias to RocksDB database.
505///
506/// See crate level documentation for a simple usage example.
507/// See [`DBCommon`] for full list of methods.
508pub type DBWithThreadMode<T> = DBCommon<T, DBWithThreadModeInner>;
509
510/// A type alias to DB instance type with the single-threaded column family
511/// creations/deletions
512///
513/// # Compatibility and multi-threaded mode
514///
515/// Previously, [`DB`] was defined as a direct `struct`. Now, it's type-aliased for
516/// compatibility. Use `DBCommon<MultiThreaded>` for multi-threaded
517/// column family alternations.
518///
519/// # Limited performance implication for single-threaded mode
520///
521/// Even with [`SingleThreaded`], almost all of RocksDB operations is
522/// multi-threaded unless the underlying RocksDB instance is
523/// specifically configured otherwise. `SingleThreaded` only forces
524/// serialization of column family alternations by requiring `&mut self` of DB
525/// instance due to its wrapper implementation details.
526///
527/// # Multi-threaded mode
528///
529/// [`MultiThreaded`] can be appropriate for the situation of multi-threaded
530/// workload including multi-threaded column family alternations, costing the
531/// RwLock overhead inside `DB`.
532#[cfg(not(feature = "multi-threaded-cf"))]
533pub type DB = DBWithThreadMode<SingleThreaded>;
534
535#[cfg(feature = "multi-threaded-cf")]
536pub type DB = DBWithThreadMode<MultiThreaded>;
537
538// Safety note: auto-implementing Send on most db-related types is prevented by the inner FFI
539// pointer. In most cases, however, this pointer is Send-safe because it is never aliased and
540// rocksdb internally does not rely on thread-local information for its user-exposed types.
541unsafe impl<T: ThreadMode + Send, I: DBInner> Send for DBCommon<T, I> {}
542
543// Sync is similarly safe for many types because they do not expose interior mutability, and their
544// use within the rocksdb library is generally behind a const reference
545unsafe impl<T: ThreadMode, I: DBInner> Sync for DBCommon<T, I> {}
546
547// Specifies whether open DB for read only.
548enum AccessType<'a> {
549    ReadWrite,
550    ReadOnly { error_if_log_file_exist: bool },
551    Secondary { secondary_path: &'a Path },
552    WithTTL { ttl: Duration },
553}
554
555/// Methods of `DBWithThreadMode`.
556impl<T: ThreadMode> DBWithThreadMode<T> {
557    /// Opens a database with default options.
558    pub fn open_default<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
559        let mut opts = Options::default();
560        opts.create_if_missing(true);
561        Self::open(&opts, path)
562    }
563
564    /// Opens the database with the specified options.
565    pub fn open<P: AsRef<Path>>(opts: &Options, path: P) -> Result<Self, Error> {
566        Self::open_cf(opts, path, None::<&str>)
567    }
568
569    /// Opens the database for read only with the specified options.
570    pub fn open_for_read_only<P: AsRef<Path>>(
571        opts: &Options,
572        path: P,
573        error_if_log_file_exist: bool,
574    ) -> Result<Self, Error> {
575        Self::open_cf_for_read_only(opts, path, None::<&str>, error_if_log_file_exist)
576    }
577
578    /// Opens the database as a secondary.
579    pub fn open_as_secondary<P: AsRef<Path>>(
580        opts: &Options,
581        primary_path: P,
582        secondary_path: P,
583    ) -> Result<Self, Error> {
584        Self::open_cf_as_secondary(opts, primary_path, secondary_path, None::<&str>)
585    }
586
587    /// Opens the database with a Time to Live compaction filter.
588    ///
589    /// This applies the given `ttl` to all column families created without an explicit TTL.
590    /// See [`DB::open_cf_descriptors_with_ttl`] for more control over individual column family TTLs.
591    ///
592    /// RocksDB stores the TTL as a 32-bit second count, so a `ttl` longer than
593    /// `i32::MAX` seconds (about 68 years) is clamped to that maximum rather
594    /// than wrapping.
595    pub fn open_with_ttl<P: AsRef<Path>>(
596        opts: &Options,
597        path: P,
598        ttl: Duration,
599    ) -> Result<Self, Error> {
600        Self::open_cf_descriptors_with_ttl(opts, path, std::iter::empty(), ttl)
601    }
602
603    /// Opens the database with a Time to Live compaction filter and column family names.
604    ///
605    /// Column families opened using this function will be created with default `Options`.
606    pub fn open_cf_with_ttl<P, I, N>(
607        opts: &Options,
608        path: P,
609        cfs: I,
610        ttl: Duration,
611    ) -> Result<Self, Error>
612    where
613        P: AsRef<Path>,
614        I: IntoIterator<Item = N>,
615        N: AsRef<str>,
616    {
617        let cfs = cfs
618            .into_iter()
619            .map(|name| ColumnFamilyDescriptor::new(name.as_ref(), Options::default()));
620
621        Self::open_cf_descriptors_with_ttl(opts, path, cfs, ttl)
622    }
623
624    /// Opens a database with the given database with a Time to Live compaction filter and
625    /// column family descriptors.
626    ///
627    /// Applies the provided `ttl` as the default TTL for all column families.
628    /// Column families will inherit this TTL by default, unless their descriptor explicitly
629    /// sets a different TTL using [`ColumnFamilyTtl::Duration`] or opts out using [`ColumnFamilyTtl::Disabled`].
630    ///
631    /// *NOTE*: The `default` column family is opened with `Options::default()` unless
632    /// explicitly configured within the `cfs` iterator.
633    /// To customize the `default` column family's options, include a `ColumnFamilyDescriptor`
634    /// with the name "default" in the `cfs` iterator.
635    ///
636    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
637    pub fn open_cf_descriptors_with_ttl<P, I>(
638        opts: &Options,
639        path: P,
640        cfs: I,
641        ttl: Duration,
642    ) -> Result<Self, Error>
643    where
644        P: AsRef<Path>,
645        I: IntoIterator<Item = ColumnFamilyDescriptor>,
646    {
647        Self::open_cf_descriptors_internal(opts, path, cfs, &AccessType::WithTTL { ttl })
648    }
649
650    /// Opens a database with the given database options and column family names.
651    ///
652    /// Column families opened using this function will be created with default `Options`.
653    pub fn open_cf<P, I, N>(opts: &Options, path: P, cfs: I) -> Result<Self, Error>
654    where
655        P: AsRef<Path>,
656        I: IntoIterator<Item = N>,
657        N: AsRef<str>,
658    {
659        let cfs = cfs
660            .into_iter()
661            .map(|name| ColumnFamilyDescriptor::new(name.as_ref(), Options::default()));
662
663        Self::open_cf_descriptors_internal(opts, path, cfs, &AccessType::ReadWrite)
664    }
665
666    /// Opens a database with the given database options and column family names.
667    ///
668    /// Column families opened using given `Options`.
669    pub fn open_cf_with_opts<P, I, N>(opts: &Options, path: P, cfs: I) -> Result<Self, Error>
670    where
671        P: AsRef<Path>,
672        I: IntoIterator<Item = (N, Options)>,
673        N: AsRef<str>,
674    {
675        let cfs = cfs
676            .into_iter()
677            .map(|(name, opts)| ColumnFamilyDescriptor::new(name.as_ref(), opts));
678
679        Self::open_cf_descriptors(opts, path, cfs)
680    }
681
682    /// Opens a database for read only with the given database options and column family names.
683    /// *NOTE*: `default` column family is opened with `Options::default()`.
684    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
685    pub fn open_cf_for_read_only<P, I, N>(
686        opts: &Options,
687        path: P,
688        cfs: I,
689        error_if_log_file_exist: bool,
690    ) -> Result<Self, Error>
691    where
692        P: AsRef<Path>,
693        I: IntoIterator<Item = N>,
694        N: AsRef<str>,
695    {
696        let cfs = cfs
697            .into_iter()
698            .map(|name| ColumnFamilyDescriptor::new(name.as_ref(), Options::default()));
699
700        Self::open_cf_descriptors_internal(
701            opts,
702            path,
703            cfs,
704            &AccessType::ReadOnly {
705                error_if_log_file_exist,
706            },
707        )
708    }
709
710    /// Opens a database for read only with the given database options and column family names.
711    /// *NOTE*: `default` column family is opened with `Options::default()`.
712    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
713    pub fn open_cf_with_opts_for_read_only<P, I, N>(
714        db_opts: &Options,
715        path: P,
716        cfs: I,
717        error_if_log_file_exist: bool,
718    ) -> Result<Self, Error>
719    where
720        P: AsRef<Path>,
721        I: IntoIterator<Item = (N, Options)>,
722        N: AsRef<str>,
723    {
724        let cfs = cfs
725            .into_iter()
726            .map(|(name, cf_opts)| ColumnFamilyDescriptor::new(name.as_ref(), cf_opts));
727
728        Self::open_cf_descriptors_internal(
729            db_opts,
730            path,
731            cfs,
732            &AccessType::ReadOnly {
733                error_if_log_file_exist,
734            },
735        )
736    }
737
738    /// Opens a database for ready only with the given database options and
739    /// column family descriptors.
740    /// *NOTE*: `default` column family is opened with `Options::default()`.
741    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
742    pub fn open_cf_descriptors_read_only<P, I>(
743        opts: &Options,
744        path: P,
745        cfs: I,
746        error_if_log_file_exist: bool,
747    ) -> Result<Self, Error>
748    where
749        P: AsRef<Path>,
750        I: IntoIterator<Item = ColumnFamilyDescriptor>,
751    {
752        Self::open_cf_descriptors_internal(
753            opts,
754            path,
755            cfs,
756            &AccessType::ReadOnly {
757                error_if_log_file_exist,
758            },
759        )
760    }
761
762    /// Opens the database as a secondary with the given database options and column family names.
763    /// *NOTE*: `default` column family is opened with `Options::default()`.
764    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
765    pub fn open_cf_as_secondary<P, I, N>(
766        opts: &Options,
767        primary_path: P,
768        secondary_path: P,
769        cfs: I,
770    ) -> Result<Self, Error>
771    where
772        P: AsRef<Path>,
773        I: IntoIterator<Item = N>,
774        N: AsRef<str>,
775    {
776        let cfs = cfs
777            .into_iter()
778            .map(|name| ColumnFamilyDescriptor::new(name.as_ref(), Options::default()));
779
780        Self::open_cf_descriptors_internal(
781            opts,
782            primary_path,
783            cfs,
784            &AccessType::Secondary {
785                secondary_path: secondary_path.as_ref(),
786            },
787        )
788    }
789
790    /// Opens the database as a secondary with the given database options and
791    /// column family descriptors.
792    /// *NOTE*: `default` column family is opened with `Options::default()`.
793    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
794    pub fn open_cf_descriptors_as_secondary<P, I>(
795        opts: &Options,
796        path: P,
797        secondary_path: P,
798        cfs: I,
799    ) -> Result<Self, Error>
800    where
801        P: AsRef<Path>,
802        I: IntoIterator<Item = ColumnFamilyDescriptor>,
803    {
804        Self::open_cf_descriptors_internal(
805            opts,
806            path,
807            cfs,
808            &AccessType::Secondary {
809                secondary_path: secondary_path.as_ref(),
810            },
811        )
812    }
813
814    /// Opens a database with the given database options and column family descriptors.
815    /// *NOTE*: `default` column family is opened with `Options::default()`.
816    /// If you want to open `default` cf with different options, set them explicitly in `cfs`.
817    pub fn open_cf_descriptors<P, I>(opts: &Options, path: P, cfs: I) -> Result<Self, Error>
818    where
819        P: AsRef<Path>,
820        I: IntoIterator<Item = ColumnFamilyDescriptor>,
821    {
822        Self::open_cf_descriptors_internal(opts, path, cfs, &AccessType::ReadWrite)
823    }
824
825    /// Internal implementation for opening RocksDB.
826    fn open_cf_descriptors_internal<P, I>(
827        opts: &Options,
828        path: P,
829        cfs: I,
830        access_type: &AccessType,
831    ) -> Result<Self, Error>
832    where
833        P: AsRef<Path>,
834        I: IntoIterator<Item = ColumnFamilyDescriptor>,
835    {
836        let cfs: Vec<_> = cfs.into_iter().collect();
837        let outlive = iter::once(opts.outlive.clone())
838            .chain(cfs.iter().map(|cf| cf.options.outlive.clone()))
839            .collect();
840
841        let cpath = to_cpath(&path)?;
842
843        if let Err(e) = fs::create_dir_all(&path) {
844            return Err(Error::new(format!(
845                "Failed to create RocksDB directory: `{e:?}`."
846            )));
847        }
848
849        let db: *mut ffi::rocksdb_t;
850        let mut cf_map = BTreeMap::new();
851
852        if cfs.is_empty() {
853            db = Self::open_raw(opts, &cpath, access_type)?;
854        } else {
855            let mut cfs_v = cfs;
856            // Always open the default column family.
857            if !cfs_v.iter().any(|cf| cf.name == DEFAULT_COLUMN_FAMILY_NAME) {
858                cfs_v.push(ColumnFamilyDescriptor {
859                    name: String::from(DEFAULT_COLUMN_FAMILY_NAME),
860                    options: Options::default(),
861                    ttl: ColumnFamilyTtl::SameAsDb,
862                });
863            }
864            // We need to store our CStrings in an intermediate vector
865            // so that their pointers remain valid.
866            let c_cfs: Vec<CString> = cfs_v
867                .iter()
868                .map(|cf| CString::new(cf.name.as_bytes()).unwrap())
869                .collect();
870
871            let cfnames: Vec<_> = c_cfs.iter().map(|cf| cf.as_ptr()).collect();
872
873            // These handles will be populated by DB.
874            let mut cfhandles: Vec<_> = cfs_v.iter().map(|_| ptr::null_mut()).collect();
875
876            let cfopts: Vec<_> = cfs_v
877                .iter()
878                .map(|cf| cf.options.inner.cast_const())
879                .collect();
880
881            db = Self::open_cf_raw(
882                opts,
883                &cpath,
884                &cfs_v,
885                &cfnames,
886                &cfopts,
887                &mut cfhandles,
888                access_type,
889            )?;
890            for handle in &cfhandles {
891                if handle.is_null() {
892                    return Err(Error::new(
893                        "Received null column family handle from DB.".to_owned(),
894                    ));
895                }
896            }
897
898            for (cf_desc, inner) in cfs_v.iter().zip(cfhandles) {
899                cf_map.insert(cf_desc.name.clone(), inner);
900            }
901        }
902
903        if db.is_null() {
904            return Err(Error::new("Could not initialize database.".to_owned()));
905        }
906
907        Ok(Self {
908            inner: DBWithThreadModeInner { inner: db },
909            path: path.as_ref().to_path_buf(),
910            cfs: T::new_cf_map_internal(cf_map),
911            _outlive: outlive,
912        })
913    }
914
915    fn open_raw(
916        opts: &Options,
917        cpath: &CString,
918        access_type: &AccessType,
919    ) -> Result<*mut ffi::rocksdb_t, Error> {
920        let db = unsafe {
921            match *access_type {
922                AccessType::ReadOnly {
923                    error_if_log_file_exist,
924                } => ffi_try!(ffi::rocksdb_open_for_read_only(
925                    opts.inner,
926                    cpath.as_ptr(),
927                    c_uchar::from(error_if_log_file_exist),
928                )),
929                AccessType::ReadWrite => {
930                    ffi_try!(ffi::rocksdb_open(opts.inner, cpath.as_ptr()))
931                }
932                AccessType::Secondary { secondary_path } => {
933                    ffi_try!(ffi::rocksdb_open_as_secondary(
934                        opts.inner,
935                        cpath.as_ptr(),
936                        to_cpath(secondary_path)?.as_ptr(),
937                    ))
938                }
939                AccessType::WithTTL { ttl } => ffi_try!(ffi::rocksdb_open_with_ttl(
940                    opts.inner,
941                    cpath.as_ptr(),
942                    ttl_to_seconds(ttl),
943                )),
944            }
945        };
946        Ok(db)
947    }
948
949    #[allow(clippy::pedantic)]
950    fn open_cf_raw(
951        opts: &Options,
952        cpath: &CString,
953        cfs_v: &[ColumnFamilyDescriptor],
954        cfnames: &[*const c_char],
955        cfopts: &[*const ffi::rocksdb_options_t],
956        cfhandles: &mut [*mut ffi::rocksdb_column_family_handle_t],
957        access_type: &AccessType,
958    ) -> Result<*mut ffi::rocksdb_t, Error> {
959        let db = unsafe {
960            match *access_type {
961                AccessType::ReadOnly {
962                    error_if_log_file_exist,
963                } => ffi_try!(ffi::rocksdb_open_for_read_only_column_families(
964                    opts.inner,
965                    cpath.as_ptr(),
966                    cfs_v.len() as c_int,
967                    cfnames.as_ptr(),
968                    cfopts.as_ptr(),
969                    cfhandles.as_mut_ptr(),
970                    c_uchar::from(error_if_log_file_exist),
971                )),
972                AccessType::ReadWrite => ffi_try!(ffi::rocksdb_open_column_families(
973                    opts.inner,
974                    cpath.as_ptr(),
975                    cfs_v.len() as c_int,
976                    cfnames.as_ptr(),
977                    cfopts.as_ptr(),
978                    cfhandles.as_mut_ptr(),
979                )),
980                AccessType::Secondary { secondary_path } => {
981                    ffi_try!(ffi::rocksdb_open_as_secondary_column_families(
982                        opts.inner,
983                        cpath.as_ptr(),
984                        to_cpath(secondary_path)?.as_ptr(),
985                        cfs_v.len() as c_int,
986                        cfnames.as_ptr(),
987                        cfopts.as_ptr(),
988                        cfhandles.as_mut_ptr(),
989                    ))
990                }
991                AccessType::WithTTL { ttl } => {
992                    let ttls: Vec<_> = cfs_v
993                        .iter()
994                        .map(|cf| match cf.ttl {
995                            ColumnFamilyTtl::Disabled => i32::MAX,
996                            ColumnFamilyTtl::Duration(duration) => ttl_to_seconds(duration),
997                            ColumnFamilyTtl::SameAsDb => ttl_to_seconds(ttl),
998                        })
999                        .collect();
1000
1001                    ffi_try!(ffi::rocksdb_open_column_families_with_ttl(
1002                        opts.inner,
1003                        cpath.as_ptr(),
1004                        cfs_v.len() as c_int,
1005                        cfnames.as_ptr(),
1006                        cfopts.as_ptr(),
1007                        cfhandles.as_mut_ptr(),
1008                        ttls.as_ptr(),
1009                    ))
1010                }
1011            }
1012        };
1013        Ok(db)
1014    }
1015
1016    /// Removes the database entries in the range `["from", "to")` using given write options.
1017    pub fn delete_range_cf_opt<K: AsRef<[u8]>>(
1018        &self,
1019        cf: &impl AsColumnFamilyRef,
1020        from: K,
1021        to: K,
1022        writeopts: &WriteOptions,
1023    ) -> Result<(), Error> {
1024        let from = from.as_ref();
1025        let to = to.as_ref();
1026
1027        unsafe {
1028            ffi_try!(ffi::rocksdb_delete_range_cf(
1029                self.inner.inner(),
1030                writeopts.inner,
1031                cf.inner(),
1032                from.as_ptr() as *const c_char,
1033                from.len() as size_t,
1034                to.as_ptr() as *const c_char,
1035                to.len() as size_t,
1036            ));
1037            Ok(())
1038        }
1039    }
1040
1041    /// Removes the database entries in the range `["from", "to")` using default write options.
1042    pub fn delete_range_cf<K: AsRef<[u8]>>(
1043        &self,
1044        cf: &impl AsColumnFamilyRef,
1045        from: K,
1046        to: K,
1047    ) -> Result<(), Error> {
1048        DEFAULT_WRITE_OPTS.with(|opts| self.delete_range_cf_opt(cf, from, to, opts))
1049    }
1050
1051    pub fn write_opt(&self, batch: &WriteBatch, writeopts: &WriteOptions) -> Result<(), Error> {
1052        unsafe {
1053            ffi_try!(ffi::rocksdb_write(
1054                self.inner.inner(),
1055                writeopts.inner,
1056                batch.inner
1057            ));
1058        }
1059        Ok(())
1060    }
1061
1062    pub fn write(&self, batch: &WriteBatch) -> Result<(), Error> {
1063        DEFAULT_WRITE_OPTS.with(|opts| self.write_opt(batch, opts))
1064    }
1065
1066    pub fn write_without_wal(&self, batch: &WriteBatch) -> Result<(), Error> {
1067        let mut wo = WriteOptions::new();
1068        wo.disable_wal(true);
1069        self.write_opt(batch, &wo)
1070    }
1071
1072    pub fn write_wbwi(&self, wbwi: &WriteBatchWithIndex) -> Result<(), Error> {
1073        DEFAULT_WRITE_OPTS.with(|opts| self.write_wbwi_opt(wbwi, opts))
1074    }
1075
1076    pub fn write_wbwi_opt(
1077        &self,
1078        wbwi: &WriteBatchWithIndex,
1079        writeopts: &WriteOptions,
1080    ) -> Result<(), Error> {
1081        unsafe {
1082            ffi_try!(ffi::rocksdb_write_writebatch_wi(
1083                self.inner.inner(),
1084                writeopts.inner,
1085                wbwi.inner
1086            ));
1087
1088            Ok(())
1089        }
1090    }
1091}
1092
1093/// Common methods of `DBWithThreadMode` and `OptimisticTransactionDB`.
1094impl<T: ThreadMode, D: DBInner> DBCommon<T, D> {
1095    pub(crate) fn new(inner: D, cfs: T, path: PathBuf, outlive: Vec<OptionsMustOutliveDB>) -> Self {
1096        Self {
1097            inner,
1098            cfs,
1099            path,
1100            _outlive: outlive,
1101        }
1102    }
1103
1104    pub fn list_cf<P: AsRef<Path>>(opts: &Options, path: P) -> Result<Vec<String>, Error> {
1105        let cpath = to_cpath(path)?;
1106        let mut length = 0;
1107
1108        unsafe {
1109            let ptr = ffi_try!(ffi::rocksdb_list_column_families(
1110                opts.inner,
1111                cpath.as_ptr(),
1112                &raw mut length,
1113            ));
1114
1115            let vec = slice::from_raw_parts(ptr, length)
1116                .iter()
1117                .map(|ptr| from_cstr_without_free(*ptr))
1118                .collect();
1119            ffi::rocksdb_list_column_families_destroy(ptr, length);
1120            Ok(vec)
1121        }
1122    }
1123
1124    pub fn destroy<P: AsRef<Path>>(opts: &Options, path: P) -> Result<(), Error> {
1125        let cpath = to_cpath(path)?;
1126        unsafe {
1127            ffi_try!(ffi::rocksdb_destroy_db(opts.inner, cpath.as_ptr()));
1128        }
1129        Ok(())
1130    }
1131
1132    pub fn repair<P: AsRef<Path>>(opts: &Options, path: P) -> Result<(), Error> {
1133        let cpath = to_cpath(path)?;
1134        unsafe {
1135            ffi_try!(ffi::rocksdb_repair_db(opts.inner, cpath.as_ptr()));
1136        }
1137        Ok(())
1138    }
1139
1140    pub fn path(&self) -> &Path {
1141        self.path.as_path()
1142    }
1143
1144    /// Flushes the WAL buffer. If `sync` is set to `true`, also syncs
1145    /// the data to disk.
1146    pub fn flush_wal(&self, sync: bool) -> Result<(), Error> {
1147        unsafe {
1148            ffi_try!(ffi::rocksdb_flush_wal(
1149                self.inner.inner(),
1150                c_uchar::from(sync)
1151            ));
1152        }
1153        Ok(())
1154    }
1155
1156    /// Suspend deleting obsolete files. Compactions will continue to occur,
1157    /// but no obsolete files will be deleted. To resume file deletions, each
1158    /// call to disable_file_deletions() must be matched by a subsequent call to
1159    /// enable_file_deletions(). For more details, see enable_file_deletions().
1160    pub fn disable_file_deletions(&self) -> Result<(), Error> {
1161        unsafe {
1162            ffi_try!(ffi::rocksdb_disable_file_deletions(self.inner.inner()));
1163        }
1164        Ok(())
1165    }
1166
1167    /// Resume deleting obsolete files, following up on `disable_file_deletions()`.
1168    ///
1169    /// File deletions disabling and enabling is not controlled by a binary flag,
1170    /// instead it's represented as a counter to allow different callers to
1171    /// independently disable file deletion. Disabling file deletion can be
1172    /// critical for operations like making a backup. So the counter implementation
1173    /// makes the file deletion disabled as long as there is one caller requesting
1174    /// so, and only when every caller agrees to re-enable file deletion, it will
1175    /// be enabled. Two threads can call this method concurrently without
1176    /// synchronization -- i.e., file deletions will be enabled only after both
1177    /// threads call enable_file_deletions()
1178    pub fn enable_file_deletions(&self) -> Result<(), Error> {
1179        unsafe {
1180            ffi_try!(ffi::rocksdb_enable_file_deletions(self.inner.inner()));
1181        }
1182        Ok(())
1183    }
1184
1185    /// Flushes database memtables to SST files on the disk.
1186    pub fn flush_opt(&self, flushopts: &FlushOptions) -> Result<(), Error> {
1187        unsafe {
1188            ffi_try!(ffi::rocksdb_flush(self.inner.inner(), flushopts.inner));
1189        }
1190        Ok(())
1191    }
1192
1193    /// Flushes database memtables to SST files on the disk using default options.
1194    pub fn flush(&self) -> Result<(), Error> {
1195        DEFAULT_FLUSH_OPTS.with(|opts| self.flush_opt(opts))
1196    }
1197
1198    /// Flushes database memtables to SST files on the disk for a given column family.
1199    pub fn flush_cf_opt(
1200        &self,
1201        cf: &impl AsColumnFamilyRef,
1202        flushopts: &FlushOptions,
1203    ) -> Result<(), Error> {
1204        unsafe {
1205            ffi_try!(ffi::rocksdb_flush_cf(
1206                self.inner.inner(),
1207                flushopts.inner,
1208                cf.inner()
1209            ));
1210        }
1211        Ok(())
1212    }
1213
1214    /// Flushes multiple column families.
1215    ///
1216    /// If atomic flush is not enabled, it is equivalent to calling flush_cf multiple times.
1217    /// If atomic flush is enabled, it will flush all column families specified in `cfs` up to the latest sequence
1218    /// number at the time when flush is requested.
1219    pub fn flush_cfs_opt(
1220        &self,
1221        cfs: &[&impl AsColumnFamilyRef],
1222        opts: &FlushOptions,
1223    ) -> Result<(), Error> {
1224        let mut cfs = cfs.iter().map(|cf| cf.inner()).collect::<Vec<_>>();
1225        unsafe {
1226            ffi_try!(ffi::rocksdb_flush_cfs(
1227                self.inner.inner(),
1228                opts.inner,
1229                cfs.as_mut_ptr(),
1230                cfs.len() as libc::c_int,
1231            ));
1232        }
1233        Ok(())
1234    }
1235
1236    /// Flushes database memtables to SST files on the disk for a given column family using default
1237    /// options.
1238    pub fn flush_cf(&self, cf: &impl AsColumnFamilyRef) -> Result<(), Error> {
1239        DEFAULT_FLUSH_OPTS.with(|opts| self.flush_cf_opt(cf, opts))
1240    }
1241
1242    /// Return the bytes associated with a key value with read options. If you only intend to use
1243    /// the vector returned temporarily, consider using [`get_pinned_opt`](#method.get_pinned_opt)
1244    /// to avoid unnecessary memory copy.
1245    pub fn get_opt<K: AsRef<[u8]>>(
1246        &self,
1247        key: K,
1248        readopts: &ReadOptions,
1249    ) -> Result<Option<Vec<u8>>, Error> {
1250        self.get_pinned_opt(key, readopts)
1251            .map(|x| x.map(|v| v.as_ref().to_vec()))
1252    }
1253
1254    /// Return the bytes associated with a key value. If you only intend to use the vector returned
1255    /// temporarily, consider using [`get_pinned`](#method.get_pinned) to avoid unnecessary memory
1256    /// copy.
1257    pub fn get<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<Vec<u8>>, Error> {
1258        DEFAULT_READ_OPTS.with(|opts| self.get_opt(key.as_ref(), opts))
1259    }
1260
1261    /// Return the bytes associated with a key value and the given column family with read options.
1262    /// If you only intend to use the vector returned temporarily, consider using
1263    /// [`get_pinned_cf_opt`](#method.get_pinned_cf_opt) to avoid unnecessary memory.
1264    pub fn get_cf_opt<K: AsRef<[u8]>>(
1265        &self,
1266        cf: &impl AsColumnFamilyRef,
1267        key: K,
1268        readopts: &ReadOptions,
1269    ) -> Result<Option<Vec<u8>>, Error> {
1270        self.get_pinned_cf_opt(cf, key, readopts)
1271            .map(|x| x.map(|v| v.as_ref().to_vec()))
1272    }
1273
1274    /// Return the bytes associated with a key value and the given column family. If you only
1275    /// intend to use the vector returned temporarily, consider using
1276    /// [`get_pinned_cf`](#method.get_pinned_cf) to avoid unnecessary memory.
1277    pub fn get_cf<K: AsRef<[u8]>>(
1278        &self,
1279        cf: &impl AsColumnFamilyRef,
1280        key: K,
1281    ) -> Result<Option<Vec<u8>>, Error> {
1282        DEFAULT_READ_OPTS.with(|opts| self.get_cf_opt(cf, key.as_ref(), opts))
1283    }
1284
1285    /// Return the value associated with a key using RocksDB's PinnableSlice
1286    /// so as to avoid unnecessary memory copy.
1287    pub fn get_pinned_opt<K: AsRef<[u8]>>(
1288        &'_ self,
1289        key: K,
1290        readopts: &ReadOptions,
1291    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
1292        if readopts.inner.is_null() {
1293            return Err(Error::new(
1294                "Unable to create RocksDB read options. This is a fairly trivial call, and its \
1295                 failure may be indicative of a mis-compiled or mis-loaded RocksDB library."
1296                    .to_owned(),
1297            ));
1298        }
1299
1300        let key = key.as_ref();
1301        unsafe {
1302            let val = ffi_try!(ffi::rocksdb_get_pinned(
1303                self.inner.inner(),
1304                readopts.inner,
1305                key.as_ptr() as *const c_char,
1306                key.len() as size_t,
1307            ));
1308            if val.is_null() {
1309                Ok(None)
1310            } else {
1311                Ok(Some(DBPinnableSlice::from_c(val)))
1312            }
1313        }
1314    }
1315
1316    /// Return the value associated with a key using RocksDB's PinnableSlice
1317    /// so as to avoid unnecessary memory copy. Similar to get_pinned_opt but
1318    /// leverages default options.
1319    pub fn get_pinned<K: AsRef<[u8]>>(
1320        &'_ self,
1321        key: K,
1322    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
1323        DEFAULT_READ_OPTS.with(|opts| self.get_pinned_opt(key, opts))
1324    }
1325
1326    /// Return the value associated with a key using RocksDB's PinnableSlice
1327    /// so as to avoid unnecessary memory copy. Similar to get_pinned_opt but
1328    /// allows specifying ColumnFamily
1329    pub fn get_pinned_cf_opt<K: AsRef<[u8]>>(
1330        &'_ self,
1331        cf: &impl AsColumnFamilyRef,
1332        key: K,
1333        readopts: &ReadOptions,
1334    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
1335        if readopts.inner.is_null() {
1336            return Err(Error::new(
1337                "Unable to create RocksDB read options. This is a fairly trivial call, and its \
1338                 failure may be indicative of a mis-compiled or mis-loaded RocksDB library."
1339                    .to_owned(),
1340            ));
1341        }
1342
1343        let key = key.as_ref();
1344        unsafe {
1345            let val = ffi_try!(ffi::rocksdb_get_pinned_cf(
1346                self.inner.inner(),
1347                readopts.inner,
1348                cf.inner(),
1349                key.as_ptr() as *const c_char,
1350                key.len() as size_t,
1351            ));
1352            if val.is_null() {
1353                Ok(None)
1354            } else {
1355                Ok(Some(DBPinnableSlice::from_c(val)))
1356            }
1357        }
1358    }
1359
1360    /// Return the value associated with a key using RocksDB's PinnableSlice
1361    /// so as to avoid unnecessary memory copy. Similar to get_pinned_cf_opt but
1362    /// leverages default options.
1363    pub fn get_pinned_cf<K: AsRef<[u8]>>(
1364        &'_ self,
1365        cf: &impl AsColumnFamilyRef,
1366        key: K,
1367    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
1368        DEFAULT_READ_OPTS.with(|opts| self.get_pinned_cf_opt(cf, key, opts))
1369    }
1370
1371    /// Read a value directly into a caller-provided buffer, avoiding memory allocation.
1372    ///
1373    /// This is the most efficient way to read values when you have a pre-allocated
1374    /// buffer. It completely avoids the allocation overhead of [`get`](#method.get)
1375    /// and even the pinning overhead of [`get_pinned`](#method.get_pinned).
1376    ///
1377    /// # Arguments
1378    ///
1379    /// * `key` - The key to look up
1380    /// * `buffer` - A mutable byte slice to write the value into. Can be empty if you
1381    ///   only want to check if a key exists and get its value size.
1382    ///
1383    /// # Returns
1384    ///
1385    /// * `Ok(GetIntoBufferResult::NotFound)` - The key doesn't exist
1386    /// * `Ok(GetIntoBufferResult::Found(size))` - Value was copied into the buffer.
1387    ///   `size` is the number of bytes written.
1388    /// * `Ok(GetIntoBufferResult::BufferTooSmall(size))` - The value exists but the buffer
1389    ///   is too small. `size` is the actual value size needed. No data is written.
1390    /// * `Err(...)` - Database error occurred
1391    ///
1392    /// # Performance
1393    ///
1394    /// This method is ideal for high-throughput scenarios where you can reuse a buffer:
1395    ///
1396    /// ```ignore
1397    /// use rust_rocksdb::{DB, GetIntoBufferResult};
1398    ///
1399    /// let db: DB = /* open database */;
1400    /// let keys_to_lookup: Vec<&[u8]> = /* keys to look up */;
1401    /// let mut buffer = vec![0u8; 4096]; // Reusable buffer
1402    ///
1403    /// for key in keys_to_lookup {
1404    ///     match db.get_into_buffer(key, &mut buffer).unwrap() {
1405    ///         GetIntoBufferResult::Found(len) => {
1406    ///             process_value(&buffer[..len]);
1407    ///         }
1408    ///         GetIntoBufferResult::BufferTooSmall(needed) => {
1409    ///             buffer.resize(needed, 0);
1410    ///             // Retry with larger buffer...
1411    ///         }
1412    ///         GetIntoBufferResult::NotFound => {}
1413    ///     }
1414    /// }
1415    /// ```
1416    ///
1417    /// # Example
1418    ///
1419    /// ```
1420    /// use rust_rocksdb::{DB, GetIntoBufferResult};
1421    ///
1422    /// let tempdir = tempfile::Builder::new()
1423    ///     .prefix("rocksdb_get_into_buffer")
1424    ///     .tempdir()
1425    ///     .unwrap();
1426    /// let db = DB::open_default(tempdir.path()).unwrap();
1427    /// db.put(b"key", b"value").unwrap();
1428    ///
1429    /// let mut buffer = [0u8; 100];
1430    /// match db.get_into_buffer(b"key", &mut buffer).unwrap() {
1431    ///     GetIntoBufferResult::Found(size) => {
1432    ///         assert_eq!(&buffer[..size], b"value");
1433    ///     }
1434    ///     GetIntoBufferResult::NotFound => panic!("expected value"),
1435    ///     GetIntoBufferResult::BufferTooSmall(needed) => {
1436    ///         panic!("buffer too small, need {} bytes", needed)
1437    ///     }
1438    /// }
1439    /// ```
1440    pub fn get_into_buffer<K: AsRef<[u8]>>(
1441        &self,
1442        key: K,
1443        buffer: &mut [u8],
1444    ) -> Result<GetIntoBufferResult, Error> {
1445        DEFAULT_READ_OPTS.with(|opts| self.get_into_buffer_opt(key, buffer, opts))
1446    }
1447
1448    /// Read a value directly into a caller-provided buffer with custom read options.
1449    ///
1450    /// This is the same as [`get_into_buffer`](#method.get_into_buffer) but allows
1451    /// specifying custom [`ReadOptions`], such as setting a snapshot or fill cache behavior.
1452    ///
1453    /// See [`get_into_buffer`](#method.get_into_buffer) for full documentation.
1454    pub fn get_into_buffer_opt<K: AsRef<[u8]>>(
1455        &self,
1456        key: K,
1457        buffer: &mut [u8],
1458        readopts: &ReadOptions,
1459    ) -> Result<GetIntoBufferResult, Error> {
1460        if readopts.inner.is_null() {
1461            return Err(Error::new(
1462                "Unable to create RocksDB read options. This is a fairly trivial call, and its \
1463                 failure may be indicative of a mis-compiled or mis-loaded RocksDB library."
1464                    .to_owned(),
1465            ));
1466        }
1467
1468        let key = key.as_ref();
1469        let mut val_len: size_t = 0;
1470        let mut found: c_uchar = 0;
1471
1472        unsafe {
1473            let success = ffi_try!(ffi::rocksdb_get_into_buffer(
1474                self.inner.inner(),
1475                readopts.inner,
1476                key.as_ptr() as *const c_char,
1477                key.len() as size_t,
1478                buffer.as_mut_ptr() as *mut c_char,
1479                buffer.len() as size_t,
1480                &raw mut val_len,
1481                &raw mut found,
1482            ));
1483
1484            if found == 0 {
1485                Ok(GetIntoBufferResult::NotFound)
1486            } else if success != 0 {
1487                Ok(GetIntoBufferResult::Found(val_len))
1488            } else {
1489                Ok(GetIntoBufferResult::BufferTooSmall(val_len))
1490            }
1491        }
1492    }
1493
1494    /// Read a value from a column family directly into a caller-provided buffer.
1495    ///
1496    /// This is the column family variant of [`get_into_buffer`](#method.get_into_buffer).
1497    /// See that method for full documentation on the zero-allocation buffer API.
1498    ///
1499    /// # Arguments
1500    ///
1501    /// * `cf` - The column family to read from
1502    /// * `key` - The key to look up
1503    /// * `buffer` - A mutable byte slice to write the value into
1504    pub fn get_into_buffer_cf<K: AsRef<[u8]>>(
1505        &self,
1506        cf: &impl AsColumnFamilyRef,
1507        key: K,
1508        buffer: &mut [u8],
1509    ) -> Result<GetIntoBufferResult, Error> {
1510        DEFAULT_READ_OPTS.with(|opts| self.get_into_buffer_cf_opt(cf, key, buffer, opts))
1511    }
1512
1513    /// Read a value from a column family directly into a caller-provided buffer
1514    /// with custom read options.
1515    ///
1516    /// This is the column family variant of [`get_into_buffer_opt`](#method.get_into_buffer_opt).
1517    /// See [`get_into_buffer`](#method.get_into_buffer) for full documentation.
1518    pub fn get_into_buffer_cf_opt<K: AsRef<[u8]>>(
1519        &self,
1520        cf: &impl AsColumnFamilyRef,
1521        key: K,
1522        buffer: &mut [u8],
1523        readopts: &ReadOptions,
1524    ) -> Result<GetIntoBufferResult, Error> {
1525        if readopts.inner.is_null() {
1526            return Err(Error::new(
1527                "Unable to create RocksDB read options. This is a fairly trivial call, and its \
1528                 failure may be indicative of a mis-compiled or mis-loaded RocksDB library."
1529                    .to_owned(),
1530            ));
1531        }
1532
1533        let key = key.as_ref();
1534        let mut val_len: size_t = 0;
1535        let mut found: c_uchar = 0;
1536
1537        unsafe {
1538            let success = ffi_try!(ffi::rocksdb_get_into_buffer_cf(
1539                self.inner.inner(),
1540                readopts.inner,
1541                cf.inner(),
1542                key.as_ptr() as *const c_char,
1543                key.len() as size_t,
1544                buffer.as_mut_ptr() as *mut c_char,
1545                buffer.len() as size_t,
1546                &raw mut val_len,
1547                &raw mut found,
1548            ));
1549
1550            if found == 0 {
1551                Ok(GetIntoBufferResult::NotFound)
1552            } else if success != 0 {
1553                Ok(GetIntoBufferResult::Found(val_len))
1554            } else {
1555                Ok(GetIntoBufferResult::BufferTooSmall(val_len))
1556            }
1557        }
1558    }
1559
1560    /// Return the values associated with the given keys.
1561    pub fn multi_get<K, I>(&self, keys: I) -> Vec<Result<Option<Vec<u8>>, Error>>
1562    where
1563        K: AsRef<[u8]>,
1564        I: IntoIterator<Item = K>,
1565    {
1566        DEFAULT_READ_OPTS.with(|opts| self.multi_get_opt(keys, opts))
1567    }
1568
1569    /// Return the values associated with the given keys using read options.
1570    pub fn multi_get_opt<K, I>(
1571        &self,
1572        keys: I,
1573        readopts: &ReadOptions,
1574    ) -> Vec<Result<Option<Vec<u8>>, Error>>
1575    where
1576        K: AsRef<[u8]>,
1577        I: IntoIterator<Item = K>,
1578    {
1579        let owned_keys: Vec<K> = keys.into_iter().collect();
1580        let (ptr_keys, keys_sizes): (Vec<*const c_char>, Vec<usize>) = owned_keys
1581            .iter()
1582            .map(|k| {
1583                let key = k.as_ref();
1584                (key.as_ptr() as *const c_char, key.len())
1585            })
1586            .unzip();
1587
1588        let mut values: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
1589        let mut values_sizes: Vec<usize> = Vec::with_capacity(ptr_keys.len());
1590        let mut errors: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
1591        unsafe {
1592            ffi::rocksdb_multi_get(
1593                self.inner.inner(),
1594                readopts.inner,
1595                ptr_keys.len(),
1596                ptr_keys.as_ptr(),
1597                keys_sizes.as_ptr(),
1598                values.as_mut_ptr(),
1599                values_sizes.as_mut_ptr(),
1600                errors.as_mut_ptr(),
1601            );
1602        }
1603
1604        unsafe {
1605            values.set_len(ptr_keys.len());
1606            values_sizes.set_len(ptr_keys.len());
1607            errors.set_len(ptr_keys.len());
1608        }
1609
1610        convert_values(values, values_sizes, errors)
1611    }
1612
1613    /// Returns pinned values associated with the given keys using default read options.
1614    ///
1615    /// RocksDB processes the keys in one native batch. Results stay in input order.
1616    pub fn multi_get_pinned<K, I>(
1617        &'_ self,
1618        keys: I,
1619    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1620    where
1621        K: AsRef<[u8]>,
1622        I: IntoIterator<Item = K>,
1623    {
1624        DEFAULT_READ_OPTS.with(|opts| self.multi_get_pinned_opt(keys, opts))
1625    }
1626
1627    /// Returns pinned values associated with the given keys using the provided read options.
1628    ///
1629    /// RocksDB processes the keys in one native batch. Results stay in input order.
1630    pub fn multi_get_pinned_opt<K, I>(
1631        &'_ self,
1632        keys: I,
1633        readopts: &ReadOptions,
1634    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1635    where
1636        K: AsRef<[u8]>,
1637        I: IntoIterator<Item = K>,
1638    {
1639        let mut keys = keys.into_iter();
1640        let Some(first) = keys.next() else {
1641            return Vec::new();
1642        };
1643        // Decide before collecting. A single key does not benefit from the
1644        // native batch, and buying a key-slice vector, two result vectors and
1645        // a default column family handle to do one point lookup is a loss.
1646        let Some(second) = keys.next() else {
1647            return vec![self.get_pinned_opt(first.as_ref(), readopts)];
1648        };
1649        let mut owned_keys = Vec::with_capacity(2 + keys.size_hint().0);
1650        owned_keys.push(first);
1651        owned_keys.push(second);
1652        owned_keys.extend(keys);
1653        self.batched_multi_get_pinned_owned(&owned_keys, false, readopts)
1654    }
1655
1656    /// Returns pinned values associated with the given keys and column families
1657    /// using default read options.
1658    pub fn multi_get_pinned_cf<'a, 'b: 'a, K, I, W>(
1659        &'a self,
1660        keys: I,
1661    ) -> Vec<Result<Option<DBPinnableSlice<'a>>, Error>>
1662    where
1663        K: AsRef<[u8]>,
1664        I: IntoIterator<Item = (&'b W, K)>,
1665        W: 'b + AsColumnFamilyRef,
1666    {
1667        DEFAULT_READ_OPTS.with(|opts| self.multi_get_pinned_cf_opt(keys, opts))
1668    }
1669
1670    /// Returns pinned values associated with the given keys and column families
1671    /// using the provided read options.
1672    pub fn multi_get_pinned_cf_opt<'a, 'b: 'a, K, I, W>(
1673        &'a self,
1674        keys: I,
1675        readopts: &ReadOptions,
1676    ) -> Vec<Result<Option<DBPinnableSlice<'a>>, Error>>
1677    where
1678        K: AsRef<[u8]>,
1679        I: IntoIterator<Item = (&'b W, K)>,
1680        W: 'b + AsColumnFamilyRef,
1681    {
1682        keys.into_iter()
1683            .map(|(cf, k)| self.get_pinned_cf_opt(cf, k, readopts))
1684            .collect()
1685    }
1686
1687    /// Returns pinned values for default-column-family keys in one native batch.
1688    ///
1689    /// Set `sorted_input` only when keys are sorted according to the column
1690    /// family's comparator. Results stay in input order, including duplicates.
1691    pub fn batched_multi_get_pinned<K, I>(
1692        &'_ self,
1693        keys: I,
1694        sorted_input: bool,
1695    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1696    where
1697        K: AsRef<[u8]>,
1698        I: IntoIterator<Item = K>,
1699    {
1700        DEFAULT_READ_OPTS.with(|opts| self.batched_multi_get_pinned_opt(keys, sorted_input, opts))
1701    }
1702
1703    /// Returns pinned values for default-column-family keys in one native batch
1704    /// using the provided read options.
1705    pub fn batched_multi_get_pinned_opt<K, I>(
1706        &'_ self,
1707        keys: I,
1708        sorted_input: bool,
1709        readopts: &ReadOptions,
1710    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1711    where
1712        K: AsRef<[u8]>,
1713        I: IntoIterator<Item = K>,
1714    {
1715        let owned_keys: Vec<K> = keys.into_iter().collect();
1716        self.batched_multi_get_pinned_owned(&owned_keys, sorted_input, readopts)
1717    }
1718
1719    /// Returns pinned values for keys in one column family using one native batch.
1720    ///
1721    /// Set `sorted_input` only when keys are sorted according to the column
1722    /// family's comparator. Results stay in input order, including duplicates.
1723    pub fn batched_multi_get_pinned_cf<K, I>(
1724        &'_ self,
1725        cf: &impl AsColumnFamilyRef,
1726        keys: I,
1727        sorted_input: bool,
1728    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1729    where
1730        K: AsRef<[u8]>,
1731        I: IntoIterator<Item = K>,
1732    {
1733        DEFAULT_READ_OPTS
1734            .with(|opts| self.batched_multi_get_pinned_cf_opt(cf, keys, sorted_input, opts))
1735    }
1736
1737    /// Returns pinned values for keys in one column family using one native batch
1738    /// and the provided read options.
1739    pub fn batched_multi_get_pinned_cf_opt<K, I>(
1740        &'_ self,
1741        cf: &impl AsColumnFamilyRef,
1742        keys: I,
1743        sorted_input: bool,
1744        readopts: &ReadOptions,
1745    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1746    where
1747        K: AsRef<[u8]>,
1748        I: IntoIterator<Item = K>,
1749    {
1750        let owned_keys: Vec<K> = keys.into_iter().collect();
1751        let key_slices = Self::key_slices(&owned_keys);
1752        self.batched_multi_get_pinned_inner(cf.inner(), &key_slices, sorted_input, readopts)
1753    }
1754
1755    /// Returns one owner for all default-column-family pinned results.
1756    ///
1757    /// Values borrow from the returned batch, avoiding one native wrapper
1758    /// allocation and one destroy call per successful key.
1759    pub fn batched_multi_get_pinned_batch<K, I>(
1760        &'_ self,
1761        keys: I,
1762        sorted_input: bool,
1763    ) -> Result<DBPinnableBatch<'_>, Error>
1764    where
1765        K: AsRef<[u8]>,
1766        I: IntoIterator<Item = K>,
1767    {
1768        DEFAULT_READ_OPTS
1769            .with(|opts| self.batched_multi_get_pinned_batch_opt(keys, sorted_input, opts))
1770    }
1771
1772    /// Returns one owner for all default-column-family pinned results using
1773    /// the provided read options.
1774    pub fn batched_multi_get_pinned_batch_opt<K, I>(
1775        &'_ self,
1776        keys: I,
1777        sorted_input: bool,
1778        readopts: &ReadOptions,
1779    ) -> Result<DBPinnableBatch<'_>, Error>
1780    where
1781        K: AsRef<[u8]>,
1782        I: IntoIterator<Item = K>,
1783    {
1784        let owned_keys: Vec<K> = keys.into_iter().collect();
1785        let key_slices = Self::key_slices(&owned_keys);
1786        self.create_pinnable_batch(ptr::null_mut(), &key_slices, sorted_input, readopts)
1787    }
1788
1789    /// Returns one owner for all pinned results from one column family.
1790    pub fn batched_multi_get_pinned_batch_cf<K, I>(
1791        &'_ self,
1792        cf: &impl AsColumnFamilyRef,
1793        keys: I,
1794        sorted_input: bool,
1795    ) -> Result<DBPinnableBatch<'_>, Error>
1796    where
1797        K: AsRef<[u8]>,
1798        I: IntoIterator<Item = K>,
1799    {
1800        DEFAULT_READ_OPTS
1801            .with(|opts| self.batched_multi_get_pinned_batch_cf_opt(cf, keys, sorted_input, opts))
1802    }
1803
1804    /// Returns one owner for all pinned results from one column family using
1805    /// the provided read options.
1806    pub fn batched_multi_get_pinned_batch_cf_opt<K, I>(
1807        &'_ self,
1808        cf: &impl AsColumnFamilyRef,
1809        keys: I,
1810        sorted_input: bool,
1811        readopts: &ReadOptions,
1812    ) -> Result<DBPinnableBatch<'_>, Error>
1813    where
1814        K: AsRef<[u8]>,
1815        I: IntoIterator<Item = K>,
1816    {
1817        let owned_keys: Vec<K> = keys.into_iter().collect();
1818        let key_slices = Self::key_slices(&owned_keys);
1819        self.create_pinnable_batch(cf.inner(), &key_slices, sorted_input, readopts)
1820    }
1821
1822    /// Return the values associated with the given keys and column families.
1823    pub fn multi_get_cf<'a, 'b: 'a, K, I, W>(
1824        &'a self,
1825        keys: I,
1826    ) -> Vec<Result<Option<Vec<u8>>, Error>>
1827    where
1828        K: AsRef<[u8]>,
1829        I: IntoIterator<Item = (&'b W, K)>,
1830        W: 'b + AsColumnFamilyRef,
1831    {
1832        DEFAULT_READ_OPTS.with(|opts| self.multi_get_cf_opt(keys, opts))
1833    }
1834
1835    /// Return the values associated with the given keys and column families using read options.
1836    pub fn multi_get_cf_opt<'a, 'b: 'a, K, I, W>(
1837        &'a self,
1838        keys: I,
1839        readopts: &ReadOptions,
1840    ) -> Vec<Result<Option<Vec<u8>>, Error>>
1841    where
1842        K: AsRef<[u8]>,
1843        I: IntoIterator<Item = (&'b W, K)>,
1844        W: 'b + AsColumnFamilyRef,
1845    {
1846        let cfs_and_owned_keys: Vec<(&'b W, K)> = keys.into_iter().collect();
1847        let (ptr_keys, keys_sizes): (Vec<*const c_char>, Vec<usize>) = cfs_and_owned_keys
1848            .iter()
1849            .map(|(_, k)| {
1850                let key = k.as_ref();
1851                (key.as_ptr() as *const c_char, key.len())
1852            })
1853            .unzip();
1854        let ptr_cfs: Vec<*const ffi::rocksdb_column_family_handle_t> = cfs_and_owned_keys
1855            .iter()
1856            .map(|(c, _)| c.inner().cast_const())
1857            .collect();
1858        let mut values: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
1859        let mut values_sizes: Vec<usize> = Vec::with_capacity(ptr_keys.len());
1860        let mut errors: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
1861        unsafe {
1862            ffi::rocksdb_multi_get_cf(
1863                self.inner.inner(),
1864                readopts.inner,
1865                ptr_cfs.as_ptr(),
1866                ptr_keys.len(),
1867                ptr_keys.as_ptr(),
1868                keys_sizes.as_ptr(),
1869                values.as_mut_ptr(),
1870                values_sizes.as_mut_ptr(),
1871                errors.as_mut_ptr(),
1872            );
1873        }
1874
1875        unsafe {
1876            values.set_len(ptr_keys.len());
1877            values_sizes.set_len(ptr_keys.len());
1878            errors.set_len(ptr_keys.len());
1879        }
1880
1881        convert_values(values, values_sizes, errors)
1882    }
1883
1884    /// Return the values associated with the given keys and the specified column family
1885    /// where internally the read requests are processed in batch if block-based table
1886    /// SST format is used.  It is a more optimized version of multi_get_cf.
1887    pub fn batched_multi_get_cf<'a, K, I>(
1888        &'_ self,
1889        cf: &impl AsColumnFamilyRef,
1890        keys: I,
1891        sorted_input: bool,
1892    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1893    where
1894        K: AsRef<[u8]> + 'a + ?Sized,
1895        I: IntoIterator<Item = &'a K>,
1896    {
1897        DEFAULT_READ_OPTS.with(|opts| self.batched_multi_get_cf_opt(cf, keys, sorted_input, opts))
1898    }
1899
1900    /// Return the values associated with the given keys and the specified column family
1901    /// where internally the read requests are processed in batch if block-based table
1902    /// SST format is used. It is a more optimized version of multi_get_cf_opt.
1903    pub fn batched_multi_get_cf_opt<'a, K, I>(
1904        &'_ self,
1905        cf: &impl AsColumnFamilyRef,
1906        keys: I,
1907        sorted_input: bool,
1908        readopts: &ReadOptions,
1909    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1910    where
1911        K: AsRef<[u8]> + 'a + ?Sized,
1912        I: IntoIterator<Item = &'a K>,
1913    {
1914        let key_slices: Vec<_> = keys
1915            .into_iter()
1916            .map(|k| {
1917                let k = k.as_ref();
1918                ffi::rocksdb_slice_t {
1919                    data: k.as_ptr() as *const c_char,
1920                    size: k.len(),
1921                }
1922            })
1923            .collect();
1924        self.batched_multi_get_pinned_inner(cf.inner(), &key_slices, sorted_input, readopts)
1925    }
1926
1927    /// Return the values associated with the given keys and the specified column family
1928    /// using an optimized slice-based API.
1929    ///
1930    /// This method uses RocksDB's optimized `rocksdb_batched_multi_get_cf_slice` C API,
1931    /// which takes a `rocksdb_slice_t` array directly. This eliminates the internal
1932    /// overhead of converting keys from separate pointer+size arrays to Slice objects.
1933    ///
1934    /// # Arguments
1935    ///
1936    /// * `cf` - The column family to read from
1937    /// * `keys` - An iterator of keys to look up
1938    /// * `sorted_input` - If `true`, indicates the keys are already sorted in ascending
1939    ///   order, which allows RocksDB to skip internal sorting and improve performance.
1940    ///   **Important**: If you pass `true` but keys are not sorted, results may be incorrect.
1941    ///
1942    /// # Returns
1943    ///
1944    /// A vector of results in the same order as the input keys. Each element is:
1945    /// - `Ok(Some(DBPinnableSlice))` if the key was found
1946    /// - `Ok(None)` if the key was not found
1947    /// - `Err(...)` if an error occurred for that key
1948    ///
1949    /// # Performance
1950    ///
1951    /// This is the fastest batch lookup method when:
1952    /// - You're looking up many keys (10+) from the same column family
1953    /// - You can pre-sort your keys (set `sorted_input = true`)
1954    /// - Block-based table format is used (default)
1955    ///
1956    /// For small numbers of keys, the overhead of batching may not be worth it.
1957    /// Consider using [`get_pinned_cf`](#method.get_pinned_cf) for single key lookups.
1958    ///
1959    /// # Example
1960    ///
1961    /// ```
1962    /// use rust_rocksdb::{DB, Options, ColumnFamilyDescriptor};
1963    ///
1964    /// let tempdir = tempfile::Builder::new().prefix("batch_slice").tempdir().unwrap();
1965    /// let mut opts = Options::default();
1966    /// opts.create_if_missing(true);
1967    /// opts.create_missing_column_families(true);
1968    /// let db = DB::open_cf_descriptors(&opts, tempdir.path(),
1969    ///     vec![ColumnFamilyDescriptor::new("cf", Options::default())]).unwrap();
1970    ///
1971    /// let cf = db.cf_handle("cf").unwrap();
1972    /// db.put_cf(&cf, b"k1", b"v1").unwrap();
1973    /// db.put_cf(&cf, b"k2", b"v2").unwrap();
1974    ///
1975    /// // Keys are sorted, so we can set sorted_input = true
1976    /// let keys: Vec<&[u8]> = vec![b"k1", b"k2", b"k3"];
1977    /// let results = db.batched_multi_get_cf_slice(&cf, keys, true);
1978    ///
1979    /// assert!(results[0].as_ref().unwrap().is_some()); // k1 found
1980    /// assert!(results[1].as_ref().unwrap().is_some()); // k2 found
1981    /// assert!(results[2].as_ref().unwrap().is_none()); // k3 not found
1982    /// ```
1983    pub fn batched_multi_get_cf_slice<'a, K, I>(
1984        &'_ self,
1985        cf: &impl AsColumnFamilyRef,
1986        keys: I,
1987        sorted_input: bool,
1988    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
1989    where
1990        K: AsRef<[u8]> + 'a + ?Sized,
1991        I: IntoIterator<Item = &'a K>,
1992    {
1993        DEFAULT_READ_OPTS
1994            .with(|opts| self.batched_multi_get_cf_slice_opt(cf, keys, sorted_input, opts))
1995    }
1996
1997    /// Return the values associated with the given keys and the specified column family
1998    /// using an optimized slice-based API with custom read options.
1999    ///
2000    /// This is the same as [`batched_multi_get_cf_slice`](#method.batched_multi_get_cf_slice)
2001    /// but allows specifying custom [`ReadOptions`].
2002    ///
2003    /// See [`batched_multi_get_cf_slice`](#method.batched_multi_get_cf_slice) for full documentation.
2004    pub fn batched_multi_get_cf_slice_opt<'a, K, I>(
2005        &'_ self,
2006        cf: &impl AsColumnFamilyRef,
2007        keys: I,
2008        sorted_input: bool,
2009        readopts: &ReadOptions,
2010    ) -> Vec<Result<Option<DBPinnableSlice<'_>>, Error>>
2011    where
2012        K: AsRef<[u8]> + 'a + ?Sized,
2013        I: IntoIterator<Item = &'a K>,
2014    {
2015        // Convert keys to rocksdb_slice_t array
2016        let slices: Vec<ffi::rocksdb_slice_t> = keys
2017            .into_iter()
2018            .map(|k| {
2019                let k = k.as_ref();
2020                ffi::rocksdb_slice_t {
2021                    data: k.as_ptr() as *const c_char,
2022                    size: k.len(),
2023                }
2024            })
2025            .collect();
2026
2027        self.batched_multi_get_pinned_inner(cf.inner(), &slices, sorted_input, readopts)
2028    }
2029
2030    fn key_slices<K: AsRef<[u8]>>(keys: &[K]) -> Vec<ffi::rocksdb_slice_t> {
2031        keys.iter()
2032            .map(|key| {
2033                let key = key.as_ref();
2034                ffi::rocksdb_slice_t {
2035                    data: key.as_ptr() as *const c_char,
2036                    size: key.len(),
2037                }
2038            })
2039            .collect()
2040    }
2041
2042    fn batched_multi_get_pinned_owned<'a, K: AsRef<[u8]>>(
2043        &'a self,
2044        keys: &[K],
2045        sorted_input: bool,
2046        readopts: &ReadOptions,
2047    ) -> Vec<Result<Option<DBPinnableSlice<'a>>, Error>> {
2048        let key_slices = Self::key_slices(keys);
2049        if key_slices.is_empty() {
2050            return Vec::new();
2051        }
2052        let default_cf = OwnedColumnFamilyHandle::default_for(self.inner.inner());
2053        self.batched_multi_get_pinned_inner(default_cf.inner, &key_slices, sorted_input, readopts)
2054    }
2055
2056    fn create_pinnable_batch<'a>(
2057        &'a self,
2058        cf: *mut ffi::rocksdb_column_family_handle_t,
2059        keys: &[ffi::rocksdb_slice_t],
2060        sorted_input: bool,
2061        readopts: &ReadOptions,
2062    ) -> Result<DBPinnableBatch<'a>, Error> {
2063        let batch = unsafe {
2064            ffi_try!(ffi::rust_rocksdb_batched_multi_get_pinned(
2065                self.inner.inner(),
2066                readopts.inner,
2067                cf,
2068                keys.len(),
2069                keys.as_ptr(),
2070                c_uchar::from(sorted_input),
2071            ))
2072        };
2073        if batch.is_null() {
2074            // `ffi_try!` only returns early when the extension set `errptr`.
2075            // A null batch with no error means the extension could not even
2076            // allocate the message, so report it instead of unwrapping.
2077            return Err(Error::new(
2078                "rust_rocksdb_batched_multi_get_pinned returned no batch".to_owned(),
2079            ));
2080        }
2081        // SAFETY: The extension returns a uniquely owned batch.
2082        Ok(unsafe { DBPinnableBatch::from_c(batch) })
2083    }
2084
2085    fn batched_multi_get_pinned_inner<'a>(
2086        &'a self,
2087        cf: *mut ffi::rocksdb_column_family_handle_t,
2088        keys: &[ffi::rocksdb_slice_t],
2089        sorted_input: bool,
2090        readopts: &ReadOptions,
2091    ) -> Vec<Result<Option<DBPinnableSlice<'a>>, Error>> {
2092        if keys.is_empty() {
2093            return Vec::new();
2094        }
2095        let output = match self.execute_batched_multi_get(cf, keys, sorted_input, readopts) {
2096            Ok(output) => output,
2097            Err(error) => {
2098                let message = error.to_string();
2099                return (0..keys.len())
2100                    .map(|_| Err(Error::new(message.clone())))
2101                    .collect();
2102            }
2103        };
2104        output
2105            .values
2106            .into_iter()
2107            .zip(output.errors)
2108            .map(|(value, error)| unsafe { Self::convert_pinned_result(value, error) })
2109            .collect()
2110    }
2111
2112    fn execute_batched_multi_get(
2113        &self,
2114        cf: *mut ffi::rocksdb_column_family_handle_t,
2115        keys: &[ffi::rocksdb_slice_t],
2116        sorted_input: bool,
2117        readopts: &ReadOptions,
2118    ) -> Result<PinnedMultiGetOutput, Error> {
2119        let mut pinned_values = vec![ptr::null_mut(); keys.len()];
2120        let mut errors = vec![ptr::null_mut(); keys.len()];
2121        unsafe {
2122            ffi_try!(ffi::rust_rocksdb_batched_multi_get_cf_slice_safe(
2123                self.inner.inner(),
2124                readopts.inner,
2125                cf,
2126                keys.len(),
2127                keys.as_ptr(),
2128                pinned_values.as_mut_ptr(),
2129                errors.as_mut_ptr(),
2130                c_uchar::from(sorted_input),
2131            ));
2132        }
2133        Ok(PinnedMultiGetOutput {
2134            values: pinned_values,
2135            errors,
2136        })
2137    }
2138
2139    /// Converts one result returned by `rocksdb_batched_multi_get_cf_slice`.
2140    ///
2141    /// # Safety
2142    ///
2143    /// `value` must be null or an owned pinnable slice. `error` must be null or
2144    /// an owned RocksDB error string.
2145    unsafe fn convert_pinned_result<'a>(
2146        value: *mut ffi::rocksdb_pinnableslice_t,
2147        error: *mut c_char,
2148    ) -> Result<Option<DBPinnableSlice<'a>>, Error> {
2149        if error.is_null() {
2150            return Ok((!value.is_null()).then(|| unsafe { DBPinnableSlice::from_c(value) }));
2151        }
2152        if !value.is_null() {
2153            unsafe {
2154                ffi::rocksdb_pinnableslice_destroy(value);
2155            }
2156        }
2157        Err(convert_rocksdb_error(error))
2158    }
2159
2160    /// Returns `false` if the given key definitely doesn't exist in the database, otherwise returns
2161    /// `true`. This function uses default `ReadOptions`.
2162    pub fn key_may_exist<K: AsRef<[u8]>>(&self, key: K) -> bool {
2163        DEFAULT_READ_OPTS.with(|opts| self.key_may_exist_opt(key, opts))
2164    }
2165
2166    /// Returns `false` if the given key definitely doesn't exist in the database, otherwise returns
2167    /// `true`.
2168    pub fn key_may_exist_opt<K: AsRef<[u8]>>(&self, key: K, readopts: &ReadOptions) -> bool {
2169        let key = key.as_ref();
2170        unsafe {
2171            0 != ffi::rocksdb_key_may_exist(
2172                self.inner.inner(),
2173                readopts.inner,
2174                key.as_ptr() as *const c_char,
2175                key.len() as size_t,
2176                ptr::null_mut(), /*value*/
2177                ptr::null_mut(), /*val_len*/
2178                ptr::null(),     /*timestamp*/
2179                0,               /*timestamp_len*/
2180                ptr::null_mut(), /*value_found*/
2181            )
2182        }
2183    }
2184
2185    /// Returns `false` if the given key definitely doesn't exist in the specified column family,
2186    /// otherwise returns `true`. This function uses default `ReadOptions`.
2187    pub fn key_may_exist_cf<K: AsRef<[u8]>>(&self, cf: &impl AsColumnFamilyRef, key: K) -> bool {
2188        DEFAULT_READ_OPTS.with(|opts| self.key_may_exist_cf_opt(cf, key, opts))
2189    }
2190
2191    /// Returns `false` if the given key definitely doesn't exist in the specified column family,
2192    /// otherwise returns `true`.
2193    pub fn key_may_exist_cf_opt<K: AsRef<[u8]>>(
2194        &self,
2195        cf: &impl AsColumnFamilyRef,
2196        key: K,
2197        readopts: &ReadOptions,
2198    ) -> bool {
2199        let key = key.as_ref();
2200        0 != unsafe {
2201            ffi::rocksdb_key_may_exist_cf(
2202                self.inner.inner(),
2203                readopts.inner,
2204                cf.inner(),
2205                key.as_ptr() as *const c_char,
2206                key.len() as size_t,
2207                ptr::null_mut(), /*value*/
2208                ptr::null_mut(), /*val_len*/
2209                ptr::null(),     /*timestamp*/
2210                0,               /*timestamp_len*/
2211                ptr::null_mut(), /*value_found*/
2212            )
2213        }
2214    }
2215
2216    /// If the key definitely does not exist in the database, then this method
2217    /// returns `(false, None)`, else `(true, None)` if it may.
2218    /// If the key is found in memory, then it returns `(true, Some<CSlice>)`.
2219    ///
2220    /// This check is potentially lighter-weight than calling `get()`. One way
2221    /// to make this lighter weight is to avoid doing any IOs.
2222    pub fn key_may_exist_cf_opt_value<K: AsRef<[u8]>>(
2223        &self,
2224        cf: &impl AsColumnFamilyRef,
2225        key: K,
2226        readopts: &ReadOptions,
2227    ) -> (bool, Option<CSlice>) {
2228        let key = key.as_ref();
2229        let mut val: *mut c_char = ptr::null_mut();
2230        let mut val_len: usize = 0;
2231        let mut value_found: c_uchar = 0;
2232        let may_exists = 0
2233            != unsafe {
2234                ffi::rocksdb_key_may_exist_cf(
2235                    self.inner.inner(),
2236                    readopts.inner,
2237                    cf.inner(),
2238                    key.as_ptr() as *const c_char,
2239                    key.len() as size_t,
2240                    &raw mut val,         /*value*/
2241                    &raw mut val_len,     /*val_len*/
2242                    ptr::null(),          /*timestamp*/
2243                    0,                    /*timestamp_len*/
2244                    &raw mut value_found, /*value_found*/
2245                )
2246            };
2247        // The value is only allocated (using malloc) and returned if it is found and
2248        // value_found isn't NULL. In that case the user is responsible for freeing it.
2249        if may_exists && value_found != 0 {
2250            (
2251                may_exists,
2252                Some(unsafe { CSlice::from_raw_parts(val, val_len) }),
2253            )
2254        } else {
2255            (may_exists, None)
2256        }
2257    }
2258
2259    fn create_inner_cf_handle(
2260        &self,
2261        name: impl CStrLike,
2262        opts: &Options,
2263    ) -> Result<*mut ffi::rocksdb_column_family_handle_t, Error> {
2264        let cf_name = name.bake().map_err(|err| {
2265            Error::new(format!(
2266                "Failed to convert path to CString when creating cf: {err}"
2267            ))
2268        })?;
2269
2270        // Can't use ffi_try: rocksdb_create_column_family has a bug where it allocates a
2271        // result that needs to be freed on error
2272        let mut err: *mut ::libc::c_char = ::std::ptr::null_mut();
2273        let cf_handle = unsafe {
2274            ffi::rocksdb_create_column_family(
2275                self.inner.inner(),
2276                opts.inner,
2277                cf_name.as_ptr(),
2278                &raw mut err,
2279            )
2280        };
2281        if !err.is_null() {
2282            if !cf_handle.is_null() {
2283                unsafe { ffi::rocksdb_column_family_handle_destroy(cf_handle) };
2284            }
2285            return Err(convert_rocksdb_error(err));
2286        }
2287        Ok(cf_handle)
2288    }
2289
2290    pub fn iterator<'a: 'b, 'b>(
2291        &'a self,
2292        mode: IteratorMode,
2293    ) -> DBIteratorWithThreadMode<'b, Self> {
2294        let readopts = ReadOptions::default();
2295        self.iterator_opt(mode, readopts)
2296    }
2297
2298    pub fn iterator_opt<'a: 'b, 'b>(
2299        &'a self,
2300        mode: IteratorMode,
2301        readopts: ReadOptions,
2302    ) -> DBIteratorWithThreadMode<'b, Self> {
2303        DBIteratorWithThreadMode::new(self, readopts, mode)
2304    }
2305
2306    /// Opens an iterator using the provided ReadOptions.
2307    /// This is used when you want to iterate over a specific ColumnFamily with a modified ReadOptions
2308    pub fn iterator_cf_opt<'a: 'b, 'b>(
2309        &'a self,
2310        cf_handle: &impl AsColumnFamilyRef,
2311        readopts: ReadOptions,
2312        mode: IteratorMode,
2313    ) -> DBIteratorWithThreadMode<'b, Self> {
2314        DBIteratorWithThreadMode::new_cf(self, cf_handle.inner(), readopts, mode)
2315    }
2316
2317    /// Opens an iterator with `set_total_order_seek` enabled.
2318    /// This must be used to iterate across prefixes when `set_memtable_factory` has been called
2319    /// with a Hash-based implementation.
2320    pub fn full_iterator<'a: 'b, 'b>(
2321        &'a self,
2322        mode: IteratorMode,
2323    ) -> DBIteratorWithThreadMode<'b, Self> {
2324        let mut opts = ReadOptions::default();
2325        opts.set_total_order_seek(true);
2326        DBIteratorWithThreadMode::new(self, opts, mode)
2327    }
2328
2329    pub fn prefix_iterator<'a: 'b, 'b, P: AsRef<[u8]>>(
2330        &'a self,
2331        prefix: P,
2332    ) -> DBIteratorWithThreadMode<'b, Self> {
2333        let mut opts = ReadOptions::default();
2334        opts.set_prefix_same_as_start(true);
2335        DBIteratorWithThreadMode::new(
2336            self,
2337            opts,
2338            IteratorMode::From(prefix.as_ref(), Direction::Forward),
2339        )
2340    }
2341
2342    pub fn iterator_cf<'a: 'b, 'b>(
2343        &'a self,
2344        cf_handle: &impl AsColumnFamilyRef,
2345        mode: IteratorMode,
2346    ) -> DBIteratorWithThreadMode<'b, Self> {
2347        let opts = ReadOptions::default();
2348        DBIteratorWithThreadMode::new_cf(self, cf_handle.inner(), opts, mode)
2349    }
2350
2351    pub fn full_iterator_cf<'a: 'b, 'b>(
2352        &'a self,
2353        cf_handle: &impl AsColumnFamilyRef,
2354        mode: IteratorMode,
2355    ) -> DBIteratorWithThreadMode<'b, Self> {
2356        let mut opts = ReadOptions::default();
2357        opts.set_total_order_seek(true);
2358        DBIteratorWithThreadMode::new_cf(self, cf_handle.inner(), opts, mode)
2359    }
2360
2361    pub fn prefix_iterator_cf<'a, P: AsRef<[u8]>>(
2362        &'a self,
2363        cf_handle: &impl AsColumnFamilyRef,
2364        prefix: P,
2365    ) -> DBIteratorWithThreadMode<'a, Self> {
2366        let mut opts = ReadOptions::default();
2367        opts.set_prefix_same_as_start(true);
2368        DBIteratorWithThreadMode::<'a, Self>::new_cf(
2369            self,
2370            cf_handle.inner(),
2371            opts,
2372            IteratorMode::From(prefix.as_ref(), Direction::Forward),
2373        )
2374    }
2375
2376    /// Returns `true` if there exists at least one key with the given prefix
2377    /// in the default column family using default read options.
2378    ///
2379    /// When to use: prefer this for one-shot checks. It enables
2380    /// `prefix_same_as_start(true)` and bounds the iterator to the
2381    /// prefix via `PrefixRange`, minimizing stray IO per call.
2382    pub fn prefix_exists<P: AsRef<[u8]>>(&self, prefix: P) -> Result<bool, Error> {
2383        let p = prefix.as_ref();
2384        with_prefix_read_opts(p, |opts| self.prefix_exists_opt(p, opts))
2385    }
2386
2387    /// Returns `true` if there exists at least one key with the given prefix
2388    /// in the default column family using the provided read options.
2389    pub fn prefix_exists_opt<P: AsRef<[u8]>>(
2390        &self,
2391        prefix: P,
2392        readopts: &ReadOptions,
2393    ) -> Result<bool, Error> {
2394        let prefix = prefix.as_ref();
2395        let iter = unsafe { self.create_iterator(readopts) };
2396        let res = unsafe {
2397            ffi::rocksdb_iter_seek(
2398                iter,
2399                prefix.as_ptr() as *const c_char,
2400                prefix.len() as size_t,
2401            );
2402            if ffi::rocksdb_iter_valid(iter) != 0 {
2403                let mut key_len: size_t = 0;
2404                let key_ptr = ffi::rocksdb_iter_key(iter, &raw mut key_len);
2405                // An empty key is legal, and `from_raw_parts` wants a
2406                // dereferenceable pointer even at length 0.
2407                let key = if key_len == 0 {
2408                    &[][..]
2409                } else {
2410                    slice::from_raw_parts(key_ptr.cast::<u8>(), key_len as usize)
2411                };
2412                Ok(key.starts_with(prefix))
2413            } else if let Err(e) = (|| {
2414                // Check status to differentiate end-of-range vs error
2415                ffi_try!(ffi::rocksdb_iter_get_error(iter));
2416                Ok::<(), Error>(())
2417            })() {
2418                Err(e)
2419            } else {
2420                Ok(false)
2421            }
2422        };
2423        unsafe { ffi::rocksdb_iter_destroy(iter) };
2424        res
2425    }
2426
2427    /// Creates a reusable prefix prober over the default column family using
2428    /// read options optimized for prefix probes.
2429    ///
2430    /// When to use: prefer this in hot loops with many checks per second. It
2431    /// reuses a raw iterator to avoid per-call allocation/FFI overhead. If you
2432    /// need custom tuning (e.g. async IO, readahead, cache-only), use
2433    /// `prefix_prober_with_opts`.
2434    pub fn prefix_prober(&self) -> PrefixProber<'_, Self> {
2435        let mut opts = ReadOptions::default();
2436        opts.set_prefix_same_as_start(true);
2437        PrefixProber {
2438            raw: DBRawIteratorWithThreadMode::new(self, opts),
2439        }
2440    }
2441
2442    /// Creates a reusable prefix prober over the default column family using
2443    /// the provided read options (owned).
2444    ///
2445    /// When to use: advanced tuning for heavy workloads. Callers can set
2446    /// `set_async_io(true)`, `set_readahead_size`, `set_read_tier`, etc. Note:
2447    /// the prober owns `ReadOptions` to keep internal buffers alive.
2448    pub fn prefix_prober_with_opts(&self, readopts: ReadOptions) -> PrefixProber<'_, Self> {
2449        PrefixProber {
2450            raw: DBRawIteratorWithThreadMode::new(self, readopts),
2451        }
2452    }
2453
2454    /// Creates a reusable prefix prober over the specified column family using
2455    /// read options optimized for prefix probes.
2456    pub fn prefix_prober_cf(&self, cf_handle: &impl AsColumnFamilyRef) -> PrefixProber<'_, Self> {
2457        let mut opts = ReadOptions::default();
2458        opts.set_prefix_same_as_start(true);
2459        PrefixProber {
2460            raw: DBRawIteratorWithThreadMode::new_cf(self, cf_handle.inner(), opts),
2461        }
2462    }
2463
2464    /// Creates a reusable prefix prober over the specified column family using
2465    /// the provided read options (owned).
2466    ///
2467    /// When to use: advanced tuning for heavy workloads on a specific CF.
2468    pub fn prefix_prober_cf_with_opts(
2469        &self,
2470        cf_handle: &impl AsColumnFamilyRef,
2471        readopts: ReadOptions,
2472    ) -> PrefixProber<'_, Self> {
2473        PrefixProber {
2474            raw: DBRawIteratorWithThreadMode::new_cf(self, cf_handle.inner(), readopts),
2475        }
2476    }
2477
2478    /// Returns `true` if there exists at least one key with the given prefix
2479    /// in the specified column family using default read options.
2480    ///
2481    /// When to use: one-shot checks on a CF. Enables
2482    /// `prefix_same_as_start(true)` and bounds the iterator via `PrefixRange`.
2483    pub fn prefix_exists_cf<P: AsRef<[u8]>>(
2484        &self,
2485        cf_handle: &impl AsColumnFamilyRef,
2486        prefix: P,
2487    ) -> Result<bool, Error> {
2488        let p = prefix.as_ref();
2489        with_prefix_read_opts(p, |opts| self.prefix_exists_cf_opt(cf_handle, p, opts))
2490    }
2491
2492    /// Returns `true` if there exists at least one key with the given prefix
2493    /// in the specified column family using the provided read options.
2494    pub fn prefix_exists_cf_opt<P: AsRef<[u8]>>(
2495        &self,
2496        cf_handle: &impl AsColumnFamilyRef,
2497        prefix: P,
2498        readopts: &ReadOptions,
2499    ) -> Result<bool, Error> {
2500        let prefix = prefix.as_ref();
2501        let iter = unsafe { self.create_iterator_cf(cf_handle.inner(), readopts) };
2502        let res = unsafe {
2503            ffi::rocksdb_iter_seek(
2504                iter,
2505                prefix.as_ptr() as *const c_char,
2506                prefix.len() as size_t,
2507            );
2508            if ffi::rocksdb_iter_valid(iter) != 0 {
2509                let mut key_len: size_t = 0;
2510                let key_ptr = ffi::rocksdb_iter_key(iter, &raw mut key_len);
2511                // An empty key is legal, and `from_raw_parts` wants a
2512                // dereferenceable pointer even at length 0.
2513                let key = if key_len == 0 {
2514                    &[][..]
2515                } else {
2516                    slice::from_raw_parts(key_ptr.cast::<u8>(), key_len as usize)
2517                };
2518                Ok(key.starts_with(prefix))
2519            } else if let Err(e) = (|| {
2520                ffi_try!(ffi::rocksdb_iter_get_error(iter));
2521                Ok::<(), Error>(())
2522            })() {
2523                Err(e)
2524            } else {
2525                Ok(false)
2526            }
2527        };
2528        unsafe { ffi::rocksdb_iter_destroy(iter) };
2529        res
2530    }
2531
2532    /// Opens a raw iterator over the database, using the default read options
2533    pub fn raw_iterator<'a: 'b, 'b>(&'a self) -> DBRawIteratorWithThreadMode<'b, Self> {
2534        let opts = ReadOptions::default();
2535        DBRawIteratorWithThreadMode::new(self, opts)
2536    }
2537
2538    /// Opens a raw iterator over the given column family, using the default read options
2539    pub fn raw_iterator_cf<'a: 'b, 'b>(
2540        &'a self,
2541        cf_handle: &impl AsColumnFamilyRef,
2542    ) -> DBRawIteratorWithThreadMode<'b, Self> {
2543        let opts = ReadOptions::default();
2544        DBRawIteratorWithThreadMode::new_cf(self, cf_handle.inner(), opts)
2545    }
2546
2547    /// Opens raw iterators for multiple column families from one consistent
2548    /// RocksDB state.
2549    ///
2550    /// The returned iterators match the input column family order and own their
2551    /// native handles. They share one `ReadOptions`, because one native
2552    /// `rocksdb_create_iterators` call applies a single options object to every
2553    /// iterator it creates.
2554    pub fn raw_iterators_cf<'a, 'b, W, I>(
2555        &'a self,
2556        column_families: I,
2557    ) -> Result<Vec<DBRawIteratorWithThreadMode<'a, Self>>, Error>
2558    where
2559        W: AsColumnFamilyRef + 'b,
2560        I: IntoIterator<Item = &'b W>,
2561    {
2562        let mut cf_handles: Vec<_> = column_families
2563            .into_iter()
2564            .map(AsColumnFamilyRef::inner)
2565            .collect();
2566        if cf_handles.is_empty() {
2567            return Ok(Vec::new());
2568        }
2569        let created = self.create_iterators_cf(&mut cf_handles)?;
2570        Ok(created
2571            .handles
2572            .into_iter()
2573            .map(|handle| {
2574                DBRawIteratorWithThreadMode::from_inner(handle, Arc::clone(&created.readopts))
2575            })
2576            .collect())
2577    }
2578
2579    fn create_iterators_cf(
2580        &self,
2581        cf_handles: &mut [*mut ffi::rocksdb_column_family_handle_t],
2582    ) -> Result<CreatedIterators, Error> {
2583        let mut iterator_handles = vec![ptr::null_mut(); cf_handles.len()];
2584        // Every iterator gets a handle on this. RocksDB's `DBIter` stores raw
2585        // `Slice*` into the options for iterate_lower_bound, iterate_upper_bound
2586        // and the read timestamps, and `ArenaWrappedDBIter::Refresh` re-reads
2587        // them, so the options have to outlive the last iterator rather than
2588        // this function. See issue #660.
2589        let readopts = Arc::new(ReadOptions::default());
2590        unsafe {
2591            ffi_try!(ffi::rust_rocksdb_create_iterators_safe(
2592                self.inner.inner(),
2593                readopts.inner,
2594                cf_handles.as_mut_ptr(),
2595                iterator_handles.as_mut_ptr(),
2596                iterator_handles.len(),
2597            ));
2598        }
2599        Self::validate_created_iterators(&iterator_handles)?;
2600        Ok(CreatedIterators {
2601            readopts,
2602            handles: iterator_handles,
2603        })
2604    }
2605
2606    fn validate_created_iterators(
2607        iterator_handles: &[*mut ffi::rocksdb_iterator_t],
2608    ) -> Result<(), Error> {
2609        if iterator_handles.iter().any(|iterator| iterator.is_null()) {
2610            unsafe {
2611                Self::destroy_iterators(iterator_handles);
2612            }
2613            return Err(Error::new(
2614                "rocksdb_create_iterators returned a null iterator".to_owned(),
2615            ));
2616        }
2617        Ok(())
2618    }
2619
2620    /// Destroys non-null iterator handles owned by the caller.
2621    ///
2622    /// # Safety
2623    ///
2624    /// Every non-null pointer must identify a live, uniquely owned RocksDB iterator.
2625    unsafe fn destroy_iterators(iterators: &[*mut ffi::rocksdb_iterator_t]) {
2626        for &iterator in iterators {
2627            if !iterator.is_null() {
2628                unsafe {
2629                    ffi::rocksdb_iter_destroy(iterator);
2630                }
2631            }
2632        }
2633    }
2634
2635    /// Opens a raw iterator over the database, using the given read options
2636    pub fn raw_iterator_opt<'a: 'b, 'b>(
2637        &'a self,
2638        readopts: ReadOptions,
2639    ) -> DBRawIteratorWithThreadMode<'b, Self> {
2640        DBRawIteratorWithThreadMode::new(self, readopts)
2641    }
2642
2643    /// Opens a raw iterator over the given column family, using the given read options
2644    pub fn raw_iterator_cf_opt<'a: 'b, 'b>(
2645        &'a self,
2646        cf_handle: &impl AsColumnFamilyRef,
2647        readopts: ReadOptions,
2648    ) -> DBRawIteratorWithThreadMode<'b, Self> {
2649        DBRawIteratorWithThreadMode::new_cf(self, cf_handle.inner(), readopts)
2650    }
2651
2652    pub fn snapshot(&'_ self) -> SnapshotWithThreadMode<'_, Self> {
2653        SnapshotWithThreadMode::<Self>::new(self)
2654    }
2655
2656    pub fn put_opt<K, V>(&self, key: K, value: V, writeopts: &WriteOptions) -> Result<(), Error>
2657    where
2658        K: AsRef<[u8]>,
2659        V: AsRef<[u8]>,
2660    {
2661        let key = key.as_ref();
2662        let value = value.as_ref();
2663
2664        unsafe {
2665            ffi_try!(ffi::rocksdb_put(
2666                self.inner.inner(),
2667                writeopts.inner,
2668                key.as_ptr() as *const c_char,
2669                key.len() as size_t,
2670                value.as_ptr() as *const c_char,
2671                value.len() as size_t,
2672            ));
2673            Ok(())
2674        }
2675    }
2676
2677    pub fn put_cf_opt<K, V>(
2678        &self,
2679        cf: &impl AsColumnFamilyRef,
2680        key: K,
2681        value: V,
2682        writeopts: &WriteOptions,
2683    ) -> Result<(), Error>
2684    where
2685        K: AsRef<[u8]>,
2686        V: AsRef<[u8]>,
2687    {
2688        let key = key.as_ref();
2689        let value = value.as_ref();
2690
2691        unsafe {
2692            ffi_try!(ffi::rocksdb_put_cf(
2693                self.inner.inner(),
2694                writeopts.inner,
2695                cf.inner(),
2696                key.as_ptr() as *const c_char,
2697                key.len() as size_t,
2698                value.as_ptr() as *const c_char,
2699                value.len() as size_t,
2700            ));
2701            Ok(())
2702        }
2703    }
2704
2705    /// Set the database entry for "key" to "value" with WriteOptions.
2706    /// If "key" already exists, it will coexist with previous entry.
2707    /// `Get` with a timestamp ts specified in ReadOptions will return
2708    /// the most recent key/value whose timestamp is smaller than or equal to ts.
2709    /// Takes an additional argument `ts` as the timestamp.
2710    /// Note: the DB must be opened with user defined timestamp enabled.
2711    pub fn put_with_ts_opt<K, V, S>(
2712        &self,
2713        key: K,
2714        ts: S,
2715        value: V,
2716        writeopts: &WriteOptions,
2717    ) -> Result<(), Error>
2718    where
2719        K: AsRef<[u8]>,
2720        V: AsRef<[u8]>,
2721        S: AsRef<[u8]>,
2722    {
2723        let key = key.as_ref();
2724        let value = value.as_ref();
2725        let ts = ts.as_ref();
2726        unsafe {
2727            ffi_try!(ffi::rocksdb_put_with_ts(
2728                self.inner.inner(),
2729                writeopts.inner,
2730                key.as_ptr() as *const c_char,
2731                key.len() as size_t,
2732                ts.as_ptr() as *const c_char,
2733                ts.len() as size_t,
2734                value.as_ptr() as *const c_char,
2735                value.len() as size_t,
2736            ));
2737            Ok(())
2738        }
2739    }
2740
2741    /// Put with timestamp in a specific column family with WriteOptions.
2742    /// If "key" already exists, it will coexist with previous entry.
2743    /// `Get` with a timestamp ts specified in ReadOptions will return
2744    /// the most recent key/value whose timestamp is smaller than or equal to ts.
2745    /// Takes an additional argument `ts` as the timestamp.
2746    /// Note: the DB must be opened with user defined timestamp enabled.
2747    pub fn put_cf_with_ts_opt<K, V, S>(
2748        &self,
2749        cf: &impl AsColumnFamilyRef,
2750        key: K,
2751        ts: S,
2752        value: V,
2753        writeopts: &WriteOptions,
2754    ) -> Result<(), Error>
2755    where
2756        K: AsRef<[u8]>,
2757        V: AsRef<[u8]>,
2758        S: AsRef<[u8]>,
2759    {
2760        let key = key.as_ref();
2761        let value = value.as_ref();
2762        let ts = ts.as_ref();
2763        unsafe {
2764            ffi_try!(ffi::rocksdb_put_cf_with_ts(
2765                self.inner.inner(),
2766                writeopts.inner,
2767                cf.inner(),
2768                key.as_ptr() as *const c_char,
2769                key.len() as size_t,
2770                ts.as_ptr() as *const c_char,
2771                ts.len() as size_t,
2772                value.as_ptr() as *const c_char,
2773                value.len() as size_t,
2774            ));
2775            Ok(())
2776        }
2777    }
2778
2779    pub fn merge_opt<K, V>(&self, key: K, value: V, writeopts: &WriteOptions) -> Result<(), Error>
2780    where
2781        K: AsRef<[u8]>,
2782        V: AsRef<[u8]>,
2783    {
2784        let key = key.as_ref();
2785        let value = value.as_ref();
2786
2787        unsafe {
2788            ffi_try!(ffi::rocksdb_merge(
2789                self.inner.inner(),
2790                writeopts.inner,
2791                key.as_ptr() as *const c_char,
2792                key.len() as size_t,
2793                value.as_ptr() as *const c_char,
2794                value.len() as size_t,
2795            ));
2796            Ok(())
2797        }
2798    }
2799
2800    pub fn merge_cf_opt<K, V>(
2801        &self,
2802        cf: &impl AsColumnFamilyRef,
2803        key: K,
2804        value: V,
2805        writeopts: &WriteOptions,
2806    ) -> Result<(), Error>
2807    where
2808        K: AsRef<[u8]>,
2809        V: AsRef<[u8]>,
2810    {
2811        let key = key.as_ref();
2812        let value = value.as_ref();
2813
2814        unsafe {
2815            ffi_try!(ffi::rocksdb_merge_cf(
2816                self.inner.inner(),
2817                writeopts.inner,
2818                cf.inner(),
2819                key.as_ptr() as *const c_char,
2820                key.len() as size_t,
2821                value.as_ptr() as *const c_char,
2822                value.len() as size_t,
2823            ));
2824            Ok(())
2825        }
2826    }
2827
2828    pub fn delete_opt<K: AsRef<[u8]>>(
2829        &self,
2830        key: K,
2831        writeopts: &WriteOptions,
2832    ) -> Result<(), Error> {
2833        let key = key.as_ref();
2834
2835        unsafe {
2836            ffi_try!(ffi::rocksdb_delete(
2837                self.inner.inner(),
2838                writeopts.inner,
2839                key.as_ptr() as *const c_char,
2840                key.len() as size_t,
2841            ));
2842            Ok(())
2843        }
2844    }
2845
2846    pub fn delete_cf_opt<K: AsRef<[u8]>>(
2847        &self,
2848        cf: &impl AsColumnFamilyRef,
2849        key: K,
2850        writeopts: &WriteOptions,
2851    ) -> Result<(), Error> {
2852        let key = key.as_ref();
2853
2854        unsafe {
2855            ffi_try!(ffi::rocksdb_delete_cf(
2856                self.inner.inner(),
2857                writeopts.inner,
2858                cf.inner(),
2859                key.as_ptr() as *const c_char,
2860                key.len() as size_t,
2861            ));
2862            Ok(())
2863        }
2864    }
2865
2866    /// Remove the database entry (if any) for "key" with WriteOptions.
2867    /// Takes an additional argument `ts` as the timestamp.
2868    /// Note: the DB must be opened with user defined timestamp enabled.
2869    pub fn delete_with_ts_opt<K, S>(
2870        &self,
2871        key: K,
2872        ts: S,
2873        writeopts: &WriteOptions,
2874    ) -> Result<(), Error>
2875    where
2876        K: AsRef<[u8]>,
2877        S: AsRef<[u8]>,
2878    {
2879        let key = key.as_ref();
2880        let ts = ts.as_ref();
2881        unsafe {
2882            ffi_try!(ffi::rocksdb_delete_with_ts(
2883                self.inner.inner(),
2884                writeopts.inner,
2885                key.as_ptr() as *const c_char,
2886                key.len() as size_t,
2887                ts.as_ptr() as *const c_char,
2888                ts.len() as size_t,
2889            ));
2890            Ok(())
2891        }
2892    }
2893
2894    /// Delete with timestamp in a specific column family with WriteOptions.
2895    /// Takes an additional argument `ts` as the timestamp.
2896    /// Note: the DB must be opened with user defined timestamp enabled.
2897    pub fn delete_cf_with_ts_opt<K, S>(
2898        &self,
2899        cf: &impl AsColumnFamilyRef,
2900        key: K,
2901        ts: S,
2902        writeopts: &WriteOptions,
2903    ) -> Result<(), Error>
2904    where
2905        K: AsRef<[u8]>,
2906        S: AsRef<[u8]>,
2907    {
2908        let key = key.as_ref();
2909        let ts = ts.as_ref();
2910        unsafe {
2911            ffi_try!(ffi::rocksdb_delete_cf_with_ts(
2912                self.inner.inner(),
2913                writeopts.inner,
2914                cf.inner(),
2915                key.as_ptr() as *const c_char,
2916                key.len() as size_t,
2917                ts.as_ptr() as *const c_char,
2918                ts.len() as size_t,
2919            ));
2920            Ok(())
2921        }
2922    }
2923
2924    pub fn put<K, V>(&self, key: K, value: V) -> Result<(), Error>
2925    where
2926        K: AsRef<[u8]>,
2927        V: AsRef<[u8]>,
2928    {
2929        DEFAULT_WRITE_OPTS.with(|opts| self.put_opt(key, value, opts))
2930    }
2931
2932    pub fn put_cf<K, V>(&self, cf: &impl AsColumnFamilyRef, key: K, value: V) -> Result<(), Error>
2933    where
2934        K: AsRef<[u8]>,
2935        V: AsRef<[u8]>,
2936    {
2937        DEFAULT_WRITE_OPTS.with(|opts| self.put_cf_opt(cf, key, value, opts))
2938    }
2939
2940    /// Set the database entry for "key" to "value".
2941    /// If "key" already exists, it will coexist with previous entry.
2942    /// `Get` with a timestamp ts specified in ReadOptions will return
2943    /// the most recent key/value whose timestamp is smaller than or equal to ts.
2944    /// Takes an additional argument `ts` as the timestamp.
2945    /// Note: the DB must be opened with user defined timestamp enabled.
2946    pub fn put_with_ts<K, V, S>(&self, key: K, ts: S, value: V) -> Result<(), Error>
2947    where
2948        K: AsRef<[u8]>,
2949        V: AsRef<[u8]>,
2950        S: AsRef<[u8]>,
2951    {
2952        DEFAULT_WRITE_OPTS
2953            .with(|opts| self.put_with_ts_opt(key.as_ref(), ts.as_ref(), value.as_ref(), opts))
2954    }
2955
2956    /// Put with timestamp in a specific column family.
2957    /// If "key" already exists, it will coexist with previous entry.
2958    /// `Get` with a timestamp ts specified in ReadOptions will return
2959    /// the most recent key/value whose timestamp is smaller than or equal to ts.
2960    /// Takes an additional argument `ts` as the timestamp.
2961    /// Note: the DB must be opened with user defined timestamp enabled.
2962    pub fn put_cf_with_ts<K, V, S>(
2963        &self,
2964        cf: &impl AsColumnFamilyRef,
2965        key: K,
2966        ts: S,
2967        value: V,
2968    ) -> Result<(), Error>
2969    where
2970        K: AsRef<[u8]>,
2971        V: AsRef<[u8]>,
2972        S: AsRef<[u8]>,
2973    {
2974        DEFAULT_WRITE_OPTS.with(|opts| {
2975            self.put_cf_with_ts_opt(cf, key.as_ref(), ts.as_ref(), value.as_ref(), opts)
2976        })
2977    }
2978
2979    pub fn merge<K, V>(&self, key: K, value: V) -> Result<(), Error>
2980    where
2981        K: AsRef<[u8]>,
2982        V: AsRef<[u8]>,
2983    {
2984        DEFAULT_WRITE_OPTS.with(|opts| self.merge_opt(key, value, opts))
2985    }
2986
2987    pub fn merge_cf<K, V>(&self, cf: &impl AsColumnFamilyRef, key: K, value: V) -> Result<(), Error>
2988    where
2989        K: AsRef<[u8]>,
2990        V: AsRef<[u8]>,
2991    {
2992        DEFAULT_WRITE_OPTS.with(|opts| self.merge_cf_opt(cf, key, value, opts))
2993    }
2994
2995    pub fn delete<K: AsRef<[u8]>>(&self, key: K) -> Result<(), Error> {
2996        DEFAULT_WRITE_OPTS.with(|opts| self.delete_opt(key, opts))
2997    }
2998
2999    pub fn delete_cf<K: AsRef<[u8]>>(
3000        &self,
3001        cf: &impl AsColumnFamilyRef,
3002        key: K,
3003    ) -> Result<(), Error> {
3004        DEFAULT_WRITE_OPTS.with(|opts| self.delete_cf_opt(cf, key, opts))
3005    }
3006
3007    /// Remove the database entry (if any) for "key".
3008    /// Takes an additional argument `ts` as the timestamp.
3009    /// Note: the DB must be opened with user defined timestamp enabled.
3010    pub fn delete_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
3011        &self,
3012        key: K,
3013        ts: S,
3014    ) -> Result<(), Error> {
3015        DEFAULT_WRITE_OPTS.with(|opts| self.delete_with_ts_opt(key, ts, opts))
3016    }
3017
3018    /// Delete with timestamp in a specific column family.
3019    /// Takes an additional argument `ts` as the timestamp.
3020    /// Note: the DB must be opened with user defined timestamp enabled.
3021    pub fn delete_cf_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
3022        &self,
3023        cf: &impl AsColumnFamilyRef,
3024        key: K,
3025        ts: S,
3026    ) -> Result<(), Error> {
3027        DEFAULT_WRITE_OPTS.with(|opts| self.delete_cf_with_ts_opt(cf, key, ts, opts))
3028    }
3029
3030    /// Remove the database entry for "key" with WriteOptions.
3031    ///
3032    /// Requires that the key exists and was not overwritten. Returns OK on success,
3033    /// and a non-OK status on error. It is not an error if "key" did not exist in the database.
3034    ///
3035    /// If a key is overwritten (by calling Put() multiple times), then the result
3036    /// of calling SingleDelete() on this key is undefined. SingleDelete() only
3037    /// behaves correctly if there has been only one Put() for this key since the
3038    /// previous call to SingleDelete() for this key.
3039    ///
3040    /// This feature is currently an experimental performance optimization
3041    /// for a very specific workload. It is up to the caller to ensure that
3042    /// SingleDelete is only used for a key that is not deleted using Delete() or
3043    /// written using Merge(). Mixing SingleDelete operations with Deletes and
3044    /// Merges can result in undefined behavior.
3045    ///
3046    /// Note: consider setting options.sync = true.
3047    ///
3048    /// For more information, see <https://github.com/facebook/rocksdb/wiki/Single-Delete>
3049    pub fn single_delete_opt<K: AsRef<[u8]>>(
3050        &self,
3051        key: K,
3052        writeopts: &WriteOptions,
3053    ) -> Result<(), Error> {
3054        let key = key.as_ref();
3055
3056        unsafe {
3057            ffi_try!(ffi::rocksdb_singledelete(
3058                self.inner.inner(),
3059                writeopts.inner,
3060                key.as_ptr() as *const c_char,
3061                key.len() as size_t,
3062            ));
3063            Ok(())
3064        }
3065    }
3066
3067    /// Remove the database entry for "key" from a specific column family with WriteOptions.
3068    ///
3069    /// See single_delete_opt() for detailed behavior and restrictions.
3070    pub fn single_delete_cf_opt<K: AsRef<[u8]>>(
3071        &self,
3072        cf: &impl AsColumnFamilyRef,
3073        key: K,
3074        writeopts: &WriteOptions,
3075    ) -> Result<(), Error> {
3076        let key = key.as_ref();
3077
3078        unsafe {
3079            ffi_try!(ffi::rocksdb_singledelete_cf(
3080                self.inner.inner(),
3081                writeopts.inner,
3082                cf.inner(),
3083                key.as_ptr() as *const c_char,
3084                key.len() as size_t,
3085            ));
3086            Ok(())
3087        }
3088    }
3089
3090    /// Remove the database entry for "key" with WriteOptions.
3091    ///
3092    /// Takes an additional argument `ts` as the timestamp.
3093    /// Note: the DB must be opened with user defined timestamp enabled.
3094    ///
3095    /// See single_delete_opt() for detailed behavior and restrictions.
3096    pub fn single_delete_with_ts_opt<K, S>(
3097        &self,
3098        key: K,
3099        ts: S,
3100        writeopts: &WriteOptions,
3101    ) -> Result<(), Error>
3102    where
3103        K: AsRef<[u8]>,
3104        S: AsRef<[u8]>,
3105    {
3106        let key = key.as_ref();
3107        let ts = ts.as_ref();
3108        unsafe {
3109            ffi_try!(ffi::rocksdb_singledelete_with_ts(
3110                self.inner.inner(),
3111                writeopts.inner,
3112                key.as_ptr() as *const c_char,
3113                key.len() as size_t,
3114                ts.as_ptr() as *const c_char,
3115                ts.len() as size_t,
3116            ));
3117            Ok(())
3118        }
3119    }
3120
3121    /// Remove the database entry for "key" from a specific column family with WriteOptions.
3122    ///
3123    /// Takes an additional argument `ts` as the timestamp.
3124    /// Note: the DB must be opened with user defined timestamp enabled.
3125    ///
3126    /// See single_delete_opt() for detailed behavior and restrictions.
3127    pub fn single_delete_cf_with_ts_opt<K, S>(
3128        &self,
3129        cf: &impl AsColumnFamilyRef,
3130        key: K,
3131        ts: S,
3132        writeopts: &WriteOptions,
3133    ) -> Result<(), Error>
3134    where
3135        K: AsRef<[u8]>,
3136        S: AsRef<[u8]>,
3137    {
3138        let key = key.as_ref();
3139        let ts = ts.as_ref();
3140        unsafe {
3141            ffi_try!(ffi::rocksdb_singledelete_cf_with_ts(
3142                self.inner.inner(),
3143                writeopts.inner,
3144                cf.inner(),
3145                key.as_ptr() as *const c_char,
3146                key.len() as size_t,
3147                ts.as_ptr() as *const c_char,
3148                ts.len() as size_t,
3149            ));
3150            Ok(())
3151        }
3152    }
3153
3154    /// Remove the database entry for "key".
3155    ///
3156    /// See single_delete_opt() for detailed behavior and restrictions.
3157    pub fn single_delete<K: AsRef<[u8]>>(&self, key: K) -> Result<(), Error> {
3158        DEFAULT_WRITE_OPTS.with(|opts| self.single_delete_opt(key, opts))
3159    }
3160
3161    /// Remove the database entry for "key" from a specific column family.
3162    ///
3163    /// See single_delete_opt() for detailed behavior and restrictions.
3164    pub fn single_delete_cf<K: AsRef<[u8]>>(
3165        &self,
3166        cf: &impl AsColumnFamilyRef,
3167        key: K,
3168    ) -> Result<(), Error> {
3169        DEFAULT_WRITE_OPTS.with(|opts| self.single_delete_cf_opt(cf, key, opts))
3170    }
3171
3172    /// Remove the database entry for "key".
3173    ///
3174    /// Takes an additional argument `ts` as the timestamp.
3175    /// Note: the DB must be opened with user defined timestamp enabled.
3176    ///
3177    /// See single_delete_opt() for detailed behavior and restrictions.
3178    pub fn single_delete_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
3179        &self,
3180        key: K,
3181        ts: S,
3182    ) -> Result<(), Error> {
3183        DEFAULT_WRITE_OPTS.with(|opts| self.single_delete_with_ts_opt(key, ts, opts))
3184    }
3185
3186    /// Remove the database entry for "key" from a specific column family.
3187    ///
3188    /// Takes an additional argument `ts` as the timestamp.
3189    /// Note: the DB must be opened with user defined timestamp enabled.
3190    ///
3191    /// See single_delete_opt() for detailed behavior and restrictions.
3192    pub fn single_delete_cf_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
3193        &self,
3194        cf: &impl AsColumnFamilyRef,
3195        key: K,
3196        ts: S,
3197    ) -> Result<(), Error> {
3198        DEFAULT_WRITE_OPTS.with(|opts| self.single_delete_cf_with_ts_opt(cf, key, ts, opts))
3199    }
3200
3201    /// Runs a manual compaction on the Range of keys given. This is not likely to be needed for typical usage.
3202    pub fn compact_range<S: AsRef<[u8]>, E: AsRef<[u8]>>(&self, start: Option<S>, end: Option<E>) {
3203        unsafe {
3204            let start = start.as_ref().map(AsRef::as_ref);
3205            let end = end.as_ref().map(AsRef::as_ref);
3206
3207            ffi::rocksdb_compact_range(
3208                self.inner.inner(),
3209                opt_bytes_to_ptr(start),
3210                start.map_or(0, <[u8]>::len) as size_t,
3211                opt_bytes_to_ptr(end),
3212                end.map_or(0, <[u8]>::len) as size_t,
3213            );
3214        }
3215    }
3216
3217    /// Same as `compact_range` but with custom options.
3218    pub fn compact_range_opt<S: AsRef<[u8]>, E: AsRef<[u8]>>(
3219        &self,
3220        start: Option<S>,
3221        end: Option<E>,
3222        opts: &CompactOptions,
3223    ) {
3224        unsafe {
3225            let start = start.as_ref().map(AsRef::as_ref);
3226            let end = end.as_ref().map(AsRef::as_ref);
3227
3228            ffi::rocksdb_compact_range_opt(
3229                self.inner.inner(),
3230                opts.inner,
3231                opt_bytes_to_ptr(start),
3232                start.map_or(0, <[u8]>::len) as size_t,
3233                opt_bytes_to_ptr(end),
3234                end.map_or(0, <[u8]>::len) as size_t,
3235            );
3236        }
3237    }
3238
3239    /// Runs a manual compaction on the Range of keys given on the
3240    /// given column family. This is not likely to be needed for typical usage.
3241    pub fn compact_range_cf<S: AsRef<[u8]>, E: AsRef<[u8]>>(
3242        &self,
3243        cf: &impl AsColumnFamilyRef,
3244        start: Option<S>,
3245        end: Option<E>,
3246    ) {
3247        unsafe {
3248            let start = start.as_ref().map(AsRef::as_ref);
3249            let end = end.as_ref().map(AsRef::as_ref);
3250
3251            ffi::rocksdb_compact_range_cf(
3252                self.inner.inner(),
3253                cf.inner(),
3254                opt_bytes_to_ptr(start),
3255                start.map_or(0, <[u8]>::len) as size_t,
3256                opt_bytes_to_ptr(end),
3257                end.map_or(0, <[u8]>::len) as size_t,
3258            );
3259        }
3260    }
3261
3262    /// Same as `compact_range_cf` but with custom options.
3263    pub fn compact_range_cf_opt<S: AsRef<[u8]>, E: AsRef<[u8]>>(
3264        &self,
3265        cf: &impl AsColumnFamilyRef,
3266        start: Option<S>,
3267        end: Option<E>,
3268        opts: &CompactOptions,
3269    ) {
3270        unsafe {
3271            let start = start.as_ref().map(AsRef::as_ref);
3272            let end = end.as_ref().map(AsRef::as_ref);
3273
3274            ffi::rocksdb_compact_range_cf_opt(
3275                self.inner.inner(),
3276                cf.inner(),
3277                opts.inner,
3278                opt_bytes_to_ptr(start),
3279                start.map_or(0, <[u8]>::len) as size_t,
3280                opt_bytes_to_ptr(end),
3281                end.map_or(0, <[u8]>::len) as size_t,
3282            );
3283        }
3284    }
3285
3286    /// Wait for all flush and compactions jobs to finish. Jobs to wait include the
3287    /// unscheduled (queued, but not scheduled yet).
3288    ///
3289    /// NOTE: This may also never return if there's sufficient ongoing writes that
3290    /// keeps flush and compaction going without stopping. The user would have to
3291    /// cease all the writes to DB to make this eventually return in a stable
3292    /// state. The user may also use timeout option in WaitForCompactOptions to
3293    /// make this stop waiting and return when timeout expires.
3294    pub fn wait_for_compact(&self, opts: &WaitForCompactOptions) -> Result<(), Error> {
3295        unsafe {
3296            ffi_try!(ffi::rocksdb_wait_for_compact(
3297                self.inner.inner(),
3298                opts.inner
3299            ));
3300        }
3301        Ok(())
3302    }
3303
3304    pub fn set_options(&self, opts: &[(&str, &str)]) -> Result<(), Error> {
3305        let copts = convert_options(opts)?;
3306        let cnames: Vec<*const c_char> = copts.iter().map(|opt| opt.0.as_ptr()).collect();
3307        let cvalues: Vec<*const c_char> = copts.iter().map(|opt| opt.1.as_ptr()).collect();
3308        let count = opts.len() as i32;
3309        unsafe {
3310            ffi_try!(ffi::rocksdb_set_options(
3311                self.inner.inner(),
3312                count,
3313                cnames.as_ptr(),
3314                cvalues.as_ptr(),
3315            ));
3316        }
3317        Ok(())
3318    }
3319
3320    pub fn set_options_cf(
3321        &self,
3322        cf: &impl AsColumnFamilyRef,
3323        opts: &[(&str, &str)],
3324    ) -> Result<(), Error> {
3325        let copts = convert_options(opts)?;
3326        let cnames: Vec<*const c_char> = copts.iter().map(|opt| opt.0.as_ptr()).collect();
3327        let cvalues: Vec<*const c_char> = copts.iter().map(|opt| opt.1.as_ptr()).collect();
3328        let count = opts.len() as i32;
3329        unsafe {
3330            ffi_try!(ffi::rocksdb_set_options_cf(
3331                self.inner.inner(),
3332                cf.inner(),
3333                count,
3334                cnames.as_ptr(),
3335                cvalues.as_ptr(),
3336            ));
3337        }
3338        Ok(())
3339    }
3340
3341    /// Implementation for property_value et al methods.
3342    ///
3343    /// `name` is the name of the property.  It will be converted into a CString
3344    /// and passed to `get_property` as argument.  `get_property` reads the
3345    /// specified property and either returns NULL or a pointer to a C allocated
3346    /// string; this method takes ownership of that string and will free it at
3347    /// the end. That string is parsed using `parse` callback which produces
3348    /// the returned result.
3349    fn property_value_impl<R>(
3350        name: impl CStrLike,
3351        get_property: impl FnOnce(*const c_char) -> *mut c_char,
3352        parse: impl FnOnce(&str) -> Result<R, Error>,
3353    ) -> Result<Option<R>, Error> {
3354        let value = match name.bake() {
3355            Ok(prop_name) => get_property(prop_name.as_ptr()),
3356            Err(e) => {
3357                return Err(Error::new(format!(
3358                    "Failed to convert property name to CString: {e}"
3359                )));
3360            }
3361        };
3362        if value.is_null() {
3363            return Ok(None);
3364        }
3365        let result = match unsafe { CStr::from_ptr(value) }.to_str() {
3366            Ok(s) => parse(s).map(|value| Some(value)),
3367            Err(e) => Err(Error::new(format!(
3368                "Failed to convert property value to string: {e}"
3369            ))),
3370        };
3371        unsafe {
3372            ffi::rocksdb_free(value as *mut c_void);
3373        }
3374        result
3375    }
3376
3377    /// Retrieves a RocksDB property by name.
3378    ///
3379    /// Full list of properties could be find
3380    /// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L428-L634).
3381    pub fn property_value(&self, name: impl CStrLike) -> Result<Option<String>, Error> {
3382        Self::property_value_impl(
3383            name,
3384            |prop_name| unsafe { ffi::rocksdb_property_value(self.inner.inner(), prop_name) },
3385            |str_value| Ok(str_value.to_owned()),
3386        )
3387    }
3388
3389    /// Retrieves a RocksDB property by name, for a specific column family.
3390    ///
3391    /// Full list of properties could be find
3392    /// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L428-L634).
3393    pub fn property_value_cf(
3394        &self,
3395        cf: &impl AsColumnFamilyRef,
3396        name: impl CStrLike,
3397    ) -> Result<Option<String>, Error> {
3398        Self::property_value_impl(
3399            name,
3400            |prop_name| unsafe {
3401                ffi::rocksdb_property_value_cf(self.inner.inner(), cf.inner(), prop_name)
3402            },
3403            |str_value| Ok(str_value.to_owned()),
3404        )
3405    }
3406
3407    fn property_int_value_impl(
3408        name: impl CStrLike,
3409        get_property: impl FnOnce(*const c_char, *mut u64) -> c_int,
3410        get_string_property: impl FnOnce(*const c_char) -> *mut c_char,
3411    ) -> Result<Option<u64>, Error> {
3412        let prop_name = name.bake().map_err(|err| {
3413            Error::new(format!("Failed to convert property name to CString: {err}"))
3414        })?;
3415        let mut value = 0;
3416        if get_property(prop_name.as_ptr(), &raw mut value) == 0 {
3417            return Ok(Some(value));
3418        }
3419
3420        Self::property_value_impl(
3421            prop_name.as_ref(),
3422            get_string_property,
3423            Self::parse_property_int_value,
3424        )
3425    }
3426
3427    fn parse_property_int_value(value: &str) -> Result<u64, Error> {
3428        value.parse::<u64>().map_err(|err| {
3429            Error::new(format!(
3430                "Failed to convert property value {value} to int: {err}"
3431            ))
3432        })
3433    }
3434
3435    /// Retrieves a RocksDB property and casts it to an integer.
3436    ///
3437    /// Full list of properties that return int values could be find
3438    /// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689).
3439    pub fn property_int_value(&self, name: impl CStrLike) -> Result<Option<u64>, Error> {
3440        Self::property_int_value_impl(
3441            name,
3442            |prop_name, value| unsafe {
3443                ffi::rocksdb_property_int(self.inner.inner(), prop_name, value)
3444            },
3445            |prop_name| unsafe { ffi::rocksdb_property_value(self.inner.inner(), prop_name) },
3446        )
3447    }
3448
3449    /// Retrieves a RocksDB property for a specific column family and casts it to an integer.
3450    ///
3451    /// Full list of properties that return int values could be find
3452    /// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689).
3453    pub fn property_int_value_cf(
3454        &self,
3455        cf: &impl AsColumnFamilyRef,
3456        name: impl CStrLike,
3457    ) -> Result<Option<u64>, Error> {
3458        Self::property_int_value_impl(
3459            name,
3460            |prop_name, value| unsafe {
3461                ffi::rocksdb_property_int_cf(self.inner.inner(), cf.inner(), prop_name, value)
3462            },
3463            |prop_name| unsafe {
3464                ffi::rocksdb_property_value_cf(self.inner.inner(), cf.inner(), prop_name)
3465            },
3466        )
3467    }
3468
3469    /// The sequence number of the most recent transaction.
3470    pub fn latest_sequence_number(&self) -> u64 {
3471        unsafe { ffi::rocksdb_get_latest_sequence_number(self.inner.inner()) }
3472    }
3473
3474    /// Return the approximate file system space used by keys in each ranges.
3475    ///
3476    /// Note that the returned sizes measure file system space usage, so
3477    /// if the user data compresses by a factor of ten, the returned
3478    /// sizes will be one-tenth the size of the corresponding user data size.
3479    ///
3480    /// Due to lack of abi, only data flushed to disk is taken into account.
3481    /// # Errors
3482    ///
3483    /// Returns the RocksDB error if the size estimate fails, for instance on an
3484    /// I/O error reading the manifest. No partial sizes are reported in that
3485    /// case.
3486    pub fn get_approximate_sizes(&self, ranges: &[Range]) -> Result<Vec<u64>, Error> {
3487        self.get_approximate_sizes_cfopt(None::<&ColumnFamily>, ranges)
3488    }
3489
3490    /// Like [`Self::get_approximate_sizes`], for a single column family.
3491    ///
3492    /// # Errors
3493    ///
3494    /// See [`Self::get_approximate_sizes`].
3495    pub fn get_approximate_sizes_cf(
3496        &self,
3497        cf: &impl AsColumnFamilyRef,
3498        ranges: &[Range],
3499    ) -> Result<Vec<u64>, Error> {
3500        self.get_approximate_sizes_cfopt(Some(cf), ranges)
3501    }
3502
3503    fn get_approximate_sizes_cfopt(
3504        &self,
3505        cf: Option<&impl AsColumnFamilyRef>,
3506        ranges: &[Range],
3507    ) -> Result<Vec<u64>, Error> {
3508        let start_keys: Vec<*const c_char> = ranges
3509            .iter()
3510            .map(|x| x.start_key.as_ptr() as *const c_char)
3511            .collect();
3512        let start_key_lens: Vec<_> = ranges.iter().map(|x| x.start_key.len()).collect();
3513        let end_keys: Vec<*const c_char> = ranges
3514            .iter()
3515            .map(|x| x.end_key.as_ptr() as *const c_char)
3516            .collect();
3517        let end_key_lens: Vec<_> = ranges.iter().map(|x| x.end_key.len()).collect();
3518        let mut sizes: Vec<u64> = vec![0; ranges.len()];
3519        let (n, start_key_ptr, start_key_len_ptr, end_key_ptr, end_key_len_ptr, size_ptr) = (
3520            ranges.len() as i32,
3521            start_keys.as_ptr(),
3522            start_key_lens.as_ptr(),
3523            end_keys.as_ptr(),
3524            end_key_lens.as_ptr(),
3525            sizes.as_mut_ptr(),
3526        );
3527        let mut err: *mut c_char = ptr::null_mut();
3528        match cf {
3529            None => unsafe {
3530                ffi::rocksdb_approximate_sizes(
3531                    self.inner.inner(),
3532                    n,
3533                    start_key_ptr,
3534                    start_key_len_ptr,
3535                    end_key_ptr,
3536                    end_key_len_ptr,
3537                    size_ptr,
3538                    &raw mut err,
3539                );
3540            },
3541            Some(cf) => unsafe {
3542                ffi::rocksdb_approximate_sizes_cf(
3543                    self.inner.inner(),
3544                    cf.inner(),
3545                    n,
3546                    start_key_ptr,
3547                    start_key_len_ptr,
3548                    end_key_ptr,
3549                    end_key_len_ptr,
3550                    size_ptr,
3551                    &raw mut err,
3552                );
3553            },
3554        }
3555        // RocksDB reports failures here through `errptr`. Ignoring it both
3556        // leaked the `strdup`ed message and returned a vector of zeros that the
3557        // caller could not distinguish from "these ranges are empty".
3558        if !err.is_null() {
3559            return Err(convert_rocksdb_error(err));
3560        }
3561        Ok(sizes)
3562    }
3563
3564    /// Iterate over batches of write operations since a given sequence.
3565    ///
3566    /// Produce an iterator that will provide the batches of write operations
3567    /// that have occurred since the given sequence (see
3568    /// `latest_sequence_number()`). Use the provided iterator to retrieve each
3569    /// (`u64`, `WriteBatch`) tuple, and then gather the individual puts and
3570    /// deletes using the `WriteBatch::iterate()` function.
3571    ///
3572    /// Calling `get_updates_since()` with a sequence number that is out of
3573    /// bounds will return an error.
3574    pub fn get_updates_since(&self, seq_number: u64) -> Result<DBWALIterator, Error> {
3575        unsafe {
3576            // rocksdb_wal_readoptions_t does not appear to have any functions
3577            // for creating and destroying it; fortunately we can pass a nullptr
3578            // here to get the default behavior
3579            let opts: *const ffi::rocksdb_wal_readoptions_t = ptr::null();
3580            let iter = ffi_try!(ffi::rocksdb_get_updates_since(
3581                self.inner.inner(),
3582                seq_number,
3583                opts
3584            ));
3585            Ok(DBWALIterator {
3586                inner: iter,
3587                start_seq_number: seq_number,
3588            })
3589        }
3590    }
3591
3592    /// Tries to catch up with the primary by reading as much as possible from the
3593    /// log files.
3594    pub fn try_catch_up_with_primary(&self) -> Result<(), Error> {
3595        unsafe {
3596            ffi_try!(ffi::rocksdb_try_catch_up_with_primary(self.inner.inner()));
3597        }
3598        Ok(())
3599    }
3600
3601    /// Loads a list of external SST files created with SstFileWriter into the DB with default opts
3602    pub fn ingest_external_file<P: AsRef<Path>>(&self, paths: Vec<P>) -> Result<(), Error> {
3603        let opts = IngestExternalFileOptions::default();
3604        self.ingest_external_file_opts(&opts, paths)
3605    }
3606
3607    /// Loads a list of external SST files created with SstFileWriter into the DB
3608    pub fn ingest_external_file_opts<P: AsRef<Path>>(
3609        &self,
3610        opts: &IngestExternalFileOptions,
3611        paths: Vec<P>,
3612    ) -> Result<(), Error> {
3613        let paths_v: Vec<CString> = paths.iter().map(to_cpath).collect::<Result<Vec<_>, _>>()?;
3614        let cpaths: Vec<_> = paths_v.iter().map(|path| path.as_ptr()).collect();
3615
3616        self.ingest_external_file_raw(opts, &paths_v, &cpaths)
3617    }
3618
3619    /// Loads a list of external SST files created with SstFileWriter into the DB for given Column Family
3620    /// with default opts
3621    pub fn ingest_external_file_cf<P: AsRef<Path>>(
3622        &self,
3623        cf: &impl AsColumnFamilyRef,
3624        paths: Vec<P>,
3625    ) -> Result<(), Error> {
3626        let opts = IngestExternalFileOptions::default();
3627        self.ingest_external_file_cf_opts(cf, &opts, paths)
3628    }
3629
3630    /// Loads a list of external SST files created with SstFileWriter into the DB for given Column Family
3631    pub fn ingest_external_file_cf_opts<P: AsRef<Path>>(
3632        &self,
3633        cf: &impl AsColumnFamilyRef,
3634        opts: &IngestExternalFileOptions,
3635        paths: Vec<P>,
3636    ) -> Result<(), Error> {
3637        let paths_v: Vec<CString> = paths.iter().map(to_cpath).collect::<Result<Vec<_>, _>>()?;
3638        let cpaths: Vec<_> = paths_v.iter().map(|path| path.as_ptr()).collect();
3639
3640        self.ingest_external_file_raw_cf(cf, opts, &paths_v, &cpaths)
3641    }
3642
3643    fn ingest_external_file_raw(
3644        &self,
3645        opts: &IngestExternalFileOptions,
3646        paths_v: &[CString],
3647        cpaths: &[*const c_char],
3648    ) -> Result<(), Error> {
3649        unsafe {
3650            ffi_try!(ffi::rocksdb_ingest_external_file(
3651                self.inner.inner(),
3652                cpaths.as_ptr(),
3653                paths_v.len(),
3654                opts.inner.cast_const()
3655            ));
3656            Ok(())
3657        }
3658    }
3659
3660    fn ingest_external_file_raw_cf(
3661        &self,
3662        cf: &impl AsColumnFamilyRef,
3663        opts: &IngestExternalFileOptions,
3664        paths_v: &[CString],
3665        cpaths: &[*const c_char],
3666    ) -> Result<(), Error> {
3667        unsafe {
3668            ffi_try!(ffi::rocksdb_ingest_external_file_cf(
3669                self.inner.inner(),
3670                cf.inner(),
3671                cpaths.as_ptr(),
3672                paths_v.len(),
3673                opts.inner.cast_const()
3674            ));
3675            Ok(())
3676        }
3677    }
3678
3679    /// Obtains the LSM-tree meta data of the default column family of the DB
3680    pub fn get_column_family_metadata(&self) -> ColumnFamilyMetaData {
3681        unsafe {
3682            let ptr = ffi::rocksdb_get_column_family_metadata(self.inner.inner());
3683
3684            let metadata = ColumnFamilyMetaData {
3685                size: ffi::rocksdb_column_family_metadata_get_size(ptr),
3686                name: from_cstr_and_free(ffi::rocksdb_column_family_metadata_get_name(ptr)),
3687                file_count: ffi::rocksdb_column_family_metadata_get_file_count(ptr),
3688            };
3689
3690            // destroy
3691            ffi::rocksdb_column_family_metadata_destroy(ptr);
3692
3693            // return
3694            metadata
3695        }
3696    }
3697
3698    /// Obtains the LSM-tree meta data of the specified column family of the DB
3699    pub fn get_column_family_metadata_cf(
3700        &self,
3701        cf: &impl AsColumnFamilyRef,
3702    ) -> ColumnFamilyMetaData {
3703        unsafe {
3704            let ptr = ffi::rocksdb_get_column_family_metadata_cf(self.inner.inner(), cf.inner());
3705
3706            let metadata = ColumnFamilyMetaData {
3707                size: ffi::rocksdb_column_family_metadata_get_size(ptr),
3708                name: from_cstr_and_free(ffi::rocksdb_column_family_metadata_get_name(ptr)),
3709                file_count: ffi::rocksdb_column_family_metadata_get_file_count(ptr),
3710            };
3711
3712            // destroy
3713            ffi::rocksdb_column_family_metadata_destroy(ptr);
3714
3715            // return
3716            metadata
3717        }
3718    }
3719
3720    /// Returns a list of all table files with their level, start key
3721    /// and end key
3722    pub fn live_files(&self) -> Result<Vec<LiveFile>, Error> {
3723        unsafe {
3724            let livefiles_ptr = ffi::rocksdb_livefiles(self.inner.inner());
3725            if livefiles_ptr.is_null() {
3726                Err(Error::new("Could not get live files".to_owned()))
3727            } else {
3728                let files = LiveFile::from_rocksdb_livefiles_ptr(livefiles_ptr);
3729
3730                // destroy livefiles metadata(s)
3731                ffi::rocksdb_livefiles_destroy(livefiles_ptr);
3732
3733                // return
3734                Ok(files)
3735            }
3736        }
3737    }
3738
3739    /// Delete sst files whose keys are entirely in the given range.
3740    ///
3741    /// Could leave some keys in the range which are in files which are not
3742    /// entirely in the range.
3743    ///
3744    /// Note: L0 files are left regardless of whether they're in the range.
3745    ///
3746    /// SnapshotWithThreadModes before the delete might not see the data in the given range.
3747    pub fn delete_file_in_range<K: AsRef<[u8]>>(&self, from: K, to: K) -> Result<(), Error> {
3748        let from = from.as_ref();
3749        let to = to.as_ref();
3750        unsafe {
3751            ffi_try!(ffi::rocksdb_delete_file_in_range(
3752                self.inner.inner(),
3753                from.as_ptr() as *const c_char,
3754                from.len() as size_t,
3755                to.as_ptr() as *const c_char,
3756                to.len() as size_t,
3757            ));
3758            Ok(())
3759        }
3760    }
3761
3762    /// Same as `delete_file_in_range` but only for specific column family
3763    pub fn delete_file_in_range_cf<K: AsRef<[u8]>>(
3764        &self,
3765        cf: &impl AsColumnFamilyRef,
3766        from: K,
3767        to: K,
3768    ) -> Result<(), Error> {
3769        let from = from.as_ref();
3770        let to = to.as_ref();
3771        unsafe {
3772            ffi_try!(ffi::rocksdb_delete_file_in_range_cf(
3773                self.inner.inner(),
3774                cf.inner(),
3775                from.as_ptr() as *const c_char,
3776                from.len() as size_t,
3777                to.as_ptr() as *const c_char,
3778                to.len() as size_t,
3779            ));
3780            Ok(())
3781        }
3782    }
3783
3784    /// Request stopping background work, if wait is true wait until it's done.
3785    pub fn cancel_all_background_work(&self, wait: bool) {
3786        unsafe {
3787            ffi::rocksdb_cancel_all_background_work(self.inner.inner(), c_uchar::from(wait));
3788        }
3789    }
3790
3791    /// Marks the column family as dropped in RocksDB.
3792    ///
3793    /// Deliberately does not take ownership of the handle. Callers must take
3794    /// the handle out of their map first, so that only one caller can ever
3795    /// *destroy* a given handle, and must put it back if this fails: destroying
3796    /// it on failure would leave the column family still present in the DB with
3797    /// no reachable handle, so the only way to touch it again would be to
3798    /// reopen the database.
3799    ///
3800    /// Taking it out of the map does not make the caller the only *reader*. In
3801    /// `MultiThreaded` mode `cf_handle` clones the same `Arc`, so other threads
3802    /// can still hold a live `BoundColumnFamily` for this handle. That is fine:
3803    /// the refcount keeps the handle alive until the last of them is gone.
3804    fn mark_column_family_dropped(
3805        &self,
3806        cf_inner: *mut ffi::rocksdb_column_family_handle_t,
3807    ) -> Result<(), Error> {
3808        unsafe {
3809            ffi_try!(ffi::rocksdb_drop_column_family(
3810                self.inner.inner(),
3811                cf_inner
3812            ));
3813        }
3814        Ok(())
3815    }
3816
3817    /// Increase the full_history_ts of column family. The new ts_low value should
3818    /// be newer than current full_history_ts value.
3819    /// If another thread updates full_history_ts_low concurrently to a higher
3820    /// timestamp than the requested ts_low, a try again error will be returned.
3821    pub fn increase_full_history_ts_low<S: AsRef<[u8]>>(
3822        &self,
3823        cf: &impl AsColumnFamilyRef,
3824        ts: S,
3825    ) -> Result<(), Error> {
3826        let ts = ts.as_ref();
3827        unsafe {
3828            ffi_try!(ffi::rocksdb_increase_full_history_ts_low(
3829                self.inner.inner(),
3830                cf.inner(),
3831                ts.as_ptr() as *const c_char,
3832                ts.len() as size_t,
3833            ));
3834            Ok(())
3835        }
3836    }
3837
3838    /// Get current full_history_ts value.
3839    pub fn get_full_history_ts_low(&self, cf: &impl AsColumnFamilyRef) -> Result<Vec<u8>, Error> {
3840        unsafe {
3841            let mut ts_lowlen = 0;
3842            let ts = ffi_try!(ffi::rocksdb_get_full_history_ts_low(
3843                self.inner.inner(),
3844                cf.inner(),
3845                &raw mut ts_lowlen,
3846            ));
3847
3848            if ts.is_null() {
3849                Err(Error::new("Could not get full_history_ts_low".to_owned()))
3850            } else {
3851                let mut vec = vec![0; ts_lowlen];
3852                ptr::copy_nonoverlapping(ts.cast::<u8>(), vec.as_mut_ptr(), ts_lowlen);
3853                ffi::rocksdb_free(ts as *mut c_void);
3854                Ok(vec)
3855            }
3856        }
3857    }
3858
3859    /// Returns the DB identity. This is typically ASCII bytes, but that is not guaranteed.
3860    pub fn get_db_identity(&self) -> Result<Vec<u8>, Error> {
3861        unsafe {
3862            let mut length: usize = 0;
3863            let identity_ptr = ffi::rocksdb_get_db_identity(self.inner.inner(), &raw mut length);
3864            let identity_vec = raw_data(identity_ptr, length);
3865            ffi::rocksdb_free(identity_ptr as *mut c_void);
3866            // In RocksDB: get_db_identity copies a std::string so it should not fail, but
3867            // the API allows it to be overridden, so it might
3868            identity_vec.ok_or_else(|| Error::new("get_db_identity returned NULL".to_string()))
3869        }
3870    }
3871}
3872
3873impl<I: DBInner> DBCommon<SingleThreaded, I> {
3874    /// Creates column family with given name and options
3875    pub fn create_cf<N: AsRef<str>>(&mut self, name: N, opts: &Options) -> Result<(), Error> {
3876        let inner = self.create_inner_cf_handle(name.as_ref(), opts)?;
3877        self.cfs
3878            .cfs
3879            .insert(name.as_ref().to_string(), ColumnFamily { inner });
3880        Ok(())
3881    }
3882
3883    #[doc = include_str!("db_create_column_family_with_import.md")]
3884    pub fn create_column_family_with_import<N: AsRef<str>>(
3885        &mut self,
3886        options: &Options,
3887        column_family_name: N,
3888        import_options: &ImportColumnFamilyOptions,
3889        metadata: &ExportImportFilesMetaData,
3890    ) -> Result<(), Error> {
3891        let name = column_family_name.as_ref();
3892        let c_name = CString::new(name).map_err(|err| {
3893            Error::new(format!(
3894                "Failed to convert name to CString while importing column family: {err}"
3895            ))
3896        })?;
3897        let inner = unsafe {
3898            ffi_try!(ffi::rocksdb_create_column_family_with_import(
3899                self.inner.inner(),
3900                options.inner,
3901                c_name.as_ptr(),
3902                import_options.inner,
3903                metadata.inner
3904            ))
3905        };
3906        self.cfs
3907            .cfs
3908            .insert(column_family_name.as_ref().into(), ColumnFamily { inner });
3909        Ok(())
3910    }
3911
3912    /// Drops the column family with the given name
3913    pub fn drop_cf(&mut self, name: &str) -> Result<(), Error> {
3914        let Some(cf) = self.cfs.cfs.remove(name) else {
3915            return Err(Error::new(format!("Invalid column family: {name}")));
3916        };
3917        match self.mark_column_family_dropped(cf.inner) {
3918            // `cf` is dropped here. In single-threaded mode that destroys the
3919            // handle; in `MultiThreaded` mode it drops one `Arc` reference and
3920            // the handle is destroyed once the last `BoundColumnFamily` clone
3921            // handed out by `cf_handle` is gone.
3922            Ok(()) => Ok(()),
3923            Err(e) => {
3924                // The column family is still there, so put the handle back
3925                // rather than destroying the only way to reach it.
3926                self.cfs.cfs.insert(name.to_owned(), cf);
3927                Err(e)
3928            }
3929        }
3930    }
3931
3932    /// Returns the underlying column family handle
3933    pub fn cf_handle(&self, name: &str) -> Option<&ColumnFamily> {
3934        self.cfs.cfs.get(name)
3935    }
3936
3937    /// Returns the list of column families currently open.
3938    ///
3939    /// The order of names is unspecified and may vary between calls.
3940    pub fn cf_names(&self) -> Vec<String> {
3941        self.cfs.cfs.keys().cloned().collect()
3942    }
3943}
3944
3945impl<I: DBInner> DBCommon<MultiThreaded, I> {
3946    /// Creates column family with given name and options
3947    pub fn create_cf<N: AsRef<str>>(&self, name: N, opts: &Options) -> Result<(), Error> {
3948        // Note that we acquire the cfs lock before inserting: otherwise we might race
3949        // another caller who observed the handle as missing.
3950        let mut cfs = self.cfs.cfs.write();
3951        let inner = self.create_inner_cf_handle(name.as_ref(), opts)?;
3952        cfs.insert(
3953            name.as_ref().to_string(),
3954            Arc::new(UnboundColumnFamily { inner }),
3955        );
3956        Ok(())
3957    }
3958
3959    #[doc = include_str!("db_create_column_family_with_import.md")]
3960    pub fn create_column_family_with_import<N: AsRef<str>>(
3961        &self,
3962        options: &Options,
3963        column_family_name: N,
3964        import_options: &ImportColumnFamilyOptions,
3965        metadata: &ExportImportFilesMetaData,
3966    ) -> Result<(), Error> {
3967        // Acquire CF lock upfront, before creating the CF, to avoid a race with concurrent creators
3968        let mut cfs = self.cfs.cfs.write();
3969        let name = column_family_name.as_ref();
3970        let c_name = CString::new(name).map_err(|err| {
3971            Error::new(format!(
3972                "Failed to convert name to CString while importing column family: {err}"
3973            ))
3974        })?;
3975        let inner = unsafe {
3976            ffi_try!(ffi::rocksdb_create_column_family_with_import(
3977                self.inner.inner(),
3978                options.inner,
3979                c_name.as_ptr(),
3980                import_options.inner,
3981                metadata.inner
3982            ))
3983        };
3984        cfs.insert(
3985            column_family_name.as_ref().to_string(),
3986            Arc::new(UnboundColumnFamily { inner }),
3987        );
3988        Ok(())
3989    }
3990
3991    /// Drops the column family with the given name by internally locking the inner column
3992    /// family map. This avoids needing `&mut self` reference
3993    pub fn drop_cf(&self, name: &str) -> Result<(), Error> {
3994        // Take the handle out under the write lock before touching RocksDB.
3995        // Looking it up under a read lock and removing it afterwards would let
3996        // two concurrent callers observe the same handle: the first would drop
3997        // and destroy it, and the second would then hand a freed pointer to
3998        // `rocksdb_drop_column_family`.
3999        let Some(cf) = self.cfs.cfs.write().remove(name) else {
4000            return Err(Error::new(format!("Invalid column family: {name}")));
4001        };
4002        match self.mark_column_family_dropped(cf.inner) {
4003            // `cf` is dropped here. In single-threaded mode that destroys the
4004            // handle; in `MultiThreaded` mode it drops one `Arc` reference and
4005            // the handle is destroyed once the last `BoundColumnFamily` clone
4006            // handed out by `cf_handle` is gone.
4007            Ok(()) => Ok(()),
4008            Err(e) => {
4009                // The column family is still there, so put the handle back
4010                // rather than destroying the only way to reach it.
4011                self.cfs.cfs.write().insert(name.to_owned(), cf);
4012                Err(e)
4013            }
4014        }
4015    }
4016
4017    /// Returns the underlying column family handle
4018    pub fn cf_handle(&'_ self, name: &str) -> Option<Arc<BoundColumnFamily<'_>>> {
4019        self.cfs
4020            .cfs
4021            .read()
4022            .get(name)
4023            .cloned()
4024            .map(UnboundColumnFamily::bound_column_family)
4025    }
4026
4027    /// Returns the list of column families currently open.
4028    ///
4029    /// The order of names is unspecified and may vary between calls.
4030    pub fn cf_names(&self) -> Vec<String> {
4031        self.cfs.cfs.read().keys().cloned().collect()
4032    }
4033}
4034
4035impl<T: ThreadMode, I: DBInner> Drop for DBCommon<T, I> {
4036    fn drop(&mut self) {
4037        self.cfs.drop_all_cfs_internal();
4038    }
4039}
4040
4041impl<T: ThreadMode, I: DBInner> fmt::Debug for DBCommon<T, I> {
4042    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4043        write!(f, "RocksDB {{ path: {} }}", self.path().display())
4044    }
4045}
4046
4047/// The metadata that describes a column family.
4048#[derive(Debug, Clone)]
4049pub struct ColumnFamilyMetaData {
4050    // The size of this column family in bytes, which is equal to the sum of
4051    // the file size of its "levels".
4052    pub size: u64,
4053    // The name of the column family.
4054    pub name: String,
4055    // The number of files in this column family.
4056    pub file_count: usize,
4057}
4058
4059/// The metadata that describes a SST file
4060#[derive(Debug, Clone)]
4061pub struct LiveFile {
4062    /// Name of the column family the file belongs to
4063    pub column_family_name: String,
4064    /// Name of the file
4065    pub name: String,
4066    /// The directory containing the file, without a trailing '/'. This could be
4067    /// a DB path, wal_dir, etc.
4068    pub directory: String,
4069    /// Size of the file
4070    pub size: usize,
4071    /// Level at which this file resides
4072    pub level: i32,
4073    /// Smallest user defined key in the file
4074    pub start_key: Option<Vec<u8>>,
4075    /// Largest user defined key in the file
4076    pub end_key: Option<Vec<u8>>,
4077    pub smallest_seqno: u64,
4078    pub largest_seqno: u64,
4079    /// Number of entries/alive keys in the file
4080    pub num_entries: u64,
4081    /// Number of deletions/tomb key(s) in the file
4082    pub num_deletions: u64,
4083}
4084
4085impl LiveFile {
4086    /// Create a `Vec<LiveFile>` from a `rocksdb_livefiles_t` pointer
4087    pub(crate) fn from_rocksdb_livefiles_ptr(
4088        files: *const ffi::rocksdb_livefiles_t,
4089    ) -> Vec<LiveFile> {
4090        unsafe {
4091            let n = ffi::rocksdb_livefiles_count(files);
4092
4093            let mut livefiles = Vec::with_capacity(n as usize);
4094            let mut key_size: usize = 0;
4095
4096            for i in 0..n {
4097                // rocksdb_livefiles_* returns pointers to strings, not copies
4098                let column_family_name =
4099                    from_cstr_without_free(ffi::rocksdb_livefiles_column_family_name(files, i));
4100                let name = from_cstr_without_free(ffi::rocksdb_livefiles_name(files, i));
4101                let directory = from_cstr_without_free(ffi::rocksdb_livefiles_directory(files, i));
4102                let size = ffi::rocksdb_livefiles_size(files, i);
4103                let level = ffi::rocksdb_livefiles_level(files, i);
4104
4105                // get smallest key inside file
4106                let smallest_key = ffi::rocksdb_livefiles_smallestkey(files, i, &raw mut key_size);
4107                let smallest_key = raw_data(smallest_key, key_size);
4108
4109                // get largest key inside file
4110                let largest_key = ffi::rocksdb_livefiles_largestkey(files, i, &raw mut key_size);
4111                let largest_key = raw_data(largest_key, key_size);
4112
4113                livefiles.push(LiveFile {
4114                    column_family_name,
4115                    name,
4116                    directory,
4117                    size,
4118                    level,
4119                    start_key: smallest_key,
4120                    end_key: largest_key,
4121                    largest_seqno: ffi::rocksdb_livefiles_largest_seqno(files, i),
4122                    smallest_seqno: ffi::rocksdb_livefiles_smallest_seqno(files, i),
4123                    num_entries: ffi::rocksdb_livefiles_entries(files, i),
4124                    num_deletions: ffi::rocksdb_livefiles_deletions(files, i),
4125                });
4126            }
4127
4128            livefiles
4129        }
4130    }
4131}
4132
4133struct LiveFileGuard(*mut rocksdb_livefile_t);
4134
4135impl LiveFileGuard {
4136    fn into_raw(mut self) -> *mut rocksdb_livefile_t {
4137        let ptr = self.0;
4138        self.0 = ptr::null_mut();
4139        ptr
4140    }
4141}
4142
4143impl Drop for LiveFileGuard {
4144    fn drop(&mut self) {
4145        if !self.0.is_null() {
4146            unsafe {
4147                rocksdb_livefile_destroy(self.0);
4148            }
4149        }
4150    }
4151}
4152
4153struct LiveFilesGuard(*mut rocksdb_livefiles_t);
4154
4155impl LiveFilesGuard {
4156    fn into_raw(mut self) -> *mut rocksdb_livefiles_t {
4157        let ptr = self.0;
4158        self.0 = ptr::null_mut();
4159        ptr
4160    }
4161}
4162
4163impl Drop for LiveFilesGuard {
4164    fn drop(&mut self) {
4165        if !self.0.is_null() {
4166            unsafe {
4167                rocksdb_livefiles_destroy(self.0);
4168            }
4169        }
4170    }
4171}
4172
4173/// Metadata returned as output from [`Checkpoint::export_column_family`][export_column_family] and
4174/// used as input to [`DB::create_column_family_with_import`].
4175///
4176/// [export_column_family]: crate::checkpoint::Checkpoint::export_column_family
4177#[derive(Debug)]
4178pub struct ExportImportFilesMetaData {
4179    pub(crate) inner: *mut ffi::rocksdb_export_import_files_metadata_t,
4180}
4181
4182impl ExportImportFilesMetaData {
4183    pub fn get_db_comparator_name(&self) -> String {
4184        unsafe {
4185            let c_name =
4186                ffi::rocksdb_export_import_files_metadata_get_db_comparator_name(self.inner);
4187            from_cstr_and_free(c_name)
4188        }
4189    }
4190
4191    pub fn set_db_comparator_name(&mut self, name: &str) {
4192        let c_name = CString::new(name.as_bytes()).unwrap();
4193        unsafe {
4194            ffi::rocksdb_export_import_files_metadata_set_db_comparator_name(
4195                self.inner,
4196                c_name.as_ptr(),
4197            );
4198        };
4199    }
4200
4201    pub fn get_files(&self) -> Vec<LiveFile> {
4202        unsafe {
4203            let livefiles_ptr = ffi::rocksdb_export_import_files_metadata_get_files(self.inner);
4204            let files = LiveFile::from_rocksdb_livefiles_ptr(livefiles_ptr);
4205            ffi::rocksdb_livefiles_destroy(livefiles_ptr);
4206            files
4207        }
4208    }
4209
4210    pub fn set_files(&mut self, files: &[LiveFile]) -> Result<(), Error> {
4211        // Use a non-null empty pointer for zero-length keys
4212        static EMPTY: [u8; 0] = [];
4213        let empty_ptr = EMPTY.as_ptr() as *const libc::c_char;
4214
4215        unsafe {
4216            let live_files = LiveFilesGuard(ffi::rocksdb_livefiles_create());
4217
4218            for file in files {
4219                let live_file = LiveFileGuard(ffi::rocksdb_livefile_create());
4220                ffi::rocksdb_livefile_set_level(live_file.0, file.level);
4221
4222                // SAFETY: C strings are copied inside the FFI layer so do not need to be kept alive
4223                let c_cf_name = CString::new(file.column_family_name.as_str()).map_err(|err| {
4224                    Error::new(format!("Unable to convert column family to CString: {err}"))
4225                })?;
4226                ffi::rocksdb_livefile_set_column_family_name(live_file.0, c_cf_name.as_ptr());
4227
4228                let c_name = CString::new(file.name.as_str()).map_err(|err| {
4229                    Error::new(format!("Unable to convert file name to CString: {err}"))
4230                })?;
4231                ffi::rocksdb_livefile_set_name(live_file.0, c_name.as_ptr());
4232
4233                let c_directory = CString::new(file.directory.as_str()).map_err(|err| {
4234                    Error::new(format!("Unable to convert directory to CString: {err}"))
4235                })?;
4236                ffi::rocksdb_livefile_set_directory(live_file.0, c_directory.as_ptr());
4237
4238                ffi::rocksdb_livefile_set_size(live_file.0, file.size);
4239
4240                let (start_key_ptr, start_key_len) = match &file.start_key {
4241                    None => (empty_ptr, 0),
4242                    Some(key) => (key.as_ptr() as *const libc::c_char, key.len()),
4243                };
4244                ffi::rocksdb_livefile_set_smallest_key(live_file.0, start_key_ptr, start_key_len);
4245
4246                let (largest_key_ptr, largest_key_len) = match &file.end_key {
4247                    None => (empty_ptr, 0),
4248                    Some(key) => (key.as_ptr() as *const libc::c_char, key.len()),
4249                };
4250                ffi::rocksdb_livefile_set_largest_key(
4251                    live_file.0,
4252                    largest_key_ptr,
4253                    largest_key_len,
4254                );
4255                ffi::rocksdb_livefile_set_smallest_seqno(live_file.0, file.smallest_seqno);
4256                ffi::rocksdb_livefile_set_largest_seqno(live_file.0, file.largest_seqno);
4257                ffi::rocksdb_livefile_set_num_entries(live_file.0, file.num_entries);
4258                ffi::rocksdb_livefile_set_num_deletions(live_file.0, file.num_deletions);
4259
4260                // moves ownership of live_files into live_file
4261                ffi::rocksdb_livefiles_add(live_files.0, live_file.into_raw());
4262            }
4263
4264            // moves ownership of live_files into inner
4265            ffi::rocksdb_export_import_files_metadata_set_files(self.inner, live_files.into_raw());
4266            Ok(())
4267        }
4268    }
4269}
4270
4271impl Default for ExportImportFilesMetaData {
4272    fn default() -> Self {
4273        let inner = unsafe { ffi::rocksdb_export_import_files_metadata_create() };
4274        assert!(
4275            !inner.is_null(),
4276            "Could not create rocksdb_export_import_files_metadata_t"
4277        );
4278
4279        Self { inner }
4280    }
4281}
4282
4283impl Drop for ExportImportFilesMetaData {
4284    fn drop(&mut self) {
4285        unsafe {
4286            ffi::rocksdb_export_import_files_metadata_destroy(self.inner);
4287        }
4288    }
4289}
4290
4291unsafe impl Send for ExportImportFilesMetaData {}
4292unsafe impl Sync for ExportImportFilesMetaData {}
4293
4294/// Converts a TTL to the `int` seconds count RocksDB's TTL API takes,
4295/// saturating instead of wrapping.
4296///
4297/// `Duration::as_secs` is a `u64`, so a plain `as i32` cast wraps: a TTL of
4298/// `Duration::from_secs(4_294_967_301)` (~136 years, i.e. "effectively never")
4299/// became `5`, and RocksDB then compaction-deleted the whole column family a few
4300/// seconds after the data was written.
4301///
4302/// Clamping to `i32::MAX` (~68 years) rather than mapping an over-large TTL to
4303/// RocksDB's never-expire sentinel (`ttl <= 0`, see `DBWithTTLImpl::IsStale`) is
4304/// deliberate: silently turning a finite TTL the caller asked for into "keep
4305/// forever" is a worse surprise than expiring it 68 years out, and `i32::MAX` is
4306/// the longest TTL the C API can express anyway.
4307fn ttl_to_seconds(ttl: Duration) -> c_int {
4308    c_int::try_from(ttl.as_secs()).unwrap_or(c_int::MAX)
4309}
4310
4311fn convert_options(opts: &[(&str, &str)]) -> Result<Vec<(CString, CString)>, Error> {
4312    opts.iter()
4313        .map(|(name, value)| {
4314            let cname = match CString::new(name.as_bytes()) {
4315                Ok(cname) => cname,
4316                Err(e) => return Err(Error::new(format!("Invalid option name `{e}`"))),
4317            };
4318            let cvalue = match CString::new(value.as_bytes()) {
4319                Ok(cvalue) => cvalue,
4320                Err(e) => return Err(Error::new(format!("Invalid option value: `{e}`"))),
4321            };
4322            Ok((cname, cvalue))
4323        })
4324        .collect()
4325}
4326
4327pub(crate) fn convert_values(
4328    values: Vec<*mut c_char>,
4329    values_sizes: Vec<usize>,
4330    errors: Vec<*mut c_char>,
4331) -> Vec<Result<Option<Vec<u8>>, Error>> {
4332    values
4333        .into_iter()
4334        .zip(values_sizes)
4335        .zip(errors)
4336        .map(|((v, s), e)| {
4337            if e.is_null() {
4338                let value = unsafe { crate::ffi_util::raw_data(v, s) };
4339                unsafe {
4340                    ffi::rocksdb_free(v as *mut c_void);
4341                }
4342                Ok(value)
4343            } else {
4344                Err(convert_rocksdb_error(e))
4345            }
4346        })
4347        .collect()
4348}
4349
4350#[cfg(test)]
4351mod tests {
4352    use crate::{ColumnFamilyDescriptor, DB, Options};
4353
4354    /// One `rocksdb_create_iterators` call applies a single `ReadOptions` to
4355    /// every iterator it builds, and RocksDB's `DBIter` keeps raw `Slice*`
4356    /// into that object for the iterate bounds and read timestamps. So all the
4357    /// returned iterators have to keep the *same* options object alive, not a
4358    /// copy each and not none at all. Dropping it at the end of
4359    /// `create_iterators_cf` left those pointers dangling. See issue #660.
4360    #[test]
4361    fn raw_iterators_cf_share_one_live_readopts() {
4362        let dir = tempfile::Builder::new()
4363            .prefix("rocksdb-raw-iterators-cf-readopts")
4364            .tempdir()
4365            .unwrap();
4366
4367        let mut opts = Options::default();
4368        opts.create_if_missing(true);
4369        opts.create_missing_column_families(true);
4370        let db = DB::open_cf_descriptors(
4371            &opts,
4372            dir.path(),
4373            [
4374                ColumnFamilyDescriptor::new("first", Options::default()),
4375                ColumnFamilyDescriptor::new("second", Options::default()),
4376            ],
4377        )
4378        .unwrap();
4379
4380        let first = db.cf_handle("first").unwrap();
4381        let second = db.cf_handle("second").unwrap();
4382        let iterators = db.raw_iterators_cf([&first, &second]).unwrap();
4383        assert_eq!(iterators.len(), 2);
4384
4385        let shared = iterators[0].readopts_ptr();
4386        assert!(!shared.is_null());
4387        for iterator in &iterators {
4388            assert_eq!(
4389                iterator.readopts_ptr(),
4390                shared,
4391                "each iterator must hold the options object it was created from"
4392            );
4393        }
4394    }
4395}