1use std::{
17 collections::BTreeMap,
18 ffi::CString,
19 fs, iter,
20 marker::PhantomData,
21 path::{Path, PathBuf},
22 ptr,
23 sync::{Arc, Mutex},
24};
25
26use crate::CStrLike;
27use std::ffi::CStr;
28
29use crate::column_family::ColumnFamilyTtl;
30use crate::{
31 AsColumnFamilyRef, BoundColumnFamily, ColumnFamily, ColumnFamilyDescriptor, DB,
32 DBIteratorWithThreadMode, DBPinnableSlice, DBRawIteratorWithThreadMode,
33 DEFAULT_COLUMN_FAMILY_NAME, Direction, Error, FlushOptions, FlushWalOptions, IteratorMode,
34 MultiThreaded, Options, ReadOptions, SingleThreaded, SnapshotWithThreadMode, ThreadMode,
35 Transaction, TransactionDBOptions, TransactionOptions, WriteBatchWithTransaction, WriteOptions,
36 column_family::UnboundColumnFamily,
37 db::{DBAccess, convert_values},
38 db_options::OptionsMustOutliveDB,
39 ffi,
40 ffi_util::to_cpath,
41};
42use ffi::rocksdb_transaction_t;
43use libc::{c_char, c_int, c_uchar, c_void, size_t};
44
45thread_local! { static DEFAULT_READ_OPTS: ReadOptions = ReadOptions::default(); }
52thread_local! { static DEFAULT_WRITE_OPTS: WriteOptions = WriteOptions::default(); }
53thread_local! { static DEFAULT_FLUSH_OPTS: FlushOptions = FlushOptions::default(); }
54thread_local! { static DEFAULT_TXN_OPTS: TransactionOptions = TransactionOptions::default(); }
59
60#[cfg(not(feature = "multi-threaded-cf"))]
61type DefaultThreadMode = crate::SingleThreaded;
62#[cfg(feature = "multi-threaded-cf")]
63type DefaultThreadMode = crate::MultiThreaded;
64
65pub struct TransactionDB<T: ThreadMode = DefaultThreadMode> {
106 pub(crate) inner: *mut ffi::rocksdb_transactiondb_t,
107 cfs: T,
108 path: PathBuf,
109 prepared: Mutex<Vec<*mut rocksdb_transaction_t>>,
111 _outlive: Vec<OptionsMustOutliveDB>,
112}
113
114unsafe impl<T: ThreadMode> Send for TransactionDB<T> {}
115unsafe impl<T: ThreadMode> Sync for TransactionDB<T> {}
116
117impl<T: ThreadMode> DBAccess for TransactionDB<T> {
118 unsafe fn create_snapshot(&self) -> *const ffi::rocksdb_snapshot_t {
119 unsafe { ffi::rocksdb_transactiondb_create_snapshot(self.inner) }
120 }
121
122 unsafe fn release_snapshot(&self, snapshot: *const ffi::rocksdb_snapshot_t) {
123 unsafe {
124 ffi::rocksdb_transactiondb_release_snapshot(self.inner, snapshot);
125 }
126 }
127
128 unsafe fn create_iterator(&self, readopts: &ReadOptions) -> *mut ffi::rocksdb_iterator_t {
129 unsafe { ffi::rocksdb_transactiondb_create_iterator(self.inner, readopts.inner) }
130 }
131
132 unsafe fn create_iterator_cf(
133 &self,
134 cf_handle: *mut ffi::rocksdb_column_family_handle_t,
135 readopts: &ReadOptions,
136 ) -> *mut ffi::rocksdb_iterator_t {
137 unsafe {
138 ffi::rocksdb_transactiondb_create_iterator_cf(self.inner, readopts.inner, cf_handle)
139 }
140 }
141
142 fn get_opt<K: AsRef<[u8]>>(
143 &self,
144 key: K,
145 readopts: &ReadOptions,
146 ) -> Result<Option<Vec<u8>>, Error> {
147 self.get_opt(key, readopts)
148 }
149
150 fn get_cf_opt<K: AsRef<[u8]>>(
151 &self,
152 cf: &impl AsColumnFamilyRef,
153 key: K,
154 readopts: &ReadOptions,
155 ) -> Result<Option<Vec<u8>>, Error> {
156 self.get_cf_opt(cf, key, readopts)
157 }
158
159 fn get_pinned_opt<K: AsRef<[u8]>>(
160 &'_ self,
161 key: K,
162 readopts: &ReadOptions,
163 ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
164 self.get_pinned_opt(key, readopts)
165 }
166
167 fn get_pinned_cf_opt<K: AsRef<[u8]>>(
168 &'_ self,
169 cf: &impl AsColumnFamilyRef,
170 key: K,
171 readopts: &ReadOptions,
172 ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
173 self.get_pinned_cf_opt(cf, key, readopts)
174 }
175
176 fn multi_get_opt<K, I>(
177 &self,
178 keys: I,
179 readopts: &ReadOptions,
180 ) -> Vec<Result<Option<Vec<u8>>, Error>>
181 where
182 K: AsRef<[u8]>,
183 I: IntoIterator<Item = K>,
184 {
185 self.multi_get_opt(keys, readopts)
186 }
187
188 fn multi_get_cf_opt<'b, K, I, W>(
189 &self,
190 keys_cf: I,
191 readopts: &ReadOptions,
192 ) -> Vec<Result<Option<Vec<u8>>, Error>>
193 where
194 K: AsRef<[u8]>,
195 I: IntoIterator<Item = (&'b W, K)>,
196 W: AsColumnFamilyRef + 'b,
197 {
198 self.multi_get_cf_opt(keys_cf, readopts)
199 }
200}
201
202impl<T: ThreadMode> TransactionDB<T> {
203 pub fn open_default<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
205 let mut opts = Options::default();
206 opts.create_if_missing(true);
207 let txn_db_opts = TransactionDBOptions::default();
208 Self::open(&opts, &txn_db_opts, path)
209 }
210
211 pub fn open<P: AsRef<Path>>(
213 opts: &Options,
214 txn_db_opts: &TransactionDBOptions,
215 path: P,
216 ) -> Result<Self, Error> {
217 Self::open_cf(opts, txn_db_opts, path, None::<&str>)
218 }
219
220 pub fn open_cf<P, I, N>(
224 opts: &Options,
225 txn_db_opts: &TransactionDBOptions,
226 path: P,
227 cfs: I,
228 ) -> Result<Self, Error>
229 where
230 P: AsRef<Path>,
231 I: IntoIterator<Item = N>,
232 N: AsRef<str>,
233 {
234 let cfs = cfs
235 .into_iter()
236 .map(|name| ColumnFamilyDescriptor::new(name.as_ref(), Options::default()));
237
238 Self::open_cf_descriptors_internal(opts, txn_db_opts, path, cfs)
239 }
240
241 pub fn open_cf_descriptors<P, I>(
243 opts: &Options,
244 txn_db_opts: &TransactionDBOptions,
245 path: P,
246 cfs: I,
247 ) -> Result<Self, Error>
248 where
249 P: AsRef<Path>,
250 I: IntoIterator<Item = ColumnFamilyDescriptor>,
251 {
252 Self::open_cf_descriptors_internal(opts, txn_db_opts, path, cfs)
253 }
254
255 fn open_cf_descriptors_internal<P, I>(
257 opts: &Options,
258 txn_db_opts: &TransactionDBOptions,
259 path: P,
260 cfs: I,
261 ) -> Result<Self, Error>
262 where
263 P: AsRef<Path>,
264 I: IntoIterator<Item = ColumnFamilyDescriptor>,
265 {
266 let cfs: Vec<_> = cfs.into_iter().collect();
267 let outlive = iter::once(opts.outlive.clone())
268 .chain(cfs.iter().map(|cf| cf.options.outlive.clone()))
269 .collect();
270
271 let cpath = to_cpath(&path)?;
272
273 if let Err(e) = fs::create_dir_all(&path) {
274 return Err(Error::new(format!(
275 "Failed to create RocksDB directory: `{e:?}`."
276 )));
277 }
278
279 let db: *mut ffi::rocksdb_transactiondb_t;
280 let mut cf_map = BTreeMap::new();
281
282 if cfs.is_empty() {
283 db = Self::open_raw(opts, txn_db_opts, &cpath)?;
284 } else {
285 let mut cfs_v = cfs;
286 if !cfs_v.iter().any(|cf| cf.name == DEFAULT_COLUMN_FAMILY_NAME) {
288 cfs_v.push(ColumnFamilyDescriptor {
289 name: String::from(DEFAULT_COLUMN_FAMILY_NAME),
290 options: Options::default(),
291 ttl: ColumnFamilyTtl::SameAsDb, });
293 }
294 let c_cfs: Vec<CString> = cfs_v
297 .iter()
298 .map(|cf| CString::new(cf.name.as_bytes()).unwrap())
299 .collect();
300
301 let cfnames: Vec<_> = c_cfs.iter().map(|cf| cf.as_ptr()).collect();
302
303 let mut cfhandles: Vec<_> = cfs_v.iter().map(|_| ptr::null_mut()).collect();
305
306 let cfopts: Vec<_> = cfs_v
307 .iter()
308 .map(|cf| cf.options.inner.cast_const())
309 .collect();
310
311 db = Self::open_cf_raw(
312 opts,
313 txn_db_opts,
314 &cpath,
315 &cfs_v,
316 &cfnames,
317 &cfopts,
318 &mut cfhandles,
319 )?;
320
321 for handle in &cfhandles {
322 if handle.is_null() {
323 return Err(Error::new(
324 "Received null column family handle from DB.".to_owned(),
325 ));
326 }
327 }
328
329 for (cf_desc, inner) in cfs_v.iter().zip(cfhandles) {
330 cf_map.insert(cf_desc.name.clone(), inner);
331 }
332 }
333
334 if db.is_null() {
335 return Err(Error::new("Could not initialize database.".to_owned()));
336 }
337
338 let prepared = unsafe {
339 let mut cnt = 0;
340 let ptr = ffi::rocksdb_transactiondb_get_prepared_transactions(db, &raw mut cnt);
341 let mut vec = vec![std::ptr::null_mut(); cnt];
342 if !ptr.is_null() {
343 std::ptr::copy_nonoverlapping(ptr, vec.as_mut_ptr(), cnt);
344 ffi::rocksdb_free(ptr as *mut c_void);
345 }
346 vec
347 };
348
349 Ok(TransactionDB {
350 inner: db,
351 cfs: T::new_cf_map_internal(cf_map),
352 path: path.as_ref().to_path_buf(),
353 prepared: Mutex::new(prepared),
354 _outlive: outlive,
355 })
356 }
357
358 fn open_raw(
359 opts: &Options,
360 txn_db_opts: &TransactionDBOptions,
361 cpath: &CString,
362 ) -> Result<*mut ffi::rocksdb_transactiondb_t, Error> {
363 unsafe {
364 let db = ffi_try!(ffi::rocksdb_transactiondb_open(
365 opts.inner,
366 txn_db_opts.inner,
367 cpath.as_ptr()
368 ));
369 Ok(db)
370 }
371 }
372
373 fn open_cf_raw(
374 opts: &Options,
375 txn_db_opts: &TransactionDBOptions,
376 cpath: &CString,
377 cfs_v: &[ColumnFamilyDescriptor],
378 cfnames: &[*const c_char],
379 cfopts: &[*const ffi::rocksdb_options_t],
380 cfhandles: &mut [*mut ffi::rocksdb_column_family_handle_t],
381 ) -> Result<*mut ffi::rocksdb_transactiondb_t, Error> {
382 unsafe {
383 let db = ffi_try!(ffi::rocksdb_transactiondb_open_column_families(
384 opts.inner,
385 txn_db_opts.inner,
386 cpath.as_ptr(),
387 cfs_v.len() as c_int,
388 cfnames.as_ptr(),
389 cfopts.as_ptr(),
390 cfhandles.as_mut_ptr(),
391 ));
392 Ok(db)
393 }
394 }
395
396 fn create_inner_cf_handle(
397 &self,
398 name: &str,
399 opts: &Options,
400 ) -> Result<*mut ffi::rocksdb_column_family_handle_t, Error> {
401 let cf_name = CString::new(name.as_bytes()).map_err(|_| {
402 Error::new("Failed to convert path to CString when creating cf".to_owned())
403 })?;
404
405 Ok(unsafe {
406 ffi_try!(ffi::rocksdb_transactiondb_create_column_family(
407 self.inner,
408 opts.inner,
409 cf_name.as_ptr(),
410 ))
411 })
412 }
413
414 pub fn list_cf<P: AsRef<Path>>(opts: &Options, path: P) -> Result<Vec<String>, Error> {
415 DB::list_cf(opts, path)
416 }
417
418 pub fn destroy<P: AsRef<Path>>(opts: &Options, path: P) -> Result<(), Error> {
419 DB::destroy(opts, path)
420 }
421
422 pub fn repair<P: AsRef<Path>>(opts: &Options, path: P) -> Result<(), Error> {
423 DB::repair(opts, path)
424 }
425
426 pub fn path(&self) -> &Path {
427 self.path.as_path()
428 }
429
430 pub fn flush_wal(&self, sync: bool) -> Result<(), Error> {
433 unsafe {
434 ffi_try!(ffi::rocksdb_transactiondb_flush_wal(
435 self.inner,
436 c_uchar::from(sync)
437 ));
438 }
439 Ok(())
440 }
441
442 pub fn flush_wal_opt(&self, flushopts: &FlushWalOptions) -> Result<(), Error> {
449 unsafe {
450 ffi_try!(ffi::rocksdb_transactiondb_flush_wal_with_options(
451 self.inner,
452 flushopts.as_ptr()
453 ));
454 }
455 Ok(())
456 }
457
458 pub fn flush_opt(&self, flushopts: &FlushOptions) -> Result<(), Error> {
460 unsafe {
461 ffi_try!(ffi::rocksdb_transactiondb_flush(
462 self.inner,
463 flushopts.inner
464 ));
465 }
466 Ok(())
467 }
468
469 pub fn flush(&self) -> Result<(), Error> {
471 DEFAULT_FLUSH_OPTS.with(|opts| self.flush_opt(opts))
472 }
473
474 pub fn flush_cf_opt(
476 &self,
477 cf: &impl AsColumnFamilyRef,
478 flushopts: &FlushOptions,
479 ) -> Result<(), Error> {
480 unsafe {
481 ffi_try!(ffi::rocksdb_transactiondb_flush_cf(
482 self.inner,
483 flushopts.inner,
484 cf.inner()
485 ));
486 }
487 Ok(())
488 }
489
490 pub fn flush_cfs_opt(
496 &self,
497 cfs: &[&impl AsColumnFamilyRef],
498 opts: &FlushOptions,
499 ) -> Result<(), Error> {
500 let mut cfs = cfs.iter().map(|cf| cf.inner()).collect::<Vec<_>>();
501 unsafe {
502 ffi_try!(ffi::rocksdb_transactiondb_flush_cfs(
503 self.inner,
504 opts.inner,
505 cfs.as_mut_ptr(),
506 cfs.len() as c_int,
507 ));
508 }
509 Ok(())
510 }
511
512 pub fn flush_cf(&self, cf: &impl AsColumnFamilyRef) -> Result<(), Error> {
515 DEFAULT_FLUSH_OPTS.with(|opts| self.flush_cf_opt(cf, opts))
516 }
517
518 pub fn transaction(&'_ self) -> Transaction<'_, Self> {
520 DEFAULT_WRITE_OPTS.with(|write_opts| {
521 DEFAULT_TXN_OPTS.with(|txn_opts| self.transaction_opt(write_opts, txn_opts))
522 })
523 }
524
525 pub fn transaction_opt<'a>(
527 &'a self,
528 write_opts: &WriteOptions,
529 txn_opts: &TransactionOptions,
530 ) -> Transaction<'a, Self> {
531 Transaction {
532 inner: unsafe {
533 ffi::rocksdb_transaction_begin(
534 self.inner,
535 write_opts.inner,
536 txn_opts.inner,
537 std::ptr::null_mut(),
538 )
539 },
540 _marker: PhantomData,
541 }
542 }
543
544 pub fn prepared_transactions(&'_ self) -> Vec<Transaction<'_, Self>> {
549 self.prepared
550 .lock()
551 .unwrap()
552 .drain(0..)
553 .map(|inner| Transaction {
554 inner,
555 _marker: PhantomData,
556 })
557 .collect()
558 }
559
560 pub fn get<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<Vec<u8>>, Error> {
562 self.get_pinned(key).map(|x| x.map(|v| v.as_ref().to_vec()))
563 }
564
565 pub fn get_cf<K: AsRef<[u8]>>(
567 &self,
568 cf: &impl AsColumnFamilyRef,
569 key: K,
570 ) -> Result<Option<Vec<u8>>, Error> {
571 self.get_pinned_cf(cf, key)
572 .map(|x| x.map(|v| v.as_ref().to_vec()))
573 }
574
575 pub fn get_opt<K: AsRef<[u8]>>(
577 &self,
578 key: K,
579 readopts: &ReadOptions,
580 ) -> Result<Option<Vec<u8>>, Error> {
581 self.get_pinned_opt(key, readopts)
582 .map(|x| x.map(|v| v.as_ref().to_vec()))
583 }
584
585 pub fn get_cf_opt<K: AsRef<[u8]>>(
587 &self,
588 cf: &impl AsColumnFamilyRef,
589 key: K,
590 readopts: &ReadOptions,
591 ) -> Result<Option<Vec<u8>>, Error> {
592 self.get_pinned_cf_opt(cf, key, readopts)
593 .map(|x| x.map(|v| v.as_ref().to_vec()))
594 }
595
596 pub fn get_pinned<K: AsRef<[u8]>>(
597 &'_ self,
598 key: K,
599 ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
600 DEFAULT_READ_OPTS.with(|opts| self.get_pinned_opt(key, opts))
601 }
602
603 pub fn get_pinned_cf<K: AsRef<[u8]>>(
605 &'_ self,
606 cf: &impl AsColumnFamilyRef,
607 key: K,
608 ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
609 DEFAULT_READ_OPTS.with(|opts| self.get_pinned_cf_opt(cf, key, opts))
610 }
611
612 pub fn get_pinned_opt<K: AsRef<[u8]>>(
614 &'_ self,
615 key: K,
616 readopts: &ReadOptions,
617 ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
618 let key = key.as_ref();
619 unsafe {
620 let val = ffi_try!(ffi::rocksdb_transactiondb_get_pinned(
621 self.inner,
622 readopts.inner,
623 key.as_ptr() as *const c_char,
624 key.len() as size_t,
625 ));
626 if val.is_null() {
627 Ok(None)
628 } else {
629 Ok(Some(DBPinnableSlice::from_c(val)))
630 }
631 }
632 }
633
634 pub fn get_pinned_cf_opt<K: AsRef<[u8]>>(
636 &'_ self,
637 cf: &impl AsColumnFamilyRef,
638 key: K,
639 readopts: &ReadOptions,
640 ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
641 let key = key.as_ref();
642 unsafe {
643 let val = ffi_try!(ffi::rocksdb_transactiondb_get_pinned_cf(
644 self.inner,
645 readopts.inner,
646 cf.inner(),
647 key.as_ptr() as *const c_char,
648 key.len() as size_t,
649 ));
650 if val.is_null() {
651 Ok(None)
652 } else {
653 Ok(Some(DBPinnableSlice::from_c(val)))
654 }
655 }
656 }
657
658 pub fn multi_get<K, I>(&self, keys: I) -> Vec<Result<Option<Vec<u8>>, Error>>
660 where
661 K: AsRef<[u8]>,
662 I: IntoIterator<Item = K>,
663 {
664 DEFAULT_READ_OPTS.with(|opts| self.multi_get_opt(keys, opts))
665 }
666
667 pub fn multi_get_opt<K, I>(
669 &self,
670 keys: I,
671 readopts: &ReadOptions,
672 ) -> Vec<Result<Option<Vec<u8>>, Error>>
673 where
674 K: AsRef<[u8]>,
675 I: IntoIterator<Item = K>,
676 {
677 let owned_keys: Vec<K> = keys.into_iter().collect();
678 let (ptr_keys, keys_sizes): (Vec<*const c_char>, Vec<usize>) = owned_keys
679 .iter()
680 .map(|k| {
681 let key = k.as_ref();
682 (key.as_ptr() as *const c_char, key.len())
683 })
684 .unzip();
685
686 let mut values: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
687 let mut values_sizes: Vec<usize> = Vec::with_capacity(ptr_keys.len());
688 let mut errors: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
689 unsafe {
690 ffi::rocksdb_transactiondb_multi_get(
691 self.inner,
692 readopts.inner,
693 ptr_keys.len(),
694 ptr_keys.as_ptr(),
695 keys_sizes.as_ptr(),
696 values.as_mut_ptr(),
697 values_sizes.as_mut_ptr(),
698 errors.as_mut_ptr(),
699 );
700 }
701
702 unsafe {
703 values.set_len(ptr_keys.len());
704 values_sizes.set_len(ptr_keys.len());
705 errors.set_len(ptr_keys.len());
706 }
707
708 convert_values(values, values_sizes, errors)
709 }
710
711 pub fn multi_get_cf<'a, 'b: 'a, K, I, W>(
713 &'a self,
714 keys: I,
715 ) -> Vec<Result<Option<Vec<u8>>, Error>>
716 where
717 K: AsRef<[u8]>,
718 I: IntoIterator<Item = (&'b W, K)>,
719 W: 'b + AsColumnFamilyRef,
720 {
721 DEFAULT_READ_OPTS.with(|opts| self.multi_get_cf_opt(keys, opts))
722 }
723
724 pub fn multi_get_cf_opt<'a, 'b: 'a, K, I, W>(
726 &'a self,
727 keys: I,
728 readopts: &ReadOptions,
729 ) -> Vec<Result<Option<Vec<u8>>, Error>>
730 where
731 K: AsRef<[u8]>,
732 I: IntoIterator<Item = (&'b W, K)>,
733 W: 'b + AsColumnFamilyRef,
734 {
735 let cfs_and_owned_keys: Vec<(&'b W, K)> = keys.into_iter().collect();
736 let (ptr_keys, keys_sizes): (Vec<*const c_char>, Vec<usize>) = cfs_and_owned_keys
737 .iter()
738 .map(|(_, k)| {
739 let key = k.as_ref();
740 (key.as_ptr() as *const c_char, key.len())
741 })
742 .unzip();
743 let ptr_cfs: Vec<*const ffi::rocksdb_column_family_handle_t> = cfs_and_owned_keys
744 .iter()
745 .map(|(c, _)| c.inner().cast_const())
746 .collect();
747 let mut values: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
748 let mut values_sizes: Vec<usize> = Vec::with_capacity(ptr_keys.len());
749 let mut errors: Vec<*mut c_char> = Vec::with_capacity(ptr_keys.len());
750 unsafe {
751 ffi::rocksdb_transactiondb_multi_get_cf(
752 self.inner,
753 readopts.inner,
754 ptr_cfs.as_ptr(),
755 ptr_keys.len(),
756 ptr_keys.as_ptr(),
757 keys_sizes.as_ptr(),
758 values.as_mut_ptr(),
759 values_sizes.as_mut_ptr(),
760 errors.as_mut_ptr(),
761 );
762 }
763
764 unsafe {
765 values.set_len(ptr_keys.len());
766 values_sizes.set_len(ptr_keys.len());
767 errors.set_len(ptr_keys.len());
768 }
769
770 convert_values(values, values_sizes, errors)
771 }
772
773 pub fn put<K, V>(&self, key: K, value: V) -> Result<(), Error>
774 where
775 K: AsRef<[u8]>,
776 V: AsRef<[u8]>,
777 {
778 DEFAULT_WRITE_OPTS.with(|opts| self.put_opt(key, value, opts))
779 }
780
781 pub fn put_cf<K, V>(&self, cf: &impl AsColumnFamilyRef, key: K, value: V) -> Result<(), Error>
782 where
783 K: AsRef<[u8]>,
784 V: AsRef<[u8]>,
785 {
786 DEFAULT_WRITE_OPTS.with(|opts| self.put_cf_opt(cf, key, value, opts))
787 }
788
789 pub fn put_opt<K, V>(&self, key: K, value: V, writeopts: &WriteOptions) -> Result<(), Error>
790 where
791 K: AsRef<[u8]>,
792 V: AsRef<[u8]>,
793 {
794 let key = key.as_ref();
795 let value = value.as_ref();
796 unsafe {
797 ffi_try!(ffi::rocksdb_transactiondb_put(
798 self.inner,
799 writeopts.inner,
800 key.as_ptr() as *const c_char,
801 key.len() as size_t,
802 value.as_ptr() as *const c_char,
803 value.len() as size_t
804 ));
805 }
806 Ok(())
807 }
808
809 pub fn put_cf_opt<K, V>(
810 &self,
811 cf: &impl AsColumnFamilyRef,
812 key: K,
813 value: V,
814 writeopts: &WriteOptions,
815 ) -> Result<(), Error>
816 where
817 K: AsRef<[u8]>,
818 V: AsRef<[u8]>,
819 {
820 let key = key.as_ref();
821 let value = value.as_ref();
822 unsafe {
823 ffi_try!(ffi::rocksdb_transactiondb_put_cf(
824 self.inner,
825 writeopts.inner,
826 cf.inner(),
827 key.as_ptr() as *const c_char,
828 key.len() as size_t,
829 value.as_ptr() as *const c_char,
830 value.len() as size_t
831 ));
832 }
833 Ok(())
834 }
835
836 pub fn write(&self, batch: &WriteBatchWithTransaction<true>) -> Result<(), Error> {
837 DEFAULT_WRITE_OPTS.with(|opts| self.write_opt(batch, opts))
838 }
839
840 pub fn write_opt(
841 &self,
842 batch: &WriteBatchWithTransaction<true>,
843 writeopts: &WriteOptions,
844 ) -> Result<(), Error> {
845 unsafe {
846 ffi_try!(ffi::rocksdb_transactiondb_write(
847 self.inner,
848 writeopts.inner,
849 batch.inner
850 ));
851 }
852 Ok(())
853 }
854
855 pub fn merge<K, V>(&self, key: K, value: V) -> Result<(), Error>
856 where
857 K: AsRef<[u8]>,
858 V: AsRef<[u8]>,
859 {
860 DEFAULT_WRITE_OPTS.with(|opts| self.merge_opt(key, value, opts))
861 }
862
863 pub fn merge_cf<K, V>(&self, cf: &impl AsColumnFamilyRef, key: K, value: V) -> Result<(), Error>
864 where
865 K: AsRef<[u8]>,
866 V: AsRef<[u8]>,
867 {
868 DEFAULT_WRITE_OPTS.with(|opts| self.merge_cf_opt(cf, key, value, opts))
869 }
870
871 pub fn merge_opt<K, V>(&self, key: K, value: V, writeopts: &WriteOptions) -> Result<(), Error>
872 where
873 K: AsRef<[u8]>,
874 V: AsRef<[u8]>,
875 {
876 let key = key.as_ref();
877 let value = value.as_ref();
878 unsafe {
879 ffi_try!(ffi::rocksdb_transactiondb_merge(
880 self.inner,
881 writeopts.inner,
882 key.as_ptr() as *const c_char,
883 key.len() as size_t,
884 value.as_ptr() as *const c_char,
885 value.len() as size_t,
886 ));
887 Ok(())
888 }
889 }
890
891 pub fn merge_cf_opt<K, V>(
892 &self,
893 cf: &impl AsColumnFamilyRef,
894 key: K,
895 value: V,
896 writeopts: &WriteOptions,
897 ) -> Result<(), Error>
898 where
899 K: AsRef<[u8]>,
900 V: AsRef<[u8]>,
901 {
902 let key = key.as_ref();
903 let value = value.as_ref();
904 unsafe {
905 ffi_try!(ffi::rocksdb_transactiondb_merge_cf(
906 self.inner,
907 writeopts.inner,
908 cf.inner(),
909 key.as_ptr() as *const c_char,
910 key.len() as size_t,
911 value.as_ptr() as *const c_char,
912 value.len() as size_t,
913 ));
914 Ok(())
915 }
916 }
917
918 pub fn delete<K: AsRef<[u8]>>(&self, key: K) -> Result<(), Error> {
919 DEFAULT_WRITE_OPTS.with(|opts| self.delete_opt(key, opts))
920 }
921
922 pub fn delete_cf<K: AsRef<[u8]>>(
923 &self,
924 cf: &impl AsColumnFamilyRef,
925 key: K,
926 ) -> Result<(), Error> {
927 DEFAULT_WRITE_OPTS.with(|opts| self.delete_cf_opt(cf, key, opts))
928 }
929
930 pub fn delete_opt<K: AsRef<[u8]>>(
931 &self,
932 key: K,
933 writeopts: &WriteOptions,
934 ) -> Result<(), Error> {
935 let key = key.as_ref();
936 unsafe {
937 ffi_try!(ffi::rocksdb_transactiondb_delete(
938 self.inner,
939 writeopts.inner,
940 key.as_ptr() as *const c_char,
941 key.len() as size_t,
942 ));
943 }
944 Ok(())
945 }
946
947 pub fn delete_cf_opt<K: AsRef<[u8]>>(
948 &self,
949 cf: &impl AsColumnFamilyRef,
950 key: K,
951 writeopts: &WriteOptions,
952 ) -> Result<(), Error> {
953 let key = key.as_ref();
954 unsafe {
955 ffi_try!(ffi::rocksdb_transactiondb_delete_cf(
956 self.inner,
957 writeopts.inner,
958 cf.inner(),
959 key.as_ptr() as *const c_char,
960 key.len() as size_t,
961 ));
962 }
963 Ok(())
964 }
965
966 pub fn iterator<'a: 'b, 'b>(
967 &'a self,
968 mode: IteratorMode,
969 ) -> DBIteratorWithThreadMode<'b, Self> {
970 let readopts = ReadOptions::default();
971 self.iterator_opt(mode, readopts)
972 }
973
974 pub fn iterator_opt<'a: 'b, 'b>(
975 &'a self,
976 mode: IteratorMode,
977 readopts: ReadOptions,
978 ) -> DBIteratorWithThreadMode<'b, Self> {
979 DBIteratorWithThreadMode::new(self, readopts, mode)
980 }
981
982 pub fn iterator_cf_opt<'a: 'b, 'b>(
985 &'a self,
986 cf_handle: &impl AsColumnFamilyRef,
987 readopts: ReadOptions,
988 mode: IteratorMode,
989 ) -> DBIteratorWithThreadMode<'b, Self> {
990 DBIteratorWithThreadMode::new_cf(self, cf_handle.inner(), readopts, mode)
991 }
992
993 pub fn full_iterator<'a: 'b, 'b>(
997 &'a self,
998 mode: IteratorMode,
999 ) -> DBIteratorWithThreadMode<'b, Self> {
1000 let mut opts = ReadOptions::default();
1001 opts.set_total_order_seek(true);
1002 DBIteratorWithThreadMode::new(self, opts, mode)
1003 }
1004
1005 pub fn prefix_iterator<'a: 'b, 'b, P: AsRef<[u8]>>(
1006 &'a self,
1007 prefix: P,
1008 ) -> DBIteratorWithThreadMode<'b, Self> {
1009 let mut opts = ReadOptions::default();
1010 opts.set_prefix_same_as_start(true);
1011 DBIteratorWithThreadMode::new(
1012 self,
1013 opts,
1014 IteratorMode::From(prefix.as_ref(), Direction::Forward),
1015 )
1016 }
1017
1018 pub fn iterator_cf<'a: 'b, 'b>(
1019 &'a self,
1020 cf_handle: &impl AsColumnFamilyRef,
1021 mode: IteratorMode,
1022 ) -> DBIteratorWithThreadMode<'b, Self> {
1023 let opts = ReadOptions::default();
1024 DBIteratorWithThreadMode::new_cf(self, cf_handle.inner(), opts, mode)
1025 }
1026
1027 pub fn full_iterator_cf<'a: 'b, 'b>(
1028 &'a self,
1029 cf_handle: &impl AsColumnFamilyRef,
1030 mode: IteratorMode,
1031 ) -> DBIteratorWithThreadMode<'b, Self> {
1032 let mut opts = ReadOptions::default();
1033 opts.set_total_order_seek(true);
1034 DBIteratorWithThreadMode::new_cf(self, cf_handle.inner(), opts, mode)
1035 }
1036
1037 pub fn prefix_iterator_cf<'a, P: AsRef<[u8]>>(
1038 &'a self,
1039 cf_handle: &impl AsColumnFamilyRef,
1040 prefix: P,
1041 ) -> DBIteratorWithThreadMode<'a, Self> {
1042 let mut opts = ReadOptions::default();
1043 opts.set_prefix_same_as_start(true);
1044 DBIteratorWithThreadMode::<'a, Self>::new_cf(
1045 self,
1046 cf_handle.inner(),
1047 opts,
1048 IteratorMode::From(prefix.as_ref(), Direction::Forward),
1049 )
1050 }
1051
1052 pub fn raw_iterator<'a: 'b, 'b>(&'a self) -> DBRawIteratorWithThreadMode<'b, Self> {
1054 let opts = ReadOptions::default();
1055 DBRawIteratorWithThreadMode::new(self, opts)
1056 }
1057
1058 pub fn raw_iterator_cf<'a: 'b, 'b>(
1060 &'a self,
1061 cf_handle: &impl AsColumnFamilyRef,
1062 ) -> DBRawIteratorWithThreadMode<'b, Self> {
1063 let opts = ReadOptions::default();
1064 DBRawIteratorWithThreadMode::new_cf(self, cf_handle.inner(), opts)
1065 }
1066
1067 pub fn raw_iterator_opt<'a: 'b, 'b>(
1069 &'a self,
1070 readopts: ReadOptions,
1071 ) -> DBRawIteratorWithThreadMode<'b, Self> {
1072 DBRawIteratorWithThreadMode::new(self, readopts)
1073 }
1074
1075 pub fn raw_iterator_cf_opt<'a: 'b, 'b>(
1077 &'a self,
1078 cf_handle: &impl AsColumnFamilyRef,
1079 readopts: ReadOptions,
1080 ) -> DBRawIteratorWithThreadMode<'b, Self> {
1081 DBRawIteratorWithThreadMode::new_cf(self, cf_handle.inner(), readopts)
1082 }
1083
1084 pub fn snapshot(&'_ self) -> SnapshotWithThreadMode<'_, Self> {
1085 SnapshotWithThreadMode::<Self>::new(self)
1086 }
1087
1088 fn mark_column_family_dropped(
1099 &self,
1100 cf_inner: *mut ffi::rocksdb_column_family_handle_t,
1101 ) -> Result<(), Error> {
1102 unsafe {
1103 ffi_try!(ffi::rocksdb_drop_column_family(
1104 self.inner as *mut ffi::rocksdb_t,
1105 cf_inner
1106 ));
1107 }
1108 Ok(())
1109 }
1110}
1111
1112impl TransactionDB<SingleThreaded> {
1113 pub fn create_cf<N: AsRef<str>>(&mut self, name: N, opts: &Options) -> Result<(), Error> {
1115 let inner = self.create_inner_cf_handle(name.as_ref(), opts)?;
1116 self.cfs
1117 .cfs
1118 .insert(name.as_ref().to_string(), ColumnFamily { inner });
1119 Ok(())
1120 }
1121
1122 pub fn cf_handle(&self, name: &str) -> Option<&ColumnFamily> {
1124 self.cfs.cfs.get(name)
1125 }
1126
1127 pub fn drop_cf(&mut self, name: &str) -> Result<(), Error> {
1129 let Some(cf) = self.cfs.cfs.remove(name) else {
1130 return Err(Error::new(format!("Invalid column family: {name}")));
1131 };
1132 match self.mark_column_family_dropped(cf.inner) {
1133 Ok(()) => Ok(()),
1138 Err(e) => {
1139 self.cfs.cfs.insert(name.to_owned(), cf);
1142 Err(e)
1143 }
1144 }
1145 }
1146}
1147
1148impl TransactionDB<MultiThreaded> {
1149 pub fn create_cf<N: AsRef<str>>(&self, name: N, opts: &Options) -> Result<(), Error> {
1151 let mut cfs = self.cfs.cfs.write();
1154 let inner = self.create_inner_cf_handle(name.as_ref(), opts)?;
1155 cfs.insert(
1156 name.as_ref().to_string(),
1157 Arc::new(UnboundColumnFamily { inner }),
1158 );
1159 Ok(())
1160 }
1161
1162 pub fn cf_handle(&'_ self, name: &str) -> Option<Arc<BoundColumnFamily<'_>>> {
1164 self.cfs
1165 .cfs
1166 .read()
1167 .get(name)
1168 .cloned()
1169 .map(UnboundColumnFamily::bound_column_family)
1170 }
1171
1172 pub fn drop_cf(&self, name: &str) -> Result<(), Error> {
1175 let Some(cf) = self.cfs.cfs.write().remove(name) else {
1181 return Err(Error::new(format!("Invalid column family: {name}")));
1182 };
1183 match self.mark_column_family_dropped(cf.inner) {
1184 Ok(()) => Ok(()),
1189 Err(e) => {
1190 self.cfs.cfs.write().insert(name.to_owned(), cf);
1193 Err(e)
1194 }
1195 }
1196 }
1197
1198 fn property_value_impl<R>(
1207 name: impl CStrLike,
1208 get_property: impl FnOnce(*const c_char) -> *mut c_char,
1209 parse: impl FnOnce(&str) -> Result<R, Error>,
1210 ) -> Result<Option<R>, Error> {
1211 let value = match name.bake() {
1212 Ok(prop_name) => get_property(prop_name.as_ptr()),
1213 Err(e) => {
1214 return Err(Error::new(format!(
1215 "Failed to convert property name to CString: {e}"
1216 )));
1217 }
1218 };
1219 if value.is_null() {
1220 return Ok(None);
1221 }
1222 let result = match unsafe { CStr::from_ptr(value) }.to_str() {
1223 Ok(s) => parse(s).map(|value| Some(value)),
1224 Err(e) => Err(Error::new(format!(
1225 "Failed to convert property value to string: {e}"
1226 ))),
1227 };
1228 unsafe {
1229 ffi::rocksdb_free(value as *mut c_void);
1230 }
1231 result
1232 }
1233
1234 pub fn property_value(&self, name: impl CStrLike) -> Result<Option<String>, Error> {
1239 Self::property_value_impl(
1240 name,
1241 |prop_name| unsafe { ffi::rocksdb_transactiondb_property_value(self.inner, prop_name) },
1242 |str_value| Ok(str_value.to_owned()),
1243 )
1244 }
1245
1246 fn property_int_value_impl(
1247 name: impl CStrLike,
1248 get_property: impl FnOnce(*const c_char, *mut u64) -> c_int,
1249 get_string_property: impl FnOnce(*const c_char) -> *mut c_char,
1250 ) -> Result<Option<u64>, Error> {
1251 let prop_name = name.bake().map_err(|err| {
1252 Error::new(format!("Failed to convert property name to CString: {err}"))
1253 })?;
1254 let mut value = 0;
1255 if get_property(prop_name.as_ptr(), &raw mut value) == 0 {
1256 return Ok(Some(value));
1257 }
1258
1259 Self::property_value_impl(
1260 prop_name.as_ref(),
1261 get_string_property,
1262 Self::parse_property_int_value,
1263 )
1264 }
1265
1266 fn parse_property_int_value(value: &str) -> Result<u64, Error> {
1267 value.parse::<u64>().map_err(|err| {
1268 Error::new(format!(
1269 "Failed to convert property value {value} to int: {err}"
1270 ))
1271 })
1272 }
1273
1274 pub fn property_int_value(&self, name: impl CStrLike) -> Result<Option<u64>, Error> {
1279 Self::property_int_value_impl(
1280 name,
1281 |prop_name, value| unsafe {
1282 ffi::rocksdb_transactiondb_property_int(self.inner, prop_name, value)
1283 },
1284 |prop_name| unsafe { ffi::rocksdb_transactiondb_property_value(self.inner, prop_name) },
1285 )
1286 }
1287}
1288
1289impl<T: ThreadMode> Drop for TransactionDB<T> {
1290 fn drop(&mut self) {
1291 unsafe {
1292 self.prepared_transactions().clear();
1293 self.cfs.drop_all_cfs_internal();
1294 ffi::rocksdb_transactiondb_close(self.inner);
1295 }
1296 }
1297}