tarantool/
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::borrow::Cow;
11use std::collections::BTreeMap;
12use std::mem::MaybeUninit;
13use std::ops::Range;
14use std::ptr::null_mut;
15
16use serde::{Deserialize, Serialize};
17
18use crate::error::{Error, TarantoolError, TarantoolErrorCode};
19use crate::ffi::tarantool as ffi;
20use crate::msgpack;
21use crate::space::{Space, SpaceId, SystemSpace};
22use crate::tuple::{Encode, ToTupleBuffer, Tuple, TupleBuffer};
23use crate::tuple::{KeyDef, KeyDefPart};
24use crate::tuple_from_box_api;
25use crate::unwrap_or;
26use crate::util::NumOrStr;
27use crate::util::Value;
28
29pub type IndexId = u32;
30
31/// An index is a group of key values and pointers.
32#[derive(Clone, Debug, PartialEq, Eq, Hash)]
33pub struct Index {
34    space_id: SpaceId,
35    index_id: IndexId,
36}
37
38/// Controls how to iterate over tuples in an index.
39/// Different index types support different iterator types.
40/// For example, one can start iteration from a particular value
41/// (request key) and then retrieve all tuples where keys are
42/// greater or equal (= `GE`) to this key.
43///
44/// If iterator type is not supported by the selected index type,
45/// iterator constructor must fail with `ER_UNSUPPORTED`. To be
46/// selectable for primary key, an index must support at least
47/// `Eq` and `GE` types.
48///
49/// `None` value of request key corresponds to the first or last
50/// key in the index, depending on iteration direction.
51/// (first key for `GE` and `GT` types, and last key for `LE` and `LT`).
52/// Therefore, to iterate over all tuples in an index, one can
53/// use `GE` or `LE` iteration types with start key equal to `None`.
54/// For `EQ`, the key must not be `None`.
55#[repr(i32)]
56#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
57pub enum IteratorType {
58    /// key == x ASC order
59    Eq = 0,
60
61    /// key == x DESC order
62    Req = 1,
63
64    /// all tuples
65    All = 2,
66
67    /// key <  x
68    LT = 3,
69
70    /// key <= x
71    LE = 4,
72
73    /// key >= x
74    GE = 5,
75
76    /// key >  x
77    GT = 6,
78
79    /// all bits from x are set in key
80    BitsAllSet = 7,
81
82    /// at least one x's bit is set
83    BitsAnySet = 8,
84
85    /// all bits are not set
86    BitsAllNotSet = 9,
87
88    /// key overlaps x
89    Overlaps = 10,
90
91    /// tuples in distance ascending order from specified point
92    Neighbor = 11,
93}
94
95////////////////////////////////////////////////////////////////////////////////
96// Builder
97////////////////////////////////////////////////////////////////////////////////
98
99#[allow(dead_code)]
100pub struct Builder<'a> {
101    space_id: SpaceId,
102    name: &'a str,
103    opts: IndexOptions,
104}
105
106macro_rules! define_setters {
107    ($( $setter:ident ( $field:ident : $ty:ty ) )+) => {
108        $(
109            #[inline(always)]
110            pub fn $setter(mut self, $field: $ty) -> Self {
111                self.opts.$field = Some($field.into());
112                self
113            }
114        )+
115    }
116}
117
118impl<'a> Builder<'a> {
119    /// Creates a new index builder with default options.
120    #[inline(always)]
121    pub fn new(space_id: SpaceId, name: &'a str) -> Self {
122        Self {
123            space_id,
124            name,
125            opts: IndexOptions::default(),
126        }
127    }
128
129    define_setters! {
130        index_type(r#type: IndexType)
131        id(id: SpaceId)
132        unique(unique: bool)
133        if_not_exists(if_not_exists: bool)
134        dimension(dimension: u32)
135        distance(distance: RtreeIndexDistanceType)
136        bloom_fpr(bloom_fpr: f32)
137        page_size(page_size: u32)
138        range_size(range_size: u32)
139        run_count_per_level(run_count_per_level: u32)
140        run_size_ratio(run_size_ratio: f32)
141        sequence(sequence: impl Into<SequenceOpt>)
142        func(func: String)
143    }
144
145    /// Add a part to the index's parts list.
146    ///
147    /// Use this method to set each part individually or use [`parts`] to set
148    /// parts in bulk. The difference is purely syntactical.
149    ///
150    /// [`parts`]: Self::parts
151    #[inline(always)]
152    pub fn part(mut self, part: impl Into<Part>) -> Self {
153        self.opts
154            .parts
155            .get_or_insert_with(|| Vec::with_capacity(8))
156            .push(part.into());
157        self
158    }
159
160    /// Add parts to the index's parts list.
161    ///
162    /// Use this method to set parts in bulk or use [`part`] to set
163    /// each part individually. The difference is purely syntactical.
164    ///
165    /// ```no_run
166    /// use tarantool::{space::Space, index::FieldType as FT};
167    ///
168    /// Space::find("t").unwrap()
169    ///     .index_builder("by_index_and_type")
170    ///     .parts([(0, FT::Unsigned), (1, FT::String)])
171    ///     .create();
172    ///
173    /// Space::find("t").unwrap()
174    ///     .index_builder("by_name")
175    ///     .parts(["foo", "bar", "baz"])
176    ///     .create();
177    /// ```
178    ///
179    /// [`part`]: Self::part
180    #[inline(always)]
181    pub fn parts(mut self, parts: impl IntoIterator<Item = impl Into<Part>>) -> Self {
182        let iter = parts.into_iter();
183        let (size, _) = iter.size_hint();
184        self.opts
185            .parts
186            .get_or_insert_with(|| Vec::with_capacity(size))
187            .extend(iter.map(Into::into));
188        self
189    }
190
191    /// Create a new index using the current options.
192    #[inline(always)]
193    pub fn create(self) -> crate::Result<Index> {
194        crate::schema::index::create_index(self.space_id, self.name, &self.opts)
195    }
196
197    /// Destructure the builder struct into a tuple of space_id, name and index
198    /// options.
199    #[inline(always)]
200    pub fn into_parts(self) -> (u32, &'a str, IndexOptions) {
201        (self.space_id, self.name, self.opts)
202    }
203}
204
205////////////////////////////////////////////////////////////////////////////////
206// IndexOptions
207////////////////////////////////////////////////////////////////////////////////
208
209/// List of options for new or updated index.
210///
211/// For details see [space_object:create_index - options](https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/create_index/).
212#[derive(Clone, Debug, Default, Serialize, tlua::Push, tlua::LuaRead, PartialEq)]
213pub struct IndexOptions {
214    pub r#type: Option<IndexType>,
215    pub id: Option<u32>,
216    pub unique: Option<bool>,
217    pub if_not_exists: Option<bool>,
218    pub parts: Option<Vec<Part>>,
219    pub dimension: Option<u32>,
220    pub distance: Option<RtreeIndexDistanceType>,
221    pub bloom_fpr: Option<f32>,
222    pub page_size: Option<u32>,
223    pub range_size: Option<u32>,
224    pub run_count_per_level: Option<u32>,
225    pub run_size_ratio: Option<f32>,
226    pub sequence: Option<SequenceOpt>,
227    pub func: Option<String>,
228    // Only for Tarantool >= 2.6
229    // pub hint: Option<bool>,
230}
231
232////////////////////////////////////////////////////////////////////////////////
233// SequenceOpt
234////////////////////////////////////////////////////////////////////////////////
235
236#[deprecated = "Use `index::SequenceOpt` instead"]
237/// Use [`SequenceOpt`] instead
238pub type IndexSequenceOption = SequenceOpt;
239
240/// Sequence option for new or updated index.
241///
242/// For details see [specifying a sequence in create_index](https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_schema_sequence/create_index/#box-schema-sequence-create-index).
243#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, tlua::Push, tlua::LuaRead, Hash)]
244pub enum SequenceOpt {
245    IdAndField(SeqSpec),
246    AutoGenerated(bool),
247}
248
249impl SequenceOpt {
250    #[inline(always)]
251    pub fn auto() -> Self {
252        Self::AutoGenerated(true)
253    }
254
255    #[inline(always)]
256    pub fn none() -> Self {
257        Self::AutoGenerated(false)
258    }
259
260    #[inline(always)]
261    pub fn field(field: impl Into<NumOrStr>) -> Self {
262        Self::IdAndField(SeqSpec::field(field))
263    }
264
265    #[inline(always)]
266    pub fn id(id: impl Into<NumOrStr>) -> Self {
267        Self::IdAndField(SeqSpec::id(id))
268    }
269
270    #[inline(always)]
271    pub fn spec(s: SeqSpec) -> Self {
272        Self::IdAndField(s)
273    }
274}
275
276impl From<SeqSpec> for SequenceOpt {
277    #[inline(always)]
278    fn from(s: SeqSpec) -> Self {
279        Self::spec(s)
280    }
281}
282
283impl From<bool> for SequenceOpt {
284    #[inline(always)]
285    fn from(b: bool) -> Self {
286        Self::AutoGenerated(b)
287    }
288}
289
290#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, tlua::Push, tlua::LuaRead, Hash)]
291pub struct SeqSpec {
292    id: Option<NumOrStr>,
293    field: Option<NumOrStr>,
294}
295
296impl SeqSpec {
297    #[inline(always)]
298    pub fn field(field: impl Into<NumOrStr>) -> Self {
299        Self {
300            id: None,
301            field: Some(field.into()),
302        }
303    }
304
305    #[inline(always)]
306    pub fn id(id: impl Into<NumOrStr>) -> Self {
307        Self {
308            id: Some(id.into()),
309            field: None,
310        }
311    }
312
313    #[inline(always)]
314    pub fn and_field(mut self, field: impl Into<NumOrStr>) -> Self {
315        self.field = Some(field.into());
316        self
317    }
318
319    #[inline(always)]
320    pub fn and_id(mut self, id: impl Into<NumOrStr>) -> Self {
321        self.id = Some(id.into());
322        self
323    }
324}
325
326////////////////////////////////////////////////////////////////////////////////
327// IndexType
328////////////////////////////////////////////////////////////////////////////////
329
330crate::define_str_enum! {
331    #![coerce_from_str]
332    /// Type of index.
333    pub enum IndexType {
334        Hash = "hash",
335        Tree = "tree",
336        Bitset = "bitset",
337        Rtree = "rtree",
338    }
339}
340
341impl Default for IndexType {
342    #[inline(always)]
343    fn default() -> Self {
344        Self::Tree
345    }
346}
347
348////////////////////////////////////////////////////////////////////////////////
349// FieldType
350////////////////////////////////////////////////////////////////////////////////
351
352#[deprecated = "use index::FieldType instead"]
353pub type IndexFieldType = FieldType;
354
355crate::define_str_enum! {
356    #![coerce_from_str]
357    /// Type of index part.
358    pub enum FieldType {
359        Unsigned  = "unsigned",
360        String    = "string",
361        Number    = "number",
362        Double    = "double",
363        Integer   = "integer",
364        Boolean   = "boolean",
365        Varbinary = "varbinary",
366        Scalar    = "scalar",
367        Decimal   = "decimal",
368        Uuid      = "uuid",
369        Datetime  = "datetime",
370        Array     = "array",
371    }
372}
373
374////////////////////////////////////////////////////////////////////////////////
375// IndexPart
376////////////////////////////////////////////////////////////////////////////////
377
378#[deprecated = "Use `index::Part` instead"]
379pub type IndexPart = Part;
380
381/// Index part
382///
383/// The `T` generic decides what is used to identify the field. It can be either a [`u32`] for
384/// field index or [`String`] for field name, or [`NumOrStr`] for either.
385///
386/// Field names are used in picodata metadata, indices are used in tarantool's metadata, while
387/// tarantool's index creation API can accept either.
388#[derive(
389    Clone, Default, Debug, Serialize, Deserialize, tlua::Push, tlua::LuaRead, PartialEq, Eq,
390)]
391pub struct Part<T = NumOrStr> {
392    pub field: T,
393    #[serde(default)]
394    pub r#type: Option<FieldType>,
395    #[serde(default)]
396    pub collation: Option<String>,
397    #[serde(default)]
398    pub is_nullable: Option<bool>,
399    #[serde(default)]
400    pub path: Option<String>,
401}
402
403macro_rules! define_setters {
404    ($( $setter:ident ( $field:ident : $ty:ty ) )+) => {
405        $(
406            #[inline(always)]
407            pub fn $setter(mut self, $field: $ty) -> Self {
408                self.$field = Some($field.into());
409                self
410            }
411        )+
412    }
413}
414
415impl<T> Part<T> {
416    #[inline(always)]
417    pub fn field(field: impl Into<T>) -> Self {
418        Self {
419            field: field.into(),
420            r#type: None,
421            collation: None,
422            is_nullable: None,
423            path: None,
424        }
425    }
426
427    define_setters! {
428        field_type(r#type: FieldType)
429        collation(collation: impl Into<String>)
430        is_nullable(is_nullable: bool)
431        path(path: impl Into<String>)
432    }
433
434    #[inline(always)]
435    pub fn new(fi: impl Into<T>, ft: FieldType) -> Self {
436        Self::field(fi).field_type(ft)
437    }
438}
439
440impl From<&str> for Part<String> {
441    #[inline(always)]
442    fn from(f: &str) -> Self {
443        Self::field(f.to_string())
444    }
445}
446
447impl From<String> for Part<String> {
448    #[inline(always)]
449    fn from(f: String) -> Self {
450        Self::field(f)
451    }
452}
453
454impl From<(String, FieldType)> for Part<String> {
455    #[inline(always)]
456    fn from((f, t): (String, FieldType)) -> Self {
457        Self::field(f).field_type(t)
458    }
459}
460
461impl From<(&str, FieldType)> for Part<String> {
462    #[inline(always)]
463    fn from((f, t): (&str, FieldType)) -> Self {
464        Self::field(f.to_string()).field_type(t)
465    }
466}
467
468impl From<&str> for Part {
469    #[inline(always)]
470    fn from(f: &str) -> Self {
471        Self::field(f.to_string())
472    }
473}
474
475impl From<String> for Part {
476    #[inline(always)]
477    fn from(f: String) -> Self {
478        Self::field(f)
479    }
480}
481
482impl From<(String, FieldType)> for Part {
483    #[inline(always)]
484    fn from((f, t): (String, FieldType)) -> Self {
485        Self::field(f).field_type(t)
486    }
487}
488
489impl From<(&str, FieldType)> for Part {
490    #[inline(always)]
491    fn from((f, t): (&str, FieldType)) -> Self {
492        Self::field(f.to_string()).field_type(t)
493    }
494}
495
496impl From<u32> for Part {
497    #[inline(always)]
498    fn from(value: u32) -> Self {
499        Self::field(value)
500    }
501}
502
503impl From<(u32, FieldType)> for Part {
504    #[inline(always)]
505    fn from((f, t): (u32, FieldType)) -> Self {
506        Self::field(f).field_type(t)
507    }
508}
509
510impl From<Part<String>> for Part {
511    #[inline(always)]
512    fn from(value: Part<String>) -> Self {
513        Part {
514            field: value.field.into(),
515            r#type: value.r#type,
516            collation: value.collation,
517            is_nullable: value.is_nullable,
518            path: value.path,
519        }
520    }
521}
522
523impl From<Part<u32>> for Part {
524    #[inline(always)]
525    fn from(value: Part<u32>) -> Self {
526        Part {
527            field: value.field.into(),
528            r#type: value.r#type,
529            collation: value.collation,
530            is_nullable: value.is_nullable,
531            path: value.path,
532        }
533    }
534}
535
536////////////////////////////////////////////////////////////////////////////////
537// ...
538////////////////////////////////////////////////////////////////////////////////
539
540crate::define_str_enum! {
541    #![coerce_from_str]
542    /// Type of distance for retree index.
543    pub enum RtreeIndexDistanceType {
544        Euclid = "euclid",
545        Manhattan = "manhattan",
546    }
547}
548
549impl Index {
550    #[inline(always)]
551    pub(crate) fn new(space_id: SpaceId, index_id: IndexId) -> Self {
552        Index { space_id, index_id }
553    }
554
555    /// Create an `Index` with corresponding space and index `id`s.
556    ///
557    /// # Safety
558    /// `id`s must be valid tarantool space/index id. Only use this function with
559    /// ids acquired from tarantool in some way, e.g. from lua code.
560    #[inline(always)]
561    pub const unsafe fn from_ids_unchecked(space_id: SpaceId, index_id: IndexId) -> Self {
562        Self { space_id, index_id }
563    }
564
565    /// Return id of this index.
566    #[inline(always)]
567    pub fn id(&self) -> u32 {
568        self.index_id
569    }
570
571    /// Return the space id of this index.
572    #[inline(always)]
573    pub fn space_id(&self) -> u32 {
574        self.space_id
575    }
576
577    // Return index metadata from system `_index` space.
578    #[inline]
579    pub fn meta(&self) -> Result<Metadata, Error> {
580        let sys_space: Space = SystemSpace::Index.into();
581        let tuple = sys_space.get(&[self.space_id, self.index_id])?;
582        let Some(tuple) = tuple else {
583            return Err(crate::error::BoxError::new(
584                TarantoolErrorCode::NoSuchIndexID,
585                format!(
586                    "index #{} for space #{} not found",
587                    self.index_id, self.space_id,
588                ),
589            )
590            .into());
591        };
592        tuple.decode::<Metadata>()
593    }
594
595    // Drops index.
596    #[inline(always)]
597    pub fn drop(&self) -> Result<(), Error> {
598        crate::schema::index::drop_index(self.space_id, self.index_id)
599    }
600
601    /// Get a tuple from index by the key.
602    ///
603    /// Please note that this function works much faster than [select](#method.select)
604    ///
605    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
606    ///
607    /// Returns a tuple or `None` if index is empty
608    #[inline]
609    pub fn get<K>(&self, key: &K) -> Result<Option<Tuple>, Error>
610    where
611        K: ToTupleBuffer + ?Sized,
612    {
613        let buf;
614        let data = unwrap_or!(key.tuple_data(), {
615            // TODO: use region allocation for this
616            buf = key.to_tuple_buffer()?;
617            buf.as_ref()
618        });
619        let Range { start, end } = data.as_ptr_range();
620        tuple_from_box_api!(
621            ffi::box_index_get[
622                self.space_id,
623                self.index_id,
624                start as _,
625                end as _,
626                @out
627            ]
628        )
629    }
630
631    /// Allocate and initialize iterator for index.
632    ///
633    /// This is an alternative to [space.select()](../space/struct.Space.html#method.select) which goes via a particular
634    /// index and can make use of additional parameter that specify the iterator type.
635    ///
636    /// - `type` - iterator type
637    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
638    #[inline]
639    pub fn select<K>(&self, iterator_type: IteratorType, key: &K) -> Result<IndexIterator, Error>
640    where
641        K: ToTupleBuffer + ?Sized,
642    {
643        let key_buf = key.to_tuple_buffer().unwrap();
644        let Range { start, end } = key_buf.as_ref().as_ptr_range();
645
646        let ptr = unsafe {
647            ffi::box_index_iterator(
648                self.space_id,
649                self.index_id,
650                iterator_type as _,
651                start as _,
652                end as _,
653            )
654        };
655
656        if ptr.is_null() {
657            return Err(TarantoolError::last().into());
658        }
659
660        Ok(IndexIterator {
661            ptr,
662            _key_data: key_buf,
663        })
664    }
665
666    /// Delete a tuple identified by a key.
667    ///
668    /// Same as [space.delete()](../space/struct.Space.html#method.delete), but a key is searched in this index instead
669    /// of in the primary-key index. This index ought to be unique.
670    ///
671    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
672    ///
673    /// Returns the deleted tuple or `Ok(None)` if tuple was not found.
674    #[inline]
675    pub fn delete<K>(&self, key: &K) -> Result<Option<Tuple>, Error>
676    where
677        K: ToTupleBuffer + ?Sized,
678    {
679        let buf;
680        let data = unwrap_or!(key.tuple_data(), {
681            // TODO: use region allocation for this
682            buf = key.to_tuple_buffer()?;
683            buf.as_ref()
684        });
685        let Range { start, end } = data.as_ptr_range();
686        tuple_from_box_api!(
687            ffi::box_delete[
688                self.space_id,
689                self.index_id,
690                start as _,
691                end as _,
692                @out
693            ]
694        )
695    }
696
697    /// Update a tuple.
698    ///
699    /// Same as [space.update()](../space/struct.Space.html#method.update), but a key is searched in this index instead
700    /// of primary key. This index ought to be unique.
701    ///
702    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
703    /// - `ops` - encoded operations in MsgPack array format, e.g. `[['=', field_id, value], ['!', 2, 'xxx']]`
704    ///
705    /// Returns a new tuple.
706    ///
707    /// See also: [index.upsert()](#method.upsert)
708    // TODO(gmoshkin): accept a single Ops argument instead of a slice of ops
709    #[inline]
710    pub fn update<K, Op>(&self, key: &K, ops: impl AsRef<[Op]>) -> Result<Option<Tuple>, Error>
711    where
712        K: ToTupleBuffer + ?Sized,
713        Op: ToTupleBuffer,
714    {
715        let key_buf;
716        let key_data = unwrap_or!(key.tuple_data(), {
717            // TODO: use region allocation for this
718            key_buf = key.to_tuple_buffer()?;
719            key_buf.as_ref()
720        });
721        let mut ops_buf = Vec::with_capacity(4 + ops.as_ref().len() * 4);
722        msgpack::write_array(&mut ops_buf, ops.as_ref())?;
723        unsafe { self.update_raw(key_data, ops_buf.as_ref()) }
724    }
725
726    /// # Safety
727    /// `ops` must be a slice of valid msgpack arrays.
728    #[deprecated = "use update_raw instead"]
729    pub unsafe fn update_mp<K>(&self, key: &K, ops: &[Vec<u8>]) -> Result<Option<Tuple>, Error>
730    where
731        K: ToTupleBuffer + ?Sized,
732    {
733        let key_buf;
734        let key_data = unwrap_or!(key.tuple_data(), {
735            // TODO: use region allocation for this
736            key_buf = key.to_tuple_buffer()?;
737            key_buf.as_ref()
738        });
739        let mut ops_buf = Vec::with_capacity(128);
740        msgpack::write_array(&mut ops_buf, ops)?;
741        self.update_raw(key_data, ops_buf.as_ref())
742    }
743
744    /// # Safety
745    /// `key` must be a valid msgpack array.
746    /// `ops` must be a valid msgpack array of msgpack arrays.
747    #[inline(always)]
748    pub unsafe fn update_raw(&self, key: &[u8], ops: &[u8]) -> Result<Option<Tuple>, Error> {
749        let key = key.as_ptr_range();
750        let ops = ops.as_ptr_range();
751        tuple_from_box_api!(
752            ffi::box_update[
753                self.space_id,
754                self.index_id,
755                key.start.cast(), key.end.cast(),
756                ops.start.cast(), ops.end.cast(),
757                0,
758                @out
759            ]
760        )
761    }
762
763    /// Execute an UPSERT request.
764    ///
765    /// Will try to insert tuple. Update if already exists.
766    ///
767    /// - `value` - encoded tuple in MsgPack Array format (`[field1, field2, ...]`)
768    /// - `ops` - encoded operations in MsgPack array format, e.g. `[['=', field_id, value], ['!', 2, 'xxx']]`
769    ///
770    /// See also: [index.update()](#method.update)
771    #[inline]
772    pub fn upsert<T, Op>(&self, value: &T, ops: impl AsRef<[Op]>) -> Result<(), Error>
773    where
774        T: ToTupleBuffer + ?Sized,
775        Op: ToTupleBuffer,
776    {
777        let value_buf;
778        let value_data = unwrap_or!(value.tuple_data(), {
779            // TODO: use region allocation for this
780            value_buf = value.to_tuple_buffer()?;
781            value_buf.as_ref()
782        });
783        let mut ops_buf = Vec::with_capacity(4 + ops.as_ref().len() * 4);
784        msgpack::write_array(&mut ops_buf, ops.as_ref())?;
785        unsafe { self.upsert_raw(value_data, ops_buf.as_ref()) }
786    }
787
788    /// # Safety
789    /// `ops` must be a slice of valid msgpack arrays.
790    #[deprecated = "use upsert_raw instead"]
791    pub unsafe fn upsert_mp<T>(&self, value: &T, ops: &[Vec<u8>]) -> Result<(), Error>
792    where
793        T: ToTupleBuffer + ?Sized,
794    {
795        let value_buf;
796        let value_data = unwrap_or!(value.tuple_data(), {
797            // TODO: use region allocation for this
798            value_buf = value.to_tuple_buffer()?;
799            value_buf.as_ref()
800        });
801        let mut ops_buf = Vec::with_capacity(128);
802        msgpack::write_array(&mut ops_buf, ops)?;
803        self.upsert_raw(value_data, ops_buf.as_ref())
804    }
805
806    /// # Safety
807    /// `value` must be a valid msgpack array.
808    /// `ops` must be a valid msgpack array of msgpack arrays.
809    #[inline(always)]
810    pub unsafe fn upsert_raw(&self, value: &[u8], ops: &[u8]) -> Result<(), Error> {
811        let value = value.as_ptr_range();
812        let ops = ops.as_ptr_range();
813        tuple_from_box_api!(
814            ffi::box_upsert[
815                self.space_id,
816                self.index_id,
817                value.start.cast(), value.end.cast(),
818                ops.start.cast(), ops.end.cast(),
819                0,
820                @out
821            ]
822        )
823        .map(|t| {
824            if t.is_some() {
825                unreachable!("Upsert doesn't return a tuple")
826            }
827        })
828    }
829
830    /// Return the number of elements in the index.
831    #[inline(always)]
832    pub fn len(&self) -> Result<usize, Error> {
833        let result = unsafe { ffi::box_index_len(self.space_id, self.index_id) };
834
835        if result < 0 {
836            Err(TarantoolError::last().into())
837        } else {
838            Ok(result as usize)
839        }
840    }
841
842    #[inline(always)]
843    pub fn is_empty(&self) -> Result<bool, Error> {
844        self.len().map(|l| l == 0)
845    }
846
847    /// Return the number of bytes used in memory by the index.
848    #[inline(always)]
849    pub fn bsize(&self) -> Result<usize, Error> {
850        let result = unsafe { ffi::box_index_bsize(self.space_id, self.index_id) };
851
852        if result < 0 {
853            Err(TarantoolError::last().into())
854        } else {
855            Ok(result as usize)
856        }
857    }
858
859    /// Return a random tuple from the index (useful for statistical analysis).
860    ///
861    /// - `rnd` - random seed
862    #[inline(always)]
863    pub fn random(&self, seed: u32) -> Result<Option<Tuple>, Error> {
864        tuple_from_box_api!(
865            ffi::box_index_random[
866                self.space_id,
867                self.index_id,
868                seed,
869                @out
870            ]
871        )
872    }
873
874    /// Return a first (minimal) tuple that matched the provided key.
875    ///
876    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
877    ///
878    /// Returns a tuple or `None` if index is empty
879    #[inline]
880    pub fn min<K>(&self, key: &K) -> Result<Option<Tuple>, Error>
881    where
882        K: ToTupleBuffer + ?Sized,
883    {
884        let buf;
885        let data = unwrap_or!(key.tuple_data(), {
886            // TODO: use region allocation for this
887            buf = key.to_tuple_buffer()?;
888            buf.as_ref()
889        });
890        let Range { start, end } = data.as_ptr_range();
891        tuple_from_box_api!(
892            ffi::box_index_min[
893                self.space_id,
894                self.index_id,
895                start as _,
896                end as _,
897                @out
898            ]
899        )
900    }
901
902    /// Return a last (maximal) tuple that matched the provided key.
903    ///
904    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
905    ///
906    /// Returns a tuple or `None` if index is empty
907    #[inline]
908    pub fn max<K>(&self, key: &K) -> Result<Option<Tuple>, Error>
909    where
910        K: ToTupleBuffer + ?Sized,
911    {
912        let buf;
913        let data = unwrap_or!(key.tuple_data(), {
914            // TODO: use region allocation for this
915            buf = key.to_tuple_buffer()?;
916            buf.as_ref()
917        });
918        let Range { start, end } = data.as_ptr_range();
919        tuple_from_box_api!(
920            ffi::box_index_max[
921                self.space_id,
922                self.index_id,
923                start as _,
924                end as _,
925                @out
926            ]
927        )
928    }
929
930    /// Count the number of tuples that matched the provided key.
931    ///
932    /// - `type` - iterator type
933    /// - `key` - encoded key in MsgPack Array format (`[part1, part2, ...]`).
934    #[inline]
935    pub fn count<K>(&self, iterator_type: IteratorType, key: &K) -> Result<usize, Error>
936    where
937        K: ToTupleBuffer + ?Sized,
938    {
939        let buf;
940        let data = unwrap_or!(key.tuple_data(), {
941            // TODO: use region allocation for this
942            buf = key.to_tuple_buffer()?;
943            buf.as_ref()
944        });
945        let Range { start, end } = data.as_ptr_range();
946        let result = unsafe {
947            ffi::box_index_count(
948                self.space_id,
949                self.index_id,
950                iterator_type as _,
951                start as _,
952                end as _,
953            )
954        };
955
956        if result < 0 {
957            Err(TarantoolError::last().into())
958        } else {
959            Ok(result as usize)
960        }
961    }
962
963    /// Extract key from `tuple` according to key definition of given
964    /// index.
965    ///
966    /// # Safety
967    /// The current index & it's space must exist and `tuple` must conform to
968    /// the space format.
969    ///
970    /// You should probably use [`KeyDef::extract_key`] instead.
971    #[deprecated = "use KeyDef::extract_key instead"]
972    #[inline(always)]
973    pub unsafe fn extract_key(&self, tuple: Tuple) -> Tuple {
974        unsafe {
975            let mut result_size = MaybeUninit::uninit();
976            let result_ptr = ffi::box_tuple_extract_key(
977                tuple.as_ptr(),
978                self.space_id,
979                self.index_id,
980                result_size.as_mut_ptr(),
981            );
982            Tuple::from_raw_data(result_ptr, result_size.assume_init())
983        }
984    }
985}
986
987////////////////////////////////////////////////////////////////////////////////
988// Metadata
989////////////////////////////////////////////////////////////////////////////////
990
991/// Representation of a tuple holding index metadata in system `_index` space.
992#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq)]
993pub struct Metadata<'a> {
994    pub space_id: SpaceId,
995    pub index_id: IndexId,
996    pub name: Cow<'a, str>,
997    pub r#type: IndexType,
998    pub opts: BTreeMap<Cow<'a, str>, Value<'a>>,
999    pub parts: Vec<Part<u32>>,
1000}
1001impl Encode for Metadata<'_> {}
1002
1003impl Metadata<'_> {
1004    /// Construct a [`KeyDef`] instance from index parts.
1005    #[inline(always)]
1006    pub fn to_key_def(&self) -> KeyDef {
1007        // TODO: we could optimize by caching these things and only recreating
1008        // then once box_schema_version changes.
1009        let mut kd_parts = Vec::with_capacity(self.parts.len());
1010        for p in &self.parts {
1011            kd_parts.push(KeyDefPart::from_index_part(p));
1012        }
1013        KeyDef::new(&kd_parts).unwrap()
1014    }
1015
1016    /// Construct a [`KeyDef`] instance from index parts for comparing keys only.
1017    ///
1018    /// The difference between this function and [`Self::to_key_def`] is that
1019    /// the latter is used to compare tuples of a space, while the former is
1020    /// used to compare only the keys.
1021    #[inline]
1022    pub fn to_key_def_for_key(&self) -> KeyDef {
1023        let mut kd_parts = Vec::with_capacity(self.parts.len());
1024        for (p, i) in self.parts.iter().zip(0..) {
1025            let collation = p.collation.as_deref().map(|s| {
1026                std::ffi::CString::new(s)
1027                    .expect("it's your fault if you put '\0' in collation")
1028                    .into()
1029            });
1030            let kd_p = KeyDefPart {
1031                // `p.field_no` is the location of the key part in the original tuple,
1032                // but here we only care about the location of the part in the key itself
1033                field_no: i,
1034                field_type: p.r#type.map(From::from).unwrap_or_default(),
1035                collation,
1036                is_nullable: p.is_nullable.unwrap_or(false),
1037                // `p.path` describes the location of the key part in the original tuple,
1038                // but in the key the part will be placed at the top level,
1039                // hence path is always empty
1040                path: None,
1041            };
1042            kd_parts.push(kd_p);
1043        }
1044        KeyDef::new(&kd_parts).unwrap()
1045    }
1046}
1047
1048////////////////////////////////////////////////////////////////////////////////
1049// IndexIterator
1050////////////////////////////////////////////////////////////////////////////////
1051
1052/// Index iterator. Can be used with `for` statement.
1053pub struct IndexIterator {
1054    ptr: *mut ffi::BoxIterator,
1055    _key_data: TupleBuffer,
1056}
1057
1058impl Iterator for IndexIterator {
1059    type Item = Tuple;
1060
1061    #[inline(always)]
1062    fn next(&mut self) -> Option<Self::Item> {
1063        let mut result_ptr = null_mut();
1064        if unsafe { ffi::box_iterator_next(self.ptr, &mut result_ptr) } < 0 {
1065            return None;
1066        }
1067        Tuple::try_from_ptr(result_ptr)
1068    }
1069}
1070
1071impl Drop for IndexIterator {
1072    #[inline(always)]
1073    fn drop(&mut self) {
1074        unsafe { ffi::box_iterator_free(self.ptr) };
1075    }
1076}
1077
1078#[cfg(feature = "internal_test")]
1079mod tests {
1080    use super::*;
1081    use crate::space;
1082
1083    #[crate::test(tarantool = "crate")]
1084    fn index_metadata() {
1085        let space = Space::builder("test_index_metadata_space")
1086            .field(("id", space::FieldType::Unsigned))
1087            .field(("s", space::FieldType::String))
1088            .field(("map", space::FieldType::Map))
1089            .create()
1090            .unwrap();
1091
1092        let index = space
1093            .index_builder("pk")
1094            .index_type(IndexType::Hash)
1095            .create()
1096            .unwrap();
1097        let meta = index.meta().unwrap();
1098        assert_eq!(
1099            meta,
1100            Metadata {
1101                space_id: space.id(),
1102                index_id: 0,
1103                name: "pk".into(),
1104                r#type: IndexType::Hash,
1105                opts: BTreeMap::from([("unique".into(), Value::from(true)),]),
1106                parts: vec![Part {
1107                    field: 0,
1108                    r#type: Some(FieldType::Unsigned),
1109                    ..Default::default()
1110                }],
1111            }
1112        );
1113
1114        let index = space
1115            .index_builder("i")
1116            .unique(false)
1117            .index_type(IndexType::Tree)
1118            .part(("s", FieldType::String))
1119            .part(Part {
1120                field: NumOrStr::Str("map.key".into()),
1121                r#type: Some(FieldType::Unsigned),
1122                is_nullable: Some(true),
1123                ..Default::default()
1124            })
1125            .part(("map.value[1]", FieldType::String))
1126            .create()
1127            .unwrap();
1128        let meta = index.meta().unwrap();
1129        assert_eq!(
1130            meta,
1131            Metadata {
1132                space_id: space.id(),
1133                index_id: 1,
1134                name: "i".into(),
1135                r#type: IndexType::Tree,
1136                opts: BTreeMap::from([("unique".into(), Value::from(false)),]),
1137                parts: vec![
1138                    Part {
1139                        field: 1,
1140                        r#type: Some(FieldType::String),
1141                        ..Default::default()
1142                    },
1143                    Part {
1144                        field: 2,
1145                        r#type: Some(FieldType::Unsigned),
1146                        is_nullable: Some(true),
1147                        path: Some(".key".into()),
1148                        ..Default::default()
1149                    },
1150                    Part {
1151                        field: 2,
1152                        r#type: Some(FieldType::String),
1153                        path: Some(".value[1]".into()),
1154                        ..Default::default()
1155                    },
1156                ],
1157            }
1158        );
1159
1160        space.drop().unwrap();
1161    }
1162
1163    #[crate::test(tarantool = "crate")]
1164    fn key_def_for_key() {
1165        let space = Space::builder("test_key_def_for_keys_space")
1166            .field(("id", space::FieldType::Unsigned))
1167            .field(("s", space::FieldType::String))
1168            .field(("map", space::FieldType::Map))
1169            .create()
1170            .unwrap();
1171
1172        space.index_builder("pk").create().unwrap();
1173
1174        let index = space
1175            .index_builder("i")
1176            .unique(false)
1177            .part(("map.arr[1]", FieldType::String))
1178            .part(("map.val", FieldType::Unsigned))
1179            .part(("s", FieldType::String))
1180            .part(("id", FieldType::Unsigned))
1181            .create()
1182            .unwrap();
1183        let key_def = index.meta().unwrap().to_key_def_for_key();
1184
1185        assert!(key_def
1186            .compare_with_key(
1187                &Tuple::new(&("foo", 13, "bar", 37)).unwrap(),
1188                &("foo", 13, "bar", 37),
1189            )
1190            .is_eq());
1191
1192        assert!(key_def
1193            .compare_with_key(
1194                &Tuple::new(&("foo", 13, "bar", 37)).unwrap(),
1195                &("foo", 14, "bar", 37),
1196            )
1197            .is_lt());
1198
1199        assert!(key_def
1200            .compare_with_key(
1201                &Tuple::new(&("foo", 13, "baz", 37)).unwrap(),
1202                &("foo", 13, "bar", 37),
1203            )
1204            .is_gt());
1205
1206        space.drop().unwrap();
1207    }
1208
1209    #[crate::test(tarantool = "crate")]
1210    fn sys_index_metadata() {
1211        let sys_index = Space::from(SystemSpace::Index);
1212        for tuple in sys_index.select(IteratorType::All, &()).unwrap() {
1213            // Check index metadata is deserializable from what is actually in _index
1214            let _meta: Metadata = tuple.decode().unwrap();
1215        }
1216    }
1217}