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}