Skip to main content

reifydb_transaction/interceptor/
table_row.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_codec::row::{bytes::EncodedBytes, table::EncodedTableRowBuilder};
5use reifydb_core::interface::catalog::table::Table;
6use reifydb_value::{Result, value::row_number::RowNumber};
7
8use super::WithInterceptors;
9use crate::interceptor::chain::InterceptorChain;
10
11pub struct TableRowPreInsertContext<'a> {
12	pub table: &'a Table,
13	pub ids: &'a [RowNumber],
14	pub rows: &'a mut [EncodedTableRowBuilder],
15}
16
17impl<'a> TableRowPreInsertContext<'a> {
18	pub fn new(table: &'a Table, ids: &'a [RowNumber], rows: &'a mut [EncodedTableRowBuilder]) -> Self {
19		assert_eq!(ids.len(), rows.len(), "ids/rows length mismatch");
20		Self {
21			table,
22			ids,
23			rows,
24		}
25	}
26}
27
28pub trait TableRowPreInsertInterceptor: Send + Sync {
29	fn intercept<'a>(&self, ctx: &mut TableRowPreInsertContext<'a>) -> Result<()>;
30}
31
32impl InterceptorChain<dyn TableRowPreInsertInterceptor + Send + Sync> {
33	pub fn execute(&self, mut ctx: TableRowPreInsertContext) -> Result<()> {
34		let original_len = ctx.rows.len();
35		for interceptor in &self.interceptors {
36			interceptor.intercept(&mut ctx)?;
37			assert_eq!(ctx.rows.len(), original_len, "pre_insert interceptor changed row count");
38		}
39		Ok(())
40	}
41}
42
43pub struct ClosureTableRowPreInsertInterceptor<F>
44where
45	F: for<'a> Fn(&mut TableRowPreInsertContext<'a>) -> Result<()> + Send + Sync,
46{
47	closure: F,
48}
49
50impl<F> ClosureTableRowPreInsertInterceptor<F>
51where
52	F: for<'a> Fn(&mut TableRowPreInsertContext<'a>) -> Result<()> + Send + Sync,
53{
54	pub fn new(closure: F) -> Self {
55		Self {
56			closure,
57		}
58	}
59}
60
61impl<F> Clone for ClosureTableRowPreInsertInterceptor<F>
62where
63	F: for<'a> Fn(&mut TableRowPreInsertContext<'a>) -> Result<()> + Send + Sync + Clone,
64{
65	fn clone(&self) -> Self {
66		Self {
67			closure: self.closure.clone(),
68		}
69	}
70}
71
72impl<F> TableRowPreInsertInterceptor for ClosureTableRowPreInsertInterceptor<F>
73where
74	F: for<'a> Fn(&mut TableRowPreInsertContext<'a>) -> Result<()> + Send + Sync,
75{
76	fn intercept<'a>(&self, ctx: &mut TableRowPreInsertContext<'a>) -> Result<()> {
77		(self.closure)(ctx)
78	}
79}
80
81pub fn table_row_pre_insert<F>(f: F) -> ClosureTableRowPreInsertInterceptor<F>
82where
83	F: for<'a> Fn(&mut TableRowPreInsertContext<'a>) -> Result<()> + Send + Sync + Clone + 'static,
84{
85	ClosureTableRowPreInsertInterceptor::new(f)
86}
87
88pub struct TableRowPostInsertContext<'a> {
89	pub table: &'a Table,
90	pub ids: &'a [RowNumber],
91	pub rows: &'a [EncodedBytes],
92}
93
94impl<'a> TableRowPostInsertContext<'a> {
95	pub fn new(table: &'a Table, ids: &'a [RowNumber], rows: &'a [EncodedBytes]) -> Self {
96		assert_eq!(ids.len(), rows.len(), "ids/rows length mismatch");
97		Self {
98			table,
99			ids,
100			rows,
101		}
102	}
103}
104
105pub trait TableRowPostInsertInterceptor: Send + Sync {
106	fn intercept<'a>(&self, ctx: &mut TableRowPostInsertContext<'a>) -> Result<()>;
107}
108
109impl InterceptorChain<dyn TableRowPostInsertInterceptor + Send + Sync> {
110	pub fn execute(&self, mut ctx: TableRowPostInsertContext) -> Result<()> {
111		for interceptor in &self.interceptors {
112			interceptor.intercept(&mut ctx)?;
113		}
114		Ok(())
115	}
116}
117
118pub struct ClosureTableRowPostInsertInterceptor<F>
119where
120	F: for<'a> Fn(&mut TableRowPostInsertContext<'a>) -> Result<()> + Send + Sync,
121{
122	closure: F,
123}
124
125impl<F> ClosureTableRowPostInsertInterceptor<F>
126where
127	F: for<'a> Fn(&mut TableRowPostInsertContext<'a>) -> Result<()> + Send + Sync,
128{
129	pub fn new(closure: F) -> Self {
130		Self {
131			closure,
132		}
133	}
134}
135
136impl<F> Clone for ClosureTableRowPostInsertInterceptor<F>
137where
138	F: for<'a> Fn(&mut TableRowPostInsertContext<'a>) -> Result<()> + Send + Sync + Clone,
139{
140	fn clone(&self) -> Self {
141		Self {
142			closure: self.closure.clone(),
143		}
144	}
145}
146
147impl<F> TableRowPostInsertInterceptor for ClosureTableRowPostInsertInterceptor<F>
148where
149	F: for<'a> Fn(&mut TableRowPostInsertContext<'a>) -> Result<()> + Send + Sync,
150{
151	fn intercept<'a>(&self, ctx: &mut TableRowPostInsertContext<'a>) -> Result<()> {
152		(self.closure)(ctx)
153	}
154}
155
156pub fn table_row_post_insert<F>(f: F) -> ClosureTableRowPostInsertInterceptor<F>
157where
158	F: for<'a> Fn(&mut TableRowPostInsertContext<'a>) -> Result<()> + Send + Sync + Clone + 'static,
159{
160	ClosureTableRowPostInsertInterceptor::new(f)
161}
162
163pub struct TableRowPreUpdateContext<'a> {
164	pub table: &'a Table,
165	pub ids: &'a [RowNumber],
166	pub rows: &'a mut [EncodedTableRowBuilder],
167}
168
169impl<'a> TableRowPreUpdateContext<'a> {
170	pub fn new(table: &'a Table, ids: &'a [RowNumber], rows: &'a mut [EncodedTableRowBuilder]) -> Self {
171		assert_eq!(ids.len(), rows.len(), "ids/rows length mismatch");
172		Self {
173			table,
174			ids,
175			rows,
176		}
177	}
178}
179
180pub trait TableRowPreUpdateInterceptor: Send + Sync {
181	fn intercept<'a>(&self, ctx: &mut TableRowPreUpdateContext<'a>) -> Result<()>;
182}
183
184impl InterceptorChain<dyn TableRowPreUpdateInterceptor + Send + Sync> {
185	pub fn execute(&self, mut ctx: TableRowPreUpdateContext) -> Result<()> {
186		let original_len = ctx.rows.len();
187		for interceptor in &self.interceptors {
188			interceptor.intercept(&mut ctx)?;
189			assert_eq!(ctx.rows.len(), original_len, "pre_update interceptor changed row count");
190		}
191		Ok(())
192	}
193}
194
195pub struct ClosureTableRowPreUpdateInterceptor<F>
196where
197	F: for<'a> Fn(&mut TableRowPreUpdateContext<'a>) -> Result<()> + Send + Sync,
198{
199	closure: F,
200}
201
202impl<F> ClosureTableRowPreUpdateInterceptor<F>
203where
204	F: for<'a> Fn(&mut TableRowPreUpdateContext<'a>) -> Result<()> + Send + Sync,
205{
206	pub fn new(closure: F) -> Self {
207		Self {
208			closure,
209		}
210	}
211}
212
213impl<F> Clone for ClosureTableRowPreUpdateInterceptor<F>
214where
215	F: for<'a> Fn(&mut TableRowPreUpdateContext<'a>) -> Result<()> + Send + Sync + Clone,
216{
217	fn clone(&self) -> Self {
218		Self {
219			closure: self.closure.clone(),
220		}
221	}
222}
223
224impl<F> TableRowPreUpdateInterceptor for ClosureTableRowPreUpdateInterceptor<F>
225where
226	F: for<'a> Fn(&mut TableRowPreUpdateContext<'a>) -> Result<()> + Send + Sync,
227{
228	fn intercept<'a>(&self, ctx: &mut TableRowPreUpdateContext<'a>) -> Result<()> {
229		(self.closure)(ctx)
230	}
231}
232
233pub fn table_row_pre_update<F>(f: F) -> ClosureTableRowPreUpdateInterceptor<F>
234where
235	F: for<'a> Fn(&mut TableRowPreUpdateContext<'a>) -> Result<()> + Send + Sync + Clone + 'static,
236{
237	ClosureTableRowPreUpdateInterceptor::new(f)
238}
239
240pub struct TableRowPostUpdateContext<'a> {
241	pub table: &'a Table,
242	pub ids: &'a [RowNumber],
243	pub posts: &'a [EncodedBytes],
244	pub pres: &'a [EncodedBytes],
245}
246
247impl<'a> TableRowPostUpdateContext<'a> {
248	pub fn new(
249		table: &'a Table,
250		ids: &'a [RowNumber],
251		posts: &'a [EncodedBytes],
252		pres: &'a [EncodedBytes],
253	) -> Self {
254		assert_eq!(ids.len(), posts.len(), "ids/posts length mismatch");
255		assert_eq!(ids.len(), pres.len(), "ids/pres length mismatch");
256		Self {
257			table,
258			ids,
259			posts,
260			pres,
261		}
262	}
263}
264
265pub trait TableRowPostUpdateInterceptor: Send + Sync {
266	fn intercept<'a>(&self, ctx: &mut TableRowPostUpdateContext<'a>) -> Result<()>;
267}
268
269impl InterceptorChain<dyn TableRowPostUpdateInterceptor + Send + Sync> {
270	pub fn execute(&self, mut ctx: TableRowPostUpdateContext) -> Result<()> {
271		for interceptor in &self.interceptors {
272			interceptor.intercept(&mut ctx)?;
273		}
274		Ok(())
275	}
276}
277
278pub struct ClosureTableRowPostUpdateInterceptor<F>
279where
280	F: for<'a> Fn(&mut TableRowPostUpdateContext<'a>) -> Result<()> + Send + Sync,
281{
282	closure: F,
283}
284
285impl<F> ClosureTableRowPostUpdateInterceptor<F>
286where
287	F: for<'a> Fn(&mut TableRowPostUpdateContext<'a>) -> Result<()> + Send + Sync,
288{
289	pub fn new(closure: F) -> Self {
290		Self {
291			closure,
292		}
293	}
294}
295
296impl<F> Clone for ClosureTableRowPostUpdateInterceptor<F>
297where
298	F: for<'a> Fn(&mut TableRowPostUpdateContext<'a>) -> Result<()> + Send + Sync + Clone,
299{
300	fn clone(&self) -> Self {
301		Self {
302			closure: self.closure.clone(),
303		}
304	}
305}
306
307impl<F> TableRowPostUpdateInterceptor for ClosureTableRowPostUpdateInterceptor<F>
308where
309	F: for<'a> Fn(&mut TableRowPostUpdateContext<'a>) -> Result<()> + Send + Sync,
310{
311	fn intercept<'a>(&self, ctx: &mut TableRowPostUpdateContext<'a>) -> Result<()> {
312		(self.closure)(ctx)
313	}
314}
315
316pub fn table_row_post_update<F>(f: F) -> ClosureTableRowPostUpdateInterceptor<F>
317where
318	F: for<'a> Fn(&mut TableRowPostUpdateContext<'a>) -> Result<()> + Send + Sync + Clone + 'static,
319{
320	ClosureTableRowPostUpdateInterceptor::new(f)
321}
322
323pub struct TableRowPreDeleteContext<'a> {
324	pub table: &'a Table,
325	pub ids: &'a [RowNumber],
326}
327
328impl<'a> TableRowPreDeleteContext<'a> {
329	pub fn new(table: &'a Table, ids: &'a [RowNumber]) -> Self {
330		Self {
331			table,
332			ids,
333		}
334	}
335}
336
337pub trait TableRowPreDeleteInterceptor: Send + Sync {
338	fn intercept<'a>(&self, ctx: &mut TableRowPreDeleteContext<'a>) -> Result<()>;
339}
340
341impl InterceptorChain<dyn TableRowPreDeleteInterceptor + Send + Sync> {
342	pub fn execute(&self, mut ctx: TableRowPreDeleteContext) -> Result<()> {
343		for interceptor in &self.interceptors {
344			interceptor.intercept(&mut ctx)?;
345		}
346		Ok(())
347	}
348}
349
350pub struct ClosureTableRowPreDeleteInterceptor<F>
351where
352	F: for<'a> Fn(&mut TableRowPreDeleteContext<'a>) -> Result<()> + Send + Sync,
353{
354	closure: F,
355}
356
357impl<F> ClosureTableRowPreDeleteInterceptor<F>
358where
359	F: for<'a> Fn(&mut TableRowPreDeleteContext<'a>) -> Result<()> + Send + Sync,
360{
361	pub fn new(closure: F) -> Self {
362		Self {
363			closure,
364		}
365	}
366}
367
368impl<F> Clone for ClosureTableRowPreDeleteInterceptor<F>
369where
370	F: for<'a> Fn(&mut TableRowPreDeleteContext<'a>) -> Result<()> + Send + Sync + Clone,
371{
372	fn clone(&self) -> Self {
373		Self {
374			closure: self.closure.clone(),
375		}
376	}
377}
378
379impl<F> TableRowPreDeleteInterceptor for ClosureTableRowPreDeleteInterceptor<F>
380where
381	F: for<'a> Fn(&mut TableRowPreDeleteContext<'a>) -> Result<()> + Send + Sync,
382{
383	fn intercept<'a>(&self, ctx: &mut TableRowPreDeleteContext<'a>) -> Result<()> {
384		(self.closure)(ctx)
385	}
386}
387
388pub fn table_row_pre_delete<F>(f: F) -> ClosureTableRowPreDeleteInterceptor<F>
389where
390	F: for<'a> Fn(&mut TableRowPreDeleteContext<'a>) -> Result<()> + Send + Sync + Clone + 'static,
391{
392	ClosureTableRowPreDeleteInterceptor::new(f)
393}
394
395pub struct TableRowPostDeleteContext<'a> {
396	pub table: &'a Table,
397	pub ids: &'a [RowNumber],
398	pub deleted_rows: &'a [EncodedBytes],
399}
400
401impl<'a> TableRowPostDeleteContext<'a> {
402	pub fn new(table: &'a Table, ids: &'a [RowNumber], deleted_rows: &'a [EncodedBytes]) -> Self {
403		assert_eq!(ids.len(), deleted_rows.len(), "ids/deleted_rows length mismatch");
404		Self {
405			table,
406			ids,
407			deleted_rows,
408		}
409	}
410}
411
412pub trait TableRowPostDeleteInterceptor: Send + Sync {
413	fn intercept<'a>(&self, ctx: &mut TableRowPostDeleteContext<'a>) -> Result<()>;
414}
415
416impl InterceptorChain<dyn TableRowPostDeleteInterceptor + Send + Sync> {
417	pub fn execute(&self, mut ctx: TableRowPostDeleteContext) -> Result<()> {
418		for interceptor in &self.interceptors {
419			interceptor.intercept(&mut ctx)?;
420		}
421		Ok(())
422	}
423}
424
425pub struct ClosureTableRowPostDeleteInterceptor<F>
426where
427	F: for<'a> Fn(&mut TableRowPostDeleteContext<'a>) -> Result<()> + Send + Sync,
428{
429	closure: F,
430}
431
432impl<F> ClosureTableRowPostDeleteInterceptor<F>
433where
434	F: for<'a> Fn(&mut TableRowPostDeleteContext<'a>) -> Result<()> + Send + Sync,
435{
436	pub fn new(closure: F) -> Self {
437		Self {
438			closure,
439		}
440	}
441}
442
443impl<F> Clone for ClosureTableRowPostDeleteInterceptor<F>
444where
445	F: for<'a> Fn(&mut TableRowPostDeleteContext<'a>) -> Result<()> + Send + Sync + Clone,
446{
447	fn clone(&self) -> Self {
448		Self {
449			closure: self.closure.clone(),
450		}
451	}
452}
453
454impl<F> TableRowPostDeleteInterceptor for ClosureTableRowPostDeleteInterceptor<F>
455where
456	F: for<'a> Fn(&mut TableRowPostDeleteContext<'a>) -> Result<()> + Send + Sync,
457{
458	fn intercept<'a>(&self, ctx: &mut TableRowPostDeleteContext<'a>) -> Result<()> {
459		(self.closure)(ctx)
460	}
461}
462
463pub fn table_row_post_delete<F>(f: F) -> ClosureTableRowPostDeleteInterceptor<F>
464where
465	F: for<'a> Fn(&mut TableRowPostDeleteContext<'a>) -> Result<()> + Send + Sync + Clone + 'static,
466{
467	ClosureTableRowPostDeleteInterceptor::new(f)
468}
469
470pub struct TableRowInterceptor;
471
472impl TableRowInterceptor {
473	pub fn pre_insert(
474		txn: &mut impl WithInterceptors,
475		table: &Table,
476		ids: &[RowNumber],
477		rows: &mut [EncodedTableRowBuilder],
478	) -> Result<()> {
479		let ctx = TableRowPreInsertContext::new(table, ids, rows);
480		txn.table_row_pre_insert_interceptors().execute(ctx)
481	}
482
483	pub fn post_insert(
484		txn: &mut impl WithInterceptors,
485		table: &Table,
486		ids: &[RowNumber],
487		bytes_slice: &[EncodedBytes],
488	) -> Result<()> {
489		let ctx = TableRowPostInsertContext::new(table, ids, bytes_slice);
490		txn.table_row_post_insert_interceptors().execute(ctx)
491	}
492
493	pub fn pre_update(
494		txn: &mut impl WithInterceptors,
495		table: &Table,
496		ids: &[RowNumber],
497		rows: &mut [EncodedTableRowBuilder],
498	) -> Result<()> {
499		let ctx = TableRowPreUpdateContext::new(table, ids, rows);
500		txn.table_row_pre_update_interceptors().execute(ctx)
501	}
502
503	pub fn post_update(
504		txn: &mut impl WithInterceptors,
505		table: &Table,
506		ids: &[RowNumber],
507		posts: &[EncodedBytes],
508		pres: &[EncodedBytes],
509	) -> Result<()> {
510		let ctx = TableRowPostUpdateContext::new(table, ids, posts, pres);
511		txn.table_row_post_update_interceptors().execute(ctx)
512	}
513
514	pub fn pre_delete(txn: &mut impl WithInterceptors, table: &Table, ids: &[RowNumber]) -> Result<()> {
515		let ctx = TableRowPreDeleteContext::new(table, ids);
516		txn.table_row_pre_delete_interceptors().execute(ctx)
517	}
518
519	pub fn post_delete(
520		txn: &mut impl WithInterceptors,
521		table: &Table,
522		ids: &[RowNumber],
523		deleted: &[EncodedBytes],
524	) -> Result<()> {
525		let ctx = TableRowPostDeleteContext::new(table, ids, deleted);
526		txn.table_row_post_delete_interceptors().execute(ctx)
527	}
528}