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}