Skip to main content

leveldb/database/
options.rs

1//! All the option types needed for interfacing with leveldb.
2//!
3//! Those are:
4//! * `Options`: used when opening a database
5//! * `ReadOptions`: used when reading from leveldb
6//! * `WriteOptions`: used when writng to leveldb
7use crate::binding::*;
8
9use crate::database::cache::Cache;
10#[cfg(feature = "experimental-extension")]
11use crate::database::cache::FlatT3CacheConfig;
12use crate::database::comparator::{Comparator, RawComparator};
13use crate::database::filter_policy::{
14    BloomFilterPolicy, CustomFilterPolicy, FilterPolicy, LevelDBFilterPolicy,
15};
16use crate::database::snapshots::Snapshot;
17use libc::{c_int, size_t};
18
19#[cfg(feature = "experimental-extension")]
20use crate::extension::logger::{LogHandler, Logger, OpaqueLogger};
21
22/// Compression choices
23#[repr(C)]
24#[derive(Copy, Clone)]
25pub enum Compression {
26    /// No compression enabled
27    No = 0,
28    /// Enable Snappy compression
29    Snappy = 1,
30    /// Enable Zstd compression
31    ///
32    /// Note: LevelDB C FFI does not provide an option to set the Zstd compression level.
33    /// It will use the internal default value of 1.
34    Zstd = 2,
35}
36
37/// Options to consider when opening a new or pre-existing LevelDB.
38///
39/// Note that in contrast to the LevelDB C API, the Comparator is not
40/// passed using this structure.
41/// For more detailed explanations, consider the
42///
43/// [LevelDB documentation](https://github.com/google/leveldb/tree/master/doc)
44pub struct Options {
45    /// create the database if missing
46    ///
47    /// default: false
48    pub create_if_missing: bool,
49    /// report an error if the DB already exists instead of opening.
50    ///
51    /// default: false
52    pub error_if_exists: bool,
53    /// paranoid checks make the database report an error as soon as
54    /// corruption is detected.
55    ///
56    /// default: false
57    pub paranoid_checks: bool,
58    /// Override the size of the write buffer to use.
59    ///
60    /// default: None
61    pub write_buffer_size: Option<size_t>,
62    /// Override the max number of open files.
63    ///
64    /// default: None
65    pub max_open_files: Option<i32>,
66    /// Override the size of the blocks leveldb uses for writing and caching.
67    ///
68    /// default: None
69    pub block_size: Option<size_t>,
70    /// Override the interval between restart points.
71    ///
72    /// default: None
73    pub block_restart_interval: Option<i32>,
74    /// Override the maximum size of flushed file before switching to new one.
75    ///
76    /// default: None
77    pub max_file_size: Option<size_t>,
78    /// Define whether LevelDB should write compressed or not.
79    ///
80    /// default: Compression::No
81    pub compression: Compression,
82    /// A cache to use during read operations.
83    ///
84    /// default: None
85    pub cache: Option<Cache>,
86    /// A custom comparator to use for key ordering.
87    ///
88    /// default: None
89    pub comparator: Option<RawComparator>,
90    /// A filter policy to use for reducing disk reads.
91    ///
92    /// default: None
93    pub filter_policy: Option<Box<dyn LevelDBFilterPolicy>>,
94    /// A custom logger for LevelDB info logging.
95    ///
96    /// default: None
97    #[cfg(feature = "experimental-extension")]
98    pub logger: Option<OpaqueLogger>,
99}
100
101impl Default for Options {
102    fn default() -> Self {
103        Self::new()
104    }
105}
106
107impl Options {
108    /// Create a new `Options` struct with default settings.
109    pub fn new() -> Options {
110        Options {
111            create_if_missing: false,
112            error_if_exists: false,
113            paranoid_checks: false,
114            write_buffer_size: None,
115            max_open_files: None,
116            block_size: None,
117            block_restart_interval: None,
118            max_file_size: None,
119            compression: Compression::No,
120            cache: None,
121            comparator: None,
122            filter_policy: None,
123            #[cfg(feature = "experimental-extension")]
124            logger: None,
125        }
126    }
127
128    /// Set a custom comparator for key ordering.
129    ///
130    /// This method allows users to set a Rust Comparator without dealing with raw C pointers.
131    pub fn set_comparator<C: Comparator>(&mut self, comparator: C) {
132        self.comparator = Some(RawComparator::new(comparator));
133    }
134
135    /// Set a built-in bloom filter policy.
136    ///
137    /// This is a convenience method for setting the built-in LevelDB bloom filter.
138    pub fn set_bloom_filter(&mut self, bits_per_key: i32) {
139        let filter_ptr = BloomFilterPolicy::new(bits_per_key);
140        self.filter_policy = Some(Box::new(filter_ptr));
141    }
142
143    /// Set a custom filter policy.
144    ///
145    /// This method allows users to set custom filter policies that implement the FilterPolicy trait.
146    pub fn set_filter_policy<T: FilterPolicy + 'static>(&mut self, policy: T) {
147        let custom_policy = CustomFilterPolicy::new(policy);
148        self.filter_policy = Some(Box::new(custom_policy));
149    }
150
151    /// Set a custom logger.
152    ///
153    /// This method allows users to set a custom logger
154    /// that will process log messages of LevelDB operations.
155    ///
156    /// # Type Parameters
157    ///
158    /// * `H` - The log handler type, must implement `LogHandler` and be `Send + Sync + 'static`
159    #[cfg(feature = "experimental-extension")]
160    #[deprecated(since = "2.2.0", note = "Use set_log_handler instead")]
161    pub fn set_logger<H: LogHandler + Send + Sync + 'static>(&mut self, logger: Logger<H>) {
162        let mut opaque_logger = logger.into_opaque();
163        opaque_logger.initialize();
164        self.logger = Some(opaque_logger);
165    }
166
167    /// Set a custom logger.
168    ///
169    /// This method allows users to set a custom logger
170    /// that will process log messages of LevelDB operations.
171    ///
172    /// # Type Parameters
173    ///
174    /// * `H` - The log handler type, must implement `LogHandler` and be `Send + Sync + 'static`
175    #[cfg(feature = "experimental-extension")]
176    pub fn set_log_handler<H: LogHandler + Send + Sync + 'static>(&mut self, handler: H) {
177        let logger = Logger::new(handler);
178        let mut opaque_logger = logger.into_opaque();
179        opaque_logger.initialize();
180        self.logger = Some(opaque_logger);
181    }
182
183    /// Set a built-in LRU cache with specified capacity
184    #[allow(deprecated)]
185    pub fn set_builtin_lru_cache(&mut self, capacity: size_t) {
186        self.cache = Some(Cache::new(capacity));
187    }
188
189    /// Set a FlatT3Cache with specified config
190    #[cfg(feature = "experimental-extension")]
191    #[allow(deprecated)]
192    pub fn set_flat_t3_cache(&mut self, config: FlatT3CacheConfig) {
193        self.cache = Some(Cache::new_flat_t3_cache(config));
194    }
195}
196
197/// The write options to use for a write operation.
198#[derive(Copy, Clone)]
199pub struct WriteOptions {
200    /// `fsync` before acknowledging a write operation.
201    ///
202    /// default: false
203    pub sync: bool,
204}
205
206impl Default for WriteOptions {
207    fn default() -> Self {
208        Self::new()
209    }
210}
211
212impl WriteOptions {
213    /// Return a new `WriteOptions` struct with default settings.
214    pub fn new() -> WriteOptions {
215        WriteOptions { sync: false }
216    }
217}
218
219/// The read options to use for any read operation.
220#[allow(missing_copy_implementations)]
221pub struct ReadOptions<'a> {
222    /// Whether to verify the saved checksums on read.
223    ///
224    /// default: false
225    pub verify_checksums: bool,
226    /// Whether to fill the internal cache with the
227    /// results of the read.
228    ///
229    /// default: true
230    pub fill_cache: bool,
231    /// An optional snapshot to base this operation on.
232    ///
233    /// Consider using the `Snapshot` trait instead of setting
234    /// this manually.
235    ///
236    /// default: None
237    pub snapshot: Option<&'a Snapshot<'a>>,
238}
239
240impl<'a> Default for ReadOptions<'a> {
241    fn default() -> Self {
242        Self::new()
243    }
244}
245
246impl<'a> ReadOptions<'a> {
247    /// Return a `ReadOptions` struct with the default values.
248    pub fn new() -> ReadOptions<'a> {
249        ReadOptions {
250            verify_checksums: false,
251            fill_cache: true,
252            snapshot: None,
253        }
254    }
255}
256
257/// Convert Rust Options to LevelDB C options.
258///
259/// # Safety
260///
261/// This function is unsafe because it creates and manipulates raw LevelDB C objects.
262/// The caller must ensure that:
263/// - The returned pointer is properly managed and eventually passed to leveldb_options_destroy
264/// - The Options reference remains valid for the duration of this call
265pub(crate) unsafe fn c_options(options: &Options) -> *mut leveldb_options_t {
266    unsafe {
267        let c_options = leveldb_options_create();
268        leveldb_options_set_create_if_missing(c_options, options.create_if_missing as u8);
269        leveldb_options_set_error_if_exists(c_options, options.error_if_exists as u8);
270        leveldb_options_set_paranoid_checks(c_options, options.paranoid_checks as u8);
271        if let Some(wbs) = options.write_buffer_size {
272            leveldb_options_set_write_buffer_size(c_options, wbs);
273        }
274        if let Some(mf) = options.max_open_files {
275            leveldb_options_set_max_open_files(c_options, mf);
276        }
277        if let Some(bs) = options.block_size {
278            leveldb_options_set_block_size(c_options, bs);
279        }
280        if let Some(bi) = options.block_restart_interval {
281            leveldb_options_set_block_restart_interval(c_options, bi);
282        }
283        if let Some(mfs) = options.max_file_size {
284            leveldb_options_set_max_file_size(c_options, mfs);
285        }
286        leveldb_options_set_compression(c_options, options.compression as c_int);
287        if let Some(ref c) = options.comparator {
288            leveldb_options_set_comparator(c_options, c.raw_ptr());
289        }
290        if let Some(ref cache) = options.cache {
291            leveldb_options_set_cache(c_options, cache.raw_ptr());
292        }
293        if let Some(ref fp) = options.filter_policy {
294            leveldb_options_set_filter_policy(c_options, fp.raw_ptr());
295        }
296
297        #[cfg(feature = "experimental-extension")]
298        if let Some(ref logger) = options.logger {
299            leveldb_options_set_info_log(c_options, logger.raw_ptr());
300        }
301
302        c_options
303    }
304}
305
306/// Convert Rust WriteOptions to LevelDB C write options.
307///
308/// # Safety
309///
310/// This function is unsafe because it creates and manipulates raw LevelDB C objects.
311/// The caller must ensure that:
312/// - The returned pointer is properly managed and eventually passed to leveldb_writeoptions_destroy
313pub(crate) unsafe fn c_writeoptions(options: WriteOptions) -> *mut leveldb_writeoptions_t {
314    unsafe {
315        let c_writeoptions = leveldb_writeoptions_create();
316        leveldb_writeoptions_set_sync(c_writeoptions, options.sync as u8);
317        c_writeoptions
318    }
319}
320
321/// Convert Rust ReadOptions to LevelDB C read options.
322///
323/// # Safety
324///
325/// This function is unsafe because it creates and manipulates raw LevelDB C objects.
326/// The caller must ensure that:
327/// - The returned pointer is properly managed and eventually passed to leveldb_readoptions_destroy
328/// - The ReadOptions reference and any contained snapshot remain valid for the duration of this call
329pub(crate) unsafe fn c_readoptions<'a>(options: &ReadOptions<'a>) -> *mut leveldb_readoptions_t {
330    unsafe {
331        let c_readoptions = leveldb_readoptions_create();
332        leveldb_readoptions_set_verify_checksums(c_readoptions, options.verify_checksums as u8);
333        leveldb_readoptions_set_fill_cache(c_readoptions, options.fill_cache as u8);
334
335        if let Some(snapshot) = options.snapshot {
336            leveldb_readoptions_set_snapshot(c_readoptions, snapshot.raw_ptr());
337        }
338        c_readoptions
339    }
340}