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};
10use reifydb_type::IntoFragment;
11
12use crate::{StandardCommandTransaction, StandardQueryTransaction};
13
14impl CatalogTrackViewChangeOperations for StandardCommandTransaction {
15 fn track_view_def_created(&mut self, view: ViewDef) -> reifydb_core::Result<()> {
16 let change = Change {
17 pre: None,
18 post: Some(view),
19 op: Create,
20 };
21 self.changes.add_view_def_change(change);
22 Ok(())
23 }
24
25 fn track_view_def_updated(&mut self, pre: ViewDef, post: ViewDef) -> reifydb_core::Result<()> {
26 let change = Change {
27 pre: Some(pre),
28 post: Some(post),
29 op: Update,
30 };
31 self.changes.add_view_def_change(change);
32 Ok(())
33 }
34
35 fn track_view_def_deleted(&mut self, view: ViewDef) -> reifydb_core::Result<()> {
36 let change = Change {
37 pre: Some(view),
38 post: None,
39 op: Delete,
40 };
41 self.changes.add_view_def_change(change);
42 Ok(())
43 }
44}
45
46impl TransactionalViewChanges for StandardCommandTransaction {
47 fn find_view(&self, id: ViewId) -> Option<&ViewDef> {
48 for change in self.changes.view_def.iter().rev() {
50 if let Some(view) = &change.post {
51 if view.id == id {
52 return Some(view);
53 }
54 } else if let Some(view) = &change.pre {
55 if view.id == id && change.op == Delete {
56 return None;
58 }
59 }
60 }
61 None
62 }
63
64 fn find_view_by_name<'a>(&self, namespace: NamespaceId, name: impl IntoFragment<'a>) -> Option<&ViewDef> {
65 let name = name.into_fragment();
66 self.changes.view_def.iter().rev().find_map(|change| {
67 change.post.as_ref().filter(|v| v.namespace == namespace && v.name == name.text())
68 })
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<'a>(&self, namespace: NamespaceId, name: impl IntoFragment<'a>) -> bool {
80 let name = name.into_fragment();
81 self.changes.view_def.iter().rev().any(|change| {
82 change.op == Delete
83 && change
84 .pre
85 .as_ref()
86 .map(|v| v.namespace == namespace && v.name == name.text())
87 .unwrap_or(false)
88 })
89 }
90}
91
92impl TransactionalViewChanges for StandardQueryTransaction {
93 fn find_view(&self, _id: ViewId) -> Option<&ViewDef> {
94 None
95 }
96
97 fn find_view_by_name<'a>(&self, _namespace: NamespaceId, _name: impl IntoFragment<'a>) -> Option<&ViewDef> {
98 None
99 }
100
101 fn is_view_deleted(&self, _id: ViewId) -> bool {
102 false
103 }
104
105 fn is_view_deleted_by_name<'a>(&self, _namespace: NamespaceId, _name: impl IntoFragment<'a>) -> bool {
106 false
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use reifydb_catalog::transaction::CatalogTrackViewChangeOperations;
113 use reifydb_core::interface::{
114 NamespaceId, Operation,
115 OperationType::{Create, Delete, Update},
116 ViewDef, ViewId, ViewKind,
117 };
118
119 use crate::test_utils::create_test_command_transaction;
120
121 fn test_view_def(id: u64, namespace_id: u64, name: &str) -> ViewDef {
123 ViewDef {
124 id: ViewId(id),
125 namespace: NamespaceId(namespace_id),
126 name: name.to_string(),
127 columns: vec![],
128 kind: ViewKind::Deferred,
129 primary_key: None,
130 }
131 }
132
133 mod track_view_def_created {
134 use super::*;
135
136 #[test]
137 fn test_successful_creation() {
138 let mut txn = create_test_command_transaction();
139
140 let view = test_view_def(1, 1, "test_view");
141 let result = txn.track_view_def_created(view.clone());
142 assert!(result.is_ok());
143
144 assert_eq!(txn.changes.view_def.len(), 1);
146 let change = &txn.changes.view_def[0];
147 assert!(change.pre.is_none());
148 assert_eq!(change.post.as_ref().unwrap().name, "test_view");
149 assert_eq!(change.op, Create);
150
151 assert_eq!(txn.changes.log.len(), 1);
153 match &txn.changes.log[0] {
154 Operation::View {
155 id,
156 op,
157 } if *id == view.id && *op == Create => {}
158 _ => panic!("Expected View operation with Create"),
159 }
160 }
161 }
162
163 mod track_view_def_updated {
164 use super::*;
165
166 #[test]
167 fn test_multiple_updates_no_coalescing() {
168 let mut txn = create_test_command_transaction();
169 let view_v1 = test_view_def(1, 1, "view_v1");
170 let view_v2 = test_view_def(1, 1, "view_v2");
171 let view_v3 = test_view_def(1, 1, "view_v3");
172
173 txn.track_view_def_updated(view_v1.clone(), view_v2.clone()).unwrap();
175
176 assert_eq!(txn.changes.view_def.len(), 1);
178 assert_eq!(txn.changes.view_def[0].pre.as_ref().unwrap().name, "view_v1");
179 assert_eq!(txn.changes.view_def[0].post.as_ref().unwrap().name, "view_v2");
180 assert_eq!(txn.changes.view_def[0].op, Update);
181
182 txn.track_view_def_updated(view_v2, view_v3.clone()).unwrap();
184
185 assert_eq!(txn.changes.view_def.len(), 2);
187
188 assert_eq!(txn.changes.view_def[1].pre.as_ref().unwrap().name, "view_v2");
190 assert_eq!(txn.changes.view_def[1].post.as_ref().unwrap().name, "view_v3");
191
192 assert_eq!(txn.changes.log.len(), 2);
194 }
195
196 #[test]
197 fn test_create_then_update_no_coalescing() {
198 let mut txn = create_test_command_transaction();
199 let view_v1 = test_view_def(1, 1, "view_v1");
200 let view_v2 = test_view_def(1, 1, "view_v2");
201
202 txn.track_view_def_created(view_v1.clone()).unwrap();
204 assert_eq!(txn.changes.view_def.len(), 1);
205 assert_eq!(txn.changes.view_def[0].op, Create);
206
207 txn.track_view_def_updated(view_v1, view_v2.clone()).unwrap();
209
210 assert_eq!(txn.changes.view_def.len(), 2);
212
213 assert_eq!(txn.changes.view_def[0].op, Create);
215
216 assert_eq!(txn.changes.view_def[1].op, Update);
218
219 assert_eq!(txn.changes.log.len(), 2);
221 }
222
223 #[test]
224 fn test_normal_update() {
225 let mut txn = create_test_command_transaction();
226 let view_v1 = test_view_def(1, 1, "view_v1");
227 let view_v2 = test_view_def(1, 1, "view_v2");
228
229 let result = txn.track_view_def_updated(view_v1.clone(), view_v2.clone());
230 assert!(result.is_ok());
231
232 assert_eq!(txn.changes.view_def.len(), 1);
234 let change = &txn.changes.view_def[0];
235 assert_eq!(change.pre.as_ref().unwrap().name, "view_v1");
236 assert_eq!(change.post.as_ref().unwrap().name, "view_v2");
237 assert_eq!(change.op, Update);
238
239 assert_eq!(txn.changes.log.len(), 1);
241 match &txn.changes.log[0] {
242 Operation::View {
243 id,
244 op,
245 } if *id == ViewId(1) && *op == Update => {}
246 _ => panic!("Expected View operation with Update"),
247 }
248 }
249 }
250
251 mod track_view_def_deleted {
252 use super::*;
253
254 #[test]
255 fn test_delete_after_create_no_coalescing() {
256 let mut txn = create_test_command_transaction();
257 let view = test_view_def(1, 1, "test_view");
258
259 txn.track_view_def_created(view.clone()).unwrap();
261 assert_eq!(txn.changes.view_def.len(), 1);
262
263 let result = txn.track_view_def_deleted(view.clone());
265 assert!(result.is_ok());
266
267 assert_eq!(txn.changes.view_def.len(), 2);
269
270 assert_eq!(txn.changes.view_def[0].op, Create);
272
273 assert_eq!(txn.changes.view_def[1].op, Delete);
275
276 assert_eq!(txn.changes.log.len(), 2);
278 }
279
280 #[test]
281 fn test_delete_after_update_no_coalescing() {
282 let mut txn = create_test_command_transaction();
283 let view_v1 = test_view_def(1, 1, "view_v1");
284 let view_v2 = test_view_def(1, 1, "view_v2");
285
286 txn.track_view_def_updated(view_v1.clone(), view_v2.clone()).unwrap();
288 assert_eq!(txn.changes.view_def.len(), 1);
289
290 let result = txn.track_view_def_deleted(view_v2);
292 assert!(result.is_ok());
293
294 assert_eq!(txn.changes.view_def.len(), 2);
296
297 assert_eq!(txn.changes.view_def[0].op, Update);
299
300 assert_eq!(txn.changes.view_def[1].op, Delete);
302
303 assert_eq!(txn.changes.log.len(), 2);
305 }
306
307 #[test]
308 fn test_normal_delete() {
309 let mut txn = create_test_command_transaction();
310 let view = test_view_def(1, 1, "test_view");
311
312 let result = txn.track_view_def_deleted(view.clone());
313 assert!(result.is_ok());
314
315 assert_eq!(txn.changes.view_def.len(), 1);
317 let change = &txn.changes.view_def[0];
318 assert_eq!(change.pre.as_ref().unwrap().name, "test_view");
319 assert!(change.post.is_none());
320 assert_eq!(change.op, Delete);
321
322 assert_eq!(txn.changes.log.len(), 1);
324 match &txn.changes.log[0] {
325 Operation::View {
326 id,
327 op,
328 } if *id == view.id && *op == Delete => {}
329 _ => panic!("Expected View operation with Delete"),
330 }
331 }
332 }
333}