tarantool_module/
index.rs

1//! Box: indices
2//!
3//! The `index` submodule provides access for index definitions and index keys.
4//! They provide an API for ordered iteration over tuples.
5//! This API is a direct binding to corresponding methods of index objects of type `box.index` in the storage engine.
6//!
7//! See also:
8//! - [Indexes](https://www.tarantool.io/en/doc/latest/book/box/data_model/#indexes)
9//! - [Lua reference: Submodule box.index](https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_index/)
10use std::os::raw::c_char;
11use std::ptr::null_mut;
12
13use num_traits::ToPrimitive;
14
15use crate::error::{Error, TarantoolError};
16use crate::tuple::{ffi::BoxTuple, AsTuple, Tuple, TupleBuffer};
17
18/// An index is a group of key values and pointers.
19pub struct Index {
20    space_id: u32,
21    index_id: u32,
22}
23
24/// Controls how to iterate over tuples in an index.
25/// Different index types support different iterator types.
26/// For example, one can start iteration from a particular value
27/// (request key) and then retrieve all tuples where keys are
28/// greater or equal (= `GE`) to this key.
29///
30/// If iterator type is not supported by the selected index type,
31/// iterator constructor must fail with `ER_UNSUPPORTED`. To be
32/// selectable for primary key, an index must support at least
33/// `Eq` and `GE` types.
34///
35/// `None` value of request key corresponds to the first or last
36/// key in the index, depending on iteration direction.
37/// (first key for `GE` and `GT` types, and last key for `LE` and `LT`).
38/// Therefore, to iterate over all tuples in an index, one can
39/// use `GE` or `LE` iteration types with start key equal to `None`.
40/// For `EQ`, the key must not be `None`.
41#[repr(i32)]
42#[derive(Debug, Copy, Clone, ToPrimitive)]
43pub enum IteratorType {
44    /// key == x ASC order
45    Eq = 0,
46
47    /// key == x DESC order
48    Req = 1,
49
50    /// all tuples
51    All = 2,
52
53    /// key <  x
54    LT = 3,
55
56    /// key <= x
57    LE = 4,
58
59    /// key >= x
60    GE = 5,
61
62    /// key >  x
63    GT = 6,
64
65    /// all bits from x are set in key
66    BitsAllSet = 7,
67
68    /// at least one x's bit is set
69    BitsAnySet = 8,
70
71    /// all bits are not set
72    BitsAllNotSet = 9,
73
74    /// key overlaps x
75    Ovelaps = 10,
76
77    /// tuples in distance ascending order from specified point
78    Neigbor = 11,
79}
80
81impl Index {
82    pub(crate) fn new(space_id: u32, index_id: u32) -> Self {
83        Index { space_id, index_id }
84    }
85
86    /// Get a tuple from index by the key.
87    ///
88    /// Please note that this function works much more faster than [select](#method.select)
89    ///
90    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
91    ///
92    /// Returns a tuple or `None` if index is empty
93    pub fn get<K>(&self, key: &K) -> Result<Option<Tuple>, Error>
94    where
95        K: AsTuple,
96    {
97        let key_buf = key.serialize_as_tuple().unwrap();
98        let key_buf_ptr = key_buf.as_ptr() as *const c_char;
99        let mut result_ptr = null_mut::<BoxTuple>();
100
101        if unsafe {
102            ffi::box_index_get(
103                self.space_id,
104                self.index_id,
105                key_buf_ptr,
106                key_buf_ptr.offset(key_buf.len() as isize),
107                &mut result_ptr,
108            )
109        } < 0
110        {
111            return Err(TarantoolError::last().into());
112        }
113
114        Ok(if result_ptr.is_null() {
115            None
116        } else {
117            Some(Tuple::from_ptr(result_ptr))
118        })
119    }
120
121    /// Allocate and initialize iterator for index.
122    ///
123    /// This is an alternative to [space.select()](../space/struct.Space.html#method.select) which goes via a particular
124    /// index and can make use of additional parameter that specify the iterator type.
125    ///
126    /// - `type` - iterator type
127    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
128    pub fn select<K>(&self, iterator_type: IteratorType, key: &K) -> Result<IndexIterator, Error>
129    where
130        K: AsTuple,
131    {
132        let key_buf = key.serialize_as_tuple().unwrap();
133        let key_buf_ptr = key_buf.as_ptr() as *const c_char;
134
135        let ptr = unsafe {
136            ffi::box_index_iterator(
137                self.space_id,
138                self.index_id,
139                iterator_type.to_i32().unwrap(),
140                key_buf_ptr,
141                key_buf_ptr.offset(key_buf.len() as isize),
142            )
143        };
144
145        if ptr.is_null() {
146            return Err(TarantoolError::last().into());
147        }
148
149        Ok(IndexIterator {
150            ptr,
151            _key_data: key_buf,
152        })
153    }
154
155    /// Delete a tuple identified by a key.
156    ///
157    /// Same as [space.delete()](../space/struct.Space.html#method.delete), but key is searched in this index instead
158    /// of in the primary-key index. This index ought to be unique.
159    ///
160    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
161    ///
162    /// Returns the deleted tuple
163    pub fn delete<K>(&mut self, key: &K) -> Result<Option<Tuple>, Error>
164    where
165        K: AsTuple,
166    {
167        let key_buf = key.serialize_as_tuple().unwrap();
168        let key_buf_ptr = key_buf.as_ptr() as *const c_char;
169        let mut result_ptr = null_mut::<BoxTuple>();
170
171        if unsafe {
172            ffi::box_delete(
173                self.space_id,
174                self.index_id,
175                key_buf_ptr,
176                key_buf_ptr.offset(key_buf.len() as isize),
177                &mut result_ptr,
178            )
179        } < 0
180        {
181            return Err(TarantoolError::last().into());
182        }
183
184        Ok(if result_ptr.is_null() {
185            None
186        } else {
187            Some(Tuple::from_ptr(result_ptr))
188        })
189    }
190
191    /// Update a tuple.
192    ///
193    /// Same as [space.update()](../space/struct.Space.html#method.update), but key is searched in this index instead
194    /// of primary key. This index ought to be unique.
195    ///
196    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
197    /// - `ops` - encoded operations in MsgPack array format, e.g. `[['=', field_id, value], ['!', 2, 'xxx']]`
198    ///
199    /// Returns a new tuple.
200    ///
201    /// See also: [index.upsert()](#method.upsert)
202    pub fn update<K, Op>(&mut self, key: &K, ops: &Vec<Op>) -> Result<Option<Tuple>, Error>
203    where
204        K: AsTuple,
205        Op: AsTuple,
206    {
207        let key_buf = key.serialize_as_tuple().unwrap();
208        let key_buf_ptr = key_buf.as_ptr() as *const c_char;
209        let ops_buf = ops.serialize_as_tuple().unwrap();
210        let ops_buf_ptr = ops_buf.as_ptr() as *const c_char;
211        let mut result_ptr = null_mut::<BoxTuple>();
212
213        if unsafe {
214            ffi::box_update(
215                self.space_id,
216                self.index_id,
217                key_buf_ptr,
218                key_buf_ptr.offset(key_buf.len() as isize),
219                ops_buf_ptr,
220                ops_buf_ptr.offset(ops_buf.len() as isize),
221                0,
222                &mut result_ptr,
223            )
224        } < 0
225        {
226            return Err(TarantoolError::last().into());
227        }
228
229        Ok(if result_ptr.is_null() {
230            None
231        } else {
232            Some(Tuple::from_ptr(result_ptr))
233        })
234    }
235
236    /// Execute an UPSERT request.
237    ///
238    /// Will try to insert tuple. Update if already exists.
239    ///
240    /// - `value` - encoded tuple in MsgPack Array format (`[field1, field2, ...]`)
241    /// - `ops` - encoded operations in MsgPack array format, e.g. `[['=', field_id, value], ['!', 2, 'xxx']]`
242    ///
243    /// Returns a new tuple.
244    ///
245    /// See also: [index.update()](#method.update)
246    pub fn upsert<T, Op>(&mut self, value: &T, ops: &Vec<Op>) -> Result<Option<Tuple>, Error>
247    where
248        T: AsTuple,
249        Op: AsTuple,
250    {
251        let value_buf = value.serialize_as_tuple().unwrap();
252        let value_buf_ptr = value_buf.as_ptr() as *const c_char;
253        let ops_buf = ops.serialize_as_tuple().unwrap();
254        let ops_buf_ptr = ops_buf.as_ptr() as *const c_char;
255        let mut result_ptr = null_mut::<BoxTuple>();
256
257        if unsafe {
258            ffi::box_upsert(
259                self.space_id,
260                self.index_id,
261                value_buf_ptr,
262                value_buf_ptr.offset(value_buf.len() as isize),
263                ops_buf_ptr,
264                ops_buf_ptr.offset(ops_buf.len() as isize),
265                0,
266                &mut result_ptr,
267            )
268        } < 0
269        {
270            return Err(TarantoolError::last().into());
271        }
272
273        Ok(if result_ptr.is_null() {
274            None
275        } else {
276            Some(Tuple::from_ptr(result_ptr))
277        })
278    }
279
280    /// Return the number of elements in the index.
281    pub fn len(&self) -> Result<usize, Error> {
282        let result = unsafe { ffi::box_index_len(self.space_id, self.index_id) };
283
284        if result < 0 {
285            Err(TarantoolError::last().into())
286        } else {
287            Ok(result as usize)
288        }
289    }
290
291    /// Return the number of bytes used in memory by the index.
292    pub fn bsize(&self) -> Result<usize, Error> {
293        let result = unsafe { ffi::box_index_bsize(self.space_id, self.index_id) };
294
295        if result < 0 {
296            Err(TarantoolError::last().into())
297        } else {
298            Ok(result as usize)
299        }
300    }
301
302    /// Return a random tuple from the index (useful for statistical analysis).
303    ///
304    /// - `rnd` - random seed
305    pub fn random(&self, seed: u32) -> Result<Option<Tuple>, Error> {
306        let mut result_ptr = null_mut::<BoxTuple>();
307        if unsafe { ffi::box_index_random(self.space_id, self.index_id, seed, &mut result_ptr) } < 0
308        {
309            return Err(TarantoolError::last().into());
310        }
311
312        Ok(if result_ptr.is_null() {
313            None
314        } else {
315            Some(Tuple::from_ptr(result_ptr))
316        })
317    }
318
319    /// Return a first (minimal) tuple matched the provided key.
320    ///
321    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
322    ///
323    /// Returns a tuple or `None` if index is empty
324    pub fn min<K>(&self, key: &K) -> Result<Option<Tuple>, Error>
325    where
326        K: AsTuple,
327    {
328        let key_buf = key.serialize_as_tuple().unwrap();
329        let key_buf_ptr = key_buf.as_ptr() as *const c_char;
330        let mut result_ptr = null_mut::<BoxTuple>();
331
332        if unsafe {
333            ffi::box_index_min(
334                self.space_id,
335                self.index_id,
336                key_buf_ptr,
337                key_buf_ptr.offset(key_buf.len() as isize),
338                &mut result_ptr,
339            )
340        } < 0
341        {
342            return Err(TarantoolError::last().into());
343        }
344
345        Ok(if result_ptr.is_null() {
346            None
347        } else {
348            Some(Tuple::from_ptr(result_ptr))
349        })
350    }
351
352    /// Return a last (maximal) tuple matched the provided key.
353    ///
354    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
355    ///
356    /// Returns a tuple or `None` if index is empty
357    pub fn max<K>(&self, key: &K) -> Result<Option<Tuple>, Error>
358    where
359        K: AsTuple,
360    {
361        let key_buf = key.serialize_as_tuple().unwrap();
362        let key_buf_ptr = key_buf.as_ptr() as *const c_char;
363        let mut result_ptr = null_mut::<BoxTuple>();
364
365        if unsafe {
366            ffi::box_index_max(
367                self.space_id,
368                self.index_id,
369                key_buf_ptr,
370                key_buf_ptr.offset(key_buf.len() as isize),
371                &mut result_ptr,
372            )
373        } < 0
374        {
375            return Err(TarantoolError::last().into());
376        }
377
378        Ok(if result_ptr.is_null() {
379            None
380        } else {
381            Some(Tuple::from_ptr(result_ptr))
382        })
383    }
384
385    /// Count the number of tuple matched the provided key.
386    ///
387    /// - `type` - iterator type
388    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
389    pub fn count<K>(&self, iterator_type: IteratorType, key: &K) -> Result<usize, Error>
390    where
391        K: AsTuple,
392    {
393        let key_buf = key.serialize_as_tuple().unwrap();
394        let key_buf_ptr = key_buf.as_ptr() as *const c_char;
395
396        let result = unsafe {
397            ffi::box_index_count(
398                self.space_id,
399                self.index_id,
400                iterator_type.to_i32().unwrap(),
401                key_buf_ptr,
402                key_buf_ptr.offset(key_buf.len() as isize),
403            )
404        };
405
406        if result < 0 {
407            Err(TarantoolError::last().into())
408        } else {
409            Ok(result as usize)
410        }
411    }
412
413    /// Extract key from tuple according to key definition of given
414    /// index. Returned buffer is allocated on `box_txn_alloc()` with
415    /// this key.
416    ///
417    /// - `tuple` - tuple from which need to extract key.
418    pub fn extract_key(&self, tuple: Tuple) -> Tuple {
419        let mut result_size: u32 = 0;
420        let result_ptr = unsafe {
421            ffi::box_tuple_extract_key(
422                tuple.into_ptr(),
423                self.space_id,
424                self.index_id,
425                &mut result_size,
426            )
427        };
428        Tuple::from_raw_data(result_ptr, result_size)
429    }
430}
431
432/// Index iterator. Can be used with `for` statement.
433pub struct IndexIterator {
434    ptr: *mut ffi::BoxIterator,
435    _key_data: TupleBuffer,
436}
437
438impl Iterator for IndexIterator {
439    type Item = Tuple;
440
441    fn next(&mut self) -> Option<Self::Item> {
442        let mut result_ptr = null_mut::<BoxTuple>();
443        if unsafe { ffi::box_iterator_next(self.ptr, &mut result_ptr) } < 0 {
444            return None;
445        }
446
447        if result_ptr.is_null() {
448            None
449        } else {
450            Some(Tuple::from_ptr(result_ptr))
451        }
452    }
453}
454
455impl Drop for IndexIterator {
456    fn drop(&mut self) {
457        unsafe { ffi::box_iterator_free(self.ptr) };
458    }
459}
460
461mod ffi {
462    use std::os::raw::{c_char, c_int};
463
464    use crate::tuple::ffi::BoxTuple;
465
466    #[repr(C)]
467    pub struct BoxIterator {
468        _unused: [u8; 0],
469    }
470
471    extern "C" {
472        pub fn box_index_iterator(
473            space_id: u32,
474            index_id: u32,
475            type_: c_int,
476            key: *const c_char,
477            key_end: *const c_char,
478        ) -> *mut BoxIterator;
479
480        pub fn box_iterator_next(iterator: *mut BoxIterator, result: *mut *mut BoxTuple) -> c_int;
481        pub fn box_iterator_free(iterator: *mut BoxIterator);
482        pub fn box_index_len(space_id: u32, index_id: u32) -> isize;
483        pub fn box_index_bsize(space_id: u32, index_id: u32) -> isize;
484        pub fn box_index_random(
485            space_id: u32,
486            index_id: u32,
487            rnd: u32,
488            result: *mut *mut BoxTuple,
489        ) -> c_int;
490
491        pub fn box_index_get(
492            space_id: u32,
493            index_id: u32,
494            key: *const c_char,
495            key_end: *const c_char,
496            result: *mut *mut BoxTuple,
497        ) -> c_int;
498
499        pub fn box_index_min(
500            space_id: u32,
501            index_id: u32,
502            key: *const c_char,
503            key_end: *const c_char,
504            result: *mut *mut BoxTuple,
505        ) -> c_int;
506
507        pub fn box_index_max(
508            space_id: u32,
509            index_id: u32,
510            key: *const c_char,
511            key_end: *const c_char,
512            result: *mut *mut BoxTuple,
513        ) -> c_int;
514
515        pub fn box_index_count(
516            space_id: u32,
517            index_id: u32,
518            type_: c_int,
519            key: *const c_char,
520            key_end: *const c_char,
521        ) -> isize;
522
523        pub fn box_delete(
524            space_id: u32,
525            index_id: u32,
526            key: *const c_char,
527            key_end: *const c_char,
528            result: *mut *mut BoxTuple,
529        ) -> c_int;
530
531        pub fn box_update(
532            space_id: u32,
533            index_id: u32,
534            key: *const c_char,
535            key_end: *const c_char,
536            ops: *const c_char,
537            ops_end: *const c_char,
538            index_base: c_int,
539            result: *mut *mut BoxTuple,
540        ) -> c_int;
541
542        pub fn box_upsert(
543            space_id: u32,
544            index_id: u32,
545            tuple: *const c_char,
546            tuple_end: *const c_char,
547            ops: *const c_char,
548            ops_end: *const c_char,
549            index_base: c_int,
550            result: *mut *mut BoxTuple,
551        ) -> c_int;
552
553        pub fn box_tuple_extract_key(
554            tuple: *const BoxTuple,
555            space_id: u32,
556            index_id: u32,
557            key_size: *mut u32,
558        ) -> *mut c_char;
559    }
560}