Skip to main content

reifydb_transaction/transaction/
admin.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::{mem::take, sync::Arc};
5
6use reifydb_core::{
7	common::CommitVersion,
8	encoded::{
9		key::{EncodedKey, EncodedKeyRange},
10		row::EncodedRow,
11	},
12	event::EventBus,
13	execution::ExecutionResult,
14	interface::{
15		WithEventBus,
16		change::{Change, ChangeOrigin},
17		store::{MultiVersionBatch, MultiVersionRow},
18	},
19};
20use reifydb_runtime::context::clock::Clock;
21use reifydb_type::{
22	Result,
23	error::Diagnostic,
24	params::Params,
25	value::{datetime::DateTime, identity::IdentityId},
26};
27use tracing::instrument;
28
29use crate::{
30	TransactionId,
31	change::{RowChange, TransactionalCatalogChanges, TransactionalChanges},
32	change_accumulator::ChangeAccumulator,
33	error::TransactionError,
34	interceptor::{
35		WithInterceptors,
36		authentication::{AuthenticationPostCreateInterceptor, AuthenticationPreDeleteInterceptor},
37		chain::InterceptorChain as Chain,
38		dictionary::{
39			DictionaryPostCreateInterceptor, DictionaryPostUpdateInterceptor,
40			DictionaryPreDeleteInterceptor, DictionaryPreUpdateInterceptor,
41		},
42		dictionary_row::{
43			DictionaryRowPostDeleteInterceptor, DictionaryRowPostInsertInterceptor,
44			DictionaryRowPostUpdateInterceptor, DictionaryRowPreDeleteInterceptor,
45			DictionaryRowPreInsertInterceptor, DictionaryRowPreUpdateInterceptor,
46		},
47		granted_role::{GrantedRolePostCreateInterceptor, GrantedRolePreDeleteInterceptor},
48		identity::{
49			IdentityPostCreateInterceptor, IdentityPostUpdateInterceptor, IdentityPreDeleteInterceptor,
50			IdentityPreUpdateInterceptor,
51		},
52		interceptors::Interceptors,
53		namespace::{
54			NamespacePostCreateInterceptor, NamespacePostUpdateInterceptor, NamespacePreDeleteInterceptor,
55			NamespacePreUpdateInterceptor,
56		},
57		ringbuffer::{
58			RingBufferPostCreateInterceptor, RingBufferPostUpdateInterceptor,
59			RingBufferPreDeleteInterceptor, RingBufferPreUpdateInterceptor,
60		},
61		ringbuffer_row::{
62			RingBufferRowPostDeleteInterceptor, RingBufferRowPostInsertInterceptor,
63			RingBufferRowPostUpdateInterceptor, RingBufferRowPreDeleteInterceptor,
64			RingBufferRowPreInsertInterceptor, RingBufferRowPreUpdateInterceptor,
65		},
66		role::{
67			RolePostCreateInterceptor, RolePostUpdateInterceptor, RolePreDeleteInterceptor,
68			RolePreUpdateInterceptor,
69		},
70		series::{
71			SeriesPostCreateInterceptor, SeriesPostUpdateInterceptor, SeriesPreDeleteInterceptor,
72			SeriesPreUpdateInterceptor,
73		},
74		series_row::{
75			SeriesRowPostDeleteInterceptor, SeriesRowPostInsertInterceptor, SeriesRowPostUpdateInterceptor,
76			SeriesRowPreDeleteInterceptor, SeriesRowPreInsertInterceptor, SeriesRowPreUpdateInterceptor,
77		},
78		table::{
79			TablePostCreateInterceptor, TablePostUpdateInterceptor, TablePreDeleteInterceptor,
80			TablePreUpdateInterceptor,
81		},
82		table_row::{
83			TableRowPostDeleteInterceptor, TableRowPostInsertInterceptor, TableRowPostUpdateInterceptor,
84			TableRowPreDeleteInterceptor, TableRowPreInsertInterceptor, TableRowPreUpdateInterceptor,
85		},
86		transaction::{PostCommitContext, PostCommitInterceptor, PreCommitContext, PreCommitInterceptor},
87		view::{
88			ViewPostCreateInterceptor, ViewPostUpdateInterceptor, ViewPreDeleteInterceptor,
89			ViewPreUpdateInterceptor,
90		},
91		view_row::{
92			ViewRowPostDeleteInterceptor, ViewRowPostInsertInterceptor, ViewRowPostUpdateInterceptor,
93			ViewRowPreDeleteInterceptor, ViewRowPreInsertInterceptor, ViewRowPreUpdateInterceptor,
94		},
95	},
96	multi::{
97		pending::PendingWrites,
98		transaction::{MultiTransaction, write::MultiWriteTransaction},
99	},
100	single::{SingleTransaction, read::SingleReadTransaction, write::SingleWriteTransaction},
101	transaction::{
102		RqlExecutor, Transaction, apply_pre_commit_writes, collect_transaction_writes, query::QueryTransaction,
103		write::Write,
104	},
105};
106
107/// An active admin transaction that supports Query + DML + DDL operations.
108///
109/// AdminTransaction is the most privileged transaction type, capable of
110/// executing DDL (shape changes), DML (data mutations), and queries.
111/// It tracks catalog definition changes (TransactionalCatalogChanges) for DDL.
112///
113/// The transaction will auto-rollback on drop if not explicitly committed.
114pub struct AdminTransaction {
115	pub multi: MultiTransaction,
116	pub single: SingleTransaction,
117	state: TransactionState,
118
119	pub cmd: Option<MultiWriteTransaction>,
120	pub event_bus: EventBus,
121	pub changes: TransactionalCatalogChanges,
122
123	// Track row changes for post-commit events
124	pub(crate) row_changes: Vec<RowChange>,
125	pub interceptors: Interceptors,
126
127	// Accumulate flow changes for transactional view pre-commit processing
128	pub(crate) accumulator: ChangeAccumulator,
129
130	/// The identity executing this transaction.
131	pub identity: IdentityId,
132
133	/// Optional RQL executor for running RQL within this transaction.
134	pub(crate) executor: Option<Arc<dyn RqlExecutor>>,
135
136	/// Clock for timestamping flow changes at commit.
137	pub(crate) clock: Clock,
138
139	/// When the transaction has been poisoned, stores the original error diagnostic.
140	poison_cause: Option<Diagnostic>,
141}
142
143#[derive(Clone, Copy, PartialEq)]
144enum TransactionState {
145	Active,
146	Committed,
147	RolledBack,
148	Poisoned,
149}
150
151impl AdminTransaction {
152	/// Creates a new active admin transaction with a pre-commit callback
153	#[instrument(name = "transaction::admin::new", level = "debug", skip_all)]
154	pub fn new(
155		multi: MultiTransaction,
156		single: SingleTransaction,
157		event_bus: EventBus,
158		interceptors: Interceptors,
159		identity: IdentityId,
160		clock: Clock,
161	) -> Result<Self> {
162		let cmd = multi.begin_command()?;
163		let txn_id = cmd.id();
164		Ok(Self {
165			cmd: Some(cmd),
166			multi,
167			single,
168			state: TransactionState::Active,
169			event_bus,
170			interceptors,
171			changes: TransactionalCatalogChanges::new(txn_id),
172			row_changes: Vec::new(),
173			accumulator: ChangeAccumulator::new(),
174			identity,
175			executor: None,
176			clock,
177			poison_cause: None,
178		})
179	}
180
181	/// Set the RQL executor for this transaction.
182	pub fn set_executor(&mut self, executor: Arc<dyn RqlExecutor>) {
183		self.executor = Some(executor);
184	}
185
186	/// Execute RQL within this transaction using the attached executor.
187	///
188	/// Panics if no `RqlExecutor` has been set on this transaction.
189	pub fn rql(&mut self, rql: &str, params: Params) -> ExecutionResult {
190		if let Err(e) = self.check_active() {
191			return ExecutionResult {
192				frames: vec![],
193				error: Some(e),
194				metrics: Default::default(),
195			};
196		}
197		let executor = self.executor.clone().expect("RqlExecutor not set");
198		let result = executor.rql(&mut Transaction::Admin(self), rql, params);
199		if let Some(ref e) = result.error {
200			self.poison(*e.0.clone());
201		}
202		result
203	}
204
205	#[instrument(name = "transaction::admin::event_bus", level = "trace", skip(self))]
206	pub fn event_bus(&self) -> &EventBus {
207		&self.event_bus
208	}
209
210	/// Check if transaction is still active and return appropriate error if
211	/// not
212	pub(crate) fn check_active(&self) -> Result<()> {
213		match self.state {
214			TransactionState::Active => Ok(()),
215			TransactionState::Committed => Err(TransactionError::AlreadyCommitted.into()),
216			TransactionState::RolledBack => Err(TransactionError::AlreadyRolledBack.into()),
217			TransactionState::Poisoned => Err(TransactionError::Poisoned {
218				cause: Box::new(self.poison_cause.clone().unwrap()),
219			}
220			.into()),
221		}
222	}
223
224	/// Mark this transaction as poisoned, storing the original error diagnostic.
225	pub(crate) fn poison(&mut self, cause: Diagnostic) {
226		self.state = TransactionState::Poisoned;
227		self.poison_cause = Some(cause);
228	}
229
230	/// Clear the poisoned state so the transaction can be reused after a restore.
231	pub(crate) fn unpoison(&mut self) {
232		self.state = TransactionState::Active;
233		self.poison_cause = None;
234	}
235
236	/// Commit the transaction.
237	/// Since single transactions are short-lived and auto-commit,
238	/// this only commits the multi transaction.
239	#[instrument(name = "transaction::admin::commit", level = "debug", skip(self))]
240	pub fn commit(&mut self) -> Result<CommitVersion> {
241		self.check_active()?;
242		let mut ctx = self.build_pre_commit_context();
243		self.interceptors.pre_commit.execute(&mut ctx)?;
244		self.finalize_commit(ctx)
245	}
246
247	#[inline]
248	fn build_pre_commit_context(&mut self) -> PreCommitContext {
249		let transaction_writes = collect_transaction_writes(self.pending_writes());
250		PreCommitContext {
251			flow_changes: self
252				.accumulator
253				.take_changes(CommitVersion(0), DateTime::from_nanos(self.clock.now_nanos())),
254			pending_writes: Vec::new(),
255			pending_shapes: Vec::new(),
256			transaction_writes,
257			view_entries: Vec::new(),
258		}
259	}
260
261	fn finalize_commit(&mut self, ctx: PreCommitContext) -> Result<CommitVersion> {
262		let Some(mut multi) = self.cmd.take() else {
263			unreachable!("Transaction state inconsistency")
264		};
265		apply_pre_commit_writes(&mut multi, &ctx.pending_writes)?;
266		let id = multi.id();
267		self.state = TransactionState::Committed;
268
269		let changes = take(&mut self.changes);
270		let row_changes = take(&mut self.row_changes);
271		let version = multi.commit()?;
272		self.interceptors.post_commit.execute(PostCommitContext::new(id, version, changes, row_changes))?;
273		Ok(version)
274	}
275
276	/// Rollback the transaction.
277	#[instrument(name = "transaction::admin::rollback", level = "debug", skip(self))]
278	pub fn rollback(&mut self) -> Result<()> {
279		self.check_active()?;
280		if let Some(mut multi) = self.cmd.take() {
281			self.state = TransactionState::RolledBack;
282			multi.rollback()
283		} else {
284			// This should never happen due to check_active
285			unreachable!("Transaction state inconsistency")
286		}
287	}
288
289	/// Get access to the pending writes in this transaction
290	#[instrument(name = "transaction::admin::pending_writes", level = "trace", skip(self))]
291	pub fn pending_writes(&self) -> &PendingWrites {
292		self.cmd.as_ref().unwrap().pending_writes()
293	}
294
295	/// Execute a function with query access to the single transaction.
296	#[instrument(name = "transaction::admin::with_single_query", level = "trace", skip(self, keys, f))]
297	pub fn with_single_query<'a, I, F, R>(&self, keys: I, f: F) -> Result<R>
298	where
299		I: IntoIterator<Item = &'a EncodedKey> + Send,
300		F: FnOnce(&mut SingleReadTransaction<'_>) -> Result<R> + Send,
301		R: Send,
302	{
303		self.check_active()?;
304		self.single.with_query(keys, f)
305	}
306
307	/// Execute a function with query access to the single transaction.
308	#[instrument(name = "transaction::admin::with_single_command", level = "trace", skip(self, keys, f))]
309	pub fn with_single_command<'a, I, F, R>(&self, keys: I, f: F) -> Result<R>
310	where
311		I: IntoIterator<Item = &'a EncodedKey> + Send,
312		F: FnOnce(&mut SingleWriteTransaction<'_>) -> Result<R> + Send,
313		R: Send,
314	{
315		self.check_active()?;
316		self.single.with_command(keys, f)
317	}
318
319	/// Execute a function with a query transaction view.
320	#[instrument(name = "transaction::admin::with_multi_query", level = "trace", skip(self, f))]
321	pub fn with_multi_query<F, R>(&self, f: F) -> Result<R>
322	where
323		F: FnOnce(&mut QueryTransaction) -> Result<R>,
324	{
325		self.check_active()?;
326
327		let mut query_txn =
328			QueryTransaction::new(self.multi.begin_query()?, self.single.clone(), self.identity);
329
330		f(&mut query_txn)
331	}
332
333	#[instrument(name = "transaction::admin::with_multi_query_as_of_exclusive", level = "trace", skip(self, f))]
334	pub fn with_multi_query_as_of_exclusive<F, R>(&self, version: CommitVersion, f: F) -> Result<R>
335	where
336		F: FnOnce(&mut QueryTransaction) -> Result<R>,
337	{
338		self.check_active()?;
339
340		let mut query_txn =
341			QueryTransaction::new(self.multi.begin_query()?, self.single.clone(), self.identity);
342
343		query_txn.read_as_of_version_exclusive(version)?;
344
345		f(&mut query_txn)
346	}
347
348	#[instrument(name = "transaction::admin::with_multi_query_as_of_inclusive", level = "trace", skip(self, f))]
349	pub fn with_multi_query_as_of_inclusive<F, R>(&self, version: CommitVersion, f: F) -> Result<R>
350	where
351		F: FnOnce(&mut QueryTransaction) -> Result<R>,
352	{
353		self.check_active()?;
354
355		let mut query_txn =
356			QueryTransaction::new(self.multi.begin_query()?, self.single.clone(), self.identity);
357
358		query_txn.multi.read_as_of_version_inclusive(version);
359
360		f(&mut query_txn)
361	}
362
363	/// Begin a single-version query transaction for specific keys
364	#[instrument(name = "transaction::admin::begin_single_query", level = "trace", skip(self, keys))]
365	pub fn begin_single_query<'a, I>(&self, keys: I) -> Result<SingleReadTransaction<'_>>
366	where
367		I: IntoIterator<Item = &'a EncodedKey>,
368	{
369		self.check_active()?;
370		self.single.begin_query(keys)
371	}
372
373	/// Begin a single-version command transaction for specific keys
374	#[instrument(name = "transaction::admin::begin_single_command", level = "trace", skip(self, keys))]
375	pub fn begin_single_command<'a, I>(&self, keys: I) -> Result<SingleWriteTransaction<'_>>
376	where
377		I: IntoIterator<Item = &'a EncodedKey>,
378	{
379		self.check_active()?;
380		self.single.begin_command(keys)
381	}
382
383	/// Get reference to catalog changes for this transaction
384	pub fn get_changes(&self) -> &TransactionalCatalogChanges {
385		&self.changes
386	}
387
388	/// Track a row change for post-commit event emission
389	pub fn track_row_change(&mut self, change: RowChange) {
390		self.row_changes.push(change);
391	}
392
393	/// Track a flow change for transactional view pre-commit processing.
394	pub fn track_flow_change(&mut self, change: Change) {
395		if let ChangeOrigin::Shape(id) = change.origin {
396			for diff in change.diffs {
397				self.accumulator.track(id, diff);
398			}
399		}
400	}
401
402	/// Get the transaction version
403	#[inline]
404	pub fn version(&self) -> CommitVersion {
405		self.cmd.as_ref().unwrap().version()
406	}
407
408	/// Get the transaction ID
409	#[inline]
410	pub fn id(&self) -> TransactionId {
411		self.cmd.as_ref().unwrap().id()
412	}
413
414	/// Get a value by key
415	#[inline]
416	pub fn get(&mut self, key: &EncodedKey) -> Result<Option<MultiVersionRow>> {
417		self.check_active()?;
418		Ok(self.cmd.as_mut().unwrap().get(key)?.map(|v| v.into_multi_version_row()))
419	}
420
421	/// Read the committed value at the transaction's read version, ignoring
422	/// pending intra-tx writes.
423	#[inline]
424	pub fn get_committed(&mut self, key: &EncodedKey) -> Result<Option<MultiVersionRow>> {
425		self.check_active()?;
426		Ok(self.cmd.as_mut().unwrap().get_committed(key)?.map(|v| v.into_multi_version_row()))
427	}
428
429	/// Check if a key exists
430	#[inline]
431	pub fn contains_key(&mut self, key: &EncodedKey) -> Result<bool> {
432		self.check_active()?;
433		self.cmd.as_mut().unwrap().contains_key(key)
434	}
435
436	/// Get a prefix batch
437	#[inline]
438	pub fn prefix(&mut self, prefix: &EncodedKey) -> Result<MultiVersionBatch> {
439		self.check_active()?;
440		self.cmd.as_mut().unwrap().prefix(prefix)
441	}
442
443	/// Get a reverse prefix batch
444	#[inline]
445	pub fn prefix_rev(&mut self, prefix: &EncodedKey) -> Result<MultiVersionBatch> {
446		self.check_active()?;
447		self.cmd.as_mut().unwrap().prefix_rev(prefix)
448	}
449
450	/// Read as of version exclusive
451	#[inline]
452	pub fn read_as_of_version_exclusive(&mut self, version: CommitVersion) -> Result<()> {
453		self.check_active()?;
454		self.cmd.as_mut().unwrap().read_as_of_version_exclusive(version);
455		Ok(())
456	}
457
458	/// Set a key-value pair
459	#[inline]
460	pub fn set(&mut self, key: &EncodedKey, row: EncodedRow) -> Result<()> {
461		self.check_active()?;
462		self.cmd.as_mut().unwrap().set(key, row)
463	}
464
465	/// Unset a key, preserving the deleted values.
466	#[inline]
467	pub fn unset(&mut self, key: &EncodedKey, row: EncodedRow) -> Result<()> {
468		self.check_active()?;
469		self.cmd.as_mut().unwrap().unset(key, row)
470	}
471
472	/// Remove a key without preserving the deleted values.
473	#[inline]
474	pub fn remove(&mut self, key: &EncodedKey) -> Result<()> {
475		self.check_active()?;
476		self.cmd.as_mut().unwrap().remove(key)
477	}
478
479	#[inline]
480	pub fn mark_preexisting(&mut self, key: &EncodedKey) -> Result<()> {
481		self.check_active()?;
482		self.cmd.as_mut().unwrap().mark_preexisting(key);
483		Ok(())
484	}
485
486	/// Create a streaming iterator for forward range queries.
487	#[inline]
488	pub fn range(
489		&mut self,
490		range: EncodedKeyRange,
491		batch_size: usize,
492	) -> Result<Box<dyn Iterator<Item = Result<MultiVersionRow>> + Send + '_>> {
493		self.check_active()?;
494		Ok(self.cmd.as_mut().unwrap().range(range, batch_size))
495	}
496
497	/// Create a streaming iterator for reverse range queries.
498	#[inline]
499	pub fn range_rev(
500		&mut self,
501		range: EncodedKeyRange,
502		batch_size: usize,
503	) -> Result<Box<dyn Iterator<Item = Result<MultiVersionRow>> + Send + '_>> {
504		self.check_active()?;
505		Ok(self.cmd.as_mut().unwrap().range_rev(range, batch_size))
506	}
507}
508
509impl WithEventBus for AdminTransaction {
510	fn event_bus(&self) -> &EventBus {
511		&self.event_bus
512	}
513}
514
515impl Write for AdminTransaction {
516	#[inline]
517	fn set(&mut self, key: &EncodedKey, row: EncodedRow) -> Result<()> {
518		AdminTransaction::set(self, key, row)
519	}
520	#[inline]
521	fn unset(&mut self, key: &EncodedKey, row: EncodedRow) -> Result<()> {
522		AdminTransaction::unset(self, key, row)
523	}
524	#[inline]
525	fn remove(&mut self, key: &EncodedKey) -> Result<()> {
526		AdminTransaction::remove(self, key)
527	}
528	#[inline]
529	fn mark_preexisting(&mut self, key: &EncodedKey) -> Result<()> {
530		AdminTransaction::mark_preexisting(self, key)
531	}
532	#[inline]
533	fn track_row_change(&mut self, change: RowChange) {
534		AdminTransaction::track_row_change(self, change)
535	}
536	#[inline]
537	fn track_flow_change(&mut self, change: Change) {
538		AdminTransaction::track_flow_change(self, change)
539	}
540}
541
542impl WithInterceptors for AdminTransaction {
543	fn table_row_pre_insert_interceptors(&mut self) -> &mut Chain<dyn TableRowPreInsertInterceptor + Send + Sync> {
544		&mut self.interceptors.table_row_pre_insert
545	}
546
547	fn table_row_post_insert_interceptors(
548		&mut self,
549	) -> &mut Chain<dyn TableRowPostInsertInterceptor + Send + Sync> {
550		&mut self.interceptors.table_row_post_insert
551	}
552
553	fn table_row_pre_update_interceptors(&mut self) -> &mut Chain<dyn TableRowPreUpdateInterceptor + Send + Sync> {
554		&mut self.interceptors.table_row_pre_update
555	}
556
557	fn table_row_post_update_interceptors(
558		&mut self,
559	) -> &mut Chain<dyn TableRowPostUpdateInterceptor + Send + Sync> {
560		&mut self.interceptors.table_row_post_update
561	}
562
563	fn table_row_pre_delete_interceptors(&mut self) -> &mut Chain<dyn TableRowPreDeleteInterceptor + Send + Sync> {
564		&mut self.interceptors.table_row_pre_delete
565	}
566
567	fn table_row_post_delete_interceptors(
568		&mut self,
569	) -> &mut Chain<dyn TableRowPostDeleteInterceptor + Send + Sync> {
570		&mut self.interceptors.table_row_post_delete
571	}
572
573	fn ringbuffer_row_pre_insert_interceptors(
574		&mut self,
575	) -> &mut Chain<dyn RingBufferRowPreInsertInterceptor + Send + Sync> {
576		&mut self.interceptors.ringbuffer_row_pre_insert
577	}
578
579	fn ringbuffer_row_post_insert_interceptors(
580		&mut self,
581	) -> &mut Chain<dyn RingBufferRowPostInsertInterceptor + Send + Sync> {
582		&mut self.interceptors.ringbuffer_row_post_insert
583	}
584
585	fn ringbuffer_row_pre_update_interceptors(
586		&mut self,
587	) -> &mut Chain<dyn RingBufferRowPreUpdateInterceptor + Send + Sync> {
588		&mut self.interceptors.ringbuffer_row_pre_update
589	}
590
591	fn ringbuffer_row_post_update_interceptors(
592		&mut self,
593	) -> &mut Chain<dyn RingBufferRowPostUpdateInterceptor + Send + Sync> {
594		&mut self.interceptors.ringbuffer_row_post_update
595	}
596
597	fn ringbuffer_row_pre_delete_interceptors(
598		&mut self,
599	) -> &mut Chain<dyn RingBufferRowPreDeleteInterceptor + Send + Sync> {
600		&mut self.interceptors.ringbuffer_row_pre_delete
601	}
602
603	fn ringbuffer_row_post_delete_interceptors(
604		&mut self,
605	) -> &mut Chain<dyn RingBufferRowPostDeleteInterceptor + Send + Sync> {
606		&mut self.interceptors.ringbuffer_row_post_delete
607	}
608
609	fn pre_commit_interceptors(&mut self) -> &mut Chain<dyn PreCommitInterceptor + Send + Sync> {
610		&mut self.interceptors.pre_commit
611	}
612
613	fn post_commit_interceptors(&mut self) -> &mut Chain<dyn PostCommitInterceptor + Send + Sync> {
614		&mut self.interceptors.post_commit
615	}
616
617	fn namespace_post_create_interceptors(
618		&mut self,
619	) -> &mut Chain<dyn NamespacePostCreateInterceptor + Send + Sync> {
620		&mut self.interceptors.namespace_post_create
621	}
622
623	fn namespace_pre_update_interceptors(&mut self) -> &mut Chain<dyn NamespacePreUpdateInterceptor + Send + Sync> {
624		&mut self.interceptors.namespace_pre_update
625	}
626
627	fn namespace_post_update_interceptors(
628		&mut self,
629	) -> &mut Chain<dyn NamespacePostUpdateInterceptor + Send + Sync> {
630		&mut self.interceptors.namespace_post_update
631	}
632
633	fn namespace_pre_delete_interceptors(&mut self) -> &mut Chain<dyn NamespacePreDeleteInterceptor + Send + Sync> {
634		&mut self.interceptors.namespace_pre_delete
635	}
636
637	fn table_post_create_interceptors(&mut self) -> &mut Chain<dyn TablePostCreateInterceptor + Send + Sync> {
638		&mut self.interceptors.table_post_create
639	}
640
641	fn table_pre_update_interceptors(&mut self) -> &mut Chain<dyn TablePreUpdateInterceptor + Send + Sync> {
642		&mut self.interceptors.table_pre_update
643	}
644
645	fn table_post_update_interceptors(&mut self) -> &mut Chain<dyn TablePostUpdateInterceptor + Send + Sync> {
646		&mut self.interceptors.table_post_update
647	}
648
649	fn table_pre_delete_interceptors(&mut self) -> &mut Chain<dyn TablePreDeleteInterceptor + Send + Sync> {
650		&mut self.interceptors.table_pre_delete
651	}
652
653	fn view_row_pre_insert_interceptors(&mut self) -> &mut Chain<dyn ViewRowPreInsertInterceptor + Send + Sync> {
654		&mut self.interceptors.view_row_pre_insert
655	}
656
657	fn view_row_post_insert_interceptors(&mut self) -> &mut Chain<dyn ViewRowPostInsertInterceptor + Send + Sync> {
658		&mut self.interceptors.view_row_post_insert
659	}
660
661	fn view_row_pre_update_interceptors(&mut self) -> &mut Chain<dyn ViewRowPreUpdateInterceptor + Send + Sync> {
662		&mut self.interceptors.view_row_pre_update
663	}
664
665	fn view_row_post_update_interceptors(&mut self) -> &mut Chain<dyn ViewRowPostUpdateInterceptor + Send + Sync> {
666		&mut self.interceptors.view_row_post_update
667	}
668
669	fn view_row_pre_delete_interceptors(&mut self) -> &mut Chain<dyn ViewRowPreDeleteInterceptor + Send + Sync> {
670		&mut self.interceptors.view_row_pre_delete
671	}
672
673	fn view_row_post_delete_interceptors(&mut self) -> &mut Chain<dyn ViewRowPostDeleteInterceptor + Send + Sync> {
674		&mut self.interceptors.view_row_post_delete
675	}
676
677	fn view_post_create_interceptors(&mut self) -> &mut Chain<dyn ViewPostCreateInterceptor + Send + Sync> {
678		&mut self.interceptors.view_post_create
679	}
680
681	fn view_pre_update_interceptors(&mut self) -> &mut Chain<dyn ViewPreUpdateInterceptor + Send + Sync> {
682		&mut self.interceptors.view_pre_update
683	}
684
685	fn view_post_update_interceptors(&mut self) -> &mut Chain<dyn ViewPostUpdateInterceptor + Send + Sync> {
686		&mut self.interceptors.view_post_update
687	}
688
689	fn view_pre_delete_interceptors(&mut self) -> &mut Chain<dyn ViewPreDeleteInterceptor + Send + Sync> {
690		&mut self.interceptors.view_pre_delete
691	}
692
693	fn ringbuffer_post_create_interceptors(
694		&mut self,
695	) -> &mut Chain<dyn RingBufferPostCreateInterceptor + Send + Sync> {
696		&mut self.interceptors.ringbuffer_post_create
697	}
698
699	fn ringbuffer_pre_update_interceptors(
700		&mut self,
701	) -> &mut Chain<dyn RingBufferPreUpdateInterceptor + Send + Sync> {
702		&mut self.interceptors.ringbuffer_pre_update
703	}
704
705	fn ringbuffer_post_update_interceptors(
706		&mut self,
707	) -> &mut Chain<dyn RingBufferPostUpdateInterceptor + Send + Sync> {
708		&mut self.interceptors.ringbuffer_post_update
709	}
710
711	fn ringbuffer_pre_delete_interceptors(
712		&mut self,
713	) -> &mut Chain<dyn RingBufferPreDeleteInterceptor + Send + Sync> {
714		&mut self.interceptors.ringbuffer_pre_delete
715	}
716
717	fn dictionary_row_pre_insert_interceptors(
718		&mut self,
719	) -> &mut Chain<dyn DictionaryRowPreInsertInterceptor + Send + Sync> {
720		&mut self.interceptors.dictionary_row_pre_insert
721	}
722
723	fn dictionary_row_post_insert_interceptors(
724		&mut self,
725	) -> &mut Chain<dyn DictionaryRowPostInsertInterceptor + Send + Sync> {
726		&mut self.interceptors.dictionary_row_post_insert
727	}
728
729	fn dictionary_row_pre_update_interceptors(
730		&mut self,
731	) -> &mut Chain<dyn DictionaryRowPreUpdateInterceptor + Send + Sync> {
732		&mut self.interceptors.dictionary_row_pre_update
733	}
734
735	fn dictionary_row_post_update_interceptors(
736		&mut self,
737	) -> &mut Chain<dyn DictionaryRowPostUpdateInterceptor + Send + Sync> {
738		&mut self.interceptors.dictionary_row_post_update
739	}
740
741	fn dictionary_row_pre_delete_interceptors(
742		&mut self,
743	) -> &mut Chain<dyn DictionaryRowPreDeleteInterceptor + Send + Sync> {
744		&mut self.interceptors.dictionary_row_pre_delete
745	}
746
747	fn dictionary_row_post_delete_interceptors(
748		&mut self,
749	) -> &mut Chain<dyn DictionaryRowPostDeleteInterceptor + Send + Sync> {
750		&mut self.interceptors.dictionary_row_post_delete
751	}
752
753	fn dictionary_post_create_interceptors(
754		&mut self,
755	) -> &mut Chain<dyn DictionaryPostCreateInterceptor + Send + Sync> {
756		&mut self.interceptors.dictionary_post_create
757	}
758
759	fn dictionary_pre_update_interceptors(
760		&mut self,
761	) -> &mut Chain<dyn DictionaryPreUpdateInterceptor + Send + Sync> {
762		&mut self.interceptors.dictionary_pre_update
763	}
764
765	fn dictionary_post_update_interceptors(
766		&mut self,
767	) -> &mut Chain<dyn DictionaryPostUpdateInterceptor + Send + Sync> {
768		&mut self.interceptors.dictionary_post_update
769	}
770
771	fn dictionary_pre_delete_interceptors(
772		&mut self,
773	) -> &mut Chain<dyn DictionaryPreDeleteInterceptor + Send + Sync> {
774		&mut self.interceptors.dictionary_pre_delete
775	}
776
777	fn series_row_pre_insert_interceptors(
778		&mut self,
779	) -> &mut Chain<dyn SeriesRowPreInsertInterceptor + Send + Sync> {
780		&mut self.interceptors.series_row_pre_insert
781	}
782
783	fn series_row_post_insert_interceptors(
784		&mut self,
785	) -> &mut Chain<dyn SeriesRowPostInsertInterceptor + Send + Sync> {
786		&mut self.interceptors.series_row_post_insert
787	}
788
789	fn series_row_pre_update_interceptors(
790		&mut self,
791	) -> &mut Chain<dyn SeriesRowPreUpdateInterceptor + Send + Sync> {
792		&mut self.interceptors.series_row_pre_update
793	}
794
795	fn series_row_post_update_interceptors(
796		&mut self,
797	) -> &mut Chain<dyn SeriesRowPostUpdateInterceptor + Send + Sync> {
798		&mut self.interceptors.series_row_post_update
799	}
800
801	fn series_row_pre_delete_interceptors(
802		&mut self,
803	) -> &mut Chain<dyn SeriesRowPreDeleteInterceptor + Send + Sync> {
804		&mut self.interceptors.series_row_pre_delete
805	}
806
807	fn series_row_post_delete_interceptors(
808		&mut self,
809	) -> &mut Chain<dyn SeriesRowPostDeleteInterceptor + Send + Sync> {
810		&mut self.interceptors.series_row_post_delete
811	}
812
813	fn series_post_create_interceptors(&mut self) -> &mut Chain<dyn SeriesPostCreateInterceptor + Send + Sync> {
814		&mut self.interceptors.series_post_create
815	}
816
817	fn series_pre_update_interceptors(&mut self) -> &mut Chain<dyn SeriesPreUpdateInterceptor + Send + Sync> {
818		&mut self.interceptors.series_pre_update
819	}
820
821	fn series_post_update_interceptors(&mut self) -> &mut Chain<dyn SeriesPostUpdateInterceptor + Send + Sync> {
822		&mut self.interceptors.series_post_update
823	}
824
825	fn series_pre_delete_interceptors(&mut self) -> &mut Chain<dyn SeriesPreDeleteInterceptor + Send + Sync> {
826		&mut self.interceptors.series_pre_delete
827	}
828
829	fn identity_post_create_interceptors(&mut self) -> &mut Chain<dyn IdentityPostCreateInterceptor + Send + Sync> {
830		&mut self.interceptors.identity_post_create
831	}
832
833	fn identity_pre_update_interceptors(&mut self) -> &mut Chain<dyn IdentityPreUpdateInterceptor + Send + Sync> {
834		&mut self.interceptors.identity_pre_update
835	}
836
837	fn identity_post_update_interceptors(&mut self) -> &mut Chain<dyn IdentityPostUpdateInterceptor + Send + Sync> {
838		&mut self.interceptors.identity_post_update
839	}
840
841	fn identity_pre_delete_interceptors(&mut self) -> &mut Chain<dyn IdentityPreDeleteInterceptor + Send + Sync> {
842		&mut self.interceptors.identity_pre_delete
843	}
844
845	fn role_post_create_interceptors(&mut self) -> &mut Chain<dyn RolePostCreateInterceptor + Send + Sync> {
846		&mut self.interceptors.role_post_create
847	}
848
849	fn role_pre_update_interceptors(&mut self) -> &mut Chain<dyn RolePreUpdateInterceptor + Send + Sync> {
850		&mut self.interceptors.role_pre_update
851	}
852
853	fn role_post_update_interceptors(&mut self) -> &mut Chain<dyn RolePostUpdateInterceptor + Send + Sync> {
854		&mut self.interceptors.role_post_update
855	}
856
857	fn role_pre_delete_interceptors(&mut self) -> &mut Chain<dyn RolePreDeleteInterceptor + Send + Sync> {
858		&mut self.interceptors.role_pre_delete
859	}
860
861	fn granted_role_post_create_interceptors(
862		&mut self,
863	) -> &mut Chain<dyn GrantedRolePostCreateInterceptor + Send + Sync> {
864		&mut self.interceptors.granted_role_post_create
865	}
866
867	fn granted_role_pre_delete_interceptors(
868		&mut self,
869	) -> &mut Chain<dyn GrantedRolePreDeleteInterceptor + Send + Sync> {
870		&mut self.interceptors.granted_role_pre_delete
871	}
872
873	fn authentication_post_create_interceptors(
874		&mut self,
875	) -> &mut Chain<dyn AuthenticationPostCreateInterceptor + Send + Sync> {
876		&mut self.interceptors.authentication_post_create
877	}
878
879	fn authentication_pre_delete_interceptors(
880		&mut self,
881	) -> &mut Chain<dyn AuthenticationPreDeleteInterceptor + Send + Sync> {
882		&mut self.interceptors.authentication_pre_delete
883	}
884}
885
886impl TransactionalChanges for AdminTransaction {}
887
888impl Drop for AdminTransaction {
889	fn drop(&mut self) {
890		if let Some(mut multi) = self.cmd.take() {
891			// Auto-rollback if still active or poisoned (not committed or rolled back)
892			if self.state == TransactionState::Active || self.state == TransactionState::Poisoned {
893				let _ = multi.rollback();
894			}
895		}
896	}
897}