Skip to content
Snippets Groups Projects
Commit 9dedddaf authored by PG Herveou's avatar PG Herveou Committed by GitHub
Browse files

frame-support Add translate_next (#14043)

* Frame Add translate_next

This works similarly to to `translate` but only translate a single entry.
This function will be useful in the context of multi-block migration.

* Add test

* add None return case

* fixes

* PR comment use `?`
parent 31f95933
No related merge requests found
...@@ -178,34 +178,48 @@ where ...@@ -178,34 +178,48 @@ where
} }
fn translate<O: Decode, F: FnMut(K, O) -> Option<V>>(mut f: F) { fn translate<O: Decode, F: FnMut(K, O) -> Option<V>>(mut f: F) {
let mut previous_key = None;
loop {
previous_key = Self::translate_next(previous_key, &mut f);
if previous_key.is_none() {
break
}
}
}
fn translate_next<O: Decode, F: FnMut(K, O) -> Option<V>>(
previous_key: Option<Vec<u8>>,
mut f: F,
) -> Option<Vec<u8>> {
let prefix = G::prefix_hash(); let prefix = G::prefix_hash();
let mut previous_key = prefix.clone(); let previous_key = previous_key.unwrap_or_else(|| prefix.clone());
while let Some(next) =
sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix))
{
previous_key = next;
let value = match unhashed::get::<O>(&previous_key) {
Some(value) => value,
None => {
log::error!("Invalid translate: fail to decode old value");
continue
},
};
let mut key_material = G::Hasher::reverse(&previous_key[prefix.len()..]);
let key = match K::decode(&mut key_material) {
Ok(key) => key,
Err(_) => {
log::error!("Invalid translate: fail to decode key");
continue
},
};
match f(key, value) { let current_key =
Some(new) => unhashed::put::<V>(&previous_key, &new), sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix))?;
None => unhashed::kill(&previous_key),
} let value = match unhashed::get::<O>(&current_key) {
Some(value) => value,
None => {
log::error!("Invalid translate: fail to decode old value");
return Some(current_key)
},
};
let mut key_material = G::Hasher::reverse(&current_key[prefix.len()..]);
let key = match K::decode(&mut key_material) {
Ok(key) => key,
Err(_) => {
log::error!("Invalid translate: fail to decode key");
return Some(current_key)
},
};
match f(key, value) {
Some(new) => unhashed::put::<V>(&current_key, &new),
None => unhashed::kill(&current_key),
} }
Some(current_key)
} }
} }
......
...@@ -303,6 +303,15 @@ pub trait IterableStorageMap<K: FullEncode, V: FullCodec>: StorageMap<K, V> { ...@@ -303,6 +303,15 @@ pub trait IterableStorageMap<K: FullEncode, V: FullCodec>: StorageMap<K, V> {
/// ///
/// NOTE: If a value fail to decode because storage is corrupted then it is skipped. /// NOTE: If a value fail to decode because storage is corrupted then it is skipped.
fn translate<O: Decode, F: FnMut(K, O) -> Option<V>>(f: F); fn translate<O: Decode, F: FnMut(K, O) -> Option<V>>(f: F);
/// Translate the next entry following `previous_key` by a function `f`.
/// By returning `None` from `f` for an element, you'll remove it from the map.
///
/// Returns the next key to iterate from in lexicographical order of the encoded key.
fn translate_next<O: Decode, F: FnMut(K, O) -> Option<V>>(
previous_key: Option<Vec<u8>>,
f: F,
) -> Option<Vec<u8>>;
} }
/// A strongly-typed double map in storage whose secondary keys and values can be iterated over. /// A strongly-typed double map in storage whose secondary keys and values can be iterated over.
......
...@@ -484,7 +484,7 @@ mod test { ...@@ -484,7 +484,7 @@ mod test {
use crate::{ use crate::{
hash::*, hash::*,
metadata_ir::{StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR}, metadata_ir::{StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR},
storage::types::ValueQuery, storage::{types::ValueQuery, IterableStorageMap},
}; };
use sp_io::{hashing::twox_128, TestExternalities}; use sp_io::{hashing::twox_128, TestExternalities};
...@@ -700,6 +700,15 @@ mod test { ...@@ -700,6 +700,15 @@ mod test {
A::translate::<u8, _>(|k, v| Some((k * v as u16).into())); A::translate::<u8, _>(|k, v| Some((k * v as u16).into()));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40), (3, 30)]); assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40), (3, 30)]);
let translate_next = |k: u16, v: u8| Some((v as u16 / k).into());
let k = A::translate_next::<u8, _>(None, translate_next);
let k = A::translate_next::<u8, _>(k, translate_next);
assert_eq!(None, A::translate_next::<u8, _>(k, translate_next));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 10), (3, 10)]);
let _ = A::translate_next::<u8, _>(None, |_, _| None);
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(3, 10)]);
let mut entries = vec![]; let mut entries = vec![];
A::build_metadata(vec![], &mut entries); A::build_metadata(vec![], &mut entries);
AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries);
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment