tarantool_module/
tuple.rs

1//! Tuples
2//!
3//! The `tuple` submodule provides read-only access for the tuple userdata type.
4//! It allows, for a single tuple: selective retrieval of the field contents, retrieval of information about size,
5//! iteration over all the fields, and conversion from/to rust structures
6//!
7//! See also:
8//! - [Tuples](https://www.tarantool.io/en/doc/2.2/book/box/data_model/#tuples)
9//! - [Lua reference: Submodule box.tuple](https://www.tarantool.io/en/doc/2.2/reference/reference_lua/box_tuple/)
10//! - [C API reference: Module tuple](https://www.tarantool.io/en/doc/2.2/dev_guide/reference_capi/tuple/)
11use std::cmp::Ordering;
12use std::io::Cursor;
13use std::os::raw::{c_char, c_int};
14use std::ptr::copy_nonoverlapping;
15use std::slice::from_raw_parts;
16
17use num_traits::ToPrimitive;
18use rmp::Marker;
19use serde::{de::DeserializeOwned, Serialize};
20
21use crate::error::{Error, TarantoolError};
22
23/// Tuple
24pub struct Tuple {
25    ptr: *mut ffi::BoxTuple,
26}
27
28impl Tuple {
29    /// Creates new tuple from `value`.
30    ///
31    /// This function will serialize structure instance `value` of type `T` into tuple internal representation
32    ///
33    /// See also: [AsTuple](trait.AsTuple.html)
34    pub fn from_struct<T>(value: &T) -> Result<Self, Error>
35    where
36        T: AsTuple,
37    {
38        let format = TupleFormat::default();
39        let buf = value.serialize_as_tuple()?;
40        let buf_ptr = buf.as_ptr() as *const c_char;
41        let tuple_ptr = unsafe {
42            ffi::box_tuple_new(format.inner, buf_ptr, buf_ptr.offset(buf.len() as isize))
43        };
44
45        unsafe { ffi::box_tuple_ref(tuple_ptr) };
46        Ok(Tuple { ptr: tuple_ptr })
47    }
48
49    pub(crate) fn from_raw_data(data_ptr: *mut c_char, len: u32) -> Self {
50        let format = TupleFormat::default();
51        let tuple_ptr =
52            unsafe { ffi::box_tuple_new(format.inner, data_ptr, data_ptr.offset(len as isize)) };
53
54        unsafe { ffi::box_tuple_ref(tuple_ptr) };
55        Tuple { ptr: tuple_ptr }
56    }
57
58    pub(crate) fn from_ptr(ptr: *mut ffi::BoxTuple) -> Self {
59        unsafe { ffi::box_tuple_ref(ptr) };
60        Tuple { ptr }
61    }
62
63    /// Return the number of fields in tuple (the size of MsgPack Array).
64    pub fn len(&self) -> u32 {
65        unsafe { ffi::box_tuple_field_count(self.ptr) }
66    }
67
68    /// Will return the number of bytes in the tuple.
69    ///
70    /// With both the memtx storage engine and the vinyl storage engine the default maximum is one megabyte
71    /// (`memtx_max_tuple_size` or `vinyl_max_tuple_size`). Every field has one or more "length" bytes preceding the
72    /// actual contents, so `bsize()` returns a value which is slightly greater than the sum of the lengths of the
73    /// contents.
74    ///
75    /// The value does not include the size of "struct tuple"
76    /// (for the current size of this structure look in the tuple.h file in Tarantool’s source code).
77    pub fn bsize(&self) -> usize {
78        unsafe { ffi::box_tuple_bsize(self.ptr) }
79    }
80
81    /// Return the associated format.
82    pub fn format(&self) -> TupleFormat {
83        TupleFormat {
84            inner: unsafe { ffi::box_tuple_format(self.ptr) },
85        }
86    }
87
88    /// Allocate and initialize a new `Tuple` iterator. The `Tuple` iterator
89    /// allow to iterate over fields at root level of MsgPack array.
90    ///
91    /// Example:
92    /// ```
93    /// let mut it = tuple.iter().unwrap();
94    ///
95    /// while let Some(field) = it.next().unwrap() {
96    ///     // process data
97    /// }
98    ///
99    /// // rewind iterator to first position
100    /// it.rewind();
101    /// assert!(it.position() == 0);
102    ///
103    /// // rewind iterator to first position
104    /// field = it.seek(3).unwrap();
105    /// assert!(it.position() == 4);
106    /// ```
107    pub fn iter(&self) -> Result<TupleIterator, Error> {
108        let inner = unsafe { ffi::box_tuple_iterator(self.ptr) };
109        if inner.is_null() {
110            Err(TarantoolError::last().into())
111        } else {
112            Ok(TupleIterator { inner })
113        }
114    }
115
116    /// Return the raw Tuple field in MsgPack format.
117    ///
118    /// The buffer is valid until next call to box_tuple_* functions.
119    ///
120    /// - `fieldno` - zero-based index in MsgPack array.
121    ///
122    /// Returns:
123    /// - `None` if `i >= box_tuple_field_count(Tuple)` or if field has a non primitive type
124    /// - field value otherwise
125    pub fn field<T>(&self, fieldno: u32) -> Result<Option<T>, Error>
126    where
127        T: DeserializeOwned,
128    {
129        let result_ptr = unsafe { ffi::box_tuple_field(self.ptr, fieldno) };
130        field_value_from_ptr(result_ptr as *mut u8)
131    }
132
133    /// Deserializes tuple contents into structure of type `T`
134    pub fn into_struct<T>(self) -> Result<T, Error>
135    where
136        T: DeserializeOwned,
137    {
138        let raw_data_size = self.bsize();
139        let mut raw_data = Vec::<u8>::with_capacity(raw_data_size);
140
141        let actual_size = unsafe {
142            ffi::box_tuple_to_buf(self.ptr, raw_data.as_ptr() as *mut c_char, raw_data_size)
143        };
144        if actual_size < 0 {
145            return Err(TarantoolError::last().into());
146        }
147
148        unsafe { raw_data.set_len(actual_size as usize) };
149        Ok(rmp_serde::from_read::<_, T>(Cursor::new(raw_data))?)
150    }
151
152    pub(crate) fn into_ptr(self) -> *mut ffi::BoxTuple {
153        self.ptr
154    }
155}
156
157impl Drop for Tuple {
158    fn drop(&mut self) {
159        unsafe { ffi::box_tuple_unref(self.ptr) };
160    }
161}
162
163impl Clone for Tuple {
164    fn clone(&self) -> Self {
165        unsafe { ffi::box_tuple_ref(self.ptr) };
166        Tuple { ptr: self.ptr }
167    }
168}
169
170/// Must be implemented for types, which will be used with box access methods as data
171pub trait AsTuple: Serialize {
172    /// Describes how object can be converted to [Tuple](struct.Tuple.html).
173    ///
174    /// Has default implementation, but can be overloaded for special cases
175    fn serialize_as_tuple(&self) -> Result<TupleBuffer, Error> {
176        Ok(rmp_serde::to_vec(self)?.into())
177    }
178}
179
180impl AsTuple for () {
181    fn serialize_as_tuple(&self) -> Result<TupleBuffer, Error> {
182        Ok(rmp_serde::to_vec(&Vec::<()>::new())?.into())
183    }
184}
185
186impl<T> AsTuple for (T,) where T: Serialize {}
187impl<T> AsTuple for Vec<T> where T: Serialize {}
188
189impl<Ta, Tb> AsTuple for (Ta, Tb)
190where
191    Ta: Serialize,
192    Tb: Serialize,
193{
194}
195
196impl<Ta, Tb, Tc> AsTuple for (Ta, Tb, Tc)
197where
198    Ta: Serialize,
199    Tb: Serialize,
200    Tc: Serialize,
201{
202}
203
204impl<Ta, Tb, Tc, Td> AsTuple for (Ta, Tb, Tc, Td)
205where
206    Ta: Serialize,
207    Tb: Serialize,
208    Tc: Serialize,
209    Td: Serialize,
210{
211}
212
213/// Buffer containing tuple contents (MsgPack array)
214///
215/// If buffer is allocated within transaction: will be disposed after transaction ended (committed or dropped).
216/// If not: will act as a regular rust `Vec<u8>`
217pub enum TupleBuffer {
218    Vector(Vec<u8>),
219    TransactionScoped { ptr: *mut u8, size: usize },
220}
221
222impl TupleBuffer {
223    /// Get raw pointer to buffer.
224    pub fn as_ptr(&self) -> *const u8 {
225        match self {
226            TupleBuffer::Vector(vec) => vec.as_ptr(),
227            TupleBuffer::TransactionScoped { ptr, size: _ } => ptr.clone(),
228        }
229    }
230
231    /// Return the number of bytes used in memory by the tuple.
232    pub fn len(&self) -> usize {
233        match self {
234            TupleBuffer::Vector(vec) => vec.len(),
235            TupleBuffer::TransactionScoped { ptr: _, size } => size.clone(),
236        }
237    }
238}
239
240impl From<Vec<u8>> for TupleBuffer {
241    fn from(buf: Vec<u8>) -> Self {
242        if unsafe { crate::transaction::ffi::box_txn() } {
243            let size = buf.len();
244            unsafe {
245                let ptr = crate::transaction::ffi::box_txn_alloc(size) as *mut u8;
246                copy_nonoverlapping(buf.as_ptr(), ptr, size);
247
248                Self::TransactionScoped { ptr, size }
249            }
250        } else {
251            Self::Vector(buf)
252        }
253    }
254}
255
256/// Tuple format
257///
258/// Each Tuple has associated format (class). Default format is used to
259/// create tuples which are not attach to any particular space.
260pub struct TupleFormat {
261    inner: *mut ffi::BoxTupleFormat,
262}
263
264impl Default for TupleFormat {
265    fn default() -> Self {
266        TupleFormat {
267            inner: unsafe { ffi::box_tuple_format_default() },
268        }
269    }
270}
271
272/// Tuple iterator
273pub struct TupleIterator {
274    inner: *mut ffi::BoxTupleIterator,
275}
276
277impl TupleIterator {
278    /// Return zero-based next position in iterator.
279    ///
280    /// That is, this function return the field id of field that will be
281    /// returned by the next call to `box_tuple_next(it)`. Returned value is zero
282    /// after initialization or rewind and `box_tuple_field_count(Tuple)`
283    /// after the end of iteration.
284    pub fn position(&self) -> u32 {
285        unsafe { ffi::box_tuple_position(self.inner) }
286    }
287
288    /// Rewind iterator to the initial position.
289    pub fn rewind(&mut self) {
290        unsafe { ffi::box_tuple_rewind(self.inner) }
291    }
292
293    /// Seek the Tuple iterator.
294    ///
295    /// Requested fieldno returned by next call to `box_tuple_next(it)`.
296    ///
297    /// - `fieldno` - zero-based position in MsgPack array.
298    ///
299    /// After call:
300    /// - `box_tuple_position(it) == fieldno` if returned value is not `None`
301    /// - `box_tuple_position(it) == box_tuple_field_count(Tuple)` if returned value is `None`.
302    pub fn seek<T>(&mut self, fieldno: u32) -> Result<Option<T>, Error>
303    where
304        T: DeserializeOwned,
305    {
306        let result_ptr = unsafe { ffi::box_tuple_seek(self.inner, fieldno) };
307        field_value_from_ptr(result_ptr as *mut u8)
308    }
309
310    /// Return the next Tuple field from Tuple iterator.
311    ///
312    /// Returns:
313    /// - `None` if `i >= box_tuple_field_count(Tuple)` or if field has a non primitive type
314    /// - field value otherwise
315    ///
316    /// After call:
317    /// - `box_tuple_position(it) == fieldno` if returned value is not `None`
318    /// - `box_tuple_position(it) == box_tuple_field_count(Tuple)` if returned value is `None`.
319    pub fn next<T>(&mut self) -> Result<Option<T>, Error>
320    where
321        T: DeserializeOwned,
322    {
323        let result_ptr = unsafe { ffi::box_tuple_next(self.inner) };
324        field_value_from_ptr(result_ptr as *mut u8)
325    }
326
327    pub fn update(&mut self) {}
328}
329
330impl Drop for TupleIterator {
331    fn drop(&mut self) {
332        unsafe { ffi::box_tuple_iterator_free(self.inner) }
333    }
334}
335
336impl TupleIterator {}
337
338#[repr(u32)]
339#[derive(Debug, ToPrimitive)]
340pub enum FieldType {
341    Any = 0,
342    Unsigned,
343    String,
344    Number,
345    Double,
346    Integer,
347    Boolean,
348    Varbinary,
349    Scalar,
350    Decimal,
351    Uuid,
352    Array,
353    Map,
354}
355
356pub struct KeyDef {
357    inner: *mut ffi::BoxKeyDef,
358}
359
360pub struct KeyDefItem {
361    pub field_id: u32,
362    pub field_type: FieldType,
363}
364
365impl KeyDef {
366    /// Create key definition with key fields with passed typed on passed positions.
367    /// May be used for tuple format creation and/or tuple comparison.
368    ///
369    /// - `items` - array with key field identifiers and key field types (see [FieldType](struct.FieldType.html))
370    pub fn new(items: Vec<KeyDefItem>) -> Self {
371        let size = items.len();
372        let mut ids = Vec::with_capacity(size);
373        let mut types = Vec::with_capacity(size);
374        for item in items {
375            ids.push(item.field_id);
376            types.push(item.field_type.to_u32().unwrap());
377        }
378
379        KeyDef {
380            inner: unsafe {
381                ffi::box_key_def_new(ids.as_mut_ptr(), types.as_mut_ptr(), size as u32)
382            },
383        }
384    }
385
386    /// Compare tuples using the key definition.
387    ///
388    /// - `tuple_a` - first tuple
389    /// - `tuple_b` - second tuple
390    ///
391    /// Returns:
392    /// - `Ordering::Equal`   if `key_fields(tuple_a) == key_fields(tuple_b)`
393    /// - `Ordering::Less`    if `key_fields(tuple_a) < key_fields(tuple_b)`
394    /// - `Ordering::Greater` if `key_fields(tuple_a) > key_fields(tuple_b)`
395    pub fn compare(&self, tuple_a: &Tuple, tuple_b: &Tuple) -> Ordering {
396        unsafe { ffi::box_tuple_compare(tuple_a.ptr, tuple_b.ptr, self.inner) }.cmp(&0)
397    }
398
399    /// Compare tuple with key using the key definition.
400    ///
401    /// - `tuple` - tuple
402    /// - `key` - key with MessagePack array header
403    ///
404    /// Returns:
405    /// - `Ordering::Equal`   if `key_fields(tuple) == parts(key)`
406    /// - `Ordering::Less`    if `key_fields(tuple) < parts(key)`
407    /// - `Ordering::Greater` if `key_fields(tuple) > parts(key)`
408    pub fn compare_with_key<K>(&self, tuple: &Tuple, key: &K) -> Ordering
409    where
410        K: AsTuple,
411    {
412        let key_buf = key.serialize_as_tuple().unwrap();
413        let key_buf_ptr = key_buf.as_ptr() as *const c_char;
414        unsafe { ffi::box_tuple_compare_with_key(tuple.ptr, key_buf_ptr, self.inner) }.cmp(&0)
415    }
416}
417
418impl Drop for KeyDef {
419    fn drop(&mut self) {
420        unsafe { ffi::box_key_def_delete(self.inner) }
421    }
422}
423
424fn field_value_from_ptr<T>(value_ptr: *mut u8) -> Result<Option<T>, Error>
425where
426    T: DeserializeOwned,
427{
428    if value_ptr.is_null() {
429        return Ok(None);
430    }
431
432    let marker = Marker::from_u8(unsafe { *value_ptr });
433    Ok(match marker {
434        Marker::FixStr(str_len) => {
435            let buf = unsafe { from_raw_parts(value_ptr as *mut u8, (str_len + 1) as usize) };
436            Some(rmp_serde::from_read_ref::<_, T>(buf)?)
437        }
438
439        Marker::Str8 | Marker::Str16 | Marker::Str32 => {
440            let head = unsafe { from_raw_parts(value_ptr as *mut u8, 9) };
441            let len = rmp::decode::read_str_len(&mut Cursor::new(head))?;
442
443            let buf = unsafe { from_raw_parts(value_ptr as *mut u8, (len + 9) as usize) };
444            Some(rmp_serde::from_read_ref::<_, T>(buf)?)
445        }
446
447        Marker::FixPos(_)
448        | Marker::FixNeg(_)
449        | Marker::Null
450        | Marker::True
451        | Marker::False
452        | Marker::U8
453        | Marker::U16
454        | Marker::U32
455        | Marker::U64
456        | Marker::I8
457        | Marker::I16
458        | Marker::I32
459        | Marker::I64
460        | Marker::F32
461        | Marker::F64 => {
462            let buf = unsafe { from_raw_parts(value_ptr as *mut u8, 9) };
463            Some(rmp_serde::from_read_ref::<_, T>(buf)?)
464        }
465
466        _ => None,
467    })
468}
469
470#[repr(C)]
471pub struct FunctionCtx {
472    inner: *mut ffi::BoxFunctionCtx,
473}
474
475impl FunctionCtx {
476    /// Return a Tuple from stored C procedure.
477    ///
478    /// Returned Tuple is automatically reference counted by Tarantool.
479    ///
480    /// - `tuple` - a Tuple to return
481    pub fn return_tuple(self, tuple: Tuple) -> Result<c_int, Error> {
482        let result = unsafe { ffi::box_return_tuple(self.inner, tuple.ptr) };
483        if result < 0 {
484            Err(TarantoolError::last().into())
485        } else {
486            Ok(result)
487        }
488    }
489
490    /// Return MessagePack from a stored C procedure. The MessagePack
491    /// is copied, so it is safe to free/reuse the passed arguments
492    /// after the call.
493    ///
494    /// MessagePack is not validated, for the sake of speed. It is
495    /// expected to be a single encoded object. An attempt to encode
496    /// and return multiple objects without wrapping them into an
497    /// `MP_ARRAY` or `MP_MAP` is undefined behaviour.
498    ///
499    /// - `value` - value to be encoded to MessagePack
500    pub fn return_mp<T>(self, value: &T) -> Result<c_int, Error>
501    where
502        T: AsTuple,
503    {
504        let buf = value.serialize_as_tuple().unwrap();
505        let buf_ptr = buf.as_ptr() as *const c_char;
506        let result =
507            unsafe { ffi::box_return_mp(self.inner, buf_ptr, buf_ptr.offset(buf.len() as isize)) };
508
509        if result < 0 {
510            Err(TarantoolError::last().into())
511        } else {
512            Ok(result)
513        }
514    }
515}
516
517#[repr(C)]
518pub struct FunctionArgs {
519    pub args: *const c_char,
520    pub args_end: *const c_char,
521}
522
523impl Into<Tuple> for FunctionArgs {
524    fn into(self) -> Tuple {
525        let len = (self.args_end as usize) - (self.args as usize);
526        Tuple::from_raw_data(self.args as *mut c_char, len as u32)
527    }
528}
529
530/// Push MessagePack data into a session data channel - socket,
531/// console or whatever is behind the session. Note, that
532/// successful push does not guarantee delivery in case it was sent
533/// into the network. Just like with `write()`/`send()` system calls.
534pub fn session_push<T>(value: &T) -> Result<(), Error>
535where
536    T: AsTuple,
537{
538    let buf = value.serialize_as_tuple().unwrap();
539    let buf_ptr = buf.as_ptr() as *const c_char;
540    if unsafe { ffi::box_session_push(buf_ptr, buf_ptr.offset(buf.len() as isize)) } < 0 {
541        Err(TarantoolError::last().into())
542    } else {
543        Ok(())
544    }
545}
546
547pub(crate) mod ffi {
548    use std::os::raw::{c_char, c_int};
549
550    #[repr(C)]
551    pub struct BoxTuple {
552        _unused: [u8; 0],
553    }
554
555    extern "C" {
556        pub fn box_tuple_new(
557            format: *mut BoxTupleFormat,
558            data: *const c_char,
559            end: *const c_char,
560        ) -> *mut BoxTuple;
561        pub fn box_tuple_ref(tuple: *mut BoxTuple) -> c_int;
562        pub fn box_tuple_unref(tuple: *mut BoxTuple);
563        pub fn box_tuple_field_count(tuple: *const BoxTuple) -> u32;
564        pub fn box_tuple_bsize(tuple: *const BoxTuple) -> usize;
565        pub fn box_tuple_to_buf(tuple: *const BoxTuple, buf: *mut c_char, size: usize) -> isize;
566    }
567
568    #[repr(C)]
569    pub struct BoxTupleFormat {
570        _unused: [u8; 0],
571    }
572
573    extern "C" {
574        pub fn box_tuple_format_default() -> *mut BoxTupleFormat;
575        pub fn box_tuple_format(tuple: *const BoxTuple) -> *mut BoxTupleFormat;
576        pub fn box_tuple_field(tuple: *const BoxTuple, fieldno: u32) -> *const c_char;
577    }
578
579    #[repr(C)]
580    pub struct BoxTupleIterator {
581        _unused: [u8; 0],
582    }
583
584    extern "C" {
585        pub fn box_tuple_iterator(tuple: *mut BoxTuple) -> *mut BoxTupleIterator;
586        pub fn box_tuple_iterator_free(it: *mut BoxTupleIterator);
587        pub fn box_tuple_position(it: *mut BoxTupleIterator) -> u32;
588        pub fn box_tuple_rewind(it: *mut BoxTupleIterator);
589        pub fn box_tuple_seek(it: *mut BoxTupleIterator, fieldno: u32) -> *const c_char;
590        pub fn box_tuple_next(it: *mut BoxTupleIterator) -> *const c_char;
591    }
592
593    #[repr(C)]
594    pub struct BoxKeyDef {
595        _unused: [u8; 0],
596    }
597
598    extern "C" {
599        pub fn box_key_def_new(
600            fields: *mut u32,
601            types: *mut u32,
602            part_count: u32,
603        ) -> *mut BoxKeyDef;
604        pub fn box_key_def_delete(key_def: *mut BoxKeyDef);
605        pub fn box_tuple_compare(
606            tuple_a: *mut BoxTuple,
607            tuple_b: *mut BoxTuple,
608            key_def: *mut BoxKeyDef,
609        ) -> c_int;
610        pub fn box_tuple_compare_with_key(
611            tuple_a: *mut BoxTuple,
612            key_b: *const c_char,
613            key_def: *mut BoxKeyDef,
614        ) -> c_int;
615    }
616
617    #[repr(C)]
618    pub struct BoxFunctionCtx {
619        _unused: [u8; 0],
620    }
621
622    extern "C" {
623        pub fn box_return_tuple(ctx: *mut BoxFunctionCtx, tuple: *mut BoxTuple) -> c_int;
624        pub fn box_return_mp(
625            ctx: *mut BoxFunctionCtx,
626            mp: *const c_char,
627            mp_end: *const c_char,
628        ) -> c_int;
629        pub fn box_session_push(data: *const c_char, data_end: *const c_char) -> c_int;
630    }
631}