1use 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}