Skip to main content

reifydb_engine/
engine.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::{
5	collections::HashMap,
6	ops::Deref,
7	sync::{
8		Arc,
9		atomic::{AtomicBool, Ordering},
10	},
11};
12
13use reifydb_auth::service::AuthEngine;
14use reifydb_catalog::{
15	catalog::Catalog,
16	interceptor::CatalogCacheInterceptor,
17	metrics::storage::metrics::MetricsReader,
18	vtable::{
19		system::operator_libary::{OperatorLibrary, OperatorLibraryEventListener},
20		tables::UserVTableDataFunction,
21		user::{UserVTable, UserVTableColumn, registry::UserVTableEntry},
22	},
23};
24use reifydb_cdc::{
25	consume::{host::CdcHost, wake::CdcWakeRegistry, watermark::CdcConsumerWatermark},
26	produce::watermark::CdcProducerWatermark,
27};
28use reifydb_core::{
29	common::CommitVersion,
30	durability::Durability,
31	error::diagnostic::engine::read_only_rejection,
32	event::{Event, EventBus},
33	execution::ExecutionResult,
34	flow::operator::OperatorDef,
35	interface::{
36		WithEventBus,
37		catalog::{
38			column::{Column, ColumnIndex},
39			flow::OperatorId,
40			id::{ColumnId, NamespaceId},
41			subscription::{SubscribeOptions, SubscribeOutcome},
42			vtable::{VTable, VTableId},
43		},
44	},
45	internal,
46	lifecycle::watermark::CheckpointFloor,
47	metrics::sample::MetricKind,
48	util::ioc::IocContainer,
49};
50use reifydb_runtime::{
51	actor::{mailbox::ActorRef, system::ActorSpawner},
52	context::{clock::Clock, rng::Rng},
53	shutdown::Shutdown,
54	version_epoch::VersionEpoch,
55};
56use reifydb_store_cdc::store::CdcStore;
57use reifydb_store_operator::store::OperatorStore;
58use reifydb_store_single::SingleStore;
59#[cfg(feature = "column")]
60use reifydb_transaction::transaction::ScanLayout;
61use reifydb_transaction::{
62	dictionary::{DictionaryAllocatorRegistry, store::SingleDictionaryStore},
63	error::TransactionError,
64	interceptor::{factory::InterceptorFactory, interceptors::Interceptors},
65	multi::{lease::VersionLeaseGuard, transaction::MultiTransaction},
66	single::SingleTransaction,
67	transaction::{Transaction, admin::AdminTransaction, command::CommandTransaction, query::QueryTransaction},
68};
69use reifydb_value::{
70	error,
71	error::Error,
72	fragment::Fragment,
73	params::Params,
74	reifydb_assertions,
75	value::{constraint::TypeConstraint, duration::Duration, identity::IdentityId},
76};
77use tracing::instrument;
78
79use crate::{
80	Result,
81	bulk_insert::builder::{BulkInsertBuilder, Unchecked, Validated},
82	queue::{interceptor::QueueSchedulingInterceptor, wake::QueueWakeRegistry},
83	vm::{
84		Admin, Command, Query,
85		executor::Executor,
86		flow_lineage::ViewLineage,
87		services::{EngineConfig, Services},
88	},
89};
90
91pub struct StandardEngine(Arc<Inner>);
92
93impl WithEventBus for StandardEngine {
94	fn event_bus(&self) -> &EventBus {
95		&self.event_bus
96	}
97}
98
99impl AuthEngine for StandardEngine {
100	fn begin_admin(&self) -> Result<AdminTransaction> {
101		StandardEngine::begin_admin(self, IdentityId::system(), Durability::Async)
102	}
103
104	fn begin_query(&self) -> Result<QueryTransaction> {
105		StandardEngine::begin_query(self, IdentityId::system())
106	}
107
108	fn catalog(&self) -> Catalog {
109		StandardEngine::catalog(self)
110	}
111}
112
113impl StandardEngine {
114	#[instrument(name = "engine::transaction::begin_command", level = "debug", skip(self))]
115	pub fn begin_command(&self, identity: IdentityId, durability: Durability) -> Result<CommandTransaction> {
116		reifydb_assertions! {
117			assert!(
118				!self.is_read_only(),
119				"begin_command called on a read-only engine: writes are permanently disabled after set_read_only(), so any caller reaching this point has bypassed the reject_if_read_only guard (identity={:?})",
120				identity
121			);
122		}
123		let interceptors = self.interceptors.create();
124		let mut txn = CommandTransaction::new(
125			self.multi.clone(),
126			self.single.clone(),
127			self.event_bus.clone(),
128			interceptors,
129			identity,
130			durability,
131		)?;
132		txn.set_executor(Arc::new(self.executor.clone()));
133		txn.set_dictionary_allocators(self.dictionary_allocators.clone());
134		Ok(txn)
135	}
136
137	#[instrument(name = "engine::transaction::begin_admin", level = "debug", skip(self))]
138	pub fn begin_admin(&self, identity: IdentityId, durability: Durability) -> Result<AdminTransaction> {
139		let interceptors = self.interceptors.create();
140		let mut txn = AdminTransaction::new(
141			self.multi.clone(),
142			self.single.clone(),
143			self.event_bus.clone(),
144			interceptors,
145			identity,
146			durability,
147		)?;
148		txn.set_executor(Arc::new(self.executor.clone()));
149		txn.set_dictionary_allocators(self.dictionary_allocators.clone());
150		Ok(txn)
151	}
152
153	#[instrument(name = "engine::transaction::begin_query", level = "trace", skip(self))]
154	pub fn begin_query(&self, identity: IdentityId) -> Result<QueryTransaction> {
155		let mut txn = QueryTransaction::new(self.multi.begin_query()?, self.single.clone(), identity);
156		txn.set_executor(Arc::new(self.executor.clone()));
157		Ok(txn)
158	}
159
160	pub fn clock(&self) -> &Clock {
161		&self.executor.runtime_context.clock
162	}
163
164	pub fn rng(&self) -> &Rng {
165		&self.executor.runtime_context.rng
166	}
167
168	pub fn version_epoch(&self) -> &VersionEpoch {
169		&self.executor.runtime_context.version_epoch
170	}
171
172	#[instrument(name = "engine::admin_as", level = "debug", skip(self, params), fields(rql = %rql))]
173	pub fn admin_as(&self, identity: IdentityId, rql: &str, params: Params) -> ExecutionResult {
174		if let Some(e) = self.reject_request(identity) {
175			return ExecutionResult::from_error(e);
176		}
177		let mut txn = match self.begin_admin(identity, Durability::Async) {
178			Ok(t) => t,
179			Err(mut e) => {
180				e.with_rql(rql.to_string());
181				return ExecutionResult::from_error(e);
182			}
183		};
184		let mut outcome = self.executor.admin(
185			&mut txn,
186			Admin {
187				rql,
188				params,
189			},
190		);
191		strip_unnamed_system_columns(&mut outcome);
192		self.commit_admin(&mut txn, &mut outcome, rql);
193		self.annotate_rql(&mut outcome, rql);
194		outcome
195	}
196
197	fn reject_request(&self, identity: IdentityId) -> Option<Error> {
198		if let Err(e) = self.reject_if_read_only() {
199			return Some(e);
200		}
201		if let Err(e) = self.reject_if_shutting_down(identity) {
202			return Some(e);
203		}
204		None
205	}
206
207	#[inline]
208	fn commit_admin(&self, txn: &mut AdminTransaction, outcome: &mut ExecutionResult, rql: &str) {
209		if outcome.is_ok()
210			&& let Err(mut e) = txn.commit()
211		{
212			e.with_rql(rql.to_string());
213			outcome.error = Some(e);
214		}
215	}
216
217	fn annotate_rql(&self, outcome: &mut ExecutionResult, rql: &str) {
218		if let Some(ref mut e) = outcome.error {
219			e.with_rql(rql.to_string());
220		}
221		reifydb_assertions! {
222			let annotated = outcome.error.as_ref().map(|e| e.rql.is_some());
223			assert!(
224				annotated != Some(false),
225				"annotate_rql is the single catch-all that attaches the originating query to every error leaving admin_as/command_as; an error reaching the user with rql=None (annotated={:?}) would render a diagnostic with no source query, defeating user-facing error reporting",
226				annotated
227			);
228		}
229	}
230
231	#[instrument(name = "engine::command_as", level = "debug", skip(self, params), fields(rql = %rql))]
232	pub fn command_as(&self, identity: IdentityId, rql: &str, params: Params) -> ExecutionResult {
233		if let Some(e) = self.reject_request(identity) {
234			return ExecutionResult::from_error(e);
235		}
236		let mut txn = match self.begin_command(identity, Durability::Async) {
237			Ok(t) => t,
238			Err(mut e) => {
239				e.with_rql(rql.to_string());
240				return ExecutionResult::from_error(e);
241			}
242		};
243		let mut outcome = self.executor.command(
244			&mut txn,
245			Command {
246				rql,
247				params,
248			},
249		);
250		strip_unnamed_system_columns(&mut outcome);
251		self.commit_command(&mut txn, &mut outcome, rql);
252		self.annotate_rql(&mut outcome, rql);
253		outcome
254	}
255
256	#[inline]
257	fn commit_command(&self, txn: &mut CommandTransaction, outcome: &mut ExecutionResult, rql: &str) {
258		if outcome.is_ok()
259			&& let Err(mut e) = txn.commit()
260		{
261			e.with_rql(rql.to_string());
262			outcome.error = Some(e);
263		}
264	}
265
266	#[instrument(name = "engine::query_as", level = "debug", skip(self, params), fields(rql = %rql))]
267	pub fn query_as(&self, identity: IdentityId, rql: &str, params: Params) -> ExecutionResult {
268		let mut txn = match self.begin_query(identity) {
269			Ok(t) => t,
270			Err(mut e) => {
271				e.with_rql(rql.to_string());
272				return ExecutionResult::from_error(e);
273			}
274		};
275		let mut outcome = self.executor.query(
276			&mut txn,
277			Query {
278				rql,
279				params,
280			},
281		);
282		strip_unnamed_system_columns(&mut outcome);
283		if let Some(ref mut e) = outcome.error {
284			e.with_rql(rql.to_string());
285		}
286		outcome
287	}
288
289	#[cfg(feature = "column")]
290	#[instrument(name = "engine::query_column_as", level = "debug", skip(self, params), fields(rql = %rql))]
291	pub fn query_column_as(&self, identity: IdentityId, rql: &str, params: Params) -> ExecutionResult {
292		let mut txn = match self.begin_query(identity) {
293			Ok(t) => t,
294			Err(mut e) => {
295				e.with_rql(rql.to_string());
296				return ExecutionResult::from_error(e);
297			}
298		};
299		txn.layout = ScanLayout::Column;
300		let mut outcome = self.executor.query(
301			&mut txn,
302			Query {
303				rql,
304				params,
305			},
306		);
307		strip_unnamed_system_columns(&mut outcome);
308		if let Some(ref mut e) = outcome.error {
309			e.with_rql(rql.to_string());
310		}
311		outcome
312	}
313
314	#[instrument(name = "engine::subscribe_as", level = "debug", skip(self, params, options), fields(query = %query))]
315	pub fn subscribe_as(
316		&self,
317		identity: IdentityId,
318		query: &str,
319		params: Params,
320		options: SubscribeOptions,
321	) -> Result<SubscribeOutcome> {
322		self.begin_query(identity)
323			.and_then(|mut txn| self.executor.subscribe(&mut txn, query, params, options))
324			.map_err(|mut e| {
325				e.with_rql(query.to_string());
326				e
327			})
328	}
329
330	pub fn register_virtual_table<T: UserVTable>(
331		&self,
332		namespace_id: NamespaceId,
333		name: &str,
334		table: T,
335	) -> Result<VTableId> {
336		let catalog = self.catalog();
337		let table_id = self.executor.virtual_table_registry.allocate_id();
338
339		let table_columns = table.vtable();
340		if name == "current"
341			&& let Some(column) = table_columns.iter().find(|column| column.kind == MetricKind::Counter)
342		{
343			return Err(error!(internal!(
344				"virtual table '{}' in namespace {:?} declares column '{}' with kind Counter; a table named 'current' may only publish levels, deltas, cumulatives and distributions",
345				name,
346				namespace_id,
347				column.name
348			)));
349		}
350		let columns = convert_vtable_user_columns_to_columns(&table_columns);
351
352		let def = Arc::new(VTable {
353			id: table_id,
354			namespace: namespace_id,
355			name: name.to_string(),
356			columns,
357		});
358
359		catalog.register_vtable_user(def.clone())?;
360
361		let data_fn: UserVTableDataFunction = Arc::new(move |_params| table.get());
362
363		let entry = UserVTableEntry {
364			def: def.clone(),
365			data_fn,
366		};
367		self.executor.virtual_table_registry.register(namespace_id, name.to_string(), entry);
368		Ok(table_id)
369	}
370}
371
372impl CdcHost for StandardEngine {
373	fn begin_command(&self) -> Result<CommandTransaction> {
374		StandardEngine::begin_command(self, IdentityId::system(), Durability::Async)
375	}
376
377	fn begin_query(&self) -> Result<QueryTransaction> {
378		StandardEngine::begin_query(self, IdentityId::system())
379	}
380
381	fn current_version(&self) -> Result<CommitVersion> {
382		StandardEngine::current_version(self)
383	}
384
385	fn done_until(&self) -> CommitVersion {
386		StandardEngine::done_until(self)
387	}
388
389	fn cdc_producer_watermark(&self) -> CommitVersion {
390		StandardEngine::cdc_producer_watermark(self)
391	}
392
393	fn wait_for_mark_timeout(&self, version: CommitVersion, timeout: Duration) -> bool {
394		StandardEngine::wait_for_mark_timeout(self, version, timeout)
395	}
396
397	fn notify_on_mark(&self, version: CommitVersion, callback: Box<dyn FnOnce() + Send>) {
398		StandardEngine::notify_on_mark(self, version, callback);
399	}
400
401	fn catalog(&self) -> &Catalog {
402		&self.catalog
403	}
404}
405
406impl Clone for StandardEngine {
407	fn clone(&self) -> Self {
408		Self(self.0.clone())
409	}
410}
411
412impl Deref for StandardEngine {
413	type Target = Inner;
414
415	fn deref(&self) -> &Self::Target {
416		&self.0
417	}
418}
419
420pub struct Inner {
421	multi: MultiTransaction,
422	single: SingleTransaction,
423	event_bus: EventBus,
424	executor: Executor,
425	interceptors: Arc<InterceptorFactory>,
426	catalog: Catalog,
427	operator_library: OperatorLibrary,
428	operator_state: OperatorStore,
429	dictionary_allocators: DictionaryAllocatorRegistry,
430	read_only: AtomicBool,
431	shutting_down: AtomicBool,
432}
433
434impl StandardEngine {
435	pub fn new(
436		multi: MultiTransaction,
437		single: SingleTransaction,
438		event_bus: EventBus,
439		interceptors: InterceptorFactory,
440		catalog: Catalog,
441		config: EngineConfig,
442	) -> Self {
443		let operator_library = OperatorLibrary::new();
444
445		let listener = OperatorLibraryEventListener::new(operator_library.clone());
446		event_bus.register(listener);
447
448		let metrics_store = config
449			.ioc
450			.resolve::<SingleStore>()
451			.expect("SingleStore must be registered in IocContainer for metrics");
452		let metrics_reader = MetricsReader::new(metrics_store);
453
454		let operator_state = config
455			.ioc
456			.resolve::<OperatorStore>()
457			.expect("OperatorStore must be registered in IocContainer");
458
459		let catalog_for_interceptor = catalog.clone();
460		interceptors.add_late(Arc::new(move |interceptors: &mut Interceptors| {
461			interceptors.pre_publish.add(Arc::new(CatalogCacheInterceptor::new(&catalog_for_interceptor)));
462		}));
463
464		let queue_wake = config.ioc.try_resolve::<QueueWakeRegistry>().unwrap_or_else(|| {
465			let registry = QueueWakeRegistry::new();
466			config.ioc.register_service(registry.clone());
467			registry
468		});
469
470		let single_for_interceptor = single.clone();
471		let wake_for_interceptor = queue_wake.clone();
472		let clock_for_interceptor = config.runtime_context.clock.clone();
473		interceptors.add_late(Arc::new(move |interceptors: &mut Interceptors| {
474			interceptors.post_commit.add(Arc::new(QueueSchedulingInterceptor::new(
475				single_for_interceptor.clone(),
476				wake_for_interceptor.clone(),
477				clock_for_interceptor.clone(),
478			)));
479		}));
480
481		let interceptors = Arc::new(interceptors);
482
483		let dictionary_allocators =
484			DictionaryAllocatorRegistry::new(Arc::new(SingleDictionaryStore::new(single.clone())));
485
486		Self(Arc::new(Inner {
487			multi,
488			single,
489			event_bus,
490			executor: Executor::new(catalog.clone(), config, operator_library.clone(), metrics_reader),
491			interceptors,
492			catalog,
493			operator_library,
494			operator_state,
495			dictionary_allocators,
496			read_only: AtomicBool::new(false),
497			shutting_down: AtomicBool::new(false),
498		}))
499	}
500
501	pub fn create_interceptors(&self) -> Interceptors {
502		self.interceptors.create()
503	}
504
505	pub fn dictionary_allocators(&self) -> DictionaryAllocatorRegistry {
506		self.dictionary_allocators.clone()
507	}
508
509	pub fn add_interceptor_factory(&self, factory: Arc<dyn Fn(&mut Interceptors) + Send + Sync>) {
510		self.interceptors.add_late(factory);
511	}
512
513	#[instrument(name = "engine::transaction::begin_query_at_version", level = "trace", skip(self, lease), fields(version = %lease.version().0
514    ))]
515	pub fn begin_query_at_version(
516		&self,
517		lease: &VersionLeaseGuard,
518		identity: IdentityId,
519	) -> Result<QueryTransaction> {
520		let mut txn =
521			QueryTransaction::new(self.multi.begin_query_at_version(lease)?, self.single.clone(), identity);
522		txn.set_executor(Arc::new(self.executor.clone()));
523		Ok(txn)
524	}
525
526	#[instrument(name = "engine::acquire_version_lease", level = "trace", skip(self), fields(version = %version.0))]
527	pub fn acquire_version_lease(&self, version: CommitVersion) -> Result<VersionLeaseGuard> {
528		self.multi.acquire_version_lease(version)
529	}
530
531	#[instrument(name = "engine::acquire_current_snapshot_lease", level = "trace", skip(self))]
532	pub fn acquire_current_snapshot_lease(&self) -> Result<(CommitVersion, VersionLeaseGuard)> {
533		self.multi.acquire_current_snapshot_lease()
534	}
535
536	#[inline]
537	pub fn multi(&self) -> &MultiTransaction {
538		&self.multi
539	}
540
541	#[inline]
542	pub fn multi_owned(&self) -> MultiTransaction {
543		self.multi.clone()
544	}
545
546	#[inline]
547	pub fn spawner(&self) -> ActorSpawner {
548		self.multi.spawner()
549	}
550
551	#[inline]
552	pub fn single(&self) -> &SingleTransaction {
553		&self.single
554	}
555
556	#[inline]
557	pub fn single_owned(&self) -> SingleTransaction {
558		self.single.clone()
559	}
560
561	#[inline]
562	pub fn emit<E: Event>(&self, event: E) {
563		self.event_bus.emit(event)
564	}
565
566	#[inline]
567	pub fn catalog(&self) -> Catalog {
568		self.catalog.clone()
569	}
570
571	#[inline]
572	pub fn services(&self) -> Arc<Services> {
573		self.executor.services().clone()
574	}
575
576	#[inline]
577	pub fn operator_store(&self) -> &OperatorLibrary {
578		&self.operator_library
579	}
580
581	pub fn apply_operator_names(&self, txn: &mut Transaction<'_>) -> Result<HashMap<OperatorId, String>> {
582		let mut names = HashMap::new();
583		for dag in self.catalog.list_flow_dags_asc(txn)? {
584			for id in dag.get_operator_ids() {
585				if let Some(node) = dag.get_operator(&id)
586					&& let OperatorDef::Apply {
587						operator: name,
588						..
589					} = &node.ty
590				{
591					names.insert(id, name.clone());
592				}
593			}
594		}
595		Ok(names)
596	}
597
598	pub fn operator_state(&self) -> OperatorStore {
599		self.operator_state.clone()
600	}
601
602	pub fn checkpoint_floor(&self) -> Arc<dyn CheckpointFloor> {
603		Arc::new(self.operator_state.clone())
604	}
605
606	#[inline]
607	pub fn current_version(&self) -> Result<CommitVersion> {
608		self.multi.current_version()
609	}
610
611	#[inline]
612	pub fn done_until(&self) -> CommitVersion {
613		self.multi.done_until()
614	}
615
616	#[inline]
617	pub fn query_done_until(&self) -> CommitVersion {
618		self.multi.query_done_until()
619	}
620
621	#[inline]
622	pub fn oracle_window_count(&self) -> usize {
623		self.multi.oracle_window_count()
624	}
625
626	#[inline]
627	pub fn wait_for_mark_timeout(&self, version: CommitVersion, timeout: Duration) -> bool {
628		self.multi.wait_for_mark_timeout(version, timeout)
629	}
630
631	#[inline]
632	pub fn notify_on_mark(&self, version: CommitVersion, callback: Box<dyn FnOnce() + Send>) {
633		self.multi.notify_on_mark(version, callback);
634	}
635
636	#[inline]
637	pub fn executor(&self) -> Executor {
638		self.executor.clone()
639	}
640
641	#[inline]
642	pub fn view_lineage(&self) -> ViewLineage {
643		self.executor.view_lineage.clone()
644	}
645
646	#[inline]
647	pub fn ioc(&self) -> &IocContainer {
648		&self.executor.ioc
649	}
650
651	#[inline]
652	pub fn queue_wake(&self) -> QueueWakeRegistry {
653		self.ioc().resolve::<QueueWakeRegistry>().expect("StandardEngine::new registers the QueueWakeRegistry")
654	}
655
656	#[inline]
657	pub fn cdc_store(&self) -> CdcStore {
658		self.executor.ioc.resolve::<CdcStore>().expect("CdcStore must be registered")
659	}
660
661	#[inline]
662	pub fn actor<M: 'static>(&self) -> Option<ActorRef<M>>
663	where
664		ActorRef<M>: Send + Sync,
665	{
666		self.executor.ioc.try_resolve::<ActorRef<M>>()
667	}
668
669	#[inline]
670	pub fn cdc_producer_watermark(&self) -> CommitVersion {
671		self.executor.ioc.try_resolve::<CdcProducerWatermark>().map(|w| w.get()).unwrap_or(CommitVersion(0))
672	}
673
674	#[inline]
675	pub fn cdc_consumer_watermark(&self) -> CommitVersion {
676		self.executor.ioc.try_resolve::<CdcConsumerWatermark>().map(|w| w.get()).unwrap_or(CommitVersion(0))
677	}
678
679	#[inline]
680	pub fn notify_cdc_consumers(&self) {
681		if let Some(registry) = self.executor.ioc.try_resolve::<CdcWakeRegistry>() {
682			registry.notify_all();
683		}
684	}
685
686	pub fn set_read_only(&self) {
687		self.read_only.store(true, Ordering::SeqCst);
688	}
689
690	pub fn is_read_only(&self) -> bool {
691		self.read_only.load(Ordering::SeqCst)
692	}
693
694	pub(crate) fn reject_if_read_only(&self) -> Result<()> {
695		if self.is_read_only() {
696			return Err(Error(Box::new(read_only_rejection(Fragment::None))));
697		}
698		Ok(())
699	}
700
701	pub fn set_shutting_down(&self) {
702		self.shutting_down.store(true, Ordering::SeqCst);
703	}
704
705	pub fn is_shutting_down(&self) -> bool {
706		self.shutting_down.load(Ordering::SeqCst)
707	}
708
709	pub(crate) fn reject_if_shutting_down(&self, identity: IdentityId) -> Result<()> {
710		if self.is_shutting_down() && !identity.is_system() {
711			return Err(TransactionError::ShuttingDown.into());
712		}
713		Ok(())
714	}
715
716	pub fn bulk_insert<'e>(&'e self, identity: IdentityId) -> BulkInsertBuilder<'e, Validated> {
717		BulkInsertBuilder::new(self, identity)
718	}
719
720	pub fn bulk_insert_unchecked<'e>(&'e self, identity: IdentityId) -> BulkInsertBuilder<'e, Unchecked> {
721		BulkInsertBuilder::new_unchecked(self, identity)
722	}
723}
724
725impl Shutdown for StandardEngine {
726	fn shutdown(&self) -> Result<()> {
727		self.interceptors.clear_late();
728		self.executor.ioc.clear();
729		self.executor.virtual_table_registry.clear();
730		self.multi().store().clear_eviction_watermark();
731		#[cfg(not(reifydb_single_threaded))]
732		if let Some(registry) = self.executor.remote_registry.as_ref() {
733			registry.shutdown();
734		}
735		Ok(())
736	}
737}
738
739fn convert_vtable_user_columns_to_columns(columns: &[UserVTableColumn]) -> Vec<Column> {
740	columns.iter()
741		.enumerate()
742		.map(|(idx, col)| {
743			let constraint = TypeConstraint::unconstrained(col.data_type.clone());
744			Column {
745				id: ColumnId(idx as u64),
746				name: col.name.clone(),
747				constraint,
748				properties: vec![],
749				index: ColumnIndex(idx as u8),
750				auto_increment: false,
751				dictionary_id: None,
752			}
753		})
754		.collect()
755}
756
757fn strip_unnamed_system_columns(outcome: &mut ExecutionResult) {
758	if let Err(e) = outcome.strip_unnamed_system_columns() {
759		outcome.frames.clear();
760		outcome.named_system_columns.clear();
761		outcome.error = Some(e);
762	}
763}