From 2e04ac7cfe987ffc32882387e29bdd469bf27df6 Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Wed, 20 Oct 2021 07:31:57 +0200 Subject: [PATCH 01/42] Impl `range_de` and `prefix_range_de` for indexes --- packages/storage-plus/src/indexes.rs | 124 +++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 70def6795..86dd0ff06 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -6,7 +6,9 @@ use serde::{Deserialize, Serialize}; use cosmwasm_std::{from_slice, Binary, Order, Record, StdError, StdResult, Storage}; +use crate::de::KeyDeserialize; use crate::helpers::namespaces_with_key; +use crate::iter_helpers::deserialize_kv; use crate::map::Map; use crate::prefix::{namespaced_prefix_range, PrefixBound}; use crate::{Bound, Prefix, Prefixer, PrimaryKey, U32Key}; @@ -248,6 +250,67 @@ where } } +#[cfg(feature = "iterator")] +impl<'a, K, T> MultiIndex<'a, K, T> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a> + KeyDeserialize, +{ + /// while range_de assumes you set the prefix to one element and call range over the last one, + /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to + /// accept, and iterates over those. There are some issues that distinguish these to and blindly + /// casting to Vec doesn't solve them. + pub fn prefix_range_de<'c>( + &self, + store: &'c dyn Storage, + min: Option>, + max: Option>, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + 'a: 'c, + K: 'c, + K::Output: 'static, + { + let mapped = namespaced_prefix_range(store, self.idx_namespace, min, max, order) + .map(deserialize_kv::); + Box::new(mapped) + } + + pub fn range_de<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + K::Output: 'static, + { + self.no_prefix_de().range_de(store, min, max, order) + } + + pub fn keys_de<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + K::Output: 'static, + { + self.no_prefix_de().keys_de(store, min, max, order) + } + + fn no_prefix_de(&self) -> Prefix { + Prefix::new(self.idx_namespace, &[]) + } +} + /// UniqueRef stores Binary(Vec[u8]) representation of private key and index value #[derive(Deserialize, Serialize)] pub(crate) struct UniqueRef { @@ -394,3 +457,64 @@ where self.no_prefix().keys(store, min, max, order) } } + +#[cfg(feature = "iterator")] +impl<'a, K, T> UniqueIndex<'a, K, T> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a> + KeyDeserialize, +{ + /// while range_de assumes you set the prefix to one element and call range over the last one, + /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to + /// accept, and iterates over those. There are some issues that distinguish these to and blindly + /// casting to Vec doesn't solve them. + pub fn prefix_range_de<'c>( + &self, + store: &'c dyn Storage, + min: Option>, + max: Option>, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + 'a: 'c, + K: 'c, + K::Output: 'static, + { + let mapped = namespaced_prefix_range(store, self.idx_namespace, min, max, order) + .map(deserialize_kv::); + Box::new(mapped) + } + + pub fn range_de<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + K::Output: 'static, + { + self.no_prefix_de().range_de(store, min, max, order) + } + + pub fn keys_de<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + K::Output: 'static, + { + self.no_prefix_de().keys_de(store, min, max, order) + } + + fn no_prefix_de(&self) -> Prefix { + Prefix::new(self.idx_namespace, &[]) + } +} From ef55884876da30fd8872d34245b10d4cf54e8d0e Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Wed, 20 Oct 2021 08:13:34 +0200 Subject: [PATCH 02/42] Test for UniqueIndex::range_de --- packages/storage-plus/src/indexed_map.rs | 35 ++++++++++++++++++++++++ packages/storage-plus/src/indexes.rs | 14 ++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index bafa5f71f..47de45200 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -260,6 +260,8 @@ where #[cfg(test)] mod test { + use std::convert::TryInto; + use super::*; use crate::indexes::{index_string_tuple, index_triple, MultiIndex, UniqueIndex}; @@ -764,6 +766,39 @@ mod test { assert_eq!(datas[0], ages[3].1); } + #[test] + fn unique_index_simple_key_range_de() { + let mut store = MockStorage::new(); + let map = build_map(); + + // save data + let (pks, datas) = save_data(&mut store, &map); + + let res: StdResult> = map + .idx + .age + .range_de(&store, None, None, Order::Ascending) + .collect(); + let ages = res.unwrap(); + + let count = ages.len(); + assert_eq!(5, count); + + // The pks, sorted by age ascending + assert_eq!(u32::from_le_bytes(pks[4].try_into().unwrap()), ages[4].0); + assert_eq!(u32::from_le_bytes(pks[3].try_into().unwrap()), ages[0].0); + assert_eq!(u32::from_le_bytes(pks[1].try_into().unwrap()), ages[1].0); + assert_eq!(u32::from_le_bytes(pks[2].try_into().unwrap()), ages[2].0); + assert_eq!(u32::from_le_bytes(pks[0].try_into().unwrap()), ages[3].0); + + // The associated data + assert_eq!(datas[4], ages[4].1); + assert_eq!(datas[3], ages[0].1); + assert_eq!(datas[1], ages[1].1); + assert_eq!(datas[2], ages[2].1); + assert_eq!(datas[0], ages[3].1); + } + #[test] fn unique_index_composite_key_range() { let mut store = MockStorage::new(); diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 86dd0ff06..80a96730d 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -386,7 +386,15 @@ where fn deserialize_unique_kv(kv: Record) -> StdResult> { let (_, v) = kv; let t = from_slice::>(&v)?; - Ok((t.pk.into(), t.value)) + Ok((t.pk.to_vec(), t.value)) +} + +fn deserialize_unique_kv_de( + kv: Record, +) -> StdResult<(K::Output, T)> { + let (_, v) = kv; + let t = from_slice::>(&v)?; + Ok((K::from_vec(t.pk.to_vec())?, t.value)) } impl<'a, K, T> UniqueIndex<'a, K, T> @@ -515,6 +523,8 @@ where } fn no_prefix_de(&self) -> Prefix { - Prefix::new(self.idx_namespace, &[]) + Prefix::with_deserialization_function(self.idx_namespace, &[], &[], |_, _, kv| { + deserialize_unique_kv_de::<_, K>(kv) + }) } } From fe57705d3f68111331c45f3bc5b23fec033f3f7e Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Wed, 20 Oct 2021 10:40:39 +0200 Subject: [PATCH 03/42] rename deserialize_unique_* fns for clarity --- packages/storage-plus/src/indexes.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 80a96730d..647b51cbd 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -383,13 +383,13 @@ where } } -fn deserialize_unique_kv(kv: Record) -> StdResult> { +fn deserialize_unique_v(kv: Record) -> StdResult> { let (_, v) = kv; let t = from_slice::>(&v)?; Ok((t.pk.to_vec(), t.value)) } -fn deserialize_unique_kv_de( +fn deserialize_unique_kv( kv: Record, ) -> StdResult<(K::Output, T)> { let (_, v) = kv; @@ -408,19 +408,19 @@ where pub fn prefix(&self, p: K::Prefix) -> Prefix, T> { Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { - deserialize_unique_kv(kv) + deserialize_unique_v(kv) }) } pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix, T> { Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { - deserialize_unique_kv(kv) + deserialize_unique_v(kv) }) } fn no_prefix(&self) -> Prefix, T> { Prefix::with_deserialization_function(self.idx_namespace, &[], &[], |_, _, kv| { - deserialize_unique_kv(kv) + deserialize_unique_v(kv) }) } @@ -524,7 +524,7 @@ where fn no_prefix_de(&self) -> Prefix { Prefix::with_deserialization_function(self.idx_namespace, &[], &[], |_, _, kv| { - deserialize_unique_kv_de::<_, K>(kv) + deserialize_unique_kv::<_, K>(kv) }) } } From e4482eed4b5edd1a2a7672cafb065a2e9c8291ea Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Wed, 20 Oct 2021 22:09:21 +0200 Subject: [PATCH 04/42] Fix UniqueIndex::range_de --- packages/storage-plus/src/indexed_map.rs | 14 ++++----- packages/storage-plus/src/indexes.rs | 40 ++++++++++++++---------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 47de45200..26531c2ea 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -260,8 +260,6 @@ where #[cfg(test)] mod test { - use std::convert::TryInto; - use super::*; use crate::indexes::{index_string_tuple, index_triple, MultiIndex, UniqueIndex}; @@ -280,7 +278,7 @@ mod test { struct DataIndexes<'a> { // Second arg is for storing pk pub name: MultiIndex<'a, (Vec, Vec), Data>, - pub age: UniqueIndex<'a, U32Key, Data>, + pub age: UniqueIndex<'a, U32Key, Data, Vec>, pub name_lastname: UniqueIndex<'a, (Vec, Vec), Data>, } @@ -785,11 +783,11 @@ mod test { assert_eq!(5, count); // The pks, sorted by age ascending - assert_eq!(u32::from_le_bytes(pks[4].try_into().unwrap()), ages[4].0); - assert_eq!(u32::from_le_bytes(pks[3].try_into().unwrap()), ages[0].0); - assert_eq!(u32::from_le_bytes(pks[1].try_into().unwrap()), ages[1].0); - assert_eq!(u32::from_le_bytes(pks[2].try_into().unwrap()), ages[2].0); - assert_eq!(u32::from_le_bytes(pks[0].try_into().unwrap()), ages[3].0); + assert_eq!(pks[4], ages[4].0); + assert_eq!(pks[3], ages[0].0); + assert_eq!(pks[1], ages[1].0); + assert_eq!(pks[2], ages[2].0); + assert_eq!(pks[0], ages[3].0); // The associated data assert_eq!(datas[4], ages[4].1); diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 647b51cbd..1f00034d2 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -1,6 +1,8 @@ // this module requires iterator to be useful at all #![cfg(feature = "iterator")] +use std::marker::PhantomData; + use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; @@ -321,13 +323,14 @@ pub(crate) struct UniqueRef { /// UniqueIndex stores (namespace, index_name, idx_value) -> {key, value} /// Allows one value per index (i.e. unique) and copies pk and data -pub struct UniqueIndex<'a, K, T> { +pub struct UniqueIndex<'a, K, T, PK = ()> { index: fn(&T) -> K, idx_map: Map<'a, K, UniqueRef>, idx_namespace: &'a [u8], + _phantom: PhantomData, } -impl<'a, K, T> UniqueIndex<'a, K, T> { +impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> { // TODO: make this a const fn /// Create a new UniqueIndex /// @@ -344,18 +347,19 @@ impl<'a, K, T> UniqueIndex<'a, K, T> { /// pub age: u32, /// } /// - /// UniqueIndex::new(|d: &Data| U32Key::new(d.age), "data__age"); + /// UniqueIndex::<_, _, ()>::new(|d: &Data| U32Key::new(d.age), "data__age"); /// ``` pub fn new(idx_fn: fn(&T) -> K, idx_namespace: &'a str) -> Self { UniqueIndex { index: idx_fn, idx_map: Map::new(idx_namespace), idx_namespace: idx_namespace.as_bytes(), + _phantom: PhantomData, } } } -impl<'a, K, T> Index for UniqueIndex<'a, K, T> +impl<'a, K, T, PK> Index for UniqueIndex<'a, K, T, PK> where T: Serialize + DeserializeOwned + Clone, K: PrimaryKey<'a>, @@ -397,7 +401,7 @@ fn deserialize_unique_kv( Ok((K::from_vec(t.pk.to_vec())?, t.value)) } -impl<'a, K, T> UniqueIndex<'a, K, T> +impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> where T: Serialize + DeserializeOwned + Clone, K: PrimaryKey<'a>, @@ -435,7 +439,7 @@ where } // short-cut for simple keys, rather than .prefix(()).range(...) -impl<'a, K, T> UniqueIndex<'a, K, T> +impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> where T: Serialize + DeserializeOwned + Clone, K: PrimaryKey<'a>, @@ -467,10 +471,11 @@ where } #[cfg(feature = "iterator")] -impl<'a, K, T> UniqueIndex<'a, K, T> +impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> where + PK: KeyDeserialize, T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a> + KeyDeserialize, + K: PrimaryKey<'a>, { /// while range_de assumes you set the prefix to one element and call range over the last one, /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to @@ -482,15 +487,16 @@ where min: Option>, max: Option>, order: cosmwasm_std::Order, - ) -> Box> + 'c> + ) -> Box> + 'c> where T: 'c, 'a: 'c, K: 'c, - K::Output: 'static, + PK: 'c, + PK::Output: 'static, { let mapped = namespaced_prefix_range(store, self.idx_namespace, min, max, order) - .map(deserialize_kv::); + .map(deserialize_kv::); Box::new(mapped) } @@ -500,10 +506,10 @@ where min: Option, max: Option, order: cosmwasm_std::Order, - ) -> Box> + 'c> + ) -> Box> + 'c> where T: 'c, - K::Output: 'static, + PK::Output: 'static, { self.no_prefix_de().range_de(store, min, max, order) } @@ -514,17 +520,17 @@ where min: Option, max: Option, order: cosmwasm_std::Order, - ) -> Box> + 'c> + ) -> Box> + 'c> where T: 'c, - K::Output: 'static, + PK::Output: 'static, { self.no_prefix_de().keys_de(store, min, max, order) } - fn no_prefix_de(&self) -> Prefix { + fn no_prefix_de(&self) -> Prefix { Prefix::with_deserialization_function(self.idx_namespace, &[], &[], |_, _, kv| { - deserialize_unique_kv::<_, K>(kv) + deserialize_unique_kv::<_, PK>(kv) }) } } From c33c6a09634de4d01db956513f5fe44c8a3ac56c Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Wed, 20 Oct 2021 22:53:52 +0200 Subject: [PATCH 05/42] test IndexedMap range_de with composite keys --- packages/storage-plus/src/indexed_map.rs | 33 ++++++++++++++++++++++-- packages/storage-plus/src/indexes.rs | 6 ++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 26531c2ea..d033c5746 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -278,8 +278,8 @@ mod test { struct DataIndexes<'a> { // Second arg is for storing pk pub name: MultiIndex<'a, (Vec, Vec), Data>, - pub age: UniqueIndex<'a, U32Key, Data, Vec>, - pub name_lastname: UniqueIndex<'a, (Vec, Vec), Data>, + pub age: UniqueIndex<'a, U32Key, Data, String>, + pub name_lastname: UniqueIndex<'a, (Vec, Vec), Data, String>, } // Future Note: this can likely be macro-derived @@ -826,6 +826,35 @@ mod test { assert_eq!(datas[1], marias[1].1); } + #[test] + fn unique_index_composite_key_range_de() { + let mut store = MockStorage::new(); + let map = build_map(); + + // save data + let (pks, datas) = save_data(&mut store, &map); + + let res: StdResult> = map + .idx + .name_lastname + .prefix(b"Maria".to_vec()) + .range_de(&store, None, None, Order::Ascending) + .collect(); + let marias = res.unwrap(); + + // Only two people are called "Maria" + let count = marias.len(); + assert_eq!(2, count); + + // The pks + assert_eq!(pks[0], marias[0].0); + assert_eq!(pks[1], marias[1].0); + + // The associated data + assert_eq!(datas[0], marias[0].1); + assert_eq!(datas[1], marias[1].1); + } + #[test] #[cfg(feature = "iterator")] fn range_de_simple_string_key() { diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 1f00034d2..4b470b3a5 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -473,7 +473,7 @@ where #[cfg(feature = "iterator")] impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> where - PK: KeyDeserialize, + PK: PrimaryKey<'a> + KeyDeserialize, T: Serialize + DeserializeOwned + Clone, K: PrimaryKey<'a>, { @@ -484,8 +484,8 @@ where pub fn prefix_range_de<'c>( &self, store: &'c dyn Storage, - min: Option>, - max: Option>, + min: Option>, + max: Option>, order: cosmwasm_std::Order, ) -> Box> + 'c> where From b6c087927434eb23612ec5939f945f19cfb9c2cd Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 26 Oct 2021 15:43:25 +0200 Subject: [PATCH 06/42] Add missing prefix_de / sub_prefix_de to UniqueIndex --- packages/storage-plus/src/indexes.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 4b470b3a5..a33e6696c 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -528,6 +528,18 @@ where self.no_prefix_de().keys_de(store, min, max, order) } + pub fn prefix_de(&self, p: K::Prefix) -> Prefix { + Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { + deserialize_unique_kv::<_, PK>(kv) + }) + } + + pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { + Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { + deserialize_unique_kv::<_, PK>(kv) + }) + } + fn no_prefix_de(&self) -> Prefix { Prefix::with_deserialization_function(self.idx_namespace, &[], &[], |_, _, kv| { deserialize_unique_kv::<_, PK>(kv) From e959889c6e9adb40413a655076301f799b7f41a0 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 26 Oct 2021 15:44:13 +0200 Subject: [PATCH 07/42] Fix UniqueIndex prefix + range deserialization test --- packages/storage-plus/src/indexed_map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index d033c5746..53d23c0cc 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -837,7 +837,7 @@ mod test { let res: StdResult> = map .idx .name_lastname - .prefix(b"Maria".to_vec()) + .prefix_de(b"Maria".to_vec()) .range_de(&store, None, None, Order::Ascending) .collect(); let marias = res.unwrap(); From b5060f2441a865da27455da45013057d29c7a162 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 26 Oct 2021 16:46:35 +0200 Subject: [PATCH 08/42] Add clarifying comment --- packages/storage-plus/src/indexes.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index a33e6696c..8e578daa5 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -323,6 +323,7 @@ pub(crate) struct UniqueRef { /// UniqueIndex stores (namespace, index_name, idx_value) -> {key, value} /// Allows one value per index (i.e. unique) and copies pk and data +/// The optional PK type defines the type of Primary Key deserialization. pub struct UniqueIndex<'a, K, T, PK = ()> { index: fn(&T) -> K, idx_map: Map<'a, K, UniqueRef>, From 46f884cc9166664aa0776c649fbd0e625f3ca020 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 26 Oct 2021 16:50:32 +0200 Subject: [PATCH 09/42] Invert deserialize unique index fn args for consistency --- packages/storage-plus/src/indexes.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 8e578daa5..3c39f9467 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -394,7 +394,7 @@ fn deserialize_unique_v(kv: Record) -> StdResult> Ok((t.pk.to_vec(), t.value)) } -fn deserialize_unique_kv( +fn deserialize_unique_kv( kv: Record, ) -> StdResult<(K::Output, T)> { let (_, v) = kv; @@ -531,19 +531,19 @@ where pub fn prefix_de(&self, p: K::Prefix) -> Prefix { Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { - deserialize_unique_kv::<_, PK>(kv) + deserialize_unique_kv::(kv) }) } pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { - deserialize_unique_kv::<_, PK>(kv) + deserialize_unique_kv::(kv) }) } fn no_prefix_de(&self) -> Prefix { Prefix::with_deserialization_function(self.idx_namespace, &[], &[], |_, _, kv| { - deserialize_unique_kv::<_, PK>(kv) + deserialize_unique_kv::(kv) }) } } From 524864f454074c5c592cd3cde7ecfa2b2854523b Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 26 Oct 2021 17:03:53 +0200 Subject: [PATCH 10/42] Add prefix_de / sub_prefix_de to MultiIndex --- packages/storage-plus/src/indexed_map.rs | 56 ++++++++++++++++++++ packages/storage-plus/src/indexes.rs | 66 +++++++++++++++++++++--- 2 files changed, 116 insertions(+), 6 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 53d23c0cc..fa0408c20 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -559,6 +559,62 @@ mod test { assert_eq!(marias[1].1, data1); } + #[test] + fn range_de_simple_key_by_multi_index() { + let mut store = MockStorage::new(); + let map = build_map(); + + // save data + let data1 = Data { + name: "Maria".to_string(), + last_name: "".to_string(), + age: 42, + }; + let pk = "5627"; + map.save(&mut store, pk, &data1).unwrap(); + + let data2 = Data { + name: "Juan".to_string(), + last_name: "Perez".to_string(), + age: 13, + }; + let pk = "5628"; + map.save(&mut store, pk, &data2).unwrap(); + + let data3 = Data { + name: "Maria".to_string(), + last_name: "Williams".to_string(), + age: 24, + }; + let pk = "5629"; + map.save(&mut store, pk, &data3).unwrap(); + + let data4 = Data { + name: "Maria Luisa".to_string(), + last_name: "Bemberg".to_string(), + age: 12, + }; + let pk = "5630"; + map.save(&mut store, pk, &data4).unwrap(); + + let marias: Vec<_> = map + .idx + .name + .prefix_de(b"Maria".to_vec()) + .range_de(&store, None, None, Order::Descending) + .collect::>() + .unwrap(); + let count = marias.len(); + assert_eq!(2, count); + + // Sorted by (descending) pk + assert_eq!(marias[0].0, b"5629"); + assert_eq!(marias[1].0, b"5627"); + // Data is correct + assert_eq!(marias[0].1, data3); + assert_eq!(marias[1].1, data1); + } + #[test] fn range_composite_key_by_multi_index() { let mut store = MockStorage::new(); diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 3c39f9467..1a07353f9 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -102,7 +102,7 @@ where } } -fn deserialize_multi_kv( +fn deserialize_multi_v( store: &dyn Storage, pk_namespace: &[u8], kv: Record, @@ -126,6 +126,30 @@ fn deserialize_multi_kv( Ok((pk.into(), v)) } +fn deserialize_multi_kv( + store: &dyn Storage, + pk_namespace: &[u8], + kv: Record, +) -> StdResult<(K::Output, T)> { + let (key, pk_len) = kv; + + // Deserialize pk_len + let pk_len = from_slice::(pk_len.as_slice())?; + + // Recover pk from last part of k + let offset = key.len() - pk_len as usize; + let pk = &key[offset..]; + + let full_key = namespaces_with_key(&[pk_namespace], pk); + + let v = store + .get(&full_key) + .ok_or_else(|| StdError::generic_err("pk not found"))?; + let v = from_slice::(&v)?; + + Ok((K::from_vec(pk.to_vec())?, v)) +} + impl<'a, K, T> Index for MultiIndex<'a, K, T> where T: Serialize + DeserializeOwned + Clone, @@ -153,7 +177,7 @@ where self.idx_namespace, &p.prefix(), self.pk_namespace, - deserialize_multi_kv, + deserialize_multi_v, ) } @@ -162,7 +186,7 @@ where self.idx_namespace, &p.prefix(), self.pk_namespace, - deserialize_multi_kv, + deserialize_multi_v, ) } @@ -171,7 +195,7 @@ where self.idx_namespace, &[], self.pk_namespace, - deserialize_multi_kv, + deserialize_multi_v, ) } @@ -247,11 +271,36 @@ where 'a: 'c, { let mapped = namespaced_prefix_range(store, self.idx_namespace, min, max, order) - .map(move |kv| (deserialize_multi_kv)(store, self.pk_namespace, kv)); + .map(move |kv| (deserialize_multi_v)(store, self.pk_namespace, kv)); Box::new(mapped) } } +#[cfg(feature = "iterator")] +impl<'a, K, T> MultiIndex<'a, K, T> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, +{ + pub fn prefix_de(&self, p: K::Prefix) -> Prefix { + Prefix::with_deserialization_function( + self.idx_namespace, + &p.prefix(), + self.pk_namespace, + deserialize_multi_kv::, + ) + } + + pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { + Prefix::with_deserialization_function( + self.idx_namespace, + &p.prefix(), + self.pk_namespace, + deserialize_multi_kv::, + ) + } +} + #[cfg(feature = "iterator")] impl<'a, K, T> MultiIndex<'a, K, T> where @@ -309,7 +358,12 @@ where } fn no_prefix_de(&self) -> Prefix { - Prefix::new(self.idx_namespace, &[]) + Prefix::with_deserialization_function( + self.idx_namespace, + &[], + self.pk_namespace, + deserialize_multi_kv::, + ) } } From 53a6840c16d239e109cb94fd897352ee86644f76 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 26 Oct 2021 23:16:23 +0200 Subject: [PATCH 11/42] MultiIndex example with String primary key --- packages/storage-plus/src/indexed_map.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index fa0408c20..02e8c7257 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -277,7 +277,7 @@ mod test { struct DataIndexes<'a> { // Second arg is for storing pk - pub name: MultiIndex<'a, (Vec, Vec), Data>, + pub name: MultiIndex<'a, (Vec, String), Data>, pub age: UniqueIndex<'a, U32Key, Data, String>, pub name_lastname: UniqueIndex<'a, (Vec, Vec), Data, String>, } @@ -307,7 +307,15 @@ mod test { // Can we make it easier to define this? (less wordy generic) fn build_map<'a>() -> IndexedMap<'a, &'a str, Data, DataIndexes<'a>> { let indexes = DataIndexes { - name: MultiIndex::new(|d, k| (d.name.as_bytes().to_vec(), k), "data", "data__name"), + name: MultiIndex::new( + |d, k| { + (d.name.as_bytes().to_vec(), unsafe { + String::from_utf8_unchecked(k) + }) + }, + "data", + "data__name", + ), age: UniqueIndex::new(|d| U32Key::new(d.age), "data__age"), name_lastname: UniqueIndex::new( |d| index_string_tuple(&d.name, &d.last_name), @@ -446,7 +454,7 @@ mod test { // index_key() over MultiIndex works (empty pk) // In a MultiIndex, an index key is composed by the index and the primary key. // Primary key may be empty (so that to iterate over all elements that match just the index) - let key = (b"Maria".to_vec(), b"".to_vec()); + let key = (b"Maria".to_vec(), "".to_string()); // Use the index_key() helper to build the (raw) index key let key = map.idx.name.index_key(key); // Iterate using a bound over the raw key @@ -460,7 +468,7 @@ mod test { // index_key() over MultiIndex works (non-empty pk) // Build key including a non-empty pk - let key = (b"Maria".to_vec(), b"1".to_vec()); + let key = (b"Maria".to_vec(), "1".to_string()); // Use the index_key() helper to build the (raw) index key let key = map.idx.name.index_key(key); // Iterate using a (exclusive) bound over the raw key. @@ -608,8 +616,8 @@ mod test { assert_eq!(2, count); // Sorted by (descending) pk - assert_eq!(marias[0].0, b"5629"); - assert_eq!(marias[1].0, b"5627"); + assert_eq!(marias[0].0, "5629"); + assert_eq!(marias[1].0, "5627"); // Data is correct assert_eq!(marias[0].1, data3); assert_eq!(marias[1].1, data1); From 6da86e062e79832cc7dbe5f63b008d120a2cc5a4 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 27 Oct 2021 10:15:19 +0200 Subject: [PATCH 12/42] MultIndex example using String also for index key --- packages/storage-plus/src/indexed_map.rs | 28 ++++++++++-------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 02e8c7257..d4ffeed96 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -277,7 +277,7 @@ mod test { struct DataIndexes<'a> { // Second arg is for storing pk - pub name: MultiIndex<'a, (Vec, String), Data>, + pub name: MultiIndex<'a, (String, String), Data>, pub age: UniqueIndex<'a, U32Key, Data, String>, pub name_lastname: UniqueIndex<'a, (Vec, Vec), Data, String>, } @@ -308,11 +308,7 @@ mod test { fn build_map<'a>() -> IndexedMap<'a, &'a str, Data, DataIndexes<'a>> { let indexes = DataIndexes { name: MultiIndex::new( - |d, k| { - (d.name.as_bytes().to_vec(), unsafe { - String::from_utf8_unchecked(k) - }) - }, + |d, k| (d.name.clone(), unsafe { String::from_utf8_unchecked(k) }), "data", "data__name", ), @@ -403,7 +399,7 @@ mod test { let count = map .idx .name - .prefix(b"Maria".to_vec()) + .prefix("Maria".to_string()) .range(&store, None, None, Order::Ascending) .count(); assert_eq!(2, count); @@ -415,7 +411,7 @@ mod test { let marias: Vec<_> = map .idx .name - .prefix(b"Maria".to_vec()) + .prefix("Maria".to_string()) .range(&store, None, None, Order::Ascending) .collect::>() .unwrap(); @@ -428,7 +424,7 @@ mod test { let count = map .idx .name - .prefix(b"Marib".to_vec()) + .prefix("Marib".to_string()) .range(&store, None, None, Order::Ascending) .count(); assert_eq!(0, count); @@ -437,7 +433,7 @@ mod test { let count = map .idx .name - .prefix(b"Mari`".to_vec()) + .prefix("Mari`".to_string()) .range(&store, None, None, Order::Ascending) .count(); assert_eq!(0, count); @@ -446,7 +442,7 @@ mod test { let count = map .idx .name - .prefix(b"Maria5".to_vec()) + .prefix("Maria5".to_string()) .range(&store, None, None, Order::Ascending) .count(); assert_eq!(0, count); @@ -454,7 +450,7 @@ mod test { // index_key() over MultiIndex works (empty pk) // In a MultiIndex, an index key is composed by the index and the primary key. // Primary key may be empty (so that to iterate over all elements that match just the index) - let key = (b"Maria".to_vec(), "".to_string()); + let key = ("Maria".to_string(), "".to_string()); // Use the index_key() helper to build the (raw) index key let key = map.idx.name.index_key(key); // Iterate using a bound over the raw key @@ -468,7 +464,7 @@ mod test { // index_key() over MultiIndex works (non-empty pk) // Build key including a non-empty pk - let key = (b"Maria".to_vec(), "1".to_string()); + let key = ("Maria".to_string(), "1".to_string()); // Use the index_key() helper to build the (raw) index key let key = map.idx.name.index_key(key); // Iterate using a (exclusive) bound over the raw key. @@ -552,7 +548,7 @@ mod test { let marias: Vec<_> = map .idx .name - .prefix(b"Maria".to_vec()) + .prefix("Maria".to_string()) .range(&store, None, None, Order::Descending) .collect::>() .unwrap(); @@ -608,7 +604,7 @@ mod test { let marias: Vec<_> = map .idx .name - .prefix_de(b"Maria".to_vec()) + .prefix_de("Maria".to_string()) .range_de(&store, None, None, Order::Descending) .collect::>() .unwrap(); @@ -762,7 +758,7 @@ mod test { -> usize { map.idx .name - .prefix(name.as_bytes().to_vec()) + .prefix(name.to_string()) .keys(store, None, None, Order::Ascending) .count() }; From b369363d09e6cd7ac2588aa4f488bff7479b25a4 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 27 Oct 2021 15:06:06 +0200 Subject: [PATCH 13/42] Fix: `MultiIndex` key deserializes to remaining key plus pk (`Map`-like) --- packages/storage-plus/src/indexed_map.rs | 65 ++++++++++++++++++++++++ packages/storage-plus/src/indexes.rs | 5 +- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index d4ffeed96..f6f1affef 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -684,6 +684,71 @@ mod test { assert_eq!(data3, marias[1].1); } + #[test] + fn range_de_composite_key_by_multi_index() { + let mut store = MockStorage::new(); + + let indexes = DataCompositeMultiIndex { + name_age: MultiIndex::new( + |d, k| index_triple(&d.name, d.age, k), + "data", + "data__name_age", + ), + }; + let map = IndexedMap::new("data", indexes); + + // save data + let data1 = Data { + name: "Maria".to_string(), + last_name: "".to_string(), + age: 42, + }; + let pk1: &[u8] = b"5627"; + map.save(&mut store, pk1, &data1).unwrap(); + + let data2 = Data { + name: "Juan".to_string(), + last_name: "Perez".to_string(), + age: 13, + }; + let pk2: &[u8] = b"5628"; + map.save(&mut store, pk2, &data2).unwrap(); + + let data3 = Data { + name: "Maria".to_string(), + last_name: "Young".to_string(), + age: 24, + }; + let pk3: &[u8] = b"5629"; + map.save(&mut store, pk3, &data3).unwrap(); + + let data4 = Data { + name: "Maria Luisa".to_string(), + last_name: "Bemberg".to_string(), + age: 43, + }; + let pk4: &[u8] = b"5630"; + map.save(&mut store, pk4, &data4).unwrap(); + + let marias: Vec<_> = map + .idx + .name_age + .sub_prefix_de(b"Maria".to_vec()) + .range_de(&store, None, None, Order::Descending) + .collect::>() + .unwrap(); + let count = marias.len(); + assert_eq!(2, count); + + // Remaining age of the index keys, plus pks (sorted by age descending) + assert_eq!((42, pk1.to_vec()), marias[0].0); + assert_eq!((24, pk3.to_vec()), marias[1].0); + + // Data + assert_eq!(data1, marias[0].1); + assert_eq!(data3, marias[1].1); + } + #[test] fn unique_index_enforced() { let mut store = MockStorage::new(); diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index 1a07353f9..bdbfc11e5 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -123,7 +123,8 @@ fn deserialize_multi_v( .ok_or_else(|| StdError::generic_err("pk not found"))?; let v = from_slice::(&v)?; - Ok((pk.into(), v)) + // FIXME: Return `key` here instead of `pk` (be consistent with `deserialize_multi_kv` and `Map` behaviour) + Ok((pk.to_vec(), v)) } fn deserialize_multi_kv( @@ -147,7 +148,7 @@ fn deserialize_multi_kv( .ok_or_else(|| StdError::generic_err("pk not found"))?; let v = from_slice::(&v)?; - Ok((K::from_vec(pk.to_vec())?, v)) + Ok((K::from_vec(key)?, v)) } impl<'a, K, T> Index for MultiIndex<'a, K, T> From 6bc3a132d75025f69991e6313f28490e70400c4c Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 27 Oct 2021 19:14:50 +0200 Subject: [PATCH 14/42] Improve comment --- packages/storage-plus/src/indexed_map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index f6f1affef..d1c831a30 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -740,7 +740,7 @@ mod test { let count = marias.len(); assert_eq!(2, count); - // Remaining age of the index keys, plus pks (sorted by age descending) + // Remaining part (age) of the index keys, plus pks (bytes) (sorted by age descending) assert_eq!((42, pk1.to_vec()), marias[0].0); assert_eq!((24, pk3.to_vec()), marias[1].0); From f98217ae0543d990f2aa1b2c5a74677794020ebe Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 3 Nov 2021 07:21:08 +0100 Subject: [PATCH 15/42] Add entry about index key deserialization --- packages/storage-plus/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/storage-plus/README.md b/packages/storage-plus/README.md index 5d2d319fb..10d5a0905 100644 --- a/packages/storage-plus/README.md +++ b/packages/storage-plus/README.md @@ -613,3 +613,15 @@ Another example that is similar, but returning only the `token_id`s, using the ` .collect(); ``` Now `pks` contains `token_id` values (as `Vec`s) for the given `owner`. + +### Index keys deserialization + +To deserialize keys of indexes (using the `*_de` functions), there are currently some requirements / limitations: + +- For `UniqueIndex`: The primary key (`PK`) type needs to be specified, in order to deserialize the primary key to it. +This generic type comes with a default of `()`, which means that no deserialization / data will be provided +for the primary key. + +- For `MultiIndex`: The last element of the index tuple must be specified with the type you want it to be deserialized. +That is, the last tuple element serves as a marker for the deserialization type (in the same way `PK` does it in +`UniqueIndex`). \ No newline at end of file From bc552521f0dab6855198ddfc2e650a20ca59a779 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 3 Nov 2021 09:19:00 +0100 Subject: [PATCH 16/42] Split indexes impl into different files --- packages/storage-plus/src/indexed_map.rs | 4 +- packages/storage-plus/src/indexed_snapshot.rs | 4 +- packages/storage-plus/src/indexes.rs | 575 +----------------- packages/storage-plus/src/lib.rs | 8 +- packages/storage-plus/src/multi_index.rs | 341 +++++++++++ packages/storage-plus/src/unique_index.rs | 250 ++++++++ 6 files changed, 603 insertions(+), 579 deletions(-) create mode 100644 packages/storage-plus/src/multi_index.rs create mode 100644 packages/storage-plus/src/unique_index.rs diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index d1c831a30..513dec2f2 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -262,8 +262,8 @@ where mod test { use super::*; - use crate::indexes::{index_string_tuple, index_triple, MultiIndex, UniqueIndex}; - use crate::U32Key; + use crate::indexes::{index_string_tuple, index_triple}; + use crate::{MultiIndex, U32Key, UniqueIndex}; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::{MemoryStorage, Order}; use serde::{Deserialize, Serialize}; diff --git a/packages/storage-plus/src/indexed_snapshot.rs b/packages/storage-plus/src/indexed_snapshot.rs index 7d72e558f..adbe2a4da 100644 --- a/packages/storage-plus/src/indexed_snapshot.rs +++ b/packages/storage-plus/src/indexed_snapshot.rs @@ -216,8 +216,8 @@ where mod test { use super::*; - use crate::indexes::{index_string_tuple, index_triple, MultiIndex, UniqueIndex}; - use crate::{Index, U32Key}; + use crate::indexes::{index_string_tuple, index_triple}; + use crate::{Index, MultiIndex, U32Key, UniqueIndex}; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::{MemoryStorage, Order}; use serde::{Deserialize, Serialize}; diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes.rs index bdbfc11e5..ce7698187 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes.rs @@ -1,19 +1,12 @@ // this module requires iterator to be useful at all #![cfg(feature = "iterator")] -use std::marker::PhantomData; - use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; +use serde::Serialize; -use cosmwasm_std::{from_slice, Binary, Order, Record, StdError, StdResult, Storage}; +use cosmwasm_std::{StdResult, Storage}; -use crate::de::KeyDeserialize; -use crate::helpers::namespaces_with_key; -use crate::iter_helpers::deserialize_kv; -use crate::map::Map; -use crate::prefix::{namespaced_prefix_range, PrefixBound}; -use crate::{Bound, Prefix, Prefixer, PrimaryKey, U32Key}; +use crate::U32Key; pub fn index_string(data: &str) -> Vec { data.as_bytes().to_vec() @@ -40,565 +33,3 @@ where fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()>; fn remove(&self, store: &mut dyn Storage, pk: &[u8], old_data: &T) -> StdResult<()>; } - -/// MultiIndex stores (namespace, index_name, idx_value, pk) -> b"pk_len". -/// Allows many values per index, and references pk. -/// The associated primary key value is stored in the main (pk_namespace) map, -/// which stores (namespace, pk_namespace, pk) -> value. -/// -/// The stored pk_len is used to recover the pk from the index namespace, and perform -/// the secondary load of the associated value from the main map. -/// -/// The MultiIndex definition must include a field for the pk. That is, the MultiIndex K value -/// is always a n-tuple (n >= 2) and its last element must be the pk. -/// The index function must therefore put the pk as last element, when generating the index. -pub struct MultiIndex<'a, K, T> { - index: fn(&T, Vec) -> K, - idx_namespace: &'a [u8], - idx_map: Map<'a, K, u32>, - pk_namespace: &'a [u8], -} - -impl<'a, K, T> MultiIndex<'a, K, T> -where - T: Serialize + DeserializeOwned + Clone, -{ - // TODO: make this a const fn - /// Create a new MultiIndex - /// - /// idx_fn - lambda creating index key from value (first argument) and primary key (second argument) - /// pk_namespace - prefix for the primary key - /// idx_namespace - prefix for the index value - /// - /// ## Example: - /// - /// ```rust - /// use cw_storage_plus::MultiIndex; - /// use serde::{Deserialize, Serialize}; - /// - /// #[derive(Deserialize, Serialize, Clone)] - /// struct Data { - /// pub name: String, - /// pub age: u32, - /// } - /// - /// MultiIndex::new( - /// |d: &Data, k: Vec| (d.age, k), - /// "age", - /// "age__owner", - /// ); - /// ``` - pub fn new( - idx_fn: fn(&T, Vec) -> K, - pk_namespace: &'a str, - idx_namespace: &'a str, - ) -> Self { - MultiIndex { - index: idx_fn, - idx_namespace: idx_namespace.as_bytes(), - idx_map: Map::new(idx_namespace), - pk_namespace: pk_namespace.as_bytes(), - } - } -} - -fn deserialize_multi_v( - store: &dyn Storage, - pk_namespace: &[u8], - kv: Record, -) -> StdResult> { - let (key, pk_len) = kv; - - // Deserialize pk_len - let pk_len = from_slice::(pk_len.as_slice())?; - - // Recover pk from last part of k - let offset = key.len() - pk_len as usize; - let pk = &key[offset..]; - - let full_key = namespaces_with_key(&[pk_namespace], pk); - - let v = store - .get(&full_key) - .ok_or_else(|| StdError::generic_err("pk not found"))?; - let v = from_slice::(&v)?; - - // FIXME: Return `key` here instead of `pk` (be consistent with `deserialize_multi_kv` and `Map` behaviour) - Ok((pk.to_vec(), v)) -} - -fn deserialize_multi_kv( - store: &dyn Storage, - pk_namespace: &[u8], - kv: Record, -) -> StdResult<(K::Output, T)> { - let (key, pk_len) = kv; - - // Deserialize pk_len - let pk_len = from_slice::(pk_len.as_slice())?; - - // Recover pk from last part of k - let offset = key.len() - pk_len as usize; - let pk = &key[offset..]; - - let full_key = namespaces_with_key(&[pk_namespace], pk); - - let v = store - .get(&full_key) - .ok_or_else(|| StdError::generic_err("pk not found"))?; - let v = from_slice::(&v)?; - - Ok((K::from_vec(key)?, v)) -} - -impl<'a, K, T> Index for MultiIndex<'a, K, T> -where - T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, -{ - fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> { - let idx = (self.index)(data, pk.to_vec()); - self.idx_map.save(store, idx, &(pk.len() as u32)) - } - - fn remove(&self, store: &mut dyn Storage, pk: &[u8], old_data: &T) -> StdResult<()> { - let idx = (self.index)(old_data, pk.to_vec()); - self.idx_map.remove(store, idx); - Ok(()) - } -} - -impl<'a, K, T> MultiIndex<'a, K, T> -where - T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, -{ - pub fn prefix(&self, p: K::Prefix) -> Prefix, T> { - Prefix::with_deserialization_function( - self.idx_namespace, - &p.prefix(), - self.pk_namespace, - deserialize_multi_v, - ) - } - - pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix, T> { - Prefix::with_deserialization_function( - self.idx_namespace, - &p.prefix(), - self.pk_namespace, - deserialize_multi_v, - ) - } - - fn no_prefix(&self) -> Prefix, T> { - Prefix::with_deserialization_function( - self.idx_namespace, - &[], - self.pk_namespace, - deserialize_multi_v, - ) - } - - pub fn index_key(&self, k: K) -> Vec { - k.joined_key() - } - - #[cfg(test)] - pub fn count(&self, store: &dyn Storage, p: K::Prefix) -> usize { - let prefix = self.prefix(p); - prefix.keys(store, None, None, Order::Ascending).count() - } - - #[cfg(test)] - pub fn all_pks(&self, store: &dyn Storage, p: K::Prefix) -> Vec> { - let prefix = self.prefix(p); - prefix - .keys(store, None, None, Order::Ascending) - .collect::>>() - } - - #[cfg(test)] - pub fn all_items(&self, store: &dyn Storage, p: K::Prefix) -> StdResult>> { - let prefix = self.prefix(p); - prefix.range(store, None, None, Order::Ascending).collect() - } -} - -// short-cut for simple keys, rather than .prefix(()).range(...) -impl<'a, K, T> MultiIndex<'a, K, T> -where - T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, -{ - // I would prefer not to copy code from Prefix, but no other way - // with lifetimes (create Prefix inside function and return ref = no no) - pub fn range<'c>( - &'c self, - store: &'c dyn Storage, - min: Option, - max: Option, - order: Order, - ) -> Box>> + 'c> - where - T: 'c, - { - self.no_prefix().range(store, min, max, order) - } - - pub fn keys<'c>( - &'c self, - store: &'c dyn Storage, - min: Option, - max: Option, - order: Order, - ) -> Box> + 'c> { - self.no_prefix().keys(store, min, max, order) - } - - /// while range assumes you set the prefix to one element and call range over the last one, - /// prefix_range accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. - pub fn prefix_range<'c>( - &'c self, - store: &'c dyn Storage, - min: Option>, - max: Option>, - order: cosmwasm_std::Order, - ) -> Box>> + 'c> - where - T: 'c, - 'a: 'c, - { - let mapped = namespaced_prefix_range(store, self.idx_namespace, min, max, order) - .map(move |kv| (deserialize_multi_v)(store, self.pk_namespace, kv)); - Box::new(mapped) - } -} - -#[cfg(feature = "iterator")] -impl<'a, K, T> MultiIndex<'a, K, T> -where - T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, -{ - pub fn prefix_de(&self, p: K::Prefix) -> Prefix { - Prefix::with_deserialization_function( - self.idx_namespace, - &p.prefix(), - self.pk_namespace, - deserialize_multi_kv::, - ) - } - - pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { - Prefix::with_deserialization_function( - self.idx_namespace, - &p.prefix(), - self.pk_namespace, - deserialize_multi_kv::, - ) - } -} - -#[cfg(feature = "iterator")] -impl<'a, K, T> MultiIndex<'a, K, T> -where - T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a> + KeyDeserialize, -{ - /// while range_de assumes you set the prefix to one element and call range over the last one, - /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. - pub fn prefix_range_de<'c>( - &self, - store: &'c dyn Storage, - min: Option>, - max: Option>, - order: cosmwasm_std::Order, - ) -> Box> + 'c> - where - T: 'c, - 'a: 'c, - K: 'c, - K::Output: 'static, - { - let mapped = namespaced_prefix_range(store, self.idx_namespace, min, max, order) - .map(deserialize_kv::); - Box::new(mapped) - } - - pub fn range_de<'c>( - &self, - store: &'c dyn Storage, - min: Option, - max: Option, - order: cosmwasm_std::Order, - ) -> Box> + 'c> - where - T: 'c, - K::Output: 'static, - { - self.no_prefix_de().range_de(store, min, max, order) - } - - pub fn keys_de<'c>( - &self, - store: &'c dyn Storage, - min: Option, - max: Option, - order: cosmwasm_std::Order, - ) -> Box> + 'c> - where - T: 'c, - K::Output: 'static, - { - self.no_prefix_de().keys_de(store, min, max, order) - } - - fn no_prefix_de(&self) -> Prefix { - Prefix::with_deserialization_function( - self.idx_namespace, - &[], - self.pk_namespace, - deserialize_multi_kv::, - ) - } -} - -/// UniqueRef stores Binary(Vec[u8]) representation of private key and index value -#[derive(Deserialize, Serialize)] -pub(crate) struct UniqueRef { - // note, we collapse the pk - combining everything under the namespace - even if it is composite - pk: Binary, - value: T, -} - -/// UniqueIndex stores (namespace, index_name, idx_value) -> {key, value} -/// Allows one value per index (i.e. unique) and copies pk and data -/// The optional PK type defines the type of Primary Key deserialization. -pub struct UniqueIndex<'a, K, T, PK = ()> { - index: fn(&T) -> K, - idx_map: Map<'a, K, UniqueRef>, - idx_namespace: &'a [u8], - _phantom: PhantomData, -} - -impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> { - // TODO: make this a const fn - /// Create a new UniqueIndex - /// - /// idx_fn - lambda creating index key from index value - /// idx_namespace - prefix for the index value - /// - /// ## Example: - /// - /// ```rust - /// use cw_storage_plus::{U32Key, UniqueIndex}; - /// - /// struct Data { - /// pub name: String, - /// pub age: u32, - /// } - /// - /// UniqueIndex::<_, _, ()>::new(|d: &Data| U32Key::new(d.age), "data__age"); - /// ``` - pub fn new(idx_fn: fn(&T) -> K, idx_namespace: &'a str) -> Self { - UniqueIndex { - index: idx_fn, - idx_map: Map::new(idx_namespace), - idx_namespace: idx_namespace.as_bytes(), - _phantom: PhantomData, - } - } -} - -impl<'a, K, T, PK> Index for UniqueIndex<'a, K, T, PK> -where - T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, -{ - fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> { - let idx = (self.index)(data); - // error if this is already set - self.idx_map - .update(store, idx, |existing| -> StdResult<_> { - match existing { - Some(_) => Err(StdError::generic_err("Violates unique constraint on index")), - None => Ok(UniqueRef:: { - pk: pk.into(), - value: data.clone(), - }), - } - })?; - Ok(()) - } - - fn remove(&self, store: &mut dyn Storage, _pk: &[u8], old_data: &T) -> StdResult<()> { - let idx = (self.index)(old_data); - self.idx_map.remove(store, idx); - Ok(()) - } -} - -fn deserialize_unique_v(kv: Record) -> StdResult> { - let (_, v) = kv; - let t = from_slice::>(&v)?; - Ok((t.pk.to_vec(), t.value)) -} - -fn deserialize_unique_kv( - kv: Record, -) -> StdResult<(K::Output, T)> { - let (_, v) = kv; - let t = from_slice::>(&v)?; - Ok((K::from_vec(t.pk.to_vec())?, t.value)) -} - -impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> -where - T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, -{ - pub fn index_key(&self, k: K) -> Vec { - k.joined_key() - } - - pub fn prefix(&self, p: K::Prefix) -> Prefix, T> { - Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { - deserialize_unique_v(kv) - }) - } - - pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix, T> { - Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { - deserialize_unique_v(kv) - }) - } - - fn no_prefix(&self) -> Prefix, T> { - Prefix::with_deserialization_function(self.idx_namespace, &[], &[], |_, _, kv| { - deserialize_unique_v(kv) - }) - } - - /// returns all items that match this secondary index, always by pk Ascending - pub fn item(&self, store: &dyn Storage, idx: K) -> StdResult>> { - let data = self - .idx_map - .may_load(store, idx)? - .map(|i| (i.pk.into(), i.value)); - Ok(data) - } -} - -// short-cut for simple keys, rather than .prefix(()).range(...) -impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> -where - T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, -{ - // I would prefer not to copy code from Prefix, but no other way - // with lifetimes (create Prefix inside function and return ref = no no) - pub fn range<'c>( - &self, - store: &'c dyn Storage, - min: Option, - max: Option, - order: Order, - ) -> Box>> + 'c> - where - T: 'c, - { - self.no_prefix().range(store, min, max, order) - } - - pub fn keys<'c>( - &self, - store: &'c dyn Storage, - min: Option, - max: Option, - order: Order, - ) -> Box> + 'c> { - self.no_prefix().keys(store, min, max, order) - } -} - -#[cfg(feature = "iterator")] -impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> -where - PK: PrimaryKey<'a> + KeyDeserialize, - T: Serialize + DeserializeOwned + Clone, - K: PrimaryKey<'a>, -{ - /// while range_de assumes you set the prefix to one element and call range over the last one, - /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. - pub fn prefix_range_de<'c>( - &self, - store: &'c dyn Storage, - min: Option>, - max: Option>, - order: cosmwasm_std::Order, - ) -> Box> + 'c> - where - T: 'c, - 'a: 'c, - K: 'c, - PK: 'c, - PK::Output: 'static, - { - let mapped = namespaced_prefix_range(store, self.idx_namespace, min, max, order) - .map(deserialize_kv::); - Box::new(mapped) - } - - pub fn range_de<'c>( - &self, - store: &'c dyn Storage, - min: Option, - max: Option, - order: cosmwasm_std::Order, - ) -> Box> + 'c> - where - T: 'c, - PK::Output: 'static, - { - self.no_prefix_de().range_de(store, min, max, order) - } - - pub fn keys_de<'c>( - &self, - store: &'c dyn Storage, - min: Option, - max: Option, - order: cosmwasm_std::Order, - ) -> Box> + 'c> - where - T: 'c, - PK::Output: 'static, - { - self.no_prefix_de().keys_de(store, min, max, order) - } - - pub fn prefix_de(&self, p: K::Prefix) -> Prefix { - Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { - deserialize_unique_kv::(kv) - }) - } - - pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { - Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { - deserialize_unique_kv::(kv) - }) - } - - fn no_prefix_de(&self) -> Prefix { - Prefix::with_deserialization_function(self.idx_namespace, &[], &[], |_, _, kv| { - deserialize_unique_kv::(kv) - }) - } -} diff --git a/packages/storage-plus/src/lib.rs b/packages/storage-plus/src/lib.rs index 6d3033ef3..7e3279574 100644 --- a/packages/storage-plus/src/lib.rs +++ b/packages/storage-plus/src/lib.rs @@ -8,9 +8,11 @@ mod item; mod iter_helpers; mod keys; mod map; +mod multi_index; mod path; mod prefix; mod snapshot; +mod unique_index; pub use endian::Endian; #[cfg(feature = "iterator")] @@ -18,15 +20,15 @@ pub use indexed_map::{IndexList, IndexedMap}; #[cfg(feature = "iterator")] pub use indexed_snapshot::IndexedSnapshotMap; #[cfg(feature = "iterator")] -pub use indexes::{ - index_string, index_string_tuple, index_triple, index_tuple, Index, MultiIndex, UniqueIndex, -}; +pub use indexes::{index_string, index_string_tuple, index_triple, index_tuple, Index}; pub use item::Item; pub use keys::{I128Key, I16Key, I32Key, I64Key, I8Key}; pub use keys::{Prefixer, PrimaryKey, U128Key, U16Key, U32Key, U64Key, U8Key}; pub use map::Map; +pub use multi_index::MultiIndex; pub use path::Path; #[cfg(feature = "iterator")] pub use prefix::{range_with_prefix, Bound, Prefix}; #[cfg(feature = "iterator")] pub use snapshot::{SnapshotItem, SnapshotMap, Strategy}; +pub use unique_index::UniqueIndex; diff --git a/packages/storage-plus/src/multi_index.rs b/packages/storage-plus/src/multi_index.rs new file mode 100644 index 000000000..2f29aa4b2 --- /dev/null +++ b/packages/storage-plus/src/multi_index.rs @@ -0,0 +1,341 @@ +// this module requires iterator to be useful at all +#![cfg(feature = "iterator")] + +use serde::de::DeserializeOwned; +use serde::Serialize; + +use cosmwasm_std::{from_slice, Order, Record, StdError, StdResult, Storage}; + +use crate::de::KeyDeserialize; +use crate::helpers::namespaces_with_key; +use crate::iter_helpers::deserialize_kv; +use crate::map::Map; +use crate::prefix::{namespaced_prefix_range, PrefixBound}; +use crate::{Bound, Index, Prefix, Prefixer, PrimaryKey}; + +/// MultiIndex stores (namespace, index_name, idx_value, pk) -> b"pk_len". +/// Allows many values per index, and references pk. +/// The associated primary key value is stored in the main (pk_namespace) map, +/// which stores (namespace, pk_namespace, pk) -> value. +/// +/// The stored pk_len is used to recover the pk from the index namespace, and perform +/// the secondary load of the associated value from the main map. +/// +/// The MultiIndex definition must include a field for the pk. That is, the MultiIndex K value +/// is always a n-tuple (n >= 2) and its last element must be the pk. +/// The index function must therefore put the pk as last element, when generating the index. +pub struct MultiIndex<'a, K, T> { + index: fn(&T, Vec) -> K, + idx_namespace: &'a [u8], + idx_map: Map<'a, K, u32>, + pk_namespace: &'a [u8], +} + +impl<'a, K, T> MultiIndex<'a, K, T> +where + T: Serialize + DeserializeOwned + Clone, +{ + // TODO: make this a const fn + /// Create a new MultiIndex + /// + /// idx_fn - lambda creating index key from value (first argument) and primary key (second argument) + /// pk_namespace - prefix for the primary key + /// idx_namespace - prefix for the index value + /// + /// ## Example: + /// + /// ```rust + /// use cw_storage_plus::MultiIndex; + /// use serde::{Deserialize, Serialize}; + /// + /// #[derive(Deserialize, Serialize, Clone)] + /// struct Data { + /// pub name: String, + /// pub age: u32, + /// } + /// + /// MultiIndex::new( + /// |d: &Data, k: Vec| (d.age, k), + /// "age", + /// "age__owner", + /// ); + /// ``` + pub fn new( + idx_fn: fn(&T, Vec) -> K, + pk_namespace: &'a str, + idx_namespace: &'a str, + ) -> Self { + MultiIndex { + index: idx_fn, + idx_namespace: idx_namespace.as_bytes(), + idx_map: Map::new(idx_namespace), + pk_namespace: pk_namespace.as_bytes(), + } + } +} + +fn deserialize_multi_v( + store: &dyn Storage, + pk_namespace: &[u8], + kv: Record, +) -> StdResult> { + let (key, pk_len) = kv; + + // Deserialize pk_len + let pk_len = from_slice::(pk_len.as_slice())?; + + // Recover pk from last part of k + let offset = key.len() - pk_len as usize; + let pk = &key[offset..]; + + let full_key = namespaces_with_key(&[pk_namespace], pk); + + let v = store + .get(&full_key) + .ok_or_else(|| StdError::generic_err("pk not found"))?; + let v = from_slice::(&v)?; + + // FIXME: Return `key` here instead of `pk` (be consistent with `deserialize_multi_kv` and `Map` behaviour) + Ok((pk.to_vec(), v)) +} + +fn deserialize_multi_kv( + store: &dyn Storage, + pk_namespace: &[u8], + kv: Record, +) -> StdResult<(K::Output, T)> { + let (key, pk_len) = kv; + + // Deserialize pk_len + let pk_len = from_slice::(pk_len.as_slice())?; + + // Recover pk from last part of k + let offset = key.len() - pk_len as usize; + let pk = &key[offset..]; + + let full_key = namespaces_with_key(&[pk_namespace], pk); + + let v = store + .get(&full_key) + .ok_or_else(|| StdError::generic_err("pk not found"))?; + let v = from_slice::(&v)?; + + Ok((K::from_vec(key)?, v)) +} + +impl<'a, K, T> Index for MultiIndex<'a, K, T> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, +{ + fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> { + let idx = (self.index)(data, pk.to_vec()); + self.idx_map.save(store, idx, &(pk.len() as u32)) + } + + fn remove(&self, store: &mut dyn Storage, pk: &[u8], old_data: &T) -> StdResult<()> { + let idx = (self.index)(old_data, pk.to_vec()); + self.idx_map.remove(store, idx); + Ok(()) + } +} + +impl<'a, K, T> MultiIndex<'a, K, T> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, +{ + pub fn prefix(&self, p: K::Prefix) -> Prefix, T> { + Prefix::with_deserialization_function( + self.idx_namespace, + &p.prefix(), + self.pk_namespace, + deserialize_multi_v, + ) + } + + pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix, T> { + Prefix::with_deserialization_function( + self.idx_namespace, + &p.prefix(), + self.pk_namespace, + deserialize_multi_v, + ) + } + + fn no_prefix(&self) -> Prefix, T> { + Prefix::with_deserialization_function( + self.idx_namespace, + &[], + self.pk_namespace, + deserialize_multi_v, + ) + } + + pub fn index_key(&self, k: K) -> Vec { + k.joined_key() + } + + #[cfg(test)] + pub fn count(&self, store: &dyn Storage, p: K::Prefix) -> usize { + let prefix = self.prefix(p); + prefix.keys(store, None, None, Order::Ascending).count() + } + + #[cfg(test)] + pub fn all_pks(&self, store: &dyn Storage, p: K::Prefix) -> Vec> { + let prefix = self.prefix(p); + prefix + .keys(store, None, None, Order::Ascending) + .collect::>>() + } + + #[cfg(test)] + pub fn all_items(&self, store: &dyn Storage, p: K::Prefix) -> StdResult>> { + let prefix = self.prefix(p); + prefix.range(store, None, None, Order::Ascending).collect() + } +} + +// short-cut for simple keys, rather than .prefix(()).range(...) +impl<'a, K, T> MultiIndex<'a, K, T> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, +{ + // I would prefer not to copy code from Prefix, but no other way + // with lifetimes (create Prefix inside function and return ref = no no) + pub fn range<'c>( + &'c self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: Order, + ) -> Box>> + 'c> + where + T: 'c, + { + self.no_prefix().range(store, min, max, order) + } + + pub fn keys<'c>( + &'c self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: Order, + ) -> Box> + 'c> { + self.no_prefix().keys(store, min, max, order) + } + + /// while range assumes you set the prefix to one element and call range over the last one, + /// prefix_range accepts bounds for the lowest and highest elements of the Prefix we wish to + /// accept, and iterates over those. There are some issues that distinguish these to and blindly + /// casting to Vec doesn't solve them. + pub fn prefix_range<'c>( + &'c self, + store: &'c dyn Storage, + min: Option>, + max: Option>, + order: cosmwasm_std::Order, + ) -> Box>> + 'c> + where + T: 'c, + 'a: 'c, + { + let mapped = namespaced_prefix_range(store, self.idx_namespace, min, max, order) + .map(move |kv| (deserialize_multi_v)(store, self.pk_namespace, kv)); + Box::new(mapped) + } +} + +#[cfg(feature = "iterator")] +impl<'a, K, T> MultiIndex<'a, K, T> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, +{ + pub fn prefix_de(&self, p: K::Prefix) -> Prefix { + Prefix::with_deserialization_function( + self.idx_namespace, + &p.prefix(), + self.pk_namespace, + deserialize_multi_kv::, + ) + } + + pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { + Prefix::with_deserialization_function( + self.idx_namespace, + &p.prefix(), + self.pk_namespace, + deserialize_multi_kv::, + ) + } +} + +#[cfg(feature = "iterator")] +impl<'a, K, T> MultiIndex<'a, K, T> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a> + KeyDeserialize, +{ + /// while range_de assumes you set the prefix to one element and call range over the last one, + /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to + /// accept, and iterates over those. There are some issues that distinguish these to and blindly + /// casting to Vec doesn't solve them. + pub fn prefix_range_de<'c>( + &self, + store: &'c dyn Storage, + min: Option>, + max: Option>, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + 'a: 'c, + K: 'c, + K::Output: 'static, + { + let mapped = namespaced_prefix_range(store, self.idx_namespace, min, max, order) + .map(deserialize_kv::); + Box::new(mapped) + } + + pub fn range_de<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + K::Output: 'static, + { + self.no_prefix_de().range_de(store, min, max, order) + } + + pub fn keys_de<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + K::Output: 'static, + { + self.no_prefix_de().keys_de(store, min, max, order) + } + + fn no_prefix_de(&self) -> Prefix { + Prefix::with_deserialization_function( + self.idx_namespace, + &[], + self.pk_namespace, + deserialize_multi_kv::, + ) + } +} diff --git a/packages/storage-plus/src/unique_index.rs b/packages/storage-plus/src/unique_index.rs new file mode 100644 index 000000000..9f1060372 --- /dev/null +++ b/packages/storage-plus/src/unique_index.rs @@ -0,0 +1,250 @@ +// this module requires iterator to be useful at all +#![cfg(feature = "iterator")] + +use std::marker::PhantomData; + +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; + +use cosmwasm_std::{from_slice, Binary, Order, Record, StdError, StdResult, Storage}; + +use crate::de::KeyDeserialize; +use crate::iter_helpers::deserialize_kv; +use crate::map::Map; +use crate::prefix::{namespaced_prefix_range, PrefixBound}; +use crate::{Bound, Index, Prefix, Prefixer, PrimaryKey}; + +/// UniqueRef stores Binary(Vec[u8]) representation of private key and index value +#[derive(Deserialize, Serialize)] +pub(crate) struct UniqueRef { + // note, we collapse the pk - combining everything under the namespace - even if it is composite + pk: Binary, + value: T, +} + +/// UniqueIndex stores (namespace, index_name, idx_value) -> {key, value} +/// Allows one value per index (i.e. unique) and copies pk and data +/// The optional PK type defines the type of Primary Key deserialization. +pub struct UniqueIndex<'a, K, T, PK = ()> { + index: fn(&T) -> K, + idx_map: Map<'a, K, UniqueRef>, + idx_namespace: &'a [u8], + _phantom: PhantomData, +} + +impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> { + // TODO: make this a const fn + /// Create a new UniqueIndex + /// + /// idx_fn - lambda creating index key from index value + /// idx_namespace - prefix for the index value + /// + /// ## Example: + /// + /// ```rust + /// use cw_storage_plus::{U32Key, UniqueIndex}; + /// + /// struct Data { + /// pub name: String, + /// pub age: u32, + /// } + /// + /// UniqueIndex::<_, _, ()>::new(|d: &Data| U32Key::new(d.age), "data__age"); + /// ``` + pub fn new(idx_fn: fn(&T) -> K, idx_namespace: &'a str) -> Self { + UniqueIndex { + index: idx_fn, + idx_map: Map::new(idx_namespace), + idx_namespace: idx_namespace.as_bytes(), + _phantom: PhantomData, + } + } +} + +impl<'a, K, T, PK> Index for UniqueIndex<'a, K, T, PK> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, +{ + fn save(&self, store: &mut dyn Storage, pk: &[u8], data: &T) -> StdResult<()> { + let idx = (self.index)(data); + // error if this is already set + self.idx_map + .update(store, idx, |existing| -> StdResult<_> { + match existing { + Some(_) => Err(StdError::generic_err("Violates unique constraint on index")), + None => Ok(UniqueRef:: { + pk: pk.into(), + value: data.clone(), + }), + } + })?; + Ok(()) + } + + fn remove(&self, store: &mut dyn Storage, _pk: &[u8], old_data: &T) -> StdResult<()> { + let idx = (self.index)(old_data); + self.idx_map.remove(store, idx); + Ok(()) + } +} + +fn deserialize_unique_v(kv: Record) -> StdResult> { + let (_, v) = kv; + let t = from_slice::>(&v)?; + Ok((t.pk.to_vec(), t.value)) +} + +fn deserialize_unique_kv( + kv: Record, +) -> StdResult<(K::Output, T)> { + let (_, v) = kv; + let t = from_slice::>(&v)?; + Ok((K::from_vec(t.pk.to_vec())?, t.value)) +} + +impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, +{ + pub fn index_key(&self, k: K) -> Vec { + k.joined_key() + } + + pub fn prefix(&self, p: K::Prefix) -> Prefix, T> { + Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { + deserialize_unique_v(kv) + }) + } + + pub fn sub_prefix(&self, p: K::SubPrefix) -> Prefix, T> { + Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { + deserialize_unique_v(kv) + }) + } + + fn no_prefix(&self) -> Prefix, T> { + Prefix::with_deserialization_function(self.idx_namespace, &[], &[], |_, _, kv| { + deserialize_unique_v(kv) + }) + } + + /// returns all items that match this secondary index, always by pk Ascending + pub fn item(&self, store: &dyn Storage, idx: K) -> StdResult>> { + let data = self + .idx_map + .may_load(store, idx)? + .map(|i| (i.pk.into(), i.value)); + Ok(data) + } +} + +// short-cut for simple keys, rather than .prefix(()).range(...) +impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, +{ + // I would prefer not to copy code from Prefix, but no other way + // with lifetimes (create Prefix inside function and return ref = no no) + pub fn range<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: Order, + ) -> Box>> + 'c> + where + T: 'c, + { + self.no_prefix().range(store, min, max, order) + } + + pub fn keys<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: Order, + ) -> Box> + 'c> { + self.no_prefix().keys(store, min, max, order) + } +} + +#[cfg(feature = "iterator")] +impl<'a, K, T, PK> UniqueIndex<'a, K, T, PK> +where + PK: PrimaryKey<'a> + KeyDeserialize, + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, +{ + /// while range_de assumes you set the prefix to one element and call range over the last one, + /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to + /// accept, and iterates over those. There are some issues that distinguish these to and blindly + /// casting to Vec doesn't solve them. + pub fn prefix_range_de<'c>( + &self, + store: &'c dyn Storage, + min: Option>, + max: Option>, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + 'a: 'c, + K: 'c, + PK: 'c, + PK::Output: 'static, + { + let mapped = namespaced_prefix_range(store, self.idx_namespace, min, max, order) + .map(deserialize_kv::); + Box::new(mapped) + } + + pub fn range_de<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + PK::Output: 'static, + { + self.no_prefix_de().range_de(store, min, max, order) + } + + pub fn keys_de<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + PK::Output: 'static, + { + self.no_prefix_de().keys_de(store, min, max, order) + } + + pub fn prefix_de(&self, p: K::Prefix) -> Prefix { + Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { + deserialize_unique_kv::(kv) + }) + } + + pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { + Prefix::with_deserialization_function(self.idx_namespace, &p.prefix(), &[], |_, _, kv| { + deserialize_unique_kv::(kv) + }) + } + + fn no_prefix_de(&self) -> Prefix { + Prefix::with_deserialization_function(self.idx_namespace, &[], &[], |_, _, kv| { + deserialize_unique_kv::(kv) + }) + } +} From 759b493c157df88c4c36cfc4b7adabe59793dca5 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 3 Nov 2021 09:36:22 +0100 Subject: [PATCH 17/42] Add iterator feature --- packages/storage-plus/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/storage-plus/src/lib.rs b/packages/storage-plus/src/lib.rs index 7e3279574..fabd69b6c 100644 --- a/packages/storage-plus/src/lib.rs +++ b/packages/storage-plus/src/lib.rs @@ -25,10 +25,12 @@ pub use item::Item; pub use keys::{I128Key, I16Key, I32Key, I64Key, I8Key}; pub use keys::{Prefixer, PrimaryKey, U128Key, U16Key, U32Key, U64Key, U8Key}; pub use map::Map; +#[cfg(feature = "iterator")] pub use multi_index::MultiIndex; pub use path::Path; #[cfg(feature = "iterator")] pub use prefix::{range_with_prefix, Bound, Prefix}; #[cfg(feature = "iterator")] pub use snapshot::{SnapshotItem, SnapshotMap, Strategy}; +#[cfg(feature = "iterator")] pub use unique_index::UniqueIndex; From 31d4826091438e220bb0e69076a5e5ac18f25eeb Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Mon, 15 Nov 2021 12:39:41 +0100 Subject: [PATCH 18/42] Refactor indexes into its own module --- .../storage-plus/src/{indexes.rs => indexes/mod.rs} | 5 +++++ .../src/{multi_index.rs => indexes/multi.rs} | 0 .../src/{unique_index.rs => indexes/unique.rs} | 0 packages/storage-plus/src/lib.rs | 10 ++++------ 4 files changed, 9 insertions(+), 6 deletions(-) rename packages/storage-plus/src/{indexes.rs => indexes/mod.rs} (92%) rename packages/storage-plus/src/{multi_index.rs => indexes/multi.rs} (100%) rename packages/storage-plus/src/{unique_index.rs => indexes/unique.rs} (100%) diff --git a/packages/storage-plus/src/indexes.rs b/packages/storage-plus/src/indexes/mod.rs similarity index 92% rename from packages/storage-plus/src/indexes.rs rename to packages/storage-plus/src/indexes/mod.rs index ce7698187..23c9e9d24 100644 --- a/packages/storage-plus/src/indexes.rs +++ b/packages/storage-plus/src/indexes/mod.rs @@ -1,5 +1,10 @@ // this module requires iterator to be useful at all #![cfg(feature = "iterator")] +mod multi; +mod unique; + +pub use multi::MultiIndex; +pub use unique::UniqueIndex; use serde::de::DeserializeOwned; use serde::Serialize; diff --git a/packages/storage-plus/src/multi_index.rs b/packages/storage-plus/src/indexes/multi.rs similarity index 100% rename from packages/storage-plus/src/multi_index.rs rename to packages/storage-plus/src/indexes/multi.rs diff --git a/packages/storage-plus/src/unique_index.rs b/packages/storage-plus/src/indexes/unique.rs similarity index 100% rename from packages/storage-plus/src/unique_index.rs rename to packages/storage-plus/src/indexes/unique.rs diff --git a/packages/storage-plus/src/lib.rs b/packages/storage-plus/src/lib.rs index fabd69b6c..53bbba036 100644 --- a/packages/storage-plus/src/lib.rs +++ b/packages/storage-plus/src/lib.rs @@ -8,11 +8,9 @@ mod item; mod iter_helpers; mod keys; mod map; -mod multi_index; mod path; mod prefix; mod snapshot; -mod unique_index; pub use endian::Endian; #[cfg(feature = "iterator")] @@ -20,17 +18,17 @@ pub use indexed_map::{IndexList, IndexedMap}; #[cfg(feature = "iterator")] pub use indexed_snapshot::IndexedSnapshotMap; #[cfg(feature = "iterator")] +pub use indexes::MultiIndex; +#[cfg(feature = "iterator")] +pub use indexes::UniqueIndex; +#[cfg(feature = "iterator")] pub use indexes::{index_string, index_string_tuple, index_triple, index_tuple, Index}; pub use item::Item; pub use keys::{I128Key, I16Key, I32Key, I64Key, I8Key}; pub use keys::{Prefixer, PrimaryKey, U128Key, U16Key, U32Key, U64Key, U8Key}; pub use map::Map; -#[cfg(feature = "iterator")] -pub use multi_index::MultiIndex; pub use path::Path; #[cfg(feature = "iterator")] pub use prefix::{range_with_prefix, Bound, Prefix}; #[cfg(feature = "iterator")] pub use snapshot::{SnapshotItem, SnapshotMap, Strategy}; -#[cfg(feature = "iterator")] -pub use unique_index::UniqueIndex; From 8ea1f22e5a3ccb391106f9df1a78820687df4b78 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 9 Nov 2021 15:52:31 +0100 Subject: [PATCH 19/42] Add sub_/prefix_de to SnapshotMap --- packages/storage-plus/src/snapshot/map.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/storage-plus/src/snapshot/map.rs b/packages/storage-plus/src/snapshot/map.rs index 29436bfdb..b5a1f6658 100644 --- a/packages/storage-plus/src/snapshot/map.rs +++ b/packages/storage-plus/src/snapshot/map.rs @@ -205,6 +205,14 @@ where self.no_prefix_de().range_de(store, min, max, order) } + pub fn prefix_de(&self, p: K::Prefix) -> Prefix { + Prefix::new(self.primary.namespace(), &p.prefix()) + } + + pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { + Prefix::new(self.primary.namespace(), &p.prefix()) + } + fn no_prefix_de(&self) -> Prefix { Prefix::new(self.primary.namespace(), &[]) } From f9e4182499dbf4de652961e6dacf7d3bab2e50b0 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 9 Nov 2021 16:04:07 +0100 Subject: [PATCH 20/42] Adapt simple key tests to better show key deserialization --- packages/storage-plus/src/snapshot/map.rs | 102 ++++++++-------------- 1 file changed, 38 insertions(+), 64 deletions(-) diff --git a/packages/storage-plus/src/snapshot/map.rs b/packages/storage-plus/src/snapshot/map.rs index b5a1f6658..abfa3e5cc 100644 --- a/packages/storage-plus/src/snapshot/map.rs +++ b/packages/storage-plus/src/snapshot/map.rs @@ -223,7 +223,7 @@ mod tests { use super::*; use cosmwasm_std::testing::MockStorage; - type TestMap = SnapshotMap<'static, &'static [u8], u64>; + type TestMap = SnapshotMap<'static, &'static str, u64>; type TestMapCompositeKey = SnapshotMap<'static, (&'static [u8], &'static [u8]), u64>; const NEVER: TestMap = @@ -257,45 +257,37 @@ mod tests { // Values at beginning of 3 -> A = 5, B = 7 // Values at beginning of 5 -> A = 8, C = 13 fn init_data(map: &TestMap, storage: &mut dyn Storage) { - map.save(storage, b"A", &5, 1).unwrap(); - map.save(storage, b"B", &7, 2).unwrap(); + map.save(storage, "A", &5, 1).unwrap(); + map.save(storage, "B", &7, 2).unwrap(); // checkpoint 3 map.add_checkpoint(storage, 3).unwrap(); // also use update to set - to ensure this works - map.save(storage, b"C", &1, 3).unwrap(); - map.update(storage, b"A", 3, |_| -> StdResult { Ok(8) }) + map.save(storage, "C", &1, 3).unwrap(); + map.update(storage, "A", 3, |_| -> StdResult { Ok(8) }) .unwrap(); - map.remove(storage, b"B", 4).unwrap(); - map.save(storage, b"C", &13, 4).unwrap(); + map.remove(storage, "B", 4).unwrap(); + map.save(storage, "C", &13, 4).unwrap(); // checkpoint 5 map.add_checkpoint(storage, 5).unwrap(); - map.remove(storage, b"A", 5).unwrap(); - map.update(storage, b"D", 5, |_| -> StdResult { Ok(22) }) + map.remove(storage, "A", 5).unwrap(); + map.update(storage, "D", 5, |_| -> StdResult { Ok(22) }) .unwrap(); // and delete it later (unknown if all data present) map.remove_checkpoint(storage, 5).unwrap(); } - const FINAL_VALUES: &[(&[u8], Option)] = &[ - (b"A", None), - (b"B", None), - (b"C", Some(13)), - (b"D", Some(22)), - ]; + const FINAL_VALUES: &[(&str, Option)] = + &[("A", None), ("B", None), ("C", Some(13)), ("D", Some(22))]; - const VALUES_START_3: &[(&[u8], Option)] = - &[(b"A", Some(5)), (b"B", Some(7)), (b"C", None), (b"D", None)]; + const VALUES_START_3: &[(&str, Option)] = + &[("A", Some(5)), ("B", Some(7)), ("C", None), ("D", None)]; - const VALUES_START_5: &[(&[u8], Option)] = &[ - (b"A", Some(8)), - (b"B", None), - (b"C", Some(13)), - (b"D", None), - ]; + const VALUES_START_5: &[(&str, Option)] = + &[("A", Some(8)), ("B", None), ("C", Some(13)), ("D", None)]; // Same as `init_data`, but we have a composite key for testing range_de. fn init_data_composite_key(map: &TestMapCompositeKey, storage: &mut dyn Storage) { @@ -332,7 +324,7 @@ mod tests { map: &TestMap, storage: &dyn Storage, height: u64, - values: &[(&[u8], Option)], + values: &[(&str, Option)], ) { for (k, v) in values.iter().cloned() { assert_eq!(v, map.may_load_at_height(storage, k, height).unwrap()); @@ -340,7 +332,7 @@ mod tests { } fn assert_missing_checkpoint(map: &TestMap, storage: &dyn Storage, height: u64) { - for k in &[b"A", b"B", b"C", b"D"] { + for k in &["A", "B", "C", "D"] { assert!(map.may_load_at_height(storage, *k, height).is_err()); } } @@ -386,57 +378,39 @@ mod tests { let mut storage = MockStorage::new(); println!("SETUP"); - EVERY.save(&mut storage, b"A", &5, 1).unwrap(); - EVERY.save(&mut storage, b"B", &7, 2).unwrap(); - EVERY.save(&mut storage, b"C", &2, 2).unwrap(); + EVERY.save(&mut storage, "A", &5, 1).unwrap(); + EVERY.save(&mut storage, "B", &7, 2).unwrap(); + EVERY.save(&mut storage, "C", &2, 2).unwrap(); // update and save - A query at 3 => 5, at 4 => 12 EVERY - .update(&mut storage, b"A", 3, |_| -> StdResult { Ok(9) }) + .update(&mut storage, "A", 3, |_| -> StdResult { Ok(9) }) .unwrap(); - EVERY.save(&mut storage, b"A", &12, 3).unwrap(); - assert_eq!( - Some(5), - EVERY.may_load_at_height(&storage, b"A", 2).unwrap() - ); - assert_eq!( - Some(5), - EVERY.may_load_at_height(&storage, b"A", 3).unwrap() - ); + EVERY.save(&mut storage, "A", &12, 3).unwrap(); + assert_eq!(Some(5), EVERY.may_load_at_height(&storage, "A", 2).unwrap()); + assert_eq!(Some(5), EVERY.may_load_at_height(&storage, "A", 3).unwrap()); assert_eq!( Some(12), - EVERY.may_load_at_height(&storage, b"A", 4).unwrap() + EVERY.may_load_at_height(&storage, "A", 4).unwrap() ); // save and remove - B query at 4 => 7, at 5 => None - EVERY.save(&mut storage, b"B", &17, 4).unwrap(); - EVERY.remove(&mut storage, b"B", 4).unwrap(); - assert_eq!( - Some(7), - EVERY.may_load_at_height(&storage, b"B", 3).unwrap() - ); - assert_eq!( - Some(7), - EVERY.may_load_at_height(&storage, b"B", 4).unwrap() - ); - assert_eq!(None, EVERY.may_load_at_height(&storage, b"B", 5).unwrap()); + EVERY.save(&mut storage, "B", &17, 4).unwrap(); + EVERY.remove(&mut storage, "B", 4).unwrap(); + assert_eq!(Some(7), EVERY.may_load_at_height(&storage, "B", 3).unwrap()); + assert_eq!(Some(7), EVERY.may_load_at_height(&storage, "B", 4).unwrap()); + assert_eq!(None, EVERY.may_load_at_height(&storage, "B", 5).unwrap()); // remove and update - C query at 5 => 2, at 6 => 16 - EVERY.remove(&mut storage, b"C", 5).unwrap(); + EVERY.remove(&mut storage, "C", 5).unwrap(); EVERY - .update(&mut storage, b"C", 5, |_| -> StdResult { Ok(16) }) + .update(&mut storage, "C", 5, |_| -> StdResult { Ok(16) }) .unwrap(); - assert_eq!( - Some(2), - EVERY.may_load_at_height(&storage, b"C", 4).unwrap() - ); - assert_eq!( - Some(2), - EVERY.may_load_at_height(&storage, b"C", 5).unwrap() - ); + assert_eq!(Some(2), EVERY.may_load_at_height(&storage, "C", 4).unwrap()); + assert_eq!(Some(2), EVERY.may_load_at_height(&storage, "C", 5).unwrap()); assert_eq!( Some(16), - EVERY.may_load_at_height(&storage, b"C", 6).unwrap() + EVERY.may_load_at_height(&storage, "C", 6).unwrap() ); } @@ -454,7 +428,7 @@ mod tests { .collect(); let all = all.unwrap(); assert_eq!(2, all.len()); - assert_eq!(all, vec![(b"C".to_vec(), 13), (b"D".to_vec(), 22)]); + assert_eq!(all, vec![("C".into(), 13), ("D".into(), 22)]); // let's try to iterate over a range let all: StdResult> = EVERY @@ -467,7 +441,7 @@ mod tests { .collect(); let all = all.unwrap(); assert_eq!(2, all.len()); - assert_eq!(all, vec![(b"C".to_vec(), 13), (b"D".to_vec(), 22)]); + assert_eq!(all, vec![("C".into(), 13), ("D".into(), 22)]); // let's try to iterate over a more restrictive range let all: StdResult> = EVERY @@ -480,7 +454,7 @@ mod tests { .collect(); let all = all.unwrap(); assert_eq!(1, all.len()); - assert_eq!(all, vec![(b"D".to_vec(), 22)]); + assert_eq!(all, vec![("D".into(), 22)]); } #[test] From 3f0702f78576b2509eb75940a9471d57b7491bbb Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 9 Nov 2021 16:09:14 +0100 Subject: [PATCH 21/42] Adapt composite key tests to better show key deserialization --- packages/storage-plus/src/snapshot/map.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/storage-plus/src/snapshot/map.rs b/packages/storage-plus/src/snapshot/map.rs index abfa3e5cc..82adf8468 100644 --- a/packages/storage-plus/src/snapshot/map.rs +++ b/packages/storage-plus/src/snapshot/map.rs @@ -224,7 +224,7 @@ mod tests { use cosmwasm_std::testing::MockStorage; type TestMap = SnapshotMap<'static, &'static str, u64>; - type TestMapCompositeKey = SnapshotMap<'static, (&'static [u8], &'static [u8]), u64>; + type TestMapCompositeKey = SnapshotMap<'static, (&'static str, &'static str), u64>; const NEVER: TestMap = SnapshotMap::new("never", "never__check", "never__change", Strategy::Never); @@ -291,24 +291,24 @@ mod tests { // Same as `init_data`, but we have a composite key for testing range_de. fn init_data_composite_key(map: &TestMapCompositeKey, storage: &mut dyn Storage) { - map.save(storage, (b"A", b"B"), &5, 1).unwrap(); - map.save(storage, (b"B", b"A"), &7, 2).unwrap(); + map.save(storage, ("A", "B"), &5, 1).unwrap(); + map.save(storage, ("B", "A"), &7, 2).unwrap(); // checkpoint 3 map.add_checkpoint(storage, 3).unwrap(); // also use update to set - to ensure this works - map.save(storage, (b"B", b"B"), &1, 3).unwrap(); - map.update(storage, (b"A", b"B"), 3, |_| -> StdResult { Ok(8) }) + map.save(storage, ("B", "B"), &1, 3).unwrap(); + map.update(storage, ("A", "B"), 3, |_| -> StdResult { Ok(8) }) .unwrap(); - map.remove(storage, (b"B", b"A"), 4).unwrap(); - map.save(storage, (b"B", b"B"), &13, 4).unwrap(); + map.remove(storage, ("B", "A"), 4).unwrap(); + map.save(storage, ("B", "B"), &13, 4).unwrap(); // checkpoint 5 map.add_checkpoint(storage, 5).unwrap(); - map.remove(storage, (b"A", b"B"), 5).unwrap(); - map.update(storage, (b"C", b"A"), 5, |_| -> StdResult { Ok(22) }) + map.remove(storage, ("A", "B"), 5).unwrap(); + map.update(storage, ("C", "A"), 5, |_| -> StdResult { Ok(22) }) .unwrap(); // and delete it later (unknown if all data present) map.remove_checkpoint(storage, 5).unwrap(); @@ -474,8 +474,8 @@ mod tests { assert_eq!( all, vec![ - ((b"B".to_vec(), b"B".to_vec()), 13), - ((b"C".to_vec(), b"A".to_vec()), 22) + (("B".into(), "B".into()), 13), + (("C".into(), "A".into()), 22) ] ); } From 18ca487bad7c5506c514f3dded6ea331fc15cd23 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 9 Nov 2021 16:14:10 +0100 Subject: [PATCH 22/42] Add prefix_de test --- packages/storage-plus/src/snapshot/map.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/storage-plus/src/snapshot/map.rs b/packages/storage-plus/src/snapshot/map.rs index 82adf8468..f99662424 100644 --- a/packages/storage-plus/src/snapshot/map.rs +++ b/packages/storage-plus/src/snapshot/map.rs @@ -479,4 +479,22 @@ mod tests { ] ); } + + #[test] + #[cfg(feature = "iterator")] + fn prefix_de_composite_key() { + use cosmwasm_std::Order; + + let mut store = MockStorage::new(); + init_data_composite_key(&EVERY_COMPOSITE_KEY, &mut store); + + // let's prefix and iterate + let all: StdResult> = EVERY_COMPOSITE_KEY + .prefix_de("C") + .range_de(&store, None, None, Order::Ascending) + .collect(); + let all = all.unwrap(); + assert_eq!(1, all.len()); + assert_eq!(all, vec![("A".into(), 22),]); + } } From eddddf02e595909a2394cc06c14e81fc9f7522f3 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 9 Nov 2021 16:16:43 +0100 Subject: [PATCH 23/42] Add missing keys_de shortcut --- packages/storage-plus/src/snapshot/map.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/storage-plus/src/snapshot/map.rs b/packages/storage-plus/src/snapshot/map.rs index f99662424..1f11bd087 100644 --- a/packages/storage-plus/src/snapshot/map.rs +++ b/packages/storage-plus/src/snapshot/map.rs @@ -205,6 +205,20 @@ where self.no_prefix_de().range_de(store, min, max, order) } + pub fn keys_de<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + K::Output: 'static, + { + self.no_prefix_de().keys_de(store, min, max, order) + } + pub fn prefix_de(&self, p: K::Prefix) -> Prefix { Prefix::new(self.primary.namespace(), &p.prefix()) } From 1148c3506d7eb74a6d8f00a13a390691b7286580 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 9 Nov 2021 16:22:20 +0100 Subject: [PATCH 24/42] Add missing prefix_range_de to SnapshotMap --- packages/storage-plus/src/snapshot/map.rs | 25 ++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/storage-plus/src/snapshot/map.rs b/packages/storage-plus/src/snapshot/map.rs index 1f11bd087..d8b6219e1 100644 --- a/packages/storage-plus/src/snapshot/map.rs +++ b/packages/storage-plus/src/snapshot/map.rs @@ -4,10 +4,11 @@ use serde::Serialize; use cosmwasm_std::{StdError, StdResult, Storage}; use crate::de::KeyDeserialize; +use crate::iter_helpers::deserialize_kv; use crate::keys::PrimaryKey; use crate::map::Map; use crate::path::Path; -use crate::prefix::Prefix; +use crate::prefix::{namespaced_prefix_range, Prefix, PrefixBound}; use crate::snapshot::Snapshot; use crate::{Bound, Prefixer, Strategy}; @@ -191,6 +192,28 @@ where T: Serialize + DeserializeOwned, K: PrimaryKey<'a> + KeyDeserialize, { + /// while range_de assumes you set the prefix to one element and call range over the last one, + /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to + /// accept, and iterates over those. There are some issues that distinguish these to and blindly + /// casting to Vec doesn't solve them. + pub fn prefix_range_de<'c>( + &self, + store: &'c dyn Storage, + min: Option>, + max: Option>, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + 'a: 'c, + K: 'c, + K::Output: 'static, + { + let mapped = namespaced_prefix_range(store, self.primary.namespace(), min, max, order) + .map(deserialize_kv::); + Box::new(mapped) + } + pub fn range_de<'c>( &self, store: &'c dyn Storage, From 3c2ce00e12728505e06ed8ac9d2ae29d81841ec8 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 19 Nov 2021 15:57:10 +0100 Subject: [PATCH 25/42] Add extra sub_prefix_de test --- packages/storage-plus/src/snapshot/map.rs | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/storage-plus/src/snapshot/map.rs b/packages/storage-plus/src/snapshot/map.rs index d8b6219e1..fffcc4525 100644 --- a/packages/storage-plus/src/snapshot/map.rs +++ b/packages/storage-plus/src/snapshot/map.rs @@ -534,4 +534,30 @@ mod tests { assert_eq!(1, all.len()); assert_eq!(all, vec![("A".into(), 22),]); } + + #[test] + #[cfg(feature = "iterator")] + fn sub_prefix_de_composite_key() { + use cosmwasm_std::Order; + + let mut store = MockStorage::new(); + init_data_composite_key(&EVERY_COMPOSITE_KEY, &mut store); + + // Let's sub-prefix and iterate. + // This is similar to calling range() directly, but added here for completeness / + // sub_prefix_de type checks + let all: StdResult> = EVERY_COMPOSITE_KEY + .sub_prefix_de(()) + .range_de(&store, None, None, Order::Ascending) + .collect(); + let all = all.unwrap(); + assert_eq!(2, all.len()); + assert_eq!( + all, + vec![ + (("B".into(), "B".into()), 13), + (("C".into(), "A".into()), 22) + ] + ); + } } From 4c8ca2445176bcb4a87efd9a0cfd026cfb4d1bab Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 19 Nov 2021 16:21:16 +0100 Subject: [PATCH 26/42] Add prefix_range_de test for completeness --- packages/storage-plus/src/snapshot/map.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/storage-plus/src/snapshot/map.rs b/packages/storage-plus/src/snapshot/map.rs index fffcc4525..a4058ac4e 100644 --- a/packages/storage-plus/src/snapshot/map.rs +++ b/packages/storage-plus/src/snapshot/map.rs @@ -517,6 +517,28 @@ mod tests { ); } + #[test] + #[cfg(feature = "iterator")] + fn prefix_range_de_composite_key() { + use cosmwasm_std::Order; + + let mut store = MockStorage::new(); + init_data_composite_key(&EVERY_COMPOSITE_KEY, &mut store); + + // let's prefix-range and iterate + let all: StdResult> = EVERY_COMPOSITE_KEY + .prefix_range_de( + &store, + None, + Some(PrefixBound::exclusive("C")), + Order::Descending, + ) + .collect(); + let all = all.unwrap(); + assert_eq!(1, all.len()); + assert_eq!(all, vec![(("B".into(), "B".into()), 13)]); + } + #[test] #[cfg(feature = "iterator")] fn prefix_de_composite_key() { From 4db7407d2c5b19709fb67004b0123cea3b149570 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Tue, 9 Nov 2021 17:10:03 +0100 Subject: [PATCH 27/42] Add sub_/prefix_de to SnapshotMap for completeness --- packages/storage-plus/src/indexed_map.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 513dec2f2..b10301afd 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -196,6 +196,22 @@ where } } +#[cfg(feature = "iterator")] +impl<'a, K, T, I> IndexedMap<'a, K, T, I> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, + I: IndexList, +{ + pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { + Prefix::new(self.pk_namespace, &p.prefix()) + } + + pub fn prefix_de(&self, p: K::Prefix) -> Prefix { + Prefix::new(self.pk_namespace, &p.prefix()) + } +} + #[cfg(feature = "iterator")] impl<'a, K, T, I> IndexedMap<'a, K, T, I> where @@ -220,7 +236,7 @@ where K: 'c, K::Output: 'static, { - let mapped = namespaced_prefix_range(store, self.primary.namespace(), min, max, order) + let mapped = namespaced_prefix_range(store, self.pk_namespace, min, max, order) .map(deserialize_kv::); Box::new(mapped) } @@ -254,7 +270,7 @@ where } fn no_prefix_de(&self) -> Prefix { - Prefix::new(self.primary.namespace(), &[]) + Prefix::new(self.pk_namespace, &[]) } } From 7f5b80f87f2152d30119f7280129818539e2d18a Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 19 Nov 2021 16:57:45 +0100 Subject: [PATCH 28/42] Add sub_/prefix_de tests --- packages/storage-plus/src/indexed_map.rs | 207 +++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index b10301afd..4a6f0afb3 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -1039,6 +1039,213 @@ mod test { ); } + #[test] + #[cfg(feature = "iterator")] + fn prefix_de_simple_string_key() { + let mut store = MockStorage::new(); + let map = build_map(); + + // save data + let (pks, datas) = save_data(&mut store, &map); + + // Let's prefix and iterate. + // This is similar to calling range() directly, but added here for completeness / prefix_de + // type checks + let all: StdResult> = map + .prefix_de(()) + .range_de(&store, None, None, Order::Ascending) + .collect(); + let all = all.unwrap(); + assert_eq!( + all, + pks.clone() + .into_iter() + .map(str::to_string) + .zip(datas.clone().into_iter()) + .collect::>() + ); + } + + #[test] + #[cfg(feature = "iterator")] + fn prefix_de_composite_key() { + let mut store = MockStorage::new(); + + let indexes = DataCompositeMultiIndex { + name_age: MultiIndex::new( + |d, k| index_triple(&d.name, d.age, k), + "data", + "data__name_age", + ), + }; + let map = IndexedMap::new("data", indexes); + + // save data + let data1 = Data { + name: "Maria".to_string(), + last_name: "".to_string(), + age: 42, + }; + let pk1: (&str, &str) = ("1", "5627"); + map.save(&mut store, pk1, &data1).unwrap(); + + let data2 = Data { + name: "Juan".to_string(), + last_name: "Perez".to_string(), + age: 13, + }; + let pk2: (&str, &str) = ("2", "5628"); + map.save(&mut store, pk2, &data2).unwrap(); + + let data3 = Data { + name: "Maria".to_string(), + last_name: "Young".to_string(), + age: 24, + }; + let pk3: (&str, &str) = ("2", "5629"); + map.save(&mut store, pk3, &data3).unwrap(); + + let data4 = Data { + name: "Maria Luisa".to_string(), + last_name: "Bemberg".to_string(), + age: 43, + }; + let pk4: (&str, &str) = ("3", "5630"); + map.save(&mut store, pk4, &data4).unwrap(); + + // let's prefix and iterate + let result: StdResult> = map + .prefix_de("2") + .range_de(&store, None, None, Order::Ascending) + .collect(); + let result = result.unwrap(); + assert_eq!( + result, + [ + ("5628".to_string(), data2.clone()), + ("5629".to_string(), data3.clone()), + ] + ); + } + + #[test] + #[cfg(feature = "iterator")] + fn prefix_de_triple_key() { + let mut store = MockStorage::new(); + + let indexes = DataCompositeMultiIndex { + name_age: MultiIndex::new( + |d, k| index_triple(&d.name, d.age, k), + "data", + "data__name_age", + ), + }; + let map = IndexedMap::new("data", indexes); + + // save data + let data1 = Data { + name: "Maria".to_string(), + last_name: "".to_string(), + age: 42, + }; + let pk1: (&str, &str, &str) = ("1", "1", "5627"); + map.save(&mut store, pk1, &data1).unwrap(); + + let data2 = Data { + name: "Juan".to_string(), + last_name: "Perez".to_string(), + age: 13, + }; + let pk2: (&str, &str, &str) = ("1", "2", "5628"); + map.save(&mut store, pk2, &data2).unwrap(); + + let data3 = Data { + name: "Maria".to_string(), + last_name: "Young".to_string(), + age: 24, + }; + let pk3: (&str, &str, &str) = ("2", "1", "5629"); + map.save(&mut store, pk3, &data3).unwrap(); + + let data4 = Data { + name: "Maria Luisa".to_string(), + last_name: "Bemberg".to_string(), + age: 43, + }; + let pk4: (&str, &str, &str) = ("2", "2", "5630"); + map.save(&mut store, pk4, &data4).unwrap(); + + // let's prefix and iterate + let result: StdResult> = map + .prefix_de(("1", "2")) + .range_de(&store, None, None, Order::Ascending) + .collect(); + let result = result.unwrap(); + assert_eq!(result, [("5628".to_string(), data2.clone()),]); + } + + #[test] + #[cfg(feature = "iterator")] + fn sub_prefix_de_triple_key() { + let mut store = MockStorage::new(); + + let indexes = DataCompositeMultiIndex { + name_age: MultiIndex::new( + |d, k| index_triple(&d.name, d.age, k), + "data", + "data__name_age", + ), + }; + let map = IndexedMap::new("data", indexes); + + // save data + let data1 = Data { + name: "Maria".to_string(), + last_name: "".to_string(), + age: 42, + }; + let pk1: (&str, &str, &str) = ("1", "1", "5627"); + map.save(&mut store, pk1, &data1).unwrap(); + + let data2 = Data { + name: "Juan".to_string(), + last_name: "Perez".to_string(), + age: 13, + }; + let pk2: (&str, &str, &str) = ("1", "2", "5628"); + map.save(&mut store, pk2, &data2).unwrap(); + + let data3 = Data { + name: "Maria".to_string(), + last_name: "Young".to_string(), + age: 24, + }; + let pk3: (&str, &str, &str) = ("2", "1", "5629"); + map.save(&mut store, pk3, &data3).unwrap(); + + let data4 = Data { + name: "Maria Luisa".to_string(), + last_name: "Bemberg".to_string(), + age: 43, + }; + let pk4: (&str, &str, &str) = ("2", "2", "5630"); + map.save(&mut store, pk4, &data4).unwrap(); + + // let's sub-prefix and iterate + let result: StdResult> = map + .sub_prefix_de("1") + .range_de(&store, None, None, Order::Ascending) + .collect(); + let result = result.unwrap(); + assert_eq!( + result, + [ + (("1".to_string(), "5627".to_string()), data1.clone()), + (("2".to_string(), "5628".to_string()), data2.clone()), + ] + ); + } + #[test] #[cfg(feature = "iterator")] fn prefix_range_de() { From e5eafe6b6c99fcb8360f361650d5515f97d3ce29 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 19 Nov 2021 17:16:58 +0100 Subject: [PATCH 29/42] Change test names for consistency --- packages/storage-plus/src/indexed_map.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 4a6f0afb3..798db6b11 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -906,7 +906,7 @@ mod test { } #[test] - fn unique_index_simple_key_range_de() { + fn range_de_simple_key_by_unique_index() { let mut store = MockStorage::new(); let map = build_map(); @@ -968,7 +968,7 @@ mod test { } #[test] - fn unique_index_composite_key_range_de() { + fn range_de_composite_key_by_unique_index() { let mut store = MockStorage::new(); let map = build_map(); @@ -1248,7 +1248,7 @@ mod test { #[test] #[cfg(feature = "iterator")] - fn prefix_range_de() { + fn prefix_range_de_simple_key() { let mut store = MockStorage::new(); let indexes = DataCompositeMultiIndex { From f56f457465739887567eb609c8f02a03efda824d Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 19 Nov 2021 17:27:27 +0100 Subject: [PATCH 30/42] Change prefix_range_de tests to use string keys --- packages/storage-plus/src/indexed_map.rs | 61 ++++++++++++++---------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 798db6b11..6dea88c04 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -1266,7 +1266,7 @@ mod test { last_name: "".to_string(), age: 42, }; - let pk1: (&[u8], &[u8]) = (b"1", b"5627"); + let pk1: (&str, &str) = ("1", "5627"); map.save(&mut store, pk1, &data1).unwrap(); let data2 = Data { @@ -1274,7 +1274,7 @@ mod test { last_name: "Perez".to_string(), age: 13, }; - let pk2: (&[u8], &[u8]) = (b"2", b"5628"); + let pk2: (&str, &str) = ("2", "5628"); map.save(&mut store, pk2, &data2).unwrap(); let data3 = Data { @@ -1282,7 +1282,7 @@ mod test { last_name: "Young".to_string(), age: 24, }; - let pk3: (&[u8], &[u8]) = (b"2", b"5629"); + let pk3: (&str, &str) = ("2", "5629"); map.save(&mut store, pk3, &data3).unwrap(); let data4 = Data { @@ -1290,14 +1290,14 @@ mod test { last_name: "Bemberg".to_string(), age: 43, }; - let pk4: (&[u8], &[u8]) = (b"3", b"5630"); + let pk4: (&str, &str) = ("3", "5630"); map.save(&mut store, pk4, &data4).unwrap(); // let's try to iterate! let result: StdResult> = map .prefix_range_de( &store, - Some(PrefixBound::inclusive(&b"2"[..])), + Some(PrefixBound::inclusive("2")), None, Order::Ascending, ) @@ -1306,9 +1306,9 @@ mod test { assert_eq!( result, [ - ((b"2".to_vec(), b"5628".to_vec()), data2.clone()), - ((b"2".to_vec(), b"5629".to_vec()), data3.clone()), - ((b"3".to_vec(), b"5630".to_vec()), data4) + (("2".to_string(), "5628".to_string()), data2.clone()), + (("2".to_string(), "5629".to_string()), data3.clone()), + (("3".to_string(), "5630".to_string()), data4) ] ); @@ -1316,8 +1316,8 @@ mod test { let result: StdResult> = map .prefix_range_de( &store, - Some(PrefixBound::inclusive(&b"2"[..])), - Some(PrefixBound::exclusive(&b"3"[..])), + Some(PrefixBound::inclusive("2")), + Some(PrefixBound::exclusive("3")), Order::Ascending, ) .collect(); @@ -1325,8 +1325,8 @@ mod test { assert_eq!( result, [ - ((b"2".to_vec(), b"5628".to_vec()), data2), - ((b"2".to_vec(), b"5629".to_vec()), data3), + (("2".to_string(), "5628".to_string()), data2), + (("2".to_string(), "5629".to_string()), data3), ] ); } @@ -1351,7 +1351,7 @@ mod test { last_name: "".to_string(), age: 42, }; - let pk1: (&[u8], &[u8], &[u8]) = (b"1", b"1", b"5627"); + let pk1: (&str, &str, &str) = ("1", "1", "5627"); map.save(&mut store, pk1, &data1).unwrap(); let data2 = Data { @@ -1359,7 +1359,7 @@ mod test { last_name: "Perez".to_string(), age: 13, }; - let pk2: (&[u8], &[u8], &[u8]) = (b"1", b"2", b"5628"); + let pk2: (&str, &str, &str) = ("1", "2", "5628"); map.save(&mut store, pk2, &data2).unwrap(); let data3 = Data { @@ -1367,7 +1367,7 @@ mod test { last_name: "Young".to_string(), age: 24, }; - let pk3: (&[u8], &[u8], &[u8]) = (b"2", b"1", b"5629"); + let pk3: (&str, &str, &str) = ("2", "1", "5629"); map.save(&mut store, pk3, &data3).unwrap(); let data4 = Data { @@ -1375,14 +1375,14 @@ mod test { last_name: "Bemberg".to_string(), age: 43, }; - let pk4: (&[u8], &[u8], &[u8]) = (b"2", b"2", b"5630"); + let pk4: (&str, &str, &str) = ("2", "2", "5630"); map.save(&mut store, pk4, &data4).unwrap(); - // let's try to iterate! + // let's prefix-range and iterate let result: StdResult> = map .prefix_range_de( &store, - Some(PrefixBound::inclusive((&b"1"[..], &b"2"[..]))), + Some(PrefixBound::inclusive(("1", "2"))), None, Order::Ascending, ) @@ -1392,23 +1392,26 @@ mod test { result, [ ( - (b"1".to_vec(), b"2".to_vec(), b"5628".to_vec()), + ("1".to_string(), "2".to_string(), "5628".to_string()), data2.clone() ), ( - (b"2".to_vec(), b"1".to_vec(), b"5629".to_vec()), + ("2".to_string(), "1".to_string(), "5629".to_string()), data3.clone() ), - ((b"2".to_vec(), b"2".to_vec(), b"5630".to_vec()), data4) + ( + ("2".to_string(), "2".to_string(), "5630".to_string()), + data4 + ) ] ); - // let's try to iterate over a range + // let's prefix-range over inclusive bounds on both sides let result: StdResult> = map .prefix_range_de( &store, - Some(PrefixBound::inclusive((&b"1"[..], &b"2"[..]))), - Some(PrefixBound::inclusive((&b"2"[..], &b"1"[..]))), + Some(PrefixBound::inclusive(("1", "2"))), + Some(PrefixBound::inclusive(("2", "1"))), Order::Ascending, ) .collect(); @@ -1416,8 +1419,14 @@ mod test { assert_eq!( result, [ - ((b"1".to_vec(), b"2".to_vec(), b"5628".to_vec()), data2), - ((b"2".to_vec(), b"1".to_vec(), b"5629".to_vec()), data3), + ( + ("1".to_string(), "2".to_string(), "5628".to_string()), + data2 + ), + ( + ("2".to_string(), "1".to_string(), "5629".to_string()), + data3 + ), ] ); } From cae3aceb88e2bad5677434ad7a04725835e139b4 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 19 Nov 2021 17:32:17 +0100 Subject: [PATCH 31/42] Remove unneeded clones --- packages/storage-plus/src/indexed_map.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 6dea88c04..7e51f46dc 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -1061,7 +1061,7 @@ mod test { pks.clone() .into_iter() .map(str::to_string) - .zip(datas.clone().into_iter()) + .zip(datas.into_iter()) .collect::>() ); } @@ -1121,10 +1121,7 @@ mod test { let result = result.unwrap(); assert_eq!( result, - [ - ("5628".to_string(), data2.clone()), - ("5629".to_string(), data3.clone()), - ] + [("5628".to_string(), data2), ("5629".to_string(), data3),] ); } @@ -1181,7 +1178,7 @@ mod test { .range_de(&store, None, None, Order::Ascending) .collect(); let result = result.unwrap(); - assert_eq!(result, [("5628".to_string(), data2.clone()),]); + assert_eq!(result, [("5628".to_string(), data2),]); } #[test] @@ -1240,8 +1237,8 @@ mod test { assert_eq!( result, [ - (("1".to_string(), "5627".to_string()), data1.clone()), - (("2".to_string(), "5628".to_string()), data2.clone()), + (("1".to_string(), "5627".to_string()), data1), + (("2".to_string(), "5628".to_string()), data2), ] ); } From c6d55d0a8948700fea5294d8686c1021f6f88896 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 19 Nov 2021 19:43:42 +0100 Subject: [PATCH 32/42] Add unique index key deserialization FIXMEs --- packages/storage-plus/src/indexes/unique.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/storage-plus/src/indexes/unique.rs b/packages/storage-plus/src/indexes/unique.rs index 9f1060372..190a65e0a 100644 --- a/packages/storage-plus/src/indexes/unique.rs +++ b/packages/storage-plus/src/indexes/unique.rs @@ -92,6 +92,7 @@ where fn deserialize_unique_v(kv: Record) -> StdResult> { let (_, v) = kv; let t = from_slice::>(&v)?; + // FIXME: Return `k` here instead of `t.pk` (be consistent with `Map` behaviour) Ok((t.pk.to_vec(), t.value)) } @@ -100,6 +101,7 @@ fn deserialize_unique_kv( ) -> StdResult<(K::Output, T)> { let (_, v) = kv; let t = from_slice::>(&v)?; + // FIXME: Return `k` deserialization here instead of `t.pk` (be consistent with `deserialize_multi_kv` and `Map` behaviour) Ok((K::from_vec(t.pk.to_vec())?, t.value)) } From 10ce6d60ec5cbf630d11700ff46261d7faba6abd Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sun, 21 Nov 2021 12:14:13 +0100 Subject: [PATCH 33/42] Change unique index test names for consistency --- packages/storage-plus/src/indexed_map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index 7e51f46dc..c5ab17230 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -873,7 +873,7 @@ mod test { } #[test] - fn unique_index_simple_key_range() { + fn range_simple_key_by_unique_index() { let mut store = MockStorage::new(); let map = build_map(); @@ -939,7 +939,7 @@ mod test { } #[test] - fn unique_index_composite_key_range() { + fn range_composite_key_by_unique_index() { let mut store = MockStorage::new(); let map = build_map(); From d4af909f133230702212df67d435c2d8feff9bdd Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 10 Nov 2021 12:56:36 +0100 Subject: [PATCH 34/42] Add range_/keys_de to IndexedSnapshotMap --- packages/storage-plus/src/indexed_snapshot.rs | 65 ++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/packages/storage-plus/src/indexed_snapshot.rs b/packages/storage-plus/src/indexed_snapshot.rs index adbe2a4da..9321ab010 100644 --- a/packages/storage-plus/src/indexed_snapshot.rs +++ b/packages/storage-plus/src/indexed_snapshot.rs @@ -6,8 +6,9 @@ use serde::de::DeserializeOwned; use serde::Serialize; use crate::de::KeyDeserialize; +use crate::iter_helpers::deserialize_kv; use crate::keys::{Prefixer, PrimaryKey}; -use crate::prefix::{Bound, Prefix}; +use crate::prefix::{namespaced_prefix_range, Bound, Prefix, PrefixBound}; use crate::snapshot::SnapshotMap; use crate::{IndexList, Path, Strategy}; @@ -212,6 +213,68 @@ where } } +#[cfg(feature = "iterator")] +impl<'a, K, T, I> IndexedSnapshotMap<'a, K, T, I> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a> + KeyDeserialize, + I: IndexList, +{ + /// while range_de assumes you set the prefix to one element and call range over the last one, + /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to + /// accept, and iterates over those. There are some issues that distinguish these to and blindly + /// casting to Vec doesn't solve them. + pub fn prefix_range_de<'c>( + &self, + store: &'c dyn Storage, + min: Option>, + max: Option>, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + 'a: 'c, + K: 'c, + K::Output: 'static, + { + let mapped = namespaced_prefix_range(store, self.pk_namespace, min, max, order) + .map(deserialize_kv::); + Box::new(mapped) + } + + pub fn range_de<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + K::Output: 'static, + { + self.no_prefix_de().range_de(store, min, max, order) + } + + pub fn keys_de<'c>( + &self, + store: &'c dyn Storage, + min: Option, + max: Option, + order: cosmwasm_std::Order, + ) -> Box> + 'c> + where + T: 'c, + K::Output: 'static, + { + self.no_prefix_de().keys_de(store, min, max, order) + } + + fn no_prefix_de(&self) -> Prefix { + Prefix::new(self.pk_namespace, &[]) + } +} + #[cfg(test)] mod test { use super::*; From e070ab42e362f15c919274ffacd740a96633cf02 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 10 Nov 2021 12:58:52 +0100 Subject: [PATCH 35/42] Add sub_/prefix_de to IndexedSnapshotMap for completeness --- packages/storage-plus/src/indexed_snapshot.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/storage-plus/src/indexed_snapshot.rs b/packages/storage-plus/src/indexed_snapshot.rs index 9321ab010..dc7afff71 100644 --- a/packages/storage-plus/src/indexed_snapshot.rs +++ b/packages/storage-plus/src/indexed_snapshot.rs @@ -213,6 +213,22 @@ where } } +#[cfg(feature = "iterator")] +impl<'a, K, T, I> IndexedSnapshotMap<'a, K, T, I> +where + T: Serialize + DeserializeOwned + Clone, + K: PrimaryKey<'a>, + I: IndexList, +{ + pub fn sub_prefix_de(&self, p: K::SubPrefix) -> Prefix { + Prefix::new(self.pk_namespace, &p.prefix()) + } + + pub fn prefix_de(&self, p: K::Prefix) -> Prefix { + Prefix::new(self.pk_namespace, &p.prefix()) + } +} + #[cfg(feature = "iterator")] impl<'a, K, T, I> IndexedSnapshotMap<'a, K, T, I> where From 535c8ffeec6f9cb845bfa99b2dcccea22db81bf9 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 19 Nov 2021 19:03:24 +0100 Subject: [PATCH 36/42] Adapt tests to better work with deserializable values --- packages/storage-plus/src/indexed_snapshot.rs | 74 ++++++++++--------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/packages/storage-plus/src/indexed_snapshot.rs b/packages/storage-plus/src/indexed_snapshot.rs index dc7afff71..e2a335994 100644 --- a/packages/storage-plus/src/indexed_snapshot.rs +++ b/packages/storage-plus/src/indexed_snapshot.rs @@ -310,7 +310,7 @@ mod test { struct DataIndexes<'a> { // Second arg is for storing pk - pub name: MultiIndex<'a, (Vec, Vec), Data>, + pub name: MultiIndex<'a, (Vec, String), Data>, pub age: UniqueIndex<'a, U32Key, Data>, pub name_lastname: UniqueIndex<'a, (Vec, Vec), Data>, } @@ -338,9 +338,17 @@ mod test { } // Can we make it easier to define this? (less wordy generic) - fn build_snapshot_map<'a>() -> IndexedSnapshotMap<'a, &'a [u8], Data, DataIndexes<'a>> { + fn build_snapshot_map<'a>() -> IndexedSnapshotMap<'a, &'a str, Data, DataIndexes<'a>> { let indexes = DataIndexes { - name: MultiIndex::new(|d, k| (d.name.as_bytes().to_vec(), k), "data", "data__name"), + name: MultiIndex::new( + |d, k| { + (d.name.as_bytes().to_vec(), unsafe { + String::from_utf8_unchecked(k) + }) + }, + "data", + "data__name", + ), age: UniqueIndex::new(|d| U32Key::new(d.age), "data__age"), name_lastname: UniqueIndex::new( |d| index_string_tuple(&d.name, &d.last_name), @@ -358,8 +366,8 @@ mod test { fn save_data<'a>( store: &mut MockStorage, - map: &IndexedSnapshotMap<'a, &'a [u8], Data, DataIndexes<'a>>, - ) -> (Vec<&'a [u8]>, Vec) { + map: &IndexedSnapshotMap<'a, &'a str, Data, DataIndexes<'a>>, + ) -> (Vec<&'a str>, Vec) { let mut pks = vec![]; let mut datas = vec![]; let mut height = 0; @@ -368,7 +376,7 @@ mod test { last_name: "Doe".to_string(), age: 42, }; - let pk: &[u8] = b"1"; + let pk: &str = "1"; map.save(store, pk, &data, height).unwrap(); height += 1; pks.push(pk); @@ -380,7 +388,7 @@ mod test { last_name: "Williams".to_string(), age: 23, }; - let pk: &[u8] = b"2"; + let pk: &str = "2"; map.save(store, pk, &data, height).unwrap(); height += 1; pks.push(pk); @@ -392,7 +400,7 @@ mod test { last_name: "Wayne".to_string(), age: 32, }; - let pk: &[u8] = b"3"; + let pk: &str = "3"; map.save(store, pk, &data, height).unwrap(); height += 1; pks.push(pk); @@ -403,7 +411,7 @@ mod test { last_name: "Rodriguez".to_string(), age: 12, }; - let pk: &[u8] = b"4"; + let pk: &str = "4"; map.save(store, pk, &data, height).unwrap(); pks.push(pk); datas.push(data); @@ -446,7 +454,7 @@ mod test { .unwrap(); assert_eq!(2, marias.len()); let (k, v) = &marias[0]; - assert_eq!(pk, k.as_slice()); + assert_eq!(pk.as_bytes(), k); assert_eq!(data, v); // other index doesn't match (1 byte after) @@ -479,7 +487,7 @@ mod test { // match on proper age let proper = U32Key::new(42); let aged = map.idx.age.item(&store, proper).unwrap().unwrap(); - assert_eq!(pk.to_vec(), aged.0); + assert_eq!(pk.as_bytes(), aged.0); assert_eq!(*data, aged.1); // no match on wrong age @@ -500,7 +508,7 @@ mod test { last_name: "".to_string(), age: 42, }; - let pk: &[u8] = b"5627"; + let pk: &str = "5627"; map.save(&mut store, pk, &data1, height).unwrap(); height += 1; @@ -509,7 +517,7 @@ mod test { last_name: "Perez".to_string(), age: 13, }; - let pk: &[u8] = b"5628"; + let pk: &str = "5628"; map.save(&mut store, pk, &data2, height).unwrap(); height += 1; @@ -518,7 +526,7 @@ mod test { last_name: "Williams".to_string(), age: 24, }; - let pk: &[u8] = b"5629"; + let pk: &str = "5629"; map.save(&mut store, pk, &data3, height).unwrap(); height += 1; @@ -527,7 +535,7 @@ mod test { last_name: "Bemberg".to_string(), age: 12, }; - let pk: &[u8] = b"5630"; + let pk: &str = "5630"; map.save(&mut store, pk, &data4, height).unwrap(); let marias: Vec<_> = map @@ -569,7 +577,7 @@ mod test { last_name: "".to_string(), age: 42, }; - let pk1: &[u8] = b"5627"; + let pk1: &str = "5627"; map.save(&mut store, pk1, &data1, height).unwrap(); height += 1; @@ -578,7 +586,7 @@ mod test { last_name: "Perez".to_string(), age: 13, }; - let pk2: &[u8] = b"5628"; + let pk2: &str = "5628"; map.save(&mut store, pk2, &data2, height).unwrap(); height += 1; @@ -587,7 +595,7 @@ mod test { last_name: "Young".to_string(), age: 24, }; - let pk3: &[u8] = b"5629"; + let pk3: &str = "5629"; map.save(&mut store, pk3, &data3, height).unwrap(); height += 1; @@ -596,7 +604,7 @@ mod test { last_name: "Bemberg".to_string(), age: 43, }; - let pk4: &[u8] = b"5630"; + let pk4: &str = "5630"; map.save(&mut store, pk4, &data4, height).unwrap(); let marias: Vec<_> = map @@ -610,8 +618,8 @@ mod test { assert_eq!(2, count); // Pks (sorted by age descending) - assert_eq!(pk1, marias[0].0); - assert_eq!(pk3, marias[1].0); + assert_eq!(pk1.as_bytes(), marias[0].0); + assert_eq!(pk3.as_bytes(), marias[1].0); // Data assert_eq!(data1, marias[0].1); @@ -633,7 +641,7 @@ mod test { last_name: "Laurens".to_string(), age: 42, }; - let pk5: &[u8] = b"4"; + let pk5: &str = "4"; // enforce this returns some error map.save(&mut store, pk5, &data5, height).unwrap_err(); @@ -643,14 +651,14 @@ mod test { // match on proper age let age42 = U32Key::new(42); let (k, v) = map.idx.age.item(&store, age42.clone()).unwrap().unwrap(); - assert_eq!(k.as_slice(), pks[0]); + assert_eq!(k, pks[0].as_bytes()); assert_eq!(v.name, datas[0].name); assert_eq!(v.age, datas[0].age); // match on other age let age23 = U32Key::new(23); let (k, v) = map.idx.age.item(&store, age23).unwrap().unwrap(); - assert_eq!(k.as_slice(), pks[1]); + assert_eq!(k, pks[1].as_bytes()); assert_eq!(v.name, datas[1].name); assert_eq!(v.age, datas[1].age); @@ -660,7 +668,7 @@ mod test { map.save(&mut store, pk5, &data5, height).unwrap(); // now 42 is the new owner let (k, v) = map.idx.age.item(&store, age42).unwrap().unwrap(); - assert_eq!(k.as_slice(), pk5); + assert_eq!(k, pk5.as_bytes()); assert_eq!(v.name, data5.name); assert_eq!(v.age, data5.age); } @@ -680,7 +688,7 @@ mod test { last_name: "Doe".to_string(), age: 24, }; - let pk5: &[u8] = b"5"; + let pk5: &str = "5"; // enforce this returns some error map.save(&mut store, pk5, &data5, height).unwrap_err(); } @@ -691,7 +699,7 @@ mod test { let map = build_snapshot_map(); let mut height = 5; - let name_count = |map: &IndexedSnapshotMap<&[u8], Data, DataIndexes>, + let name_count = |map: &IndexedSnapshotMap<&str, Data, DataIndexes>, store: &MemoryStorage, name: &str| -> usize { @@ -750,10 +758,10 @@ mod test { assert_eq!(4, count); // The pks, sorted by age ascending - assert_eq!(pks[3].to_vec(), ages[0].0); - assert_eq!(pks[1].to_vec(), ages[1].0); - assert_eq!(pks[2].to_vec(), ages[2].0); - assert_eq!(pks[0].to_vec(), ages[3].0); + assert_eq!(pks[3].as_bytes(), ages[0].0); + assert_eq!(pks[1].as_bytes(), ages[1].0); + assert_eq!(pks[2].as_bytes(), ages[2].0); + assert_eq!(pks[0].as_bytes(), ages[3].0); // The associated data assert_eq!(datas[3], ages[0].1); @@ -783,8 +791,8 @@ mod test { assert_eq!(2, count); // The pks - assert_eq!(pks[0].to_vec(), marias[0].0); - assert_eq!(pks[1].to_vec(), marias[1].0); + assert_eq!(pks[0].as_bytes(), marias[0].0); + assert_eq!(pks[1].as_bytes(), marias[1].0); // The associated data assert_eq!(datas[0], marias[0].1); From c16e79814f299e6034e9bd263a703b2dd684b692 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 19 Nov 2021 19:04:07 +0100 Subject: [PATCH 37/42] Add range_de multi index tests --- packages/storage-plus/src/indexed_snapshot.rs | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/packages/storage-plus/src/indexed_snapshot.rs b/packages/storage-plus/src/indexed_snapshot.rs index e2a335994..d2cc3a089 100644 --- a/packages/storage-plus/src/indexed_snapshot.rs +++ b/packages/storage-plus/src/indexed_snapshot.rs @@ -556,6 +556,66 @@ mod test { assert_eq!(marias[1].1, data1); } + #[test] + fn range_de_simple_key_by_multi_index() { + let mut store = MockStorage::new(); + let map = build_snapshot_map(); + let mut height = 1; + + // save data + let data1 = Data { + name: "Maria".to_string(), + last_name: "".to_string(), + age: 42, + }; + let pk: &str = "5627"; + map.save(&mut store, pk, &data1, height).unwrap(); + height += 1; + + let data2 = Data { + name: "Juan".to_string(), + last_name: "Perez".to_string(), + age: 13, + }; + let pk: &str = "5628"; + map.save(&mut store, pk, &data2, height).unwrap(); + height += 1; + + let data3 = Data { + name: "Maria".to_string(), + last_name: "Williams".to_string(), + age: 24, + }; + let pk: &str = "5629"; + map.save(&mut store, pk, &data3, height).unwrap(); + height += 1; + + let data4 = Data { + name: "Maria Luisa".to_string(), + last_name: "Bemberg".to_string(), + age: 12, + }; + let pk: &str = "5630"; + map.save(&mut store, pk, &data4, height).unwrap(); + + let marias: Vec<_> = map + .idx + .name + .prefix_de(b"Maria".to_vec()) + .range_de(&store, None, None, Order::Descending) + .collect::>() + .unwrap(); + let count = marias.len(); + assert_eq!(2, count); + + // Sorted by (descending) pk + assert_eq!(marias[0].0, "5629"); + assert_eq!(marias[1].0, "5627"); + // Data is correct + assert_eq!(marias[0].1, data3); + assert_eq!(marias[1].1, data1); + } + #[test] fn range_composite_key_by_multi_index() { let mut store = MockStorage::new(); @@ -626,6 +686,76 @@ mod test { assert_eq!(data3, marias[1].1); } + #[test] + fn range_de_composite_key_by_multi_index() { + let mut store = MockStorage::new(); + let mut height = 2; + + let indexes = DataCompositeMultiIndex { + name_age: MultiIndex::new( + |d, k| index_triple(&d.name, d.age, k), + "data", + "data__name_age", + ), + }; + let map = + IndexedSnapshotMap::new("data", "checks", "changes", Strategy::EveryBlock, indexes); + + // save data + let data1 = Data { + name: "Maria".to_string(), + last_name: "".to_string(), + age: 42, + }; + let pk1: &str = "5627"; + map.save(&mut store, pk1, &data1, height).unwrap(); + height += 1; + + let data2 = Data { + name: "Juan".to_string(), + last_name: "Perez".to_string(), + age: 13, + }; + let pk2: &str = "5628"; + map.save(&mut store, pk2, &data2, height).unwrap(); + height += 1; + + let data3 = Data { + name: "Maria".to_string(), + last_name: "Young".to_string(), + age: 24, + }; + let pk3: &str = "5629"; + map.save(&mut store, pk3, &data3, height).unwrap(); + height += 1; + + let data4 = Data { + name: "Maria Luisa".to_string(), + last_name: "Bemberg".to_string(), + age: 43, + }; + let pk4: &str = "5630"; + map.save(&mut store, pk4, &data4, height).unwrap(); + + let marias: Vec<_> = map + .idx + .name_age + .sub_prefix_de(b"Maria".to_vec()) + .range_de(&store, None, None, Order::Descending) + .collect::>() + .unwrap(); + let count = marias.len(); + assert_eq!(2, count); + + // Pks (sorted by age descending) + assert_eq!((42, pk1.as_bytes().to_owned()), marias[0].0); + assert_eq!((24, pk3.as_bytes().to_owned()), marias[1].0); + + // Data + assert_eq!(data1, marias[0].1); + assert_eq!(data3, marias[1].1); + } + #[test] fn unique_index_enforced() { let mut store = MockStorage::new(); From 30c6a50fa5d56191fb5cce8b09b9ce0fcbc2bf5a Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 19 Nov 2021 20:04:51 +0100 Subject: [PATCH 38/42] Rename unique index range tests for consistency --- packages/storage-plus/src/indexed_snapshot.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/storage-plus/src/indexed_snapshot.rs b/packages/storage-plus/src/indexed_snapshot.rs index d2cc3a089..df8b9d856 100644 --- a/packages/storage-plus/src/indexed_snapshot.rs +++ b/packages/storage-plus/src/indexed_snapshot.rs @@ -870,7 +870,7 @@ mod test { } #[test] - fn unique_index_simple_key_range() { + fn range_simple_key_by_unique_index() { let mut store = MockStorage::new(); let map = build_snapshot_map(); @@ -901,7 +901,7 @@ mod test { } #[test] - fn unique_index_composite_key_range() { + fn range_composite_key_by_unique_index() { let mut store = MockStorage::new(); let map = build_snapshot_map(); From a2a9fd70f4f02ab621f654c9f5320ff96e31c934 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Fri, 19 Nov 2021 20:05:13 +0100 Subject: [PATCH 39/42] Add range_de unique index tests --- packages/storage-plus/src/indexed_snapshot.rs | 66 ++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/packages/storage-plus/src/indexed_snapshot.rs b/packages/storage-plus/src/indexed_snapshot.rs index df8b9d856..23c58a78d 100644 --- a/packages/storage-plus/src/indexed_snapshot.rs +++ b/packages/storage-plus/src/indexed_snapshot.rs @@ -311,8 +311,10 @@ mod test { struct DataIndexes<'a> { // Second arg is for storing pk pub name: MultiIndex<'a, (Vec, String), Data>, - pub age: UniqueIndex<'a, U32Key, Data>, - pub name_lastname: UniqueIndex<'a, (Vec, Vec), Data>, + // Last generic type arg is pk deserialization type + pub age: UniqueIndex<'a, U32Key, Data, String>, + // Last generic type arg is pk deserialization type + pub name_lastname: UniqueIndex<'a, (Vec, Vec), Data, String>, } // Future Note: this can likely be macro-derived @@ -900,6 +902,37 @@ mod test { assert_eq!(datas[0], ages[3].1); } + #[test] + fn range_de_simple_key_by_unique_index() { + let mut store = MockStorage::new(); + let map = build_snapshot_map(); + + // save data + let (pks, datas) = save_data(&mut store, &map); + + let res: StdResult> = map + .idx + .age + .range_de(&store, None, None, Order::Ascending) + .collect(); + let ages = res.unwrap(); + + let count = ages.len(); + assert_eq!(4, count); + + // The pks, sorted by age ascending + assert_eq!(pks[3], ages[0].0); + assert_eq!(pks[1], ages[1].0); + assert_eq!(pks[2], ages[2].0); + assert_eq!(pks[0], ages[3].0); + + // The associated data + assert_eq!(datas[3], ages[0].1); + assert_eq!(datas[1], ages[1].1); + assert_eq!(datas[2], ages[2].1); + assert_eq!(datas[0], ages[3].1); + } + #[test] fn range_composite_key_by_unique_index() { let mut store = MockStorage::new(); @@ -928,4 +961,33 @@ mod test { assert_eq!(datas[0], marias[0].1); assert_eq!(datas[1], marias[1].1); } + + #[test] + fn range_de_composite_key_by_unique_index() { + let mut store = MockStorage::new(); + let map = build_snapshot_map(); + + // save data + let (pks, datas) = save_data(&mut store, &map); + + let res: StdResult> = map + .idx + .name_lastname + .prefix_de(b"Maria".to_vec()) + .range_de(&store, None, None, Order::Ascending) + .collect(); + let marias = res.unwrap(); + + // Only two people are called "Maria" + let count = marias.len(); + assert_eq!(2, count); + + // The pks + assert_eq!(pks[0], marias[0].0); + assert_eq!(pks[1], marias[1].0); + + // The associated data + assert_eq!(datas[0], marias[0].1); + assert_eq!(datas[1], marias[1].1); + } } From ba3099f207bbf6a4f2eee40f13372ab6b3f971be Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Sun, 21 Nov 2021 12:35:48 +0100 Subject: [PATCH 40/42] Add simple base sub_/prefix_/range_de tests for completeness --- packages/storage-plus/src/indexed_snapshot.rs | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/packages/storage-plus/src/indexed_snapshot.rs b/packages/storage-plus/src/indexed_snapshot.rs index 23c58a78d..f311ed2d5 100644 --- a/packages/storage-plus/src/indexed_snapshot.rs +++ b/packages/storage-plus/src/indexed_snapshot.rs @@ -990,4 +990,187 @@ mod test { assert_eq!(datas[0], marias[0].1); assert_eq!(datas[1], marias[1].1); } + + #[test] + #[cfg(feature = "iterator")] + fn range_de_simple_string_key() { + let mut store = MockStorage::new(); + let map = build_snapshot_map(); + + // save data + let (pks, datas) = save_data(&mut store, &map); + + // let's try to iterate! + let all: StdResult> = map.range_de(&store, None, None, Order::Ascending).collect(); + let all = all.unwrap(); + assert_eq!( + all, + pks.clone() + .into_iter() + .map(str::to_string) + .zip(datas.clone().into_iter()) + .collect::>() + ); + + // let's try to iterate over a range + let all: StdResult> = map + .range_de( + &store, + Some(Bound::Inclusive(b"3".to_vec())), + None, + Order::Ascending, + ) + .collect(); + let all = all.unwrap(); + assert_eq!( + all, + pks.into_iter() + .map(str::to_string) + .zip(datas.into_iter()) + .rev() + .take(2) + .rev() + .collect::>() + ); + } + + #[test] + #[cfg(feature = "iterator")] + fn prefix_de_simple_string_key() { + let mut store = MockStorage::new(); + let map = build_snapshot_map(); + + // save data + let (pks, datas) = save_data(&mut store, &map); + + // Let's prefix and iterate. + // This is similar to calling range() directly, but added here for completeness / prefix_de + // type checks + let all: StdResult> = map + .prefix_de(()) + .range_de(&store, None, None, Order::Ascending) + .collect(); + let all = all.unwrap(); + assert_eq!( + all, + pks.clone() + .into_iter() + .map(str::to_string) + .zip(datas.into_iter()) + .collect::>() + ); + } + + #[test] + #[cfg(feature = "iterator")] + fn sub_prefix_de_simple_string_key() { + let mut store = MockStorage::new(); + let map = build_snapshot_map(); + + // save data + let (pks, datas) = save_data(&mut store, &map); + + // Let's prefix and iterate. + // This is similar to calling range() directly, but added here for completeness / sub_prefix_de + // type checks + let all: StdResult> = map + .sub_prefix_de(()) + .range_de(&store, None, None, Order::Ascending) + .collect(); + let all = all.unwrap(); + assert_eq!( + all, + pks.clone() + .into_iter() + .map(str::to_string) + .zip(datas.into_iter()) + .collect::>() + ); + } + + #[test] + #[cfg(feature = "iterator")] + fn prefix_range_de_simple_key() { + let mut store = MockStorage::new(); + + let indexes = DataCompositeMultiIndex { + name_age: MultiIndex::new( + |d, k| index_triple(&d.name, d.age, k), + "data", + "data__name_age", + ), + }; + let map = + IndexedSnapshotMap::new("data", "checks", "changes", Strategy::EveryBlock, indexes); + + // save data + let data1 = Data { + name: "Maria".to_string(), + last_name: "".to_string(), + age: 42, + }; + let pk1: (&str, &str) = ("1", "5627"); + map.save(&mut store, pk1, &data1, 1).unwrap(); + + let data2 = Data { + name: "Juan".to_string(), + last_name: "Perez".to_string(), + age: 13, + }; + let pk2: (&str, &str) = ("2", "5628"); + map.save(&mut store, pk2, &data2, 1).unwrap(); + + let data3 = Data { + name: "Maria".to_string(), + last_name: "Young".to_string(), + age: 24, + }; + let pk3: (&str, &str) = ("2", "5629"); + map.save(&mut store, pk3, &data3, 1).unwrap(); + + let data4 = Data { + name: "Maria Luisa".to_string(), + last_name: "Bemberg".to_string(), + age: 43, + }; + let pk4: (&str, &str) = ("3", "5630"); + map.save(&mut store, pk4, &data4, 1).unwrap(); + + // let's try to iterate! + let result: StdResult> = map + .prefix_range_de( + &store, + Some(PrefixBound::inclusive("2")), + None, + Order::Ascending, + ) + .collect(); + let result = result.unwrap(); + assert_eq!( + result, + [ + (("2".to_string(), "5628".to_string()), data2.clone()), + (("2".to_string(), "5629".to_string()), data3.clone()), + (("3".to_string(), "5630".to_string()), data4) + ] + ); + + // let's try to iterate over a range + let result: StdResult> = map + .prefix_range_de( + &store, + Some(PrefixBound::inclusive("2")), + Some(PrefixBound::exclusive("3")), + Order::Ascending, + ) + .collect(); + let result = result.unwrap(); + assert_eq!( + result, + [ + (("2".to_string(), "5628".to_string()), data2), + (("2".to_string(), "5629".to_string()), data3), + ] + ); + } } From 49acea923e55e2a6459669c4b2aa0cd0362a6107 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 24 Nov 2021 11:17:13 +0100 Subject: [PATCH 41/42] Extend Index keys deserialization docs --- packages/storage-plus/README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/storage-plus/README.md b/packages/storage-plus/README.md index 10d5a0905..5dec524cb 100644 --- a/packages/storage-plus/README.md +++ b/packages/storage-plus/README.md @@ -620,8 +620,14 @@ To deserialize keys of indexes (using the `*_de` functions), there are currently - For `UniqueIndex`: The primary key (`PK`) type needs to be specified, in order to deserialize the primary key to it. This generic type comes with a default of `()`, which means that no deserialization / data will be provided -for the primary key. +for the primary key. This is for backwards compatibility with the current `UniqueIndex` impl. It can also come handy +in cases you don't need the primary key, and are interested only in the deserialized value. - For `MultiIndex`: The last element of the index tuple must be specified with the type you want it to be deserialized. That is, the last tuple element serves as a marker for the deserialization type (in the same way `PK` does it in -`UniqueIndex`). \ No newline at end of file +`UniqueIndex`). + +- There are currently some inconsistencies in the values that are returned for the different index keys. `MultiIndex` +returns a tuple with the remaining part of the index key along with the primary key, whereas `UniqueIndex` returns only +the primary key. This will be changed in the future (See https://github.com/CosmWasm/cw-plus/issues/532) for consistency +and compatibility with the base `Map` type behaviour. From b4203be7d65ae78a3c82c6c3826d3265fe2a8550 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Wed, 24 Nov 2021 13:25:22 +0100 Subject: [PATCH 42/42] Clarify prefix_range/_de docs --- packages/storage-plus/src/indexed_map.rs | 19 ++++++++++-------- packages/storage-plus/src/indexed_snapshot.rs | 10 ++++++---- packages/storage-plus/src/indexes/multi.rs | 20 +++++++++++-------- packages/storage-plus/src/indexes/unique.rs | 10 ++++++---- packages/storage-plus/src/map.rs | 19 ++++++++++-------- packages/storage-plus/src/snapshot/map.rs | 10 ++++++---- 6 files changed, 52 insertions(+), 36 deletions(-) diff --git a/packages/storage-plus/src/indexed_map.rs b/packages/storage-plus/src/indexed_map.rs index c5ab17230..73f32ba39 100644 --- a/packages/storage-plus/src/indexed_map.rs +++ b/packages/storage-plus/src/indexed_map.rs @@ -175,10 +175,11 @@ where T: Serialize + DeserializeOwned + Clone, I: IndexList, { - /// while range assumes you set the prefix to one element and call range over the last one, - /// prefix_range accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. + /// While `range` over a `prefix` fixes the prefix to one element and iterates over the + /// remaining, `prefix_range` accepts bounds for the lowest and highest elements of the `Prefix` + /// itself, and iterates over those (inclusively or exclusively, depending on `PrefixBound`). + /// There are some issues that distinguish these two, and blindly casting to `Vec` doesn't + /// solve them. pub fn prefix_range<'c>( &self, store: &'c dyn Storage, @@ -219,10 +220,12 @@ where K: PrimaryKey<'a> + KeyDeserialize, I: IndexList, { - /// while range_de assumes you set the prefix to one element and call range over the last one, - /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. + /// While `range_de` over a `prefix_de` fixes the prefix to one element and iterates over the + /// remaining, `prefix_range_de` accepts bounds for the lowest and highest elements of the + /// `Prefix` itself, and iterates over those (inclusively or exclusively, depending on + /// `PrefixBound`). + /// There are some issues that distinguish these two, and blindly casting to `Vec` doesn't + /// solve them. pub fn prefix_range_de<'c>( &self, store: &'c dyn Storage, diff --git a/packages/storage-plus/src/indexed_snapshot.rs b/packages/storage-plus/src/indexed_snapshot.rs index f311ed2d5..8e5a4f35a 100644 --- a/packages/storage-plus/src/indexed_snapshot.rs +++ b/packages/storage-plus/src/indexed_snapshot.rs @@ -236,10 +236,12 @@ where K: PrimaryKey<'a> + KeyDeserialize, I: IndexList, { - /// while range_de assumes you set the prefix to one element and call range over the last one, - /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. + /// While `range_de` over a `prefix_de` fixes the prefix to one element and iterates over the + /// remaining, `prefix_range_de` accepts bounds for the lowest and highest elements of the + /// `Prefix` itself, and iterates over those (inclusively or exclusively, depending on + /// `PrefixBound`). + /// There are some issues that distinguish these two, and blindly casting to `Vec` doesn't + /// solve them. pub fn prefix_range_de<'c>( &self, store: &'c dyn Storage, diff --git a/packages/storage-plus/src/indexes/multi.rs b/packages/storage-plus/src/indexes/multi.rs index 2f29aa4b2..2a21dff1b 100644 --- a/packages/storage-plus/src/indexes/multi.rs +++ b/packages/storage-plus/src/indexes/multi.rs @@ -228,10 +228,12 @@ where self.no_prefix().keys(store, min, max, order) } - /// while range assumes you set the prefix to one element and call range over the last one, - /// prefix_range accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. + /// While `range_de` over a `prefix_de` fixes the prefix to one element and iterates over the + /// remaining, `prefix_range_de` accepts bounds for the lowest and highest elements of the + /// `Prefix` itself, and iterates over those (inclusively or exclusively, depending on + /// `PrefixBound`). + /// There are some issues that distinguish these two, and blindly casting to `Vec` doesn't + /// solve them. pub fn prefix_range<'c>( &'c self, store: &'c dyn Storage, @@ -280,10 +282,12 @@ where T: Serialize + DeserializeOwned + Clone, K: PrimaryKey<'a> + KeyDeserialize, { - /// while range_de assumes you set the prefix to one element and call range over the last one, - /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. + /// While `range_de` over a `prefix_de` fixes the prefix to one element and iterates over the + /// remaining, `prefix_range_de` accepts bounds for the lowest and highest elements of the + /// `Prefix` itself, and iterates over those (inclusively or exclusively, depending on + /// `PrefixBound`). + /// There are some issues that distinguish these two, and blindly casting to `Vec` doesn't + /// solve them. pub fn prefix_range_de<'c>( &self, store: &'c dyn Storage, diff --git a/packages/storage-plus/src/indexes/unique.rs b/packages/storage-plus/src/indexes/unique.rs index 190a65e0a..398aaaf13 100644 --- a/packages/storage-plus/src/indexes/unique.rs +++ b/packages/storage-plus/src/indexes/unique.rs @@ -181,10 +181,12 @@ where T: Serialize + DeserializeOwned + Clone, K: PrimaryKey<'a>, { - /// while range_de assumes you set the prefix to one element and call range over the last one, - /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. + /// While `range_de` over a `prefix_de` fixes the prefix to one element and iterates over the + /// remaining, `prefix_range_de` accepts bounds for the lowest and highest elements of the + /// `Prefix` itself, and iterates over those (inclusively or exclusively, depending on + /// `PrefixBound`). + /// There are some issues that distinguish these two, and blindly casting to `Vec` doesn't + /// solve them. pub fn prefix_range_de<'c>( &self, store: &'c dyn Storage, diff --git a/packages/storage-plus/src/map.rs b/packages/storage-plus/src/map.rs index 2ac1fe0b1..5b0d39714 100644 --- a/packages/storage-plus/src/map.rs +++ b/packages/storage-plus/src/map.rs @@ -143,10 +143,11 @@ where // Other cases need to call prefix() first K: PrimaryKey<'a>, { - /// while range assumes you set the prefix to one element and call range over the last one, - /// prefix_range accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. + /// While `range` over a `prefix` fixes the prefix to one element and iterates over the + /// remaining, `prefix_range` accepts bounds for the lowest and highest elements of the `Prefix` + /// itself, and iterates over those (inclusively or exclusively, depending on `PrefixBound`). + /// There are some issues that distinguish these two, and blindly casting to `Vec` doesn't + /// solve them. pub fn prefix_range<'c>( &self, store: &'c dyn Storage, @@ -196,10 +197,12 @@ where T: Serialize + DeserializeOwned, K: PrimaryKey<'a> + KeyDeserialize, { - /// while range_de assumes you set the prefix to one element and call range over the last one, - /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. + /// While `range_de` over a `prefix_de` fixes the prefix to one element and iterates over the + /// remaining, `prefix_range_de` accepts bounds for the lowest and highest elements of the + /// `Prefix` itself, and iterates over those (inclusively or exclusively, depending on + /// `PrefixBound`). + /// There are some issues that distinguish these two, and blindly casting to `Vec` doesn't + /// solve them. pub fn prefix_range_de<'c>( &self, store: &'c dyn Storage, diff --git a/packages/storage-plus/src/snapshot/map.rs b/packages/storage-plus/src/snapshot/map.rs index a4058ac4e..fb5a4c864 100644 --- a/packages/storage-plus/src/snapshot/map.rs +++ b/packages/storage-plus/src/snapshot/map.rs @@ -192,10 +192,12 @@ where T: Serialize + DeserializeOwned, K: PrimaryKey<'a> + KeyDeserialize, { - /// while range_de assumes you set the prefix to one element and call range over the last one, - /// prefix_range_de accepts bounds for the lowest and highest elements of the Prefix we wish to - /// accept, and iterates over those. There are some issues that distinguish these to and blindly - /// casting to Vec doesn't solve them. + /// While `range_de` over a `prefix_de` fixes the prefix to one element and iterates over the + /// remaining, `prefix_range_de` accepts bounds for the lowest and highest elements of the + /// `Prefix` itself, and iterates over those (inclusively or exclusively, depending on + /// `PrefixBound`). + /// There are some issues that distinguish these two, and blindly casting to `Vec` doesn't + /// solve them. pub fn prefix_range_de<'c>( &self, store: &'c dyn Storage,