1use std::{collections::BTreeMap, ffi::CString, fs, iter, marker::PhantomData, path::Path, ptr};
17
18use libc::{c_char, c_int, size_t};
19
20use crate::column_family::ColumnFamilyTtl;
21use crate::{
22 AsColumnFamilyRef, ColumnFamilyDescriptor, DEFAULT_COLUMN_FAMILY_NAME, Error,
23 OptimisticTransactionDBOptions, OptimisticTransactionOptions, Options, ThreadMode, Transaction,
24 WriteOptions,
25 db::{DBCommon, DBInner},
26 ffi,
27 ffi_util::to_cpath,
28 write_batch::WriteBatchWithTransaction,
29};
30
31thread_local! { static DEFAULT_WRITE_OPTS: WriteOptions = WriteOptions::default(); }
38thread_local! {
42 static DEFAULT_OTXN_OPTS: OptimisticTransactionOptions =
43 OptimisticTransactionOptions::default();
44}
45
46#[cfg(not(feature = "multi-threaded-cf"))]
81pub type OptimisticTransactionDB<T = crate::SingleThreaded> =
82 DBCommon<T, OptimisticTransactionDBInner>;
83#[cfg(feature = "multi-threaded-cf")]
84pub type OptimisticTransactionDB<T = crate::MultiThreaded> =
85 DBCommon<T, OptimisticTransactionDBInner>;
86
87pub struct OptimisticTransactionDBInner {
88 base: *mut ffi::rocksdb_t,
89 pub(crate) db: *mut ffi::rocksdb_optimistictransactiondb_t,
93}
94
95impl DBInner for OptimisticTransactionDBInner {
96 #[inline]
97 fn inner(&self) -> *mut ffi::rocksdb_t {
98 self.base
99 }
100}
101
102impl Drop for OptimisticTransactionDBInner {
103 fn drop(&mut self) {
104 unsafe {
105 ffi::rocksdb_optimistictransactiondb_close_base_db(self.base);
106 ffi::rocksdb_optimistictransactiondb_close(self.db);
107 }
108 }
109}
110
111impl<T: ThreadMode> OptimisticTransactionDB<T> {
113 pub fn open_default<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
115 let mut opts = Options::default();
116 opts.create_if_missing(true);
117 Self::open(&opts, path)
118 }
119
120 pub fn open<P: AsRef<Path>>(opts: &Options, path: P) -> Result<Self, Error> {
122 Self::open_cf(opts, path, None::<&str>)
123 }
124
125 pub fn open_cf<P, I, N>(opts: &Options, path: P, cfs: I) -> Result<Self, Error>
132 where
133 P: AsRef<Path>,
134 I: IntoIterator<Item = N>,
135 N: AsRef<str>,
136 {
137 let cfs = cfs
138 .into_iter()
139 .map(|name| ColumnFamilyDescriptor::new(name.as_ref(), Options::default()));
140
141 Self::open_cf_descriptors_internal(opts, None, path, cfs)
142 }
143
144 pub fn open_cf_descriptors<P, I>(opts: &Options, path: P, cfs: I) -> Result<Self, Error>
146 where
147 P: AsRef<Path>,
148 I: IntoIterator<Item = ColumnFamilyDescriptor>,
149 {
150 Self::open_cf_descriptors_internal(opts, None, path, cfs)
151 }
152
153 pub fn open_with_otxn_db_options<P: AsRef<Path>>(
160 opts: &Options,
161 otxn_db_opts: &OptimisticTransactionDBOptions,
162 path: P,
163 ) -> Result<Self, Error> {
164 Self::open_cf_with_otxn_db_options(opts, otxn_db_opts, path, None::<&str>)
165 }
166
167 pub fn open_cf_with_otxn_db_options<P, I, N>(
175 opts: &Options,
176 otxn_db_opts: &OptimisticTransactionDBOptions,
177 path: P,
178 cfs: I,
179 ) -> Result<Self, Error>
180 where
181 P: AsRef<Path>,
182 I: IntoIterator<Item = N>,
183 N: AsRef<str>,
184 {
185 let cfs = cfs
186 .into_iter()
187 .map(|name| ColumnFamilyDescriptor::new(name.as_ref(), Options::default()));
188
189 Self::open_cf_descriptors_internal(opts, Some(otxn_db_opts), path, cfs)
190 }
191
192 pub fn open_cf_descriptors_with_otxn_db_options<P, I>(
199 opts: &Options,
200 otxn_db_opts: &OptimisticTransactionDBOptions,
201 path: P,
202 cfs: I,
203 ) -> Result<Self, Error>
204 where
205 P: AsRef<Path>,
206 I: IntoIterator<Item = ColumnFamilyDescriptor>,
207 {
208 Self::open_cf_descriptors_internal(opts, Some(otxn_db_opts), path, cfs)
209 }
210
211 fn open_cf_descriptors_internal<P, I>(
216 opts: &Options,
217 otxn_db_opts: Option<&OptimisticTransactionDBOptions>,
218 path: P,
219 cfs: I,
220 ) -> Result<Self, Error>
221 where
222 P: AsRef<Path>,
223 I: IntoIterator<Item = ColumnFamilyDescriptor>,
224 {
225 let cfs: Vec<_> = cfs.into_iter().collect();
226 let outlive = iter::once(opts.outlive.clone())
227 .chain(cfs.iter().map(|cf| cf.options.outlive.clone()))
228 .collect();
229
230 let cpath = to_cpath(&path)?;
231
232 if let Err(e) = fs::create_dir_all(&path) {
233 return Err(Error::new(format!(
234 "Failed to create RocksDB directory: `{e:?}`."
235 )));
236 }
237
238 let db: *mut ffi::rocksdb_optimistictransactiondb_t;
239 let mut cf_map = BTreeMap::new();
240
241 if cfs.is_empty() {
242 db = Self::open_raw(opts, otxn_db_opts, &cpath)?;
243 } else {
244 let mut cfs_v = cfs;
245 if !cfs_v.iter().any(|cf| cf.name == DEFAULT_COLUMN_FAMILY_NAME) {
247 cfs_v.push(ColumnFamilyDescriptor {
248 name: String::from(DEFAULT_COLUMN_FAMILY_NAME),
249 options: Options::default(),
250 ttl: ColumnFamilyTtl::SameAsDb,
251 });
252 }
253 let c_cfs: Vec<CString> = cfs_v
256 .iter()
257 .map(|cf| CString::new(cf.name.as_bytes()).unwrap())
258 .collect();
259
260 let cfnames: Vec<_> = c_cfs.iter().map(|cf| cf.as_ptr()).collect();
261
262 let mut cfhandles: Vec<_> = cfs_v.iter().map(|_| ptr::null_mut()).collect();
264
265 let cfopts: Vec<_> = cfs_v
266 .iter()
267 .map(|cf| cf.options.inner.cast_const())
268 .collect();
269
270 db = Self::open_cf_raw(
271 opts,
272 otxn_db_opts,
273 &cpath,
274 &cfs_v,
275 &cfnames,
276 &cfopts,
277 &mut cfhandles,
278 )?;
279
280 for handle in &cfhandles {
281 if handle.is_null() {
282 return Err(Error::new(
283 "Received null column family handle from DB.".to_owned(),
284 ));
285 }
286 }
287
288 for (cf_desc, inner) in cfs_v.iter().zip(cfhandles) {
289 cf_map.insert(cf_desc.name.clone(), inner);
290 }
291 }
292
293 if db.is_null() {
294 return Err(Error::new("Could not initialize database.".to_owned()));
295 }
296
297 let base = unsafe { ffi::rocksdb_optimistictransactiondb_get_base_db(db) };
298 if base.is_null() {
299 unsafe {
300 ffi::rocksdb_optimistictransactiondb_close(db);
301 }
302 return Err(Error::new("Could not initialize database.".to_owned()));
303 }
304 let inner = OptimisticTransactionDBInner { base, db };
305
306 Ok(Self::new(
307 inner,
308 T::new_cf_map_internal(cf_map),
309 path.as_ref().to_path_buf(),
310 outlive,
311 ))
312 }
313
314 fn open_raw(
315 opts: &Options,
316 otxn_db_opts: Option<&OptimisticTransactionDBOptions>,
317 cpath: &CString,
318 ) -> Result<*mut ffi::rocksdb_optimistictransactiondb_t, Error> {
319 unsafe {
320 let db = if let Some(otxn_db_opts) = otxn_db_opts {
321 ffi_try!(
322 ffi::rocksdb_optimistictransactiondb_open_with_otxn_db_options(
323 opts.inner,
324 otxn_db_opts.inner,
325 cpath.as_ptr()
326 )
327 )
328 } else {
329 ffi_try!(ffi::rocksdb_optimistictransactiondb_open(
330 opts.inner,
331 cpath.as_ptr()
332 ))
333 };
334 Ok(db)
335 }
336 }
337
338 fn open_cf_raw(
339 opts: &Options,
340 otxn_db_opts: Option<&OptimisticTransactionDBOptions>,
341 cpath: &CString,
342 cfs_v: &[ColumnFamilyDescriptor],
343 cfnames: &[*const c_char],
344 cfopts: &[*const ffi::rocksdb_options_t],
345 cfhandles: &mut [*mut ffi::rocksdb_column_family_handle_t],
346 ) -> Result<*mut ffi::rocksdb_optimistictransactiondb_t, Error> {
347 unsafe {
348 let db = if let Some(otxn_db_opts) = otxn_db_opts {
349 ffi_try!(
350 ffi::rocksdb_optimistictransactiondb_open_column_families_with_otxn_db_options(
351 opts.inner,
352 otxn_db_opts.inner,
353 cpath.as_ptr(),
354 cfs_v.len() as c_int,
355 cfnames.as_ptr(),
356 cfopts.as_ptr(),
357 cfhandles.as_mut_ptr(),
358 )
359 )
360 } else {
361 ffi_try!(ffi::rocksdb_optimistictransactiondb_open_column_families(
362 opts.inner,
363 cpath.as_ptr(),
364 cfs_v.len() as c_int,
365 cfnames.as_ptr(),
366 cfopts.as_ptr(),
367 cfhandles.as_mut_ptr(),
368 ))
369 };
370 Ok(db)
371 }
372 }
373
374 pub fn transaction(&'_ self) -> Transaction<'_, Self> {
376 DEFAULT_WRITE_OPTS.with(|write_opts| {
377 DEFAULT_OTXN_OPTS.with(|otxn_opts| self.transaction_opt(write_opts, otxn_opts))
378 })
379 }
380
381 pub fn transaction_opt(
383 &'_ self,
384 writeopts: &WriteOptions,
385 otxn_opts: &OptimisticTransactionOptions,
386 ) -> Transaction<'_, Self> {
387 Transaction {
388 inner: unsafe {
389 ffi::rocksdb_optimistictransaction_begin(
390 self.inner.db,
391 writeopts.inner,
392 otxn_opts.inner,
393 std::ptr::null_mut(),
394 )
395 },
396 _marker: PhantomData,
397 }
398 }
399
400 pub fn write_opt(
401 &self,
402 batch: &WriteBatchWithTransaction<true>,
403 writeopts: &WriteOptions,
404 ) -> Result<(), Error> {
405 unsafe {
406 ffi_try!(ffi::rocksdb_optimistictransactiondb_write(
407 self.inner.db,
408 writeopts.inner,
409 batch.inner
410 ));
411 }
412 Ok(())
413 }
414
415 pub fn write(&self, batch: &WriteBatchWithTransaction<true>) -> Result<(), Error> {
416 DEFAULT_WRITE_OPTS.with(|opts| self.write_opt(batch, opts))
417 }
418
419 pub fn write_without_wal(&self, batch: &WriteBatchWithTransaction<true>) -> Result<(), Error> {
420 let mut wo = WriteOptions::new();
421 wo.disable_wal(true);
422 self.write_opt(batch, &wo)
423 }
424
425 pub fn delete_range_cf_opt<K: AsRef<[u8]>>(
427 &self,
428 cf: &impl AsColumnFamilyRef,
429 from: K,
430 to: K,
431 writeopts: &WriteOptions,
432 ) -> Result<(), Error> {
433 let from = from.as_ref();
434 let to = to.as_ref();
435
436 unsafe {
437 ffi_try!(ffi::rocksdb_delete_range_cf(
438 self.inner.inner(),
439 writeopts.inner,
440 cf.inner(),
441 from.as_ptr() as *const c_char,
442 from.len() as size_t,
443 to.as_ptr() as *const c_char,
444 to.len() as size_t,
445 ));
446 Ok(())
447 }
448 }
449
450 pub fn delete_range_cf<K: AsRef<[u8]>>(
452 &self,
453 cf: &impl AsColumnFamilyRef,
454 from: K,
455 to: K,
456 ) -> Result<(), Error> {
457 DEFAULT_WRITE_OPTS.with(|opts| self.delete_range_cf_opt(cf, from, to, opts))
458 }
459}