1use 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#[derive(Clone, Debug, PartialEq, Eq, Hash)]
33pub struct Index {
34 space_id: SpaceId,
35 index_id: IndexId,
36}
37
38#[repr(i32)]
56#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
57pub enum IteratorType {
58 Eq = 0,
60
61 Req = 1,
63
64 All = 2,
66
67 LT = 3,
69
70 LE = 4,
72
73 GE = 5,
75
76 GT = 6,
78
79 BitsAllSet = 7,
81
82 BitsAnySet = 8,
84
85 BitsAllNotSet = 9,
87
88 Overlaps = 10,
90
91 Neighbor = 11,
93}
94
95#[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 #[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 #[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 #[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 #[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 #[inline(always)]
200 pub fn into_parts(self) -> (u32, &'a str, IndexOptions) {
201 (self.space_id, self.name, self.opts)
202 }
203}
204
205#[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 }
231
232#[deprecated = "Use `index::SequenceOpt` instead"]
237pub type IndexSequenceOption = SequenceOpt;
239
240#[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
326crate::define_str_enum! {
331 #![coerce_from_str]
332 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#[deprecated = "use index::FieldType instead"]
353pub type IndexFieldType = FieldType;
354
355crate::define_str_enum! {
356 #![coerce_from_str]
357 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#[deprecated = "Use `index::Part` instead"]
379pub type IndexPart = Part;
380
381#[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
536crate::define_str_enum! {
541 #![coerce_from_str]
542 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 #[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 #[inline(always)]
567 pub fn id(&self) -> u32 {
568 self.index_id
569 }
570
571 #[inline(always)]
573 pub fn space_id(&self) -> u32 {
574 self.space_id
575 }
576
577 #[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 #[inline(always)]
597 pub fn drop(&self) -> Result<(), Error> {
598 crate::schema::index::drop_index(self.space_id, self.index_id)
599 }
600
601 #[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 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 #[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 #[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 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 #[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 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 #[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 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 #[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 #[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 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 #[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 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 #[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 #[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 #[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 #[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 #[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 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 #[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 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 #[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 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 #[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#[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 #[inline(always)]
1006 pub fn to_key_def(&self) -> KeyDef {
1007 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 #[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 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 path: None,
1041 };
1042 kd_parts.push(kd_p);
1043 }
1044 KeyDef::new(&kd_parts).unwrap()
1045 }
1046}
1047
1048pub 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 let _meta: Metadata = tuple.decode().unwrap();
1215 }
1216 }
1217}