grovedb/batch/
just_in_time_reference_update.rs

1use std::borrow::Cow;
2
3use grovedb_costs::{
4    cost_return_on_error_no_add,
5    storage_cost::{
6        removal::{StorageRemovedBytes, StorageRemovedBytes::BasicStorageRemoval},
7        StorageCost,
8    },
9    CostResult, CostsExt, OperationCost,
10};
11use grovedb_merk::{
12    tree::{kv::KV, value_hash, TreeNode},
13    tree_type::TreeType,
14    CryptoHash, Merk,
15};
16use grovedb_storage::StorageContext;
17use grovedb_version::version::GroveVersion;
18
19use crate::{
20    batch::{MerkError, TreeCacheMerkByPath},
21    Element, ElementFlags, Error,
22};
23
24impl<'db, S, F> TreeCacheMerkByPath<S, F>
25where
26    F: FnMut(&[Vec<u8>], bool) -> CostResult<Merk<S>, Error>,
27    S: StorageContext<'db>,
28{
29    pub(crate) fn process_old_element_flags<G, SR>(
30        key: &[u8],
31        serialized: &[u8],
32        new_element: &mut Element,
33        old_element: Element,
34        old_serialized_element: &[u8],
35        in_tree_type: TreeType,
36        flags_update: &mut G,
37        split_removal_bytes: &mut SR,
38        grove_version: &GroveVersion,
39    ) -> CostResult<CryptoHash, Error>
40    where
41        G: FnMut(&StorageCost, Option<ElementFlags>, &mut ElementFlags) -> Result<bool, Error>,
42        SR: FnMut(
43            &mut ElementFlags,
44            u32,
45            u32,
46        ) -> Result<(StorageRemovedBytes, StorageRemovedBytes), Error>,
47    {
48        let mut cost = OperationCost::default();
49        if old_element.is_sum_item() {
50            return if new_element.is_sum_item() {
51                let maybe_old_flags = old_element.get_flags_owned();
52                if maybe_old_flags.is_some() {
53                    let mut updated_new_element_with_old_flags = new_element.clone();
54                    updated_new_element_with_old_flags.set_flags(maybe_old_flags.clone());
55                    // There are no storage flags, we can just hash new element
56                    let new_serialized_bytes = cost_return_on_error_no_add!(
57                        cost,
58                        updated_new_element_with_old_flags.serialize(grove_version)
59                    );
60                    let val_hash = value_hash(&new_serialized_bytes).unwrap_add_cost(&mut cost);
61                    Ok(val_hash).wrap_with_cost(cost)
62                } else {
63                    let val_hash = value_hash(serialized).unwrap_add_cost(&mut cost);
64                    Ok(val_hash).wrap_with_cost(cost)
65                }
66            } else {
67                Err(Error::NotSupported(
68                    "going from a sum item to a not sum item is not supported".to_string(),
69                ))
70                .wrap_with_cost(cost)
71            };
72        } else if new_element.is_sum_item() {
73            return Err(Error::NotSupported(
74                "going from an item to a sum item is not supported".to_string(),
75            ))
76            .wrap_with_cost(cost);
77        }
78        let mut maybe_old_flags = old_element.get_flags_owned();
79
80        let old_storage_cost = KV::node_value_byte_cost_size(
81            key.len() as u32,
82            old_serialized_element.len() as u32,
83            in_tree_type.inner_node_type(),
84        );
85
86        let original_new_element = new_element.clone();
87
88        let mut serialization_to_use = Cow::Borrowed(serialized);
89
90        let mut new_storage_cost = if maybe_old_flags.is_some() {
91            // we need to get the new storage_cost as if it had the same storage flags as
92            // before
93            let mut updated_new_element_with_old_flags = original_new_element.clone();
94            updated_new_element_with_old_flags.set_flags(maybe_old_flags.clone());
95
96            let serialized_with_old_flags = cost_return_on_error_no_add!(
97                cost,
98                updated_new_element_with_old_flags.serialize(grove_version)
99            );
100            KV::node_value_byte_cost_size(
101                key.len() as u32,
102                serialized_with_old_flags.len() as u32,
103                in_tree_type.inner_node_type(),
104            )
105        } else {
106            KV::node_value_byte_cost_size(
107                key.len() as u32,
108                serialized.len() as u32,
109                in_tree_type.inner_node_type(),
110            )
111        };
112
113        let mut i = 0;
114
115        loop {
116            // Calculate storage costs
117            let mut storage_costs =
118                TreeNode::storage_cost_for_update(new_storage_cost, old_storage_cost);
119
120            if let Some(old_element_flags) = maybe_old_flags.as_mut() {
121                if let BasicStorageRemoval(removed_bytes) = storage_costs.removed_bytes {
122                    let (_, value_removed_bytes) = cost_return_on_error_no_add!(
123                        cost,
124                        split_removal_bytes(old_element_flags, 0, removed_bytes)
125                    );
126                    storage_costs.removed_bytes = value_removed_bytes;
127                }
128            }
129
130            let mut new_element_cloned = original_new_element.clone();
131
132            let changed = cost_return_on_error_no_add!(
133                cost,
134                (flags_update)(
135                    &storage_costs,
136                    maybe_old_flags.clone(),
137                    new_element_cloned.get_flags_mut().as_mut().unwrap()
138                )
139                .map_err(|e| match e {
140                    Error::JustInTimeElementFlagsClientError(_) => {
141                        MerkError::ClientCorruptionError(e.to_string()).into()
142                    }
143                    _ => MerkError::ClientCorruptionError("non client error".to_string(),).into(),
144                })
145            );
146            if !changed {
147                // There are no storage flags, we can just hash new element
148
149                let val_hash = value_hash(&serialization_to_use).unwrap_add_cost(&mut cost);
150                return Ok(val_hash).wrap_with_cost(cost);
151            } else {
152                // There are no storage flags, we can just hash new element
153                let new_serialized_bytes =
154                    cost_return_on_error_no_add!(cost, new_element_cloned.serialize(grove_version));
155
156                new_storage_cost = KV::node_value_byte_cost_size(
157                    key.len() as u32,
158                    new_serialized_bytes.len() as u32,
159                    in_tree_type.inner_node_type(),
160                );
161
162                if serialization_to_use == new_serialized_bytes {
163                    // it hasn't actually changed, let's do the value hash of it
164                    let val_hash = value_hash(&serialization_to_use).unwrap_add_cost(&mut cost);
165                    return Ok(val_hash).wrap_with_cost(cost);
166                }
167
168                serialization_to_use = Cow::Owned(new_serialized_bytes);
169            }
170
171            // Prevent potential infinite loop
172            if i > 8 {
173                return Err(Error::CyclicError(
174                    "updated value based on costs too many times in reference",
175                ))
176                .wrap_with_cost(cost);
177            }
178            i += 1;
179        }
180    }
181}