tarantool_module/space.rs
1//! Box: spaces
2//!
3//! **CRUD operations** in Tarantool are implemented by the box.space submodule.
4//! It has the data-manipulation functions select, insert, replace, update, upsert, delete, get, put.
5//!
6//! See also:
7//! - [Lua reference: Submodule box.space](https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/)
8//! - [C API reference: Module box](https://www.tarantool.io/en/doc/latest/dev_guide/reference_capi/box/)
9use std::os::raw::c_char;
10use std::ptr::null_mut;
11
12use num_traits::ToPrimitive;
13
14use crate::error::{Error, TarantoolError};
15use crate::index::{Index, IndexIterator, IteratorType};
16use crate::tuple::{AsTuple, Tuple};
17
18/// Provides access to system spaces
19///
20/// Example:
21/// ```rust
22/// use tarantool_module::space::SystemSpace;
23/// let schema_space = SystemSpace::Schema.into();
24/// ```
25#[repr(u32)]
26#[derive(Debug, Clone, PartialEq, ToPrimitive)]
27pub enum SystemSpace {
28 /// Space if of _vinyl_deferred_delete.
29 VinylDeferredDelete = 257,
30 /// Space id of _schema.
31 Schema = 272,
32 /// Space id of _collation.
33 Collation = 276,
34 /// Space id of _vcollation.
35 VCollation = 277,
36 /// Space id of _space.
37 Space = 280,
38 /// Space id of _vspace view.
39 VSpace = 281,
40 /// Space id of _sequence.
41 Sequence = 284,
42 /// Space id of _sequence_data.
43 SequenceData = 285,
44 /// Space id of _vsequence view.
45 VSequence = 286,
46 /// Space id of _index.
47 Index = 288,
48 /// Space id of _vindex view.
49 VIndex = 289,
50 /// Space id of _func.
51 Func = 296,
52 /// Space id of _vfunc view.
53 VFunc = 297,
54 /// Space id of _user.
55 User = 304,
56 /// Space id of _vuser view.
57 VUser = 305,
58 /// Space id of _priv.
59 Priv = 312,
60 /// Space id of _vpriv view.
61 VPriv = 313,
62 /// Space id of _cluster.
63 Cluster = 320,
64 /// Space id of _trigger.
65 Trigger = 328,
66 /// Space id of _truncate.
67 Truncate = 330,
68 /// Space id of _space_sequence.
69 SpaceSequence = 340,
70 /// Space id of _fk_constraint.
71 FkConstraint = 356,
72 /// Space id of _ck_contraint.
73 CkConstraint = 364,
74 /// Space id of _func_index.
75 FuncIndex = 372,
76 /// Space id of _session_settings.
77 SessionSettings = 380,
78}
79
80impl Into<Space> for SystemSpace {
81 fn into(self) -> Space {
82 Space {
83 id: self.to_u32().unwrap(),
84 }
85 }
86}
87
88pub struct Space {
89 id: u32,
90}
91
92impl Space {
93 /// Find space by name.
94 ///
95 /// This function performs SELECT request to `_vspace` system space.
96 /// - `name` - space name
97 ///
98 /// Returns:
99 /// - `None` if not found
100 /// - `Some(space)` otherwise
101 pub fn find(name: &str) -> Option<Self> {
102 let id =
103 unsafe { ffi::box_space_id_by_name(name.as_ptr() as *const c_char, name.len() as u32) };
104
105 if id == ffi::BOX_ID_NIL {
106 None
107 } else {
108 Some(Self { id })
109 }
110 }
111
112 /// Find index by name.
113 ///
114 /// This function performs SELECT request to _vindex system space.
115 /// - `name` - index name
116 ///
117 /// Returns:
118 /// - `None` if not found
119 /// - `Some(index)` otherwise
120 pub fn index(&self, name: &str) -> Option<Index> {
121 let index_id = unsafe {
122 ffi::box_index_id_by_name(self.id, name.as_ptr() as *const c_char, name.len() as u32)
123 };
124
125 if index_id == ffi::BOX_ID_NIL {
126 None
127 } else {
128 Some(Index::new(self.id, index_id))
129 }
130 }
131
132 /// Returns index with id = 0
133 #[inline(always)]
134 pub fn primary_key(&self) -> Index {
135 Index::new(self.id, 0)
136 }
137
138 /// Insert a tuple into a space.
139 ///
140 /// - `value` - tuple value to insert
141 ///
142 /// Returns a new tuple.
143 ///
144 /// See also: `box.space[space_id]:insert(tuple)`
145 pub fn insert<T>(&mut self, value: &T) -> Result<Option<Tuple>, Error>
146 where
147 T: AsTuple,
148 {
149 let buf = value.serialize_as_tuple().unwrap();
150 let buf_ptr = buf.as_ptr() as *const c_char;
151 let mut result_ptr = null_mut::<ffi::BoxTuple>();
152
153 if unsafe {
154 ffi::box_insert(
155 self.id,
156 buf_ptr,
157 buf_ptr.offset(buf.len() as isize),
158 &mut result_ptr,
159 )
160 } < 0
161 {
162 return Err(TarantoolError::last().into());
163 }
164
165 Ok(if result_ptr.is_null() {
166 None
167 } else {
168 Some(Tuple::from_ptr(result_ptr))
169 })
170 }
171
172 /// Insert a tuple into a space.
173 /// If a tuple with the same primary key already exists, [space.replace()](#method.replace) replaces the existing
174 /// tuple with a new one. The syntax variants [space.replace()](#method.replace) and [space.put()](#method.put)
175 /// have the same effect;
176 /// the latter is sometimes used to show that the effect is the converse of [space.get()](#method.get).
177 ///
178 /// - `value` - tuple value to replace with
179 ///
180 /// Returns a new tuple.
181 pub fn replace<T>(&mut self, value: &T) -> Result<Option<Tuple>, Error>
182 where
183 T: AsTuple,
184 {
185 let buf = value.serialize_as_tuple().unwrap();
186 let buf_ptr = buf.as_ptr() as *const c_char;
187 let mut result_ptr = null_mut::<ffi::BoxTuple>();
188
189 if unsafe {
190 ffi::box_replace(
191 self.id,
192 buf_ptr,
193 buf_ptr.offset(buf.len() as isize),
194 &mut result_ptr,
195 )
196 } < 0
197 {
198 return Err(TarantoolError::last().into());
199 }
200
201 Ok(if result_ptr.is_null() {
202 None
203 } else {
204 Some(Tuple::from_ptr(result_ptr))
205 })
206 }
207
208 /// Insert a tuple into a space. If a tuple with the same primary key already exists, replaces the existing tuple
209 /// with a new one. Alias for [space.replace()](#method.replace)
210 #[inline(always)]
211 pub fn put<T>(&mut self, value: &T) -> Result<Option<Tuple>, Error>
212 where
213 T: AsTuple,
214 {
215 self.replace(value)
216 }
217
218 /// Deletes all tuples. The method is performed in background and doesn’t block consequent requests.
219 pub fn truncate(&mut self) -> Result<(), Error> {
220 if unsafe { ffi::box_truncate(self.id) } < 0 {
221 return Err(TarantoolError::last().into());
222 }
223 Ok(())
224 }
225
226 /// Return the number of tuples in the space.
227 ///
228 /// If compared with [space.count()](#method.count), this method works faster because [space.len()](#method.len)
229 /// does not scan the entire space to count the tuples.
230 #[inline(always)]
231 pub fn len(&self) -> Result<usize, Error> {
232 self.primary_key().len()
233 }
234
235 /// Number of bytes in the space.
236 ///
237 /// This number, which is stored in Tarantool’s internal memory, represents the total number of bytes in all tuples,
238 /// not including index keys. For a measure of index size, see [index.bsize()](../index/struct.Index.html#method.bsize).
239 #[inline(always)]
240 pub fn bsize(&self) -> Result<usize, Error> {
241 self.primary_key().bsize()
242 }
243
244 /// Search for a tuple in the given space.
245 #[inline(always)]
246 pub fn get<K>(&self, key: &K) -> Result<Option<Tuple>, Error>
247 where
248 K: AsTuple,
249 {
250 self.primary_key().get(key)
251 }
252
253 /// Search for a tuple or a set of tuples in the given space. This method doesn’t yield
254 /// (for details see [Сooperative multitasking](https://www.tarantool.io/en/doc/latest/book/box/atomic_index/#atomic-cooperative-multitasking)).
255 ///
256 /// - `type` - iterator type
257 /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
258 #[inline(always)]
259 pub fn select<K>(&self, iterator_type: IteratorType, key: &K) -> Result<IndexIterator, Error>
260 where
261 K: AsTuple,
262 {
263 self.primary_key().select(iterator_type, key)
264 }
265
266 /// Return the number of tuples. If compared with [space.len()](#method.len), this method works slower because
267 /// [space.count()](#method.count) scans the entire space to count the tuples.
268 ///
269 /// - `type` - iterator type
270 /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
271 pub fn count<K>(&self, iterator_type: IteratorType, key: &K) -> Result<usize, Error>
272 where
273 K: AsTuple,
274 {
275 self.primary_key().count(iterator_type, key)
276 }
277
278 /// Delete a tuple identified by a primary key.
279 ///
280 /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
281 ///
282 /// Returns the deleted tuple
283 #[inline(always)]
284 pub fn delete<K>(&mut self, key: &K) -> Result<Option<Tuple>, Error>
285 where
286 K: AsTuple,
287 {
288 self.primary_key().delete(key)
289 }
290
291 /// Update a tuple.
292 ///
293 /// The `update` function supports operations on fields — assignment, arithmetic (if the field is numeric),
294 /// cutting and pasting fragments of a field, deleting or inserting a field. Multiple operations can be combined in
295 /// a single update request, and in this case they are performed atomically and sequentially. Each operation
296 /// requires specification of a field number. When multiple operations are present, the field number for each
297 /// operation is assumed to be relative to the most recent state of the tuple, that is, as if all previous
298 /// operations in a multi-operation update have already been applied.
299 /// In other words, it is always safe to merge multiple `update` invocations into a single invocation, with no
300 /// change in semantics.
301 ///
302 /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
303 /// - `ops` - encoded operations in MsgPack array format, e.g. `[['=', field_id, value], ['!', 2, 'xxx']]`
304 ///
305 /// Returns a new tuple.
306 ///
307 /// See also: [space.upsert()](#method.upsert)
308 #[inline(always)]
309 pub fn update<K, Op>(&mut self, key: &K, ops: &Vec<Op>) -> Result<Option<Tuple>, Error>
310 where
311 K: AsTuple,
312 Op: AsTuple,
313 {
314 self.primary_key().update(key, ops)
315 }
316
317 /// Update or insert a tuple.
318 ///
319 /// If there is an existing tuple which matches the key fields of tuple, then the request has the same effect as
320 /// [space.update()](#method.update) and the `{{operator, field_no, value}, ...}` parameter is used.
321 /// If there is no existing tuple which matches the key fields of tuple, then the request has the same effect as
322 /// [space.insert()](#method.insert) and the `{tuple}` parameter is used.
323 /// However, unlike `insert` or `update`, `upsert` will not read a tuple and perform error checks before
324 /// returning – this is a design feature which enhances throughput but requires more caution on the part of the
325 /// user.
326 ///
327 /// - `value` - encoded tuple in MsgPack Array format (`[field1, field2, ...]`)
328 /// - `ops` - encoded operations in MsgPack array format, e.g. `[['=', field_id, value], ['!', 2, 'xxx']]`
329 ///
330 /// Returns a new tuple.
331 ///
332 /// See also: [space.update()](#method.update)
333 #[inline(always)]
334 pub fn upsert<T, Op>(&mut self, value: &T, ops: &Vec<Op>) -> Result<Option<Tuple>, Error>
335 where
336 T: AsTuple,
337 Op: AsTuple,
338 {
339 self.primary_key().upsert(value, ops)
340 }
341}
342
343mod ffi {
344 use std::os::raw::{c_char, c_int};
345
346 pub use crate::tuple::ffi::BoxTuple;
347
348 pub const BOX_ID_NIL: u32 = 2147483647;
349
350 extern "C" {
351 pub fn box_space_id_by_name(name: *const c_char, len: u32) -> u32;
352 pub fn box_index_id_by_name(space_id: u32, name: *const c_char, len: u32) -> u32;
353 pub fn box_insert(
354 space_id: u32,
355 tuple: *const c_char,
356 tuple_end: *const c_char,
357 result: *mut *mut BoxTuple,
358 ) -> c_int;
359 pub fn box_replace(
360 space_id: u32,
361 tuple: *const c_char,
362 tuple_end: *const c_char,
363 result: *mut *mut BoxTuple,
364 ) -> c_int;
365 pub fn box_truncate(space_id: u32) -> c_int;
366 }
367}