icydb_core/db/executor/
save.rs1use crate::{
2 Error,
3 db::{
4 Db,
5 executor::ExecutorError,
6 query::{SaveMode, SaveQuery},
7 store::DataKey,
8 },
9 deserialize,
10 obs::metrics,
11 serialize,
12 traits::EntityKind,
13 visitor::{sanitize, validate},
14};
15use std::marker::PhantomData;
16
17#[derive(Clone, Copy)]
22pub struct SaveExecutor<E: EntityKind> {
23 db: Db<E::Canister>,
24 debug: bool,
25 _marker: PhantomData<E>,
26}
27
28impl<E: EntityKind> SaveExecutor<E> {
29 #[must_use]
30 pub const fn new(db: Db<E::Canister>, debug: bool) -> Self {
31 Self {
32 db,
33 debug,
34 _marker: PhantomData,
35 }
36 }
37
38 #[must_use]
40 pub const fn debug(mut self) -> Self {
41 self.debug = true;
42 self
43 }
44
45 pub fn insert(&self, entity: E) -> Result<E, Error> {
51 let entity = self.save_entity(SaveMode::Insert, entity)?;
52
53 Ok(entity)
54 }
55
56 pub fn insert_view<V>(&self, view: E::ViewType) -> Result<E::ViewType, Error> {
58 let entity = E::from_view(view);
59 let saved_view = self.insert(entity)?.to_view();
60
61 Ok(saved_view)
62 }
63
64 pub fn update(&self, entity: E) -> Result<E, Error> {
66 let entity = self.save_entity(SaveMode::Update, entity)?;
67
68 Ok(entity)
69 }
70
71 pub fn update_view<V>(&self, view: E::ViewType) -> Result<E::ViewType, Error> {
73 let entity = E::from_view(view);
74 let saved_view = self.update(entity)?.to_view();
75
76 Ok(saved_view)
77 }
78
79 pub fn replace(&self, entity: E) -> Result<E, Error> {
81 let entity = self.save_entity(SaveMode::Replace, entity)?;
82
83 Ok(entity)
84 }
85
86 pub fn replace_view<V>(&self, view: E::ViewType) -> Result<E::ViewType, Error> {
88 let entity = E::from_view(view);
89 let saved_view = self.replace(entity)?.to_view();
90
91 Ok(saved_view)
92 }
93
94 pub fn execute(&self, query: SaveQuery) -> Result<E, Error> {
98 let e: E = deserialize(&query.bytes)?;
99 let entity = self.save_entity(query.mode, e)?;
100
101 Ok(entity)
102 }
103
104 fn save_entity(&self, mode: SaveMode, mut entity: E) -> Result<E, Error> {
106 let mut span = metrics::Span::<E>::new(metrics::ExecKind::Save);
107 let key = entity.key();
108 let ctx = self.db.context::<E>();
109
110 sanitize(&mut entity);
112 validate(&entity)?;
113
114 let data_key = DataKey::new::<E>(key);
123 let old_result = ctx.with_store(|store| store.get(&data_key))?;
124
125 let old = match (mode, old_result) {
127 (SaveMode::Insert | SaveMode::Replace, None) => None,
128
129 (SaveMode::Update | SaveMode::Replace, Some(old_bytes)) => {
130 let old = deserialize::<E>(&old_bytes)?;
131 Some(old)
132 }
133
134 (SaveMode::Insert, Some(_)) => return Err(ExecutorError::KeyExists(data_key))?,
136 (SaveMode::Update, None) => return Err(ExecutorError::KeyNotFound(data_key))?,
137 };
138
139 let bytes = serialize(&entity)?;
141
142 self.replace_indexes(old.as_ref(), &entity)?;
144
145 ctx.with_store_mut(|store| store.insert(data_key.clone(), bytes))?;
147 span.set_rows(1);
148
149 Ok(entity)
150 }
151
152 fn replace_indexes(&self, old: Option<&E>, new: &E) -> Result<(), Error> {
154 use crate::db::store::IndexKey;
155
156 for index in E::INDEXES {
158 if index.unique
160 && let Some(new_idx_key) = IndexKey::new(new, index)
161 {
162 let store = self.db.with_index(|reg| reg.try_get_store(index.store))?;
163 let violates = store.with_borrow(|s| {
164 if let Some(existing) = s.get(&new_idx_key) {
165 let new_entity_key = new.key();
166 !existing.contains(&new_entity_key) && !existing.is_empty()
167 } else {
168 false
169 }
170 });
171 if violates {
172 metrics::with_state_mut(|m| {
174 metrics::record_unique_violation_for::<E>(m);
175 });
176
177 return Err(ExecutorError::index_violation(E::PATH, index.fields).into());
178 }
179 }
180 }
181
182 for index in E::INDEXES {
184 let store = self.db.with_index(|reg| reg.try_get_store(index.store))?;
185 store.with_borrow_mut(|s| {
186 if let Some(old) = old {
187 s.remove_index_entry(old, index);
188 }
189 s.insert_index_entry(new, index)?;
190
191 Ok::<(), Error>(())
192 })?;
193 }
194
195 Ok(())
196 }
197}