rust_rocksdb/write_batch.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
15use crate::{AsColumnFamilyRef, ffi};
16use libc::{c_char, c_int, c_void, size_t};
17use std::{io::IoSlice, slice};
18
19const INLINE_WRITE_BATCH_PARTS: usize = 4;
20
21enum WriteBatchPartStorage {
22 Inline([ffi::rocksdb_slice_t; INLINE_WRITE_BATCH_PARTS]),
23 Heap(Vec<ffi::rocksdb_slice_t>),
24}
25
26struct WriteBatchParts {
27 storage: WriteBatchPartStorage,
28 count: c_int,
29}
30
31impl WriteBatchParts {
32 fn new(parts: &[IoSlice<'_>], name: &str) -> Result<Self, crate::Error> {
33 let count = c_int::try_from(parts.len()).map_err(|_| {
34 crate::Error::new(format!(
35 "{name} has {} parts; expected at most {}",
36 parts.len(),
37 c_int::MAX
38 ))
39 })?;
40 let storage = if parts.len() <= INLINE_WRITE_BATCH_PARTS {
41 Self::inline(parts)
42 } else {
43 Self::heap(parts)
44 };
45 Ok(Self { storage, count })
46 }
47
48 fn inline(parts: &[IoSlice<'_>]) -> WriteBatchPartStorage {
49 let mut slices = std::array::from_fn(|_| ffi::rocksdb_slice_t {
50 data: std::ptr::null(),
51 size: 0,
52 });
53 for (index, part) in parts.iter().enumerate() {
54 slices[index] = ffi::rocksdb_slice_t {
55 data: part.as_ptr().cast(),
56 size: part.len(),
57 };
58 }
59 WriteBatchPartStorage::Inline(slices)
60 }
61
62 fn heap(parts: &[IoSlice<'_>]) -> WriteBatchPartStorage {
63 WriteBatchPartStorage::Heap(
64 parts
65 .iter()
66 .map(|part| ffi::rocksdb_slice_t {
67 data: part.as_ptr().cast(),
68 size: part.len(),
69 })
70 .collect(),
71 )
72 }
73
74 fn as_ptr(&self) -> *const ffi::rocksdb_slice_t {
75 match &self.storage {
76 WriteBatchPartStorage::Inline(slices) => slices.as_ptr(),
77 WriteBatchPartStorage::Heap(slices) => slices.as_ptr(),
78 }
79 }
80}
81
82/// A type alias to keep compatibility. See [`WriteBatchWithTransaction`] for details
83pub type WriteBatch = WriteBatchWithTransaction<false>;
84
85/// An atomic batch of write operations.
86///
87/// [`delete_range`](#method.delete_range) is not supported in [`Transaction`].
88///
89/// Making an atomic commit of several writes:
90///
91/// ```
92/// use rust_rocksdb::{DB, Options, WriteBatchWithTransaction};
93///
94/// let tempdir = tempfile::Builder::new()
95/// .prefix("_path_for_rocksdb_storage1")
96/// .tempdir()
97/// .expect("Failed to create temporary path for the _path_for_rocksdb_storage1");
98/// let path = tempdir.path();
99/// {
100/// let db = DB::open_default(path).unwrap();
101/// let mut batch = WriteBatchWithTransaction::<false>::default();
102/// batch.put(b"my key", b"my value");
103/// batch.put(b"key2", b"value2");
104/// batch.put(b"key3", b"value3");
105///
106/// // delete_range is supported when use without transaction
107/// batch.delete_range(b"key2", b"key3");
108///
109/// db.write(&batch); // Atomically commits the batch
110/// }
111/// let _ = DB::destroy(&Options::default(), path);
112/// ```
113///
114/// [`Transaction`]: crate::Transaction
115pub struct WriteBatchWithTransaction<const TRANSACTION: bool> {
116 pub(crate) inner: *mut ffi::rocksdb_writebatch_t,
117}
118
119/// Receives the puts and deletes of a write batch.
120///
121/// The application must provide an implementation of this trait when
122/// iterating the operations within a `WriteBatch`
123pub trait WriteBatchIterator {
124 /// Called with a key and value that were `put` into the batch.
125 fn put(&mut self, key: &[u8], value: &[u8]);
126 /// Called with a key that was `delete`d from the batch.
127 fn delete(&mut self, key: &[u8]);
128 /// Called with a blob that was appended by
129 /// [`put_log_data`](WriteBatchWithTransaction::put_log_data).
130 ///
131 /// Log data is interleaved with the puts and deletes in insertion order.
132 /// The default implementation ignores it.
133 fn log_data(&mut self, _blob: &[u8]) {}
134}
135
136/// Receives the puts, deletes, and merges of a write batch with column family
137/// information.
138///
139/// This trait extends write batch iteration to support column family-specific
140/// operations. The application must implement this trait when iterating
141/// operations within a WriteBatch that contains column family-aware writes.
142///
143/// Note that for the default column family "default", the column family ID is 0.
144pub trait WriteBatchIteratorCf {
145 /// Called with a column family ID, key, and value that were put into
146 /// the specific column family of the batch.
147 fn put_cf(&mut self, cf_id: u32, key: &[u8], value: &[u8]);
148 /// Called with a column family ID and key that were `delete`d from the
149 /// specific column family of the batch.
150 fn delete_cf(&mut self, cf_id: u32, key: &[u8]);
151 /// Called with a column family ID, key, and value that were `merge`d into
152 /// the specific column family of the batch.
153 /// Merge operations combine the provided value with the existing value at
154 /// the key using a database-defined merge operator.
155 fn merge_cf(&mut self, cf_id: u32, key: &[u8], value: &[u8]);
156 /// Called with a blob that was appended by
157 /// [`put_log_data`](WriteBatchWithTransaction::put_log_data).
158 ///
159 /// Log data belongs to no column family, so there is no id here. It is
160 /// interleaved with the other records in insertion order. The default
161 /// implementation ignores it.
162 fn log_data(&mut self, _blob: &[u8]) {}
163}
164
165pub(crate) unsafe extern "C" fn writebatch_put_callback<T: WriteBatchIterator>(
166 state: *mut c_void,
167 k: *const c_char,
168 klen: usize,
169 v: *const c_char,
170 vlen: usize,
171) {
172 unsafe {
173 let callbacks = &mut *(state as *mut T);
174 let key = slice::from_raw_parts(k.cast::<u8>(), klen);
175 let value = slice::from_raw_parts(v.cast::<u8>(), vlen);
176 callbacks.put(key, value);
177 }
178}
179
180pub(crate) unsafe extern "C" fn writebatch_delete_callback<T: WriteBatchIterator>(
181 state: *mut c_void,
182 k: *const c_char,
183 klen: usize,
184) {
185 unsafe {
186 let callbacks = &mut *(state as *mut T);
187 let key = slice::from_raw_parts(k.cast::<u8>(), klen);
188 callbacks.delete(key);
189 }
190}
191
192unsafe extern "C" fn writebatch_log_data_callback<T: WriteBatchIterator>(
193 state: *mut c_void,
194 blob: *const c_char,
195 blob_len: usize,
196) {
197 unsafe {
198 let callbacks = &mut *(state as *mut T);
199 callbacks.log_data(slice::from_raw_parts(blob.cast::<u8>(), blob_len));
200 }
201}
202
203unsafe extern "C" fn writebatch_log_data_cf_callback<T: WriteBatchIteratorCf>(
204 state: *mut c_void,
205 blob: *const c_char,
206 blob_len: usize,
207) {
208 unsafe {
209 let callbacks = &mut *(state as *mut T);
210 callbacks.log_data(slice::from_raw_parts(blob.cast::<u8>(), blob_len));
211 }
212}
213
214/// Trampoline for `rocksdb_writebatch{,_wi}_update_timestamps`, which asks the
215/// caller how wide the timestamp is for each column family it encounters.
216pub(crate) unsafe extern "C" fn get_ts_size_callback<F: FnMut(u32) -> usize>(
217 state: *mut c_void,
218 cf_id: u32,
219) -> size_t {
220 unsafe { (*(state as *mut F))(cf_id) }
221}
222
223unsafe extern "C" fn writebatch_put_cf_callback<T: WriteBatchIteratorCf>(
224 state: *mut c_void,
225 cfid: u32,
226 k: *const c_char,
227 klen: usize,
228 v: *const c_char,
229 vlen: usize,
230) {
231 unsafe {
232 let callbacks = &mut *(state as *mut T);
233 let key = slice::from_raw_parts(k.cast::<u8>(), klen);
234 let value = slice::from_raw_parts(v.cast::<u8>(), vlen);
235 callbacks.put_cf(cfid, key, value);
236 }
237}
238
239unsafe extern "C" fn writebatch_delete_cf_callback<T: WriteBatchIteratorCf>(
240 state: *mut c_void,
241 cfid: u32,
242 k: *const c_char,
243 klen: usize,
244) {
245 unsafe {
246 let callbacks = &mut *(state as *mut T);
247 let key = slice::from_raw_parts(k.cast::<u8>(), klen);
248 callbacks.delete_cf(cfid, key);
249 }
250}
251
252unsafe extern "C" fn writebatch_merge_cf_callback<T: WriteBatchIteratorCf>(
253 state: *mut c_void,
254 cfid: u32,
255 k: *const c_char,
256 klen: usize,
257 v: *const c_char,
258 vlen: usize,
259) {
260 unsafe {
261 let callbacks = &mut *(state as *mut T);
262 let key = slice::from_raw_parts(k.cast::<u8>(), klen);
263 let value = slice::from_raw_parts(v.cast::<u8>(), vlen);
264 callbacks.merge_cf(cfid, key, value);
265 }
266}
267
268impl<const TRANSACTION: bool> WriteBatchWithTransaction<TRANSACTION> {
269 /// Create a new `WriteBatch` without allocating memory.
270 pub fn new() -> Self {
271 Self {
272 inner: unsafe { ffi::rocksdb_writebatch_create() },
273 }
274 }
275
276 /// Creates `WriteBatch` with the specified `capacity` in bytes. Allocates immediately.
277 pub fn with_capacity_bytes(capacity_bytes: usize) -> Self {
278 Self {
279 // zeroes from default constructor
280 // https://github.com/facebook/rocksdb/blob/0f35db55d86ea8699ea936c9e2a4e34c82458d6b/include/rocksdb/write_batch.h#L66
281 inner: unsafe { ffi::rocksdb_writebatch_create_with_params(capacity_bytes, 0, 0, 0) },
282 }
283 }
284
285 /// Construct with a reference to a byte array serialized by [`WriteBatch`].
286 pub fn from_data(data: &[u8]) -> Self {
287 unsafe {
288 let ptr = data.as_ptr();
289 let len = data.len();
290 Self {
291 inner: ffi::rocksdb_writebatch_create_from(
292 ptr as *const libc::c_char,
293 len as size_t,
294 ),
295 }
296 }
297 }
298
299 pub fn len(&self) -> usize {
300 unsafe { ffi::rocksdb_writebatch_count(self.inner) as usize }
301 }
302
303 /// Return WriteBatch serialized size (in bytes).
304 pub fn size_in_bytes(&self) -> usize {
305 unsafe {
306 let mut batch_size: size_t = 0;
307 ffi::rocksdb_writebatch_data(self.inner, &raw mut batch_size);
308 batch_size
309 }
310 }
311
312 /// Return a reference to a byte array which represents a serialized version of the batch.
313 pub fn data(&self) -> &[u8] {
314 unsafe {
315 let mut batch_size: size_t = 0;
316 let batch_data = ffi::rocksdb_writebatch_data(self.inner, &raw mut batch_size);
317 std::slice::from_raw_parts(batch_data as _, batch_size)
318 }
319 }
320
321 pub fn is_empty(&self) -> bool {
322 self.len() == 0
323 }
324
325 /// Iterate the put, delete, and log data operations within this write
326 /// batch. Note that this does _not_ return an `Iterator` but instead will
327 /// invoke the `put()`, `delete()`, and `log_data()` member functions of the
328 /// provided `WriteBatchIterator` trait implementation.
329 pub fn iterate<T: WriteBatchIterator>(&self, callbacks: &mut T) {
330 let state = std::ptr::from_mut::<T>(callbacks) as *mut c_void;
331 unsafe {
332 ffi::rocksdb_writebatch_iterate_ld(
333 self.inner,
334 state,
335 Some(writebatch_put_callback::<T>),
336 Some(writebatch_delete_callback::<T>),
337 Some(writebatch_log_data_callback::<T>),
338 );
339 }
340 }
341
342 /// Iterate the put, delete, merge, and log data operations within this write batch with
343 /// column family information. Note that this does _not_ return an `Iterator` but instead will
344 /// invoke the `put_cf()`, `delete_cf()`, `merge_cf()`, and `log_data()` member functions of
345 /// the provided `WriteBatchIteratorCf` trait implementation.
346 ///
347 /// # Notes
348 /// - For operations on the default column family ("default"), the `cf_id` parameter passed to
349 /// the callbacks will be 0
350 pub fn iterate_cf<T: WriteBatchIteratorCf>(&self, callbacks: &mut T) {
351 let state = std::ptr::from_mut::<T>(callbacks) as *mut c_void;
352 unsafe {
353 ffi::rocksdb_writebatch_iterate_cf_ld(
354 self.inner,
355 state,
356 Some(writebatch_put_cf_callback::<T>),
357 Some(writebatch_delete_cf_callback::<T>),
358 Some(writebatch_merge_cf_callback::<T>),
359 Some(writebatch_log_data_cf_callback::<T>),
360 );
361 }
362 }
363
364 /// Insert a value into the database under the given key.
365 pub fn put<K, V>(&mut self, key: K, value: V)
366 where
367 K: AsRef<[u8]>,
368 V: AsRef<[u8]>,
369 {
370 let key = key.as_ref();
371 let value = value.as_ref();
372
373 unsafe {
374 ffi::rocksdb_writebatch_put(
375 self.inner,
376 key.as_ptr() as *const c_char,
377 key.len() as size_t,
378 value.as_ptr() as *const c_char,
379 value.len() as size_t,
380 );
381 }
382 }
383
384 /// Inserts one key and value assembled from multiple byte slices.
385 ///
386 /// This avoids concatenating the parts in Rust. RocksDB copies the key and
387 /// value parts into the write batch during this call, so the slices do not
388 /// need to outlive the method.
389 pub fn put_vectored(
390 &mut self,
391 key: &[IoSlice<'_>],
392 value: &[IoSlice<'_>],
393 ) -> Result<(), crate::Error> {
394 let key = WriteBatchParts::new(key, "key")?;
395 let value = WriteBatchParts::new(value, "value")?;
396 unsafe {
397 ffi_try!(ffi::rust_rocksdb_writebatch_put_slices(
398 self.inner,
399 key.count,
400 key.as_ptr(),
401 value.count,
402 value.as_ptr(),
403 ));
404 }
405 Ok(())
406 }
407
408 /// Insert a value into the specific column family of the database under the given key.
409 pub fn put_cf<K, V>(&mut self, cf: &impl AsColumnFamilyRef, key: K, value: V)
410 where
411 K: AsRef<[u8]>,
412 V: AsRef<[u8]>,
413 {
414 let key = key.as_ref();
415 let value = value.as_ref();
416
417 unsafe {
418 ffi::rocksdb_writebatch_put_cf(
419 self.inner,
420 cf.inner(),
421 key.as_ptr() as *const c_char,
422 key.len() as size_t,
423 value.as_ptr() as *const c_char,
424 value.len() as size_t,
425 );
426 }
427 }
428
429 /// Inserts one key and value assembled from multiple byte slices into a column family.
430 ///
431 /// This avoids concatenating the parts in Rust. RocksDB copies the key and
432 /// value parts into the write batch during this call, so the slices do not
433 /// need to outlive the method.
434 pub fn put_cf_vectored(
435 &mut self,
436 cf: &impl AsColumnFamilyRef,
437 key: &[IoSlice<'_>],
438 value: &[IoSlice<'_>],
439 ) -> Result<(), crate::Error> {
440 let key = WriteBatchParts::new(key, "key")?;
441 let value = WriteBatchParts::new(value, "value")?;
442 unsafe {
443 ffi_try!(ffi::rust_rocksdb_writebatch_put_slices_cf(
444 self.inner,
445 cf.inner(),
446 key.count,
447 key.as_ptr(),
448 value.count,
449 value.as_ptr(),
450 ));
451 }
452 Ok(())
453 }
454
455 /// Insert a value into the specific column family of the database
456 /// under the given key with timestamp.
457 pub fn put_cf_with_ts<K, V, S>(&mut self, cf: &impl AsColumnFamilyRef, key: K, ts: S, value: V)
458 where
459 K: AsRef<[u8]>,
460 V: AsRef<[u8]>,
461 S: AsRef<[u8]>,
462 {
463 let key = key.as_ref();
464 let value = value.as_ref();
465 let ts = ts.as_ref();
466 unsafe {
467 ffi::rocksdb_writebatch_put_cf_with_ts(
468 self.inner,
469 cf.inner(),
470 key.as_ptr() as *const c_char,
471 key.len() as size_t,
472 ts.as_ptr() as *const c_char,
473 ts.len() as size_t,
474 value.as_ptr() as *const c_char,
475 value.len() as size_t,
476 );
477 }
478 }
479
480 pub fn merge<K, V>(&mut self, key: K, value: V)
481 where
482 K: AsRef<[u8]>,
483 V: AsRef<[u8]>,
484 {
485 let key = key.as_ref();
486 let value = value.as_ref();
487
488 unsafe {
489 ffi::rocksdb_writebatch_merge(
490 self.inner,
491 key.as_ptr() as *const c_char,
492 key.len() as size_t,
493 value.as_ptr() as *const c_char,
494 value.len() as size_t,
495 );
496 }
497 }
498
499 /// Merges one key and value assembled from multiple byte slices.
500 ///
501 /// This avoids concatenating the parts in Rust. RocksDB copies the key and
502 /// value parts into the write batch during this call, so the slices do not
503 /// need to outlive the method.
504 pub fn merge_vectored(
505 &mut self,
506 key: &[IoSlice<'_>],
507 value: &[IoSlice<'_>],
508 ) -> Result<(), crate::Error> {
509 let key = WriteBatchParts::new(key, "key")?;
510 let value = WriteBatchParts::new(value, "value")?;
511 unsafe {
512 ffi_try!(ffi::rust_rocksdb_writebatch_merge_slices(
513 self.inner,
514 key.count,
515 key.as_ptr(),
516 value.count,
517 value.as_ptr(),
518 ));
519 }
520 Ok(())
521 }
522
523 pub fn merge_cf<K, V>(&mut self, cf: &impl AsColumnFamilyRef, key: K, value: V)
524 where
525 K: AsRef<[u8]>,
526 V: AsRef<[u8]>,
527 {
528 let key = key.as_ref();
529 let value = value.as_ref();
530
531 unsafe {
532 ffi::rocksdb_writebatch_merge_cf(
533 self.inner,
534 cf.inner(),
535 key.as_ptr() as *const c_char,
536 key.len() as size_t,
537 value.as_ptr() as *const c_char,
538 value.len() as size_t,
539 );
540 }
541 }
542
543 /// Merges one key and value assembled from multiple byte slices in a column family.
544 ///
545 /// This avoids concatenating the parts in Rust. RocksDB copies the key and
546 /// value parts into the write batch during this call, so the slices do not
547 /// need to outlive the method.
548 pub fn merge_cf_vectored(
549 &mut self,
550 cf: &impl AsColumnFamilyRef,
551 key: &[IoSlice<'_>],
552 value: &[IoSlice<'_>],
553 ) -> Result<(), crate::Error> {
554 let key = WriteBatchParts::new(key, "key")?;
555 let value = WriteBatchParts::new(value, "value")?;
556 unsafe {
557 ffi_try!(ffi::rust_rocksdb_writebatch_merge_slices_cf(
558 self.inner,
559 cf.inner(),
560 key.count,
561 key.as_ptr(),
562 value.count,
563 value.as_ptr(),
564 ));
565 }
566 Ok(())
567 }
568
569 /// Removes the database entry for key. Does nothing if the key was not found.
570 pub fn delete<K: AsRef<[u8]>>(&mut self, key: K) {
571 let key = key.as_ref();
572
573 unsafe {
574 ffi::rocksdb_writebatch_delete(
575 self.inner,
576 key.as_ptr() as *const c_char,
577 key.len() as size_t,
578 );
579 }
580 }
581
582 /// Removes the entry for one key assembled from multiple byte slices.
583 ///
584 /// This avoids concatenating the parts in Rust. RocksDB copies the key
585 /// parts into the write batch during this call, so the slices do not need
586 /// to outlive the method.
587 pub fn delete_vectored(&mut self, key: &[IoSlice<'_>]) -> Result<(), crate::Error> {
588 let key = WriteBatchParts::new(key, "key")?;
589 unsafe {
590 ffi_try!(ffi::rust_rocksdb_writebatch_delete_slices(
591 self.inner,
592 key.count,
593 key.as_ptr(),
594 ));
595 }
596 Ok(())
597 }
598
599 /// Removes the database entry in the specific column family for key.
600 /// Does nothing if the key was not found.
601 pub fn delete_cf<K: AsRef<[u8]>>(&mut self, cf: &impl AsColumnFamilyRef, key: K) {
602 let key = key.as_ref();
603
604 unsafe {
605 ffi::rocksdb_writebatch_delete_cf(
606 self.inner,
607 cf.inner(),
608 key.as_ptr() as *const c_char,
609 key.len() as size_t,
610 );
611 }
612 }
613
614 /// Removes the entry for one key assembled from multiple byte slices in a column family.
615 ///
616 /// This avoids concatenating the parts in Rust. RocksDB copies the key
617 /// parts into the write batch during this call, so the slices do not need
618 /// to outlive the method.
619 pub fn delete_cf_vectored(
620 &mut self,
621 cf: &impl AsColumnFamilyRef,
622 key: &[IoSlice<'_>],
623 ) -> Result<(), crate::Error> {
624 let key = WriteBatchParts::new(key, "key")?;
625 unsafe {
626 ffi_try!(ffi::rust_rocksdb_writebatch_delete_slices_cf(
627 self.inner,
628 cf.inner(),
629 key.count,
630 key.as_ptr(),
631 ));
632 }
633 Ok(())
634 }
635
636 /// Removes the database entry for a key that was written exactly once.
637 ///
638 /// This is a cheaper delete than [`delete`](Self::delete), but it is only
639 /// correct when the key has had at most one `put` and no `merge` since the
640 /// last delete of that key. Using it on a key that was written more than
641 /// once leaves an older version of the key visible, and RocksDB does not
642 /// report that as an error.
643 pub fn single_delete<K: AsRef<[u8]>>(&mut self, key: K) {
644 let key = key.as_ref();
645
646 unsafe {
647 ffi::rocksdb_writebatch_singledelete(
648 self.inner,
649 key.as_ptr() as *const c_char,
650 key.len() as size_t,
651 );
652 }
653 }
654
655 /// Removes the entry for a write-once key in the given column family.
656 ///
657 /// See [`single_delete`](Self::single_delete) for when this is safe to use.
658 pub fn single_delete_cf<K: AsRef<[u8]>>(&mut self, cf: &impl AsColumnFamilyRef, key: K) {
659 let key = key.as_ref();
660
661 unsafe {
662 ffi::rocksdb_writebatch_singledelete_cf(
663 self.inner,
664 cf.inner(),
665 key.as_ptr() as *const c_char,
666 key.len() as size_t,
667 );
668 }
669 }
670
671 /// Removes the entry for a write-once key in a column family that uses
672 /// user-defined timestamps.
673 ///
674 /// See [`single_delete`](Self::single_delete) for when this is safe to use.
675 pub fn single_delete_cf_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
676 &mut self,
677 cf: &impl AsColumnFamilyRef,
678 key: K,
679 ts: S,
680 ) {
681 let key = key.as_ref();
682 let ts = ts.as_ref();
683
684 unsafe {
685 ffi::rocksdb_writebatch_singledelete_cf_with_ts(
686 self.inner,
687 cf.inner(),
688 key.as_ptr() as *const c_char,
689 key.len() as size_t,
690 ts.as_ptr() as *const c_char,
691 ts.len() as size_t,
692 );
693 }
694 }
695
696 /// Removes the database entry in the specific column family with timestamp for key.
697 /// Does nothing if the key was not found.
698 pub fn delete_cf_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
699 &mut self,
700 cf: &impl AsColumnFamilyRef,
701 key: K,
702 ts: S,
703 ) {
704 let key = key.as_ref();
705 let ts = ts.as_ref();
706 unsafe {
707 ffi::rocksdb_writebatch_delete_cf_with_ts(
708 self.inner,
709 cf.inner(),
710 key.as_ptr() as *const c_char,
711 key.len() as size_t,
712 ts.as_ptr() as *const c_char,
713 ts.len() as size_t,
714 );
715 }
716 }
717
718 // Append a blob of arbitrary size to the records in this batch. The blob will
719 // be stored in the transaction log but not in any other file. In particular,
720 // it will not be persisted to the SST files. When iterating over this
721 // WriteBatch, WriteBatch::Handler::LogData will be called with the contents
722 // of the blob as it is encountered. Blobs, puts, deletes, and merges will be
723 // encountered in the same order in which they were inserted. The blob will
724 // NOT consume sequence number(s) and will NOT increase the count of the batch
725 //
726 // Example application: add timestamps to the transaction log for use in
727 // replication.
728 pub fn put_log_data<V: AsRef<[u8]>>(&mut self, log_data: V) {
729 let log_data = log_data.as_ref();
730
731 unsafe {
732 ffi::rocksdb_writebatch_put_log_data(
733 self.inner,
734 log_data.as_ptr() as *const c_char,
735 log_data.len() as size_t,
736 );
737 }
738 }
739
740 /// Clear all updates buffered in this batch.
741 pub fn clear(&mut self) {
742 unsafe {
743 ffi::rocksdb_writebatch_clear(self.inner);
744 }
745 }
746
747 /// Record the current state of the batch so it can be undone later.
748 ///
749 /// Save points nest. Each [`set_save_point`](Self::set_save_point) pushes
750 /// onto a stack that [`rollback_to_save_point`](Self::rollback_to_save_point)
751 /// and [`pop_save_point`](Self::pop_save_point) pop from.
752 pub fn set_save_point(&mut self) {
753 unsafe {
754 ffi::rocksdb_writebatch_set_save_point(self.inner);
755 }
756 }
757
758 /// Undo every operation recorded since the most recent save point, and pop
759 /// that save point.
760 ///
761 /// Returns an error if there is no save point to roll back to.
762 pub fn rollback_to_save_point(&mut self) -> Result<(), crate::Error> {
763 unsafe {
764 ffi_try!(ffi::rocksdb_writebatch_rollback_to_save_point(self.inner));
765 }
766 Ok(())
767 }
768
769 /// Pop the most recent save point without undoing anything.
770 ///
771 /// Returns an error if there is no save point to pop.
772 pub fn pop_save_point(&mut self) -> Result<(), crate::Error> {
773 unsafe {
774 ffi_try!(ffi::rocksdb_writebatch_pop_save_point(self.inner));
775 }
776 Ok(())
777 }
778
779 /// Recompute the per-key protection info over the batch and check it
780 /// against what was stored when each entry was added.
781 ///
782 /// Only meaningful for a batch built with a non-zero
783 /// `protection_bytes_per_key`, which is the fourth argument to
784 /// `rocksdb_writebatch_create_with_params`. On a batch without protection
785 /// this succeeds without checking anything.
786 pub fn verify_checksum(&self) -> Result<(), crate::Error> {
787 unsafe {
788 ffi_try!(ffi::rocksdb_writebatch_verify_checksum(self.inner));
789 }
790 Ok(())
791 }
792
793 /// Overwrite the user-defined timestamp on every entry in the batch.
794 ///
795 /// `get_ts_size` is called with each column family id the batch touches and
796 /// must return the timestamp width configured for that column family, or 0
797 /// if it does not use timestamps. `ts` must be exactly as wide as every
798 /// non-zero size it returns.
799 ///
800 /// This is for reassigning a timestamp to an already-built batch, such as
801 /// when a commit timestamp is only known at write time.
802 ///
803 /// # Safety
804 ///
805 /// Every key already recorded for a column family whose `get_ts_size`
806 /// returns a non-zero width must be at least that many bytes long, which in
807 /// practice means it was written through one of the `_with_ts` methods and
808 /// already carries a timestamp suffix of exactly that width. RocksDB
809 /// overwrites the last `width` bytes of each key without checking that the
810 /// key is that long, so a shorter key makes it write in front of the key and
811 /// corrupt the heap. Mixing plain [`put`](Self::put) with a non-zero width
812 /// for the same column family is what usually triggers this.
813 ///
814 /// # Errors
815 ///
816 /// Returns an error if `ts` is empty, if its length differs from a non-zero
817 /// width returned by `get_ts_size`, or if `get_ts_size` reports that it
818 /// could not find the width for a column family.
819 pub unsafe fn update_timestamps<S, F>(
820 &mut self,
821 ts: S,
822 mut get_ts_size: F,
823 ) -> Result<(), crate::Error>
824 where
825 S: AsRef<[u8]>,
826 F: FnMut(u32) -> usize,
827 {
828 let ts = ts.as_ref();
829 let state = std::ptr::from_mut(&mut get_ts_size).cast::<c_void>();
830 unsafe {
831 ffi_try!(ffi::rocksdb_writebatch_update_timestamps(
832 self.inner,
833 ts.as_ptr() as *const c_char,
834 ts.len() as size_t,
835 state,
836 Some(get_ts_size_callback::<F>),
837 ));
838 }
839 Ok(())
840 }
841}
842
843impl WriteBatchWithTransaction<false> {
844 /// Remove database entries from start key to end key.
845 ///
846 /// Removes the database entries in the range ["begin_key", "end_key"), i.e.,
847 /// including "begin_key" and excluding "end_key". It is not an error if no
848 /// keys exist in the range ["begin_key", "end_key").
849 pub fn delete_range<K: AsRef<[u8]>>(&mut self, from: K, to: K) {
850 let (start_key, end_key) = (from.as_ref(), to.as_ref());
851
852 unsafe {
853 ffi::rocksdb_writebatch_delete_range(
854 self.inner,
855 start_key.as_ptr() as *const c_char,
856 start_key.len() as size_t,
857 end_key.as_ptr() as *const c_char,
858 end_key.len() as size_t,
859 );
860 }
861 }
862
863 /// Removes entries in a range whose bounds are assembled from byte slices.
864 ///
865 /// The range includes `from` and excludes `to`. Both bounds must be split
866 /// into the same number of parts: the System backend forwards them to
867 /// `rocksdb_writebatch_delete_rangev`, which takes one part count for the
868 /// pair. Split them differently and this returns an error.
869 ///
870 /// RocksDB copies both bounds into the write batch during this call, so the
871 /// slices do not need to outlive the method.
872 pub fn delete_range_vectored(
873 &mut self,
874 from: &[IoSlice<'_>],
875 to: &[IoSlice<'_>],
876 ) -> Result<(), crate::Error> {
877 if from.len() != to.len() {
878 return Err(crate::Error::new(format!(
879 "range start has {} parts but range end has {} parts; expected equal counts",
880 from.len(),
881 to.len()
882 )));
883 }
884 let from = WriteBatchParts::new(from, "range start")?;
885 let to = WriteBatchParts::new(to, "range end")?;
886 unsafe {
887 ffi_try!(ffi::rust_rocksdb_writebatch_delete_range_slices(
888 self.inner,
889 from.count,
890 from.as_ptr(),
891 to.count,
892 to.as_ptr(),
893 ));
894 }
895 Ok(())
896 }
897
898 /// Remove database entries in column family from start key to end key.
899 ///
900 /// Removes the database entries in the range ["begin_key", "end_key"), i.e.,
901 /// including "begin_key" and excluding "end_key". It is not an error if no
902 /// keys exist in the range ["begin_key", "end_key").
903 pub fn delete_range_cf<K: AsRef<[u8]>>(&mut self, cf: &impl AsColumnFamilyRef, from: K, to: K) {
904 let (start_key, end_key) = (from.as_ref(), to.as_ref());
905
906 unsafe {
907 ffi::rocksdb_writebatch_delete_range_cf(
908 self.inner,
909 cf.inner(),
910 start_key.as_ptr() as *const c_char,
911 start_key.len() as size_t,
912 end_key.as_ptr() as *const c_char,
913 end_key.len() as size_t,
914 );
915 }
916 }
917
918 /// Removes entries in a column family range whose bounds are assembled from byte slices.
919 ///
920 /// The range includes `from` and excludes `to`, and both bounds must be
921 /// split into the same number of parts. See
922 /// [`delete_range_vectored`](Self::delete_range_vectored).
923 ///
924 /// RocksDB copies both bounds into the write batch during this call, so the
925 /// slices do not need to outlive the method.
926 pub fn delete_range_cf_vectored(
927 &mut self,
928 cf: &impl AsColumnFamilyRef,
929 from: &[IoSlice<'_>],
930 to: &[IoSlice<'_>],
931 ) -> Result<(), crate::Error> {
932 if from.len() != to.len() {
933 return Err(crate::Error::new(format!(
934 "range start has {} parts but range end has {} parts; expected equal counts",
935 from.len(),
936 to.len()
937 )));
938 }
939 let from = WriteBatchParts::new(from, "range start")?;
940 let to = WriteBatchParts::new(to, "range end")?;
941 unsafe {
942 ffi_try!(ffi::rust_rocksdb_writebatch_delete_range_slices_cf(
943 self.inner,
944 cf.inner(),
945 from.count,
946 from.as_ptr(),
947 to.count,
948 to.as_ptr(),
949 ));
950 }
951 Ok(())
952 }
953}
954
955impl<const TRANSACTION: bool> Default for WriteBatchWithTransaction<TRANSACTION> {
956 fn default() -> Self {
957 Self::new()
958 }
959}
960
961impl<const TRANSACTION: bool> Drop for WriteBatchWithTransaction<TRANSACTION> {
962 fn drop(&mut self) {
963 unsafe {
964 ffi::rocksdb_writebatch_destroy(self.inner);
965 }
966 }
967}
968
969unsafe impl<const TRANSACTION: bool> Send for WriteBatchWithTransaction<TRANSACTION> {}