reifydb_engine/transaction/catalog/
view.rs1use reifydb_catalog::transaction::CatalogTrackViewChangeOperations;
5use reifydb_core::interface::{
6 Change, NamespaceId,
7 OperationType::{Create, Delete, Update},
8 TransactionalViewChanges, ViewDef, ViewId,
9};
10
11use crate::{StandardCommandTransaction, StandardQueryTransaction};
12
13impl CatalogTrackViewChangeOperations for StandardCommandTransaction {
14 fn track_view_def_created(&mut self, view: ViewDef) -> reifydb_core::Result<()> {
15 let change = Change {
16 pre: None,
17 post: Some(view),
18 op: Create,
19 };
20 self.changes.add_view_def_change(change);
21 Ok(())
22 }
23
24 fn track_view_def_updated(&mut self, pre: ViewDef, post: ViewDef) -> reifydb_core::Result<()> {
25 let change = Change {
26 pre: Some(pre),
27 post: Some(post),
28 op: Update,
29 };
30 self.changes.add_view_def_change(change);
31 Ok(())
32 }
33
34 fn track_view_def_deleted(&mut self, view: ViewDef) -> reifydb_core::Result<()> {
35 let change = Change {
36 pre: Some(view),
37 post: None,
38 op: Delete,
39 };
40 self.changes.add_view_def_change(change);
41 Ok(())
42 }
43}
44
45impl TransactionalViewChanges for StandardCommandTransaction {
46 fn find_view(&self, id: ViewId) -> Option<&ViewDef> {
47 for change in self.changes.view_def.iter().rev() {
49 if let Some(view) = &change.post {
50 if view.id == id {
51 return Some(view);
52 }
53 } else if let Some(view) = &change.pre {
54 if view.id == id && change.op == Delete {
55 return None;
57 }
58 }
59 }
60 None
61 }
62
63 fn find_view_by_name(&self, namespace: NamespaceId, name: &str) -> Option<&ViewDef> {
64 self.changes
65 .view_def
66 .iter()
67 .rev()
68 .find_map(|change| change.post.as_ref().filter(|v| v.namespace == namespace && v.name == name))
69 }
70
71 fn is_view_deleted(&self, id: ViewId) -> bool {
72 self.changes
73 .view_def
74 .iter()
75 .rev()
76 .any(|change| change.op == Delete && change.pre.as_ref().map(|v| v.id) == Some(id))
77 }
78
79 fn is_view_deleted_by_name(&self, namespace: NamespaceId, name: &str) -> bool {
80 self.changes.view_def.iter().rev().any(|change| {
81 change.op == Delete
82 && change
83 .pre
84 .as_ref()
85 .map(|v| v.namespace == namespace && v.name == name)
86 .unwrap_or(false)
87 })
88 }
89}
90
91impl TransactionalViewChanges for StandardQueryTransaction {
92 fn find_view(&self, _id: ViewId) -> Option<&ViewDef> {
93 None
94 }
95
96 fn find_view_by_name(&self, _namespace: NamespaceId, _name: &str) -> Option<&ViewDef> {
97 None
98 }
99
100 fn is_view_deleted(&self, _id: ViewId) -> bool {
101 false
102 }
103
104 fn is_view_deleted_by_name(&self, _namespace: NamespaceId, _name: &str) -> bool {
105 false
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use reifydb_catalog::transaction::CatalogTrackViewChangeOperations;
112 use reifydb_core::interface::{
113 NamespaceId, Operation,
114 OperationType::{Create, Delete, Update},
115 ViewDef, ViewId, ViewKind,
116 };
117
118 use crate::test_utils::create_test_command_transaction;
119
120 async fn test_view_def(id: u64, namespace_id: u64, name: &str) -> ViewDef {
122 ViewDef {
123 id: ViewId(id),
124 namespace: NamespaceId(namespace_id),
125 name: name.to_string(),
126 columns: vec![],
127 kind: ViewKind::Deferred,
128 primary_key: None,
129 }
130 }
131
132 mod track_view_def_created {
133 use super::*;
134
135 #[tokio::test]
136 async fn test_successful_creation() {
137 let mut txn = create_test_command_transaction().await;
138
139 let view = test_view_def(1, 1, "test_view").await;
140 let result = txn.track_view_def_created(view.clone());
141 assert!(result.is_ok());
142
143 assert_eq!(txn.changes.view_def.len(), 1);
145 let change = &txn.changes.view_def[0];
146 assert!(change.pre.is_none());
147 assert_eq!(change.post.as_ref().unwrap().name, "test_view");
148 assert_eq!(change.op, Create);
149
150 assert_eq!(txn.changes.log.len(), 1);
152 match &txn.changes.log[0] {
153 Operation::View {
154 id,
155 op,
156 } if *id == view.id && *op == Create => {}
157 _ => panic!("Expected View operation with Create"),
158 }
159 }
160 }
161
162 mod track_view_def_updated {
163 use super::*;
164
165 #[tokio::test]
166 async fn test_multiple_updates_no_coalescing() {
167 let mut txn = create_test_command_transaction().await;
168 let view_v1 = test_view_def(1, 1, "view_v1").await;
169 let view_v2 = test_view_def(1, 1, "view_v2").await;
170 let view_v3 = test_view_def(1, 1, "view_v3").await;
171
172 txn.track_view_def_updated(view_v1.clone(), view_v2.clone()).unwrap();
174
175 assert_eq!(txn.changes.view_def.len(), 1);
177 assert_eq!(txn.changes.view_def[0].pre.as_ref().unwrap().name, "view_v1");
178 assert_eq!(txn.changes.view_def[0].post.as_ref().unwrap().name, "view_v2");
179 assert_eq!(txn.changes.view_def[0].op, Update);
180
181 txn.track_view_def_updated(view_v2, view_v3.clone()).unwrap();
183
184 assert_eq!(txn.changes.view_def.len(), 2);
186
187 assert_eq!(txn.changes.view_def[1].pre.as_ref().unwrap().name, "view_v2");
189 assert_eq!(txn.changes.view_def[1].post.as_ref().unwrap().name, "view_v3");
190
191 assert_eq!(txn.changes.log.len(), 2);
193 }
194
195 #[tokio::test]
196 async fn test_create_then_update_no_coalescing() {
197 let mut txn = create_test_command_transaction().await;
198 let view_v1 = test_view_def(1, 1, "view_v1").await;
199 let view_v2 = test_view_def(1, 1, "view_v2").await;
200
201 txn.track_view_def_created(view_v1.clone()).unwrap();
203 assert_eq!(txn.changes.view_def.len(), 1);
204 assert_eq!(txn.changes.view_def[0].op, Create);
205
206 txn.track_view_def_updated(view_v1, view_v2.clone()).unwrap();
208
209 assert_eq!(txn.changes.view_def.len(), 2);
211
212 assert_eq!(txn.changes.view_def[0].op, Create);
214
215 assert_eq!(txn.changes.view_def[1].op, Update);
217
218 assert_eq!(txn.changes.log.len(), 2);
220 }
221
222 #[tokio::test]
223 async fn test_normal_update() {
224 let mut txn = create_test_command_transaction().await;
225 let view_v1 = test_view_def(1, 1, "view_v1").await;
226 let view_v2 = test_view_def(1, 1, "view_v2").await;
227
228 let result = txn.track_view_def_updated(view_v1.clone(), view_v2.clone());
229 assert!(result.is_ok());
230
231 assert_eq!(txn.changes.view_def.len(), 1);
233 let change = &txn.changes.view_def[0];
234 assert_eq!(change.pre.as_ref().unwrap().name, "view_v1");
235 assert_eq!(change.post.as_ref().unwrap().name, "view_v2");
236 assert_eq!(change.op, Update);
237
238 assert_eq!(txn.changes.log.len(), 1);
240 match &txn.changes.log[0] {
241 Operation::View {
242 id,
243 op,
244 } if *id == ViewId(1) && *op == Update => {}
245 _ => panic!("Expected View operation with Update"),
246 }
247 }
248 }
249
250 mod track_view_def_deleted {
251 use super::*;
252
253 #[tokio::test]
254 async fn test_delete_after_create_no_coalescing() {
255 let mut txn = create_test_command_transaction().await;
256 let view = test_view_def(1, 1, "test_view").await;
257
258 txn.track_view_def_created(view.clone()).unwrap();
260 assert_eq!(txn.changes.view_def.len(), 1);
261
262 let result = txn.track_view_def_deleted(view.clone());
264 assert!(result.is_ok());
265
266 assert_eq!(txn.changes.view_def.len(), 2);
268
269 assert_eq!(txn.changes.view_def[0].op, Create);
271
272 assert_eq!(txn.changes.view_def[1].op, Delete);
274
275 assert_eq!(txn.changes.log.len(), 2);
277 }
278
279 #[tokio::test]
280 async fn test_delete_after_update_no_coalescing() {
281 let mut txn = create_test_command_transaction().await;
282 let view_v1 = test_view_def(1, 1, "view_v1").await;
283 let view_v2 = test_view_def(1, 1, "view_v2").await;
284
285 txn.track_view_def_updated(view_v1.clone(), view_v2.clone()).unwrap();
287 assert_eq!(txn.changes.view_def.len(), 1);
288
289 let result = txn.track_view_def_deleted(view_v2);
291 assert!(result.is_ok());
292
293 assert_eq!(txn.changes.view_def.len(), 2);
295
296 assert_eq!(txn.changes.view_def[0].op, Update);
298
299 assert_eq!(txn.changes.view_def[1].op, Delete);
301
302 assert_eq!(txn.changes.log.len(), 2);
304 }
305
306 #[tokio::test]
307 async fn test_normal_delete() {
308 let mut txn = create_test_command_transaction().await;
309 let view = test_view_def(1, 1, "test_view").await;
310
311 let result = txn.track_view_def_deleted(view.clone());
312 assert!(result.is_ok());
313
314 assert_eq!(txn.changes.view_def.len(), 1);
316 let change = &txn.changes.view_def[0];
317 assert_eq!(change.pre.as_ref().unwrap().name, "test_view");
318 assert!(change.post.is_none());
319 assert_eq!(change.op, Delete);
320
321 assert_eq!(txn.changes.log.len(), 1);
323 match &txn.changes.log[0] {
324 Operation::View {
325 id,
326 op,
327 } if *id == view.id && *op == Delete => {}
328 _ => panic!("Expected View operation with Delete"),
329 }
330 }
331 }
332}