Skip to main content

rust_rocksdb/
sst_file_writer.rs

1// Copyright 2020 Lucjan Suski
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 crate::{Error, Options, env_options::EnvOptions, ffi, ffi_util::to_cpath};
17
18use libc::{self, c_char, size_t};
19use std::{ffi::CString, marker::PhantomData, path::Path};
20
21/// SstFileWriter is used to create sst files that can be added to database later
22/// All keys in files generated by SstFileWriter will have sequence number = 0.
23pub struct SstFileWriter<'a> {
24    pub(crate) inner: *mut ffi::rocksdb_sstfilewriter_t,
25    // `Options`, and any `EnvOptions` passed to `create_with_env_options`, have
26    // to stay alive for as long as the writer does. Both appear behind `'a` in
27    // the constructors, so this pins them for the writer's lifetime.
28    phantom: PhantomData<&'a Options>,
29}
30
31unsafe impl Send for SstFileWriter<'_> {}
32unsafe impl Sync for SstFileWriter<'_> {}
33
34impl<'a> SstFileWriter<'a> {
35    /// Initializes SstFileWriter with given DB options.
36    ///
37    /// Writes go through a default [`EnvOptions`]. Use
38    /// [`create_with_env_options`](Self::create_with_env_options) to control
39    /// how the file is written, for example to enable direct IO or to rate
40    /// limit the writes.
41    pub fn create(opts: &'a Options) -> Self {
42        // A default `EnvOptions` is all scalars, and C++ copies the struct into
43        // the writer, so this temporary does not need to outlive the writer.
44        // That is not true once a rate limiter is set, which is why
45        // `create_with_env_options` borrows for `'a` instead.
46        let writer = Self::create_raw(opts, &EnvOptions::default());
47
48        Self {
49            inner: writer,
50            phantom: PhantomData,
51        }
52    }
53
54    /// Initializes SstFileWriter with given DB options and file IO options.
55    ///
56    /// `env_opts` is borrowed for as long as the writer lives. C++ copies the
57    /// `EnvOptions` struct into the writer, but a rate limiter installed with
58    /// [`EnvOptions::set_ratelimiter`] is copied as a plain pointer while the
59    /// reference keeping it alive stays behind in `env_opts`, so dropping the
60    /// options first would leave the writer pointing at freed memory.
61    pub fn create_with_env_options(opts: &'a Options, env_opts: &'a EnvOptions) -> Self {
62        let writer = Self::create_raw(opts, env_opts);
63
64        Self {
65            inner: writer,
66            phantom: PhantomData,
67        }
68    }
69
70    fn create_raw(opts: &Options, env_opts: &EnvOptions) -> *mut ffi::rocksdb_sstfilewriter_t {
71        unsafe { ffi::rocksdb_sstfilewriter_create(env_opts.as_ptr(), opts.inner) }
72    }
73
74    /// Prepare SstFileWriter to write into file located at "file_path".
75    ///
76    /// Takes `&mut self` because the underlying `rocksdb::SstFileWriter::Open`
77    /// mutates the writer (it installs a new `WritableFileWriter` and table
78    /// builder) and is not thread safe. With `&self` and the `Sync` impl above,
79    /// two threads could call this concurrently on the same writer from safe
80    /// code and race on those fields.
81    pub fn open<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
82        let cpath = to_cpath(&path)?;
83        self.open_raw(&cpath)
84    }
85
86    fn open_raw(&mut self, cpath: &CString) -> Result<(), Error> {
87        unsafe {
88            ffi_try!(ffi::rocksdb_sstfilewriter_open(
89                self.inner,
90                cpath.as_ptr() as *const _
91            ));
92
93            Ok(())
94        }
95    }
96
97    /// Finalize writing to sst file and close file.
98    pub fn finish(&mut self) -> Result<(), Error> {
99        unsafe {
100            ffi_try!(ffi::rocksdb_sstfilewriter_finish(self.inner,));
101            Ok(())
102        }
103    }
104
105    /// returns the current file size
106    pub fn file_size(&self) -> u64 {
107        let mut file_size: u64 = 0;
108        unsafe {
109            ffi::rocksdb_sstfilewriter_file_size(self.inner, &raw mut file_size);
110        }
111        file_size
112    }
113
114    /// Adds a Put key with value to currently opened file
115    /// REQUIRES: key is after any previously added key according to comparator.
116    pub fn put<K, V>(&mut self, key: K, value: V) -> Result<(), Error>
117    where
118        K: AsRef<[u8]>,
119        V: AsRef<[u8]>,
120    {
121        let key = key.as_ref();
122        let value = value.as_ref();
123        unsafe {
124            ffi_try!(ffi::rocksdb_sstfilewriter_put(
125                self.inner,
126                key.as_ptr() as *const c_char,
127                key.len() as size_t,
128                value.as_ptr() as *const c_char,
129                value.len() as size_t,
130            ));
131            Ok(())
132        }
133    }
134
135    /// Adds a Put key with value to currently opened file
136    /// REQUIRES: key is after any previously added key according to comparator.
137    pub fn put_with_ts<K, V, S>(&mut self, key: K, ts: S, value: V) -> Result<(), Error>
138    where
139        K: AsRef<[u8]>,
140        V: AsRef<[u8]>,
141        S: AsRef<[u8]>,
142    {
143        let key = key.as_ref();
144        let value = value.as_ref();
145        let ts = ts.as_ref();
146        unsafe {
147            ffi_try!(ffi::rocksdb_sstfilewriter_put_with_ts(
148                self.inner,
149                key.as_ptr() as *const c_char,
150                key.len() as size_t,
151                ts.as_ptr() as *const c_char,
152                ts.len() as size_t,
153                value.as_ptr() as *const c_char,
154                value.len() as size_t,
155            ));
156            Ok(())
157        }
158    }
159
160    /// Adds a Merge key with value to currently opened file
161    /// REQUIRES: key is after any previously added key according to comparator.
162    pub fn merge<K, V>(&mut self, key: K, value: V) -> Result<(), Error>
163    where
164        K: AsRef<[u8]>,
165        V: AsRef<[u8]>,
166    {
167        let key = key.as_ref();
168        let value = value.as_ref();
169
170        unsafe {
171            ffi_try!(ffi::rocksdb_sstfilewriter_merge(
172                self.inner,
173                key.as_ptr() as *const c_char,
174                key.len() as size_t,
175                value.as_ptr() as *const c_char,
176                value.len() as size_t,
177            ));
178            Ok(())
179        }
180    }
181
182    /// Adds a deletion key to currently opened file
183    /// REQUIRES: key is after any previously added key according to comparator.
184    pub fn delete<K: AsRef<[u8]>>(&mut self, key: K) -> Result<(), Error> {
185        let key = key.as_ref();
186
187        unsafe {
188            ffi_try!(ffi::rocksdb_sstfilewriter_delete(
189                self.inner,
190                key.as_ptr() as *const c_char,
191                key.len() as size_t,
192            ));
193            Ok(())
194        }
195    }
196
197    /// Adds a range deletion tombstone to the currently opened file.
198    /// Unlike point entries, range tombstones may be added out of order.
199    /// REQUIRES: `begin_key` <= `end_key` according to the comparator.
200    /// REQUIRES: comparator is not timestamp-aware.
201    pub fn delete_range<K: AsRef<[u8]>>(&mut self, begin_key: K, end_key: K) -> Result<(), Error> {
202        let begin_key = begin_key.as_ref();
203        let end_key = end_key.as_ref();
204
205        unsafe {
206            ffi_try!(ffi::rocksdb_sstfilewriter_delete_range(
207                self.inner,
208                begin_key.as_ptr() as *const c_char,
209                begin_key.len() as size_t,
210                end_key.as_ptr() as *const c_char,
211                end_key.len() as size_t,
212            ));
213            Ok(())
214        }
215    }
216
217    /// Adds a deletion key to currently opened file
218    /// REQUIRES: key is after any previously added key according to comparator.
219    pub fn delete_with_ts<K: AsRef<[u8]>, S: AsRef<[u8]>>(
220        &mut self,
221        key: K,
222        ts: S,
223    ) -> Result<(), Error> {
224        let key = key.as_ref();
225        let ts = ts.as_ref();
226        unsafe {
227            ffi_try!(ffi::rocksdb_sstfilewriter_delete_with_ts(
228                self.inner,
229                key.as_ptr() as *const c_char,
230                key.len() as size_t,
231                ts.as_ptr() as *const c_char,
232                ts.len() as size_t,
233            ));
234            Ok(())
235        }
236    }
237}
238
239impl Drop for SstFileWriter<'_> {
240    fn drop(&mut self) {
241        unsafe {
242            ffi::rocksdb_sstfilewriter_destroy(self.inner);
243        }
244    }
245}