1use std::collections::HashMap;
9use std::sync::Arc;
10
11use arc_swap::ArcSwap;
12use dashmap::DashMap;
13use parking_lot::{Mutex, RwLock};
14use smol_str::SmolStr;
15
16use crate::capability::CapabilitySet;
17use crate::errors::PluginError;
18use crate::plugin::PluginId;
19use crate::qname::QName;
20use crate::traits::aggregate::{AggSignature, AggregatePluginFn};
21use crate::traits::algorithm::AlgorithmProvider;
22use crate::traits::background::BackgroundJobProvider;
23use crate::traits::catalog::{CatalogProvider, ReplacementScanProvider};
24use crate::traits::cdc::CdcOutputProvider;
25use crate::traits::collation::CollationProvider;
26use crate::traits::connector::{AuthProvider, AuthzPolicy};
27use crate::traits::crdt::{CrdtKind, CrdtKindProvider};
28use crate::traits::hook::SessionHook;
29use crate::traits::index::{IndexHandle, IndexKind, IndexKindProvider};
30use crate::traits::locy::{
31 GenSignature, LocyAggregate, LocyGenerator, LocyPredicate, PredSignature,
32};
33use crate::traits::operator::OptimizerRuleProvider;
34use crate::traits::procedure::{ProcedurePlugin, ProcedureSignature};
35use crate::traits::scalar::{FnSignature, ScalarPluginFn};
36use crate::traits::trigger::TriggerPlugin;
37use crate::traits::types::LogicalTypeProvider;
38use crate::traits::window::{WindowPluginFn, WindowSignature};
39
40pub struct ScalarEntry {
42 pub plugin: PluginId,
44 pub signature: FnSignature,
46 pub function: Arc<dyn ScalarPluginFn>,
48}
49
50impl std::fmt::Debug for ScalarEntry {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 f.debug_struct("ScalarEntry")
53 .field("plugin", &self.plugin)
54 .field("signature", &self.signature)
55 .finish_non_exhaustive()
56 }
57}
58
59pub struct AggregateEntry {
61 pub plugin: PluginId,
63 pub signature: AggSignature,
65 pub aggregate: Arc<dyn AggregatePluginFn>,
67}
68
69impl std::fmt::Debug for AggregateEntry {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 f.debug_struct("AggregateEntry")
72 .field("plugin", &self.plugin)
73 .field("signature", &self.signature)
74 .finish_non_exhaustive()
75 }
76}
77
78pub struct WindowEntry {
80 pub plugin: PluginId,
82 pub signature: WindowSignature,
84 pub window: Arc<dyn WindowPluginFn>,
86}
87
88impl std::fmt::Debug for WindowEntry {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 f.debug_struct("WindowEntry")
91 .field("plugin", &self.plugin)
92 .field("signature", &self.signature)
93 .finish_non_exhaustive()
94 }
95}
96
97pub struct AlgorithmEntry {
103 pub plugin: PluginId,
105 pub effective_caps: CapabilitySet,
107 pub provider: Arc<dyn AlgorithmProvider>,
109}
110
111impl std::fmt::Debug for AlgorithmEntry {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 f.debug_struct("AlgorithmEntry")
114 .field("plugin", &self.plugin)
115 .field("effective_caps", &self.effective_caps)
116 .finish_non_exhaustive()
117 }
118}
119
120pub struct ProcedureEntry {
122 pub plugin: PluginId,
124 pub signature: ProcedureSignature,
126 pub procedure: Arc<dyn ProcedurePlugin>,
128}
129
130impl std::fmt::Debug for ProcedureEntry {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 f.debug_struct("ProcedureEntry")
133 .field("plugin", &self.plugin)
134 .field("signature", &self.signature)
135 .finish_non_exhaustive()
136 }
137}
138
139pub struct LocyAggregateEntry {
141 pub plugin: PluginId,
143 pub aggregate: Arc<dyn LocyAggregate>,
145}
146
147impl std::fmt::Debug for LocyAggregateEntry {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 f.debug_struct("LocyAggregateEntry")
150 .field("plugin", &self.plugin)
151 .finish_non_exhaustive()
152 }
153}
154
155pub struct LocyPredicateEntry {
157 pub plugin: PluginId,
159 pub signature: PredSignature,
161 pub predicate: Arc<dyn LocyPredicate>,
163}
164
165impl std::fmt::Debug for LocyPredicateEntry {
166 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167 f.debug_struct("LocyPredicateEntry")
168 .field("plugin", &self.plugin)
169 .field("signature", &self.signature)
170 .finish_non_exhaustive()
171 }
172}
173
174pub struct LocyGeneratorEntry {
176 pub plugin: PluginId,
178 pub signature: GenSignature,
180 pub generator: Arc<dyn LocyGenerator>,
182}
183
184impl std::fmt::Debug for LocyGeneratorEntry {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 f.debug_struct("LocyGeneratorEntry")
187 .field("plugin", &self.plugin)
188 .field("signature", &self.signature)
189 .finish_non_exhaustive()
190 }
191}
192
193#[derive(Clone)]
208pub struct IndexHandleEntry {
209 pub kind: IndexKind,
212 pub handle: Arc<dyn IndexHandle>,
214}
215
216impl std::fmt::Debug for IndexHandleEntry {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 f.debug_struct("IndexHandleEntry")
219 .field("kind", &self.kind)
220 .finish_non_exhaustive()
221 }
222}
223
224#[derive(Clone)]
232pub struct VirtualEntry {
233 pub name: SmolStr,
235 pub table: Arc<dyn crate::traits::catalog::CatalogTable>,
237}
238
239impl std::fmt::Debug for VirtualEntry {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 f.debug_struct("VirtualEntry")
242 .field("name", &self.name)
243 .finish_non_exhaustive()
244 }
245}
246
247trait VirtualId:
252 Copy + Eq + Ord + std::hash::Hash + std::fmt::Debug + std::fmt::LowerHex + 'static
253{
254 const START: Self;
256 const SENTINEL: Self;
259 const KIND_LABEL: &'static str;
262
263 fn next(self) -> Self;
266}
267
268impl VirtualId for u16 {
269 const START: Self = uni_common::core::schema::VIRTUAL_LABEL_ID_START;
270 const SENTINEL: Self = uni_common::core::schema::VIRTUAL_LABEL_ID_SENTINEL;
271 const KIND_LABEL: &'static str = "label";
272
273 fn next(self) -> Self {
274 self.saturating_add(1)
275 }
276}
277
278impl VirtualId for u32 {
279 const START: Self = uni_common::core::edge_type::VIRTUAL_EDGE_TYPE_ID_START;
280 const SENTINEL: Self = uni_common::core::edge_type::VIRTUAL_EDGE_TYPE_ID_SENTINEL;
281 const KIND_LABEL: &'static str = "edge-type";
282
283 fn next(self) -> Self {
284 self.saturating_add(1)
285 }
286}
287
288#[derive(Debug)]
293struct VirtualIdSpace<Id: VirtualId> {
294 name_to_id: HashMap<SmolStr, Id>,
295 id_to_entry: HashMap<Id, VirtualEntry>,
296 next_id: Id,
297}
298
299impl<Id: VirtualId> Default for VirtualIdSpace<Id> {
300 fn default() -> Self {
301 Self {
302 name_to_id: HashMap::new(),
303 id_to_entry: HashMap::new(),
304 next_id: Id::START,
305 }
306 }
307}
308
309impl<Id: VirtualId> VirtualIdSpace<Id> {
310 fn register(
314 &mut self,
315 name: SmolStr,
316 table: Arc<dyn crate::traits::catalog::CatalogTable>,
317 ) -> Result<Id, PluginError> {
318 if let Some(&id) = self.name_to_id.get(&name) {
319 self.id_to_entry.insert(
320 id,
321 VirtualEntry {
322 name: name.clone(),
323 table,
324 },
325 );
326 return Ok(id);
327 }
328 if self.next_id >= Id::SENTINEL {
329 return Err(PluginError::Internal(format!(
330 "virtual {}-ID space exhausted ({} slots taken; sentinel {:#x})",
331 Id::KIND_LABEL,
332 self.id_to_entry.len(),
333 Id::SENTINEL,
334 )));
335 }
336 let id = self.next_id;
337 self.next_id = self.next_id.next();
338 self.name_to_id.insert(name.clone(), id);
339 self.id_to_entry.insert(id, VirtualEntry { name, table });
340 Ok(id)
341 }
342}
343
344#[derive(Default, Debug)]
351pub(crate) struct PluginRecord {
352 pub(crate) scalars: Vec<QName>,
353 pub(crate) aggregates: Vec<QName>,
354 pub(crate) windows: Vec<QName>,
355 pub(crate) procedures: Vec<(QName, usize)>,
360 pub(crate) locy_aggregates: Vec<QName>,
361 pub(crate) locy_predicates: Vec<QName>,
362 pub(crate) locy_generators: Vec<QName>,
363 pub(crate) algorithms: Vec<QName>,
364 pub(crate) index_kinds: Vec<IndexKind>,
365 pub(crate) label_storages: Vec<SmolStr>,
366 pub(crate) crdt_kinds: Vec<CrdtKind>,
367 pub(crate) logical_types: Vec<SmolStr>,
371 pub(crate) collations: Vec<SmolStr>,
373 pub(crate) cdc_outputs: Vec<SmolStr>,
375 pub(crate) catalogs: Vec<SmolStr>,
377 pub(crate) hook_count: usize,
378 pub(crate) auth_count: usize,
379 pub(crate) authz_count: usize,
380 pub(crate) trigger_count: usize,
381 pub(crate) replacement_scan_count: usize,
382 pub(crate) optimizer_rule_count: usize,
383 pub(crate) background_job_count: usize,
384}
385
386impl PluginRecord {
387 fn merge(&mut self, other: PluginRecord) {
396 self.scalars.extend(other.scalars);
397 self.aggregates.extend(other.aggregates);
398 self.windows.extend(other.windows);
399 self.procedures.extend(other.procedures);
400 self.locy_aggregates.extend(other.locy_aggregates);
401 self.locy_predicates.extend(other.locy_predicates);
402 self.locy_generators.extend(other.locy_generators);
403 self.algorithms.extend(other.algorithms);
404 self.index_kinds.extend(other.index_kinds);
405 self.label_storages.extend(other.label_storages);
406 self.crdt_kinds.extend(other.crdt_kinds);
407 self.logical_types.extend(other.logical_types);
408 self.collations.extend(other.collations);
409 self.cdc_outputs.extend(other.cdc_outputs);
410 self.catalogs.extend(other.catalogs);
411 self.hook_count += other.hook_count;
412 self.auth_count += other.auth_count;
413 self.authz_count += other.authz_count;
414 self.trigger_count += other.trigger_count;
415 self.replacement_scan_count += other.replacement_scan_count;
416 self.optimizer_rule_count += other.optimizer_rule_count;
417 self.background_job_count += other.background_job_count;
418 }
419}
420
421#[derive(Clone, Debug, Default)]
428pub struct PluginRecordSnapshot {
429 pub scalars: Vec<QName>,
431 pub aggregates: Vec<QName>,
433 pub windows: Vec<QName>,
435 pub procedures: Vec<(QName, usize)>,
437 pub locy_aggregates: Vec<QName>,
439 pub locy_predicates: Vec<QName>,
441 pub locy_generators: Vec<QName>,
443 pub algorithms: Vec<QName>,
445 pub index_kinds: Vec<IndexKind>,
447 pub label_storages: Vec<SmolStr>,
449 pub crdt_kinds: Vec<CrdtKind>,
451 pub logical_types: Vec<SmolStr>,
453 pub collations: Vec<SmolStr>,
455 pub cdc_outputs: Vec<SmolStr>,
457 pub catalogs: Vec<SmolStr>,
459 pub hook_count: usize,
461 pub auth_count: usize,
463 pub authz_count: usize,
465 pub trigger_count: usize,
467 pub replacement_scan_count: usize,
469 pub optimizer_rule_count: usize,
471 pub background_job_count: usize,
473}
474
475impl From<&PluginRecord> for PluginRecordSnapshot {
476 fn from(r: &PluginRecord) -> Self {
480 Self {
481 scalars: r.scalars.clone(),
482 aggregates: r.aggregates.clone(),
483 windows: r.windows.clone(),
484 procedures: r.procedures.clone(),
485 locy_aggregates: r.locy_aggregates.clone(),
486 locy_predicates: r.locy_predicates.clone(),
487 locy_generators: r.locy_generators.clone(),
488 algorithms: r.algorithms.clone(),
489 index_kinds: r.index_kinds.clone(),
490 label_storages: r.label_storages.clone(),
491 crdt_kinds: r.crdt_kinds.clone(),
492 logical_types: r.logical_types.clone(),
493 collations: r.collations.clone(),
494 cdc_outputs: r.cdc_outputs.clone(),
495 catalogs: r.catalogs.clone(),
496 hook_count: r.hook_count,
497 auth_count: r.auth_count,
498 authz_count: r.authz_count,
499 trigger_count: r.trigger_count,
500 replacement_scan_count: r.replacement_scan_count,
501 optimizer_rule_count: r.optimizer_rule_count,
502 background_job_count: r.background_job_count,
503 }
504 }
505}
506
507#[derive(Default)]
513pub struct PluginRegistry {
514 pub(crate) scalars: DashMap<QName, Arc<ScalarEntry>>,
515 pub(crate) aggregates: DashMap<QName, Arc<AggregateEntry>>,
516 pub(crate) windows: DashMap<QName, Arc<WindowEntry>>,
517 pub(crate) procedures: DashMap<QName, Vec<Arc<ProcedureEntry>>>,
524 pub(crate) locy_aggregates: DashMap<QName, Arc<LocyAggregateEntry>>,
525 pub(crate) locy_predicates: DashMap<QName, Arc<LocyPredicateEntry>>,
526 pub(crate) locy_generators: DashMap<QName, Arc<LocyGeneratorEntry>>,
527 pub(crate) optimizer_rules:
528 ArcSwap<Vec<crate::surfaces::AppendEntry<dyn OptimizerRuleProvider>>>,
529 pub(crate) algorithms: DashMap<QName, Arc<AlgorithmEntry>>,
530 pub(crate) index_kinds: DashMap<IndexKind, Arc<dyn IndexKindProvider>>,
531 index_handles: DashMap<SmolStr, IndexHandleEntry>,
532 pub(crate) label_storages: DashMap<SmolStr, Arc<dyn crate::traits::storage::Storage>>,
538 pub(crate) crdt_kinds: DashMap<CrdtKind, Arc<dyn CrdtKindProvider>>,
539 pub(crate) hooks: ArcSwap<Vec<crate::surfaces::AppendEntry<dyn SessionHook>>>,
540 pub(crate) logical_types: DashMap<SmolStr, Arc<dyn LogicalTypeProvider>>,
541 pub(crate) auth_providers: ArcSwap<Vec<crate::surfaces::AppendEntry<dyn AuthProvider>>>,
542 pub(crate) authz_policies: ArcSwap<Vec<crate::surfaces::AppendEntry<dyn AuthzPolicy>>>,
543 pub(crate) triggers: ArcSwap<Vec<crate::surfaces::AppendEntry<dyn TriggerPlugin>>>,
544 pub(crate) collations: DashMap<SmolStr, Arc<dyn CollationProvider>>,
545 pub(crate) cdc_outputs: DashMap<SmolStr, Arc<dyn CdcOutputProvider>>,
546 pub(crate) catalogs: DashMap<SmolStr, Arc<dyn CatalogProvider>>,
547 pub(crate) replacement_scans:
548 ArcSwap<Vec<crate::surfaces::AppendEntry<dyn ReplacementScanProvider>>>,
549 pub(crate) background_jobs:
550 ArcSwap<Vec<crate::surfaces::AppendEntry<dyn BackgroundJobProvider>>>,
551 virtual_labels: Mutex<VirtualIdSpace<u16>>,
557 virtual_edge_types: Mutex<VirtualIdSpace<u32>>,
561 per_plugin: RwLock<dashmap::DashMap<PluginId, PluginRecord>>,
562}
563
564impl std::fmt::Debug for PluginRegistry {
565 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
566 f.debug_struct("PluginRegistry")
567 .field("scalar_fns", &self.scalars.len())
568 .field("aggregates", &self.aggregates.len())
569 .field("procedures", &self.procedures.len())
570 .field("locy_aggregates", &self.locy_aggregates.len())
571 .field("algorithms", &self.algorithms.len())
572 .field("index_kinds", &self.index_kinds.len())
573 .field("hooks", &self.hooks.load().len())
574 .field("plugins", &self.per_plugin.read().len())
575 .finish()
576 }
577}
578
579impl PluginRegistry {
580 #[must_use]
582 pub fn new() -> Self {
583 Self::default()
584 }
585
586 #[must_use]
588 pub fn scalar_fn(&self, q: &QName) -> Option<Arc<ScalarEntry>> {
589 self.scalars.get(q).map(|e| Arc::clone(e.value()))
590 }
591
592 #[must_use]
606 pub fn iter_scalars(&self) -> Vec<(QName, Arc<ScalarEntry>)> {
607 self.scalars
608 .iter()
609 .map(|kv| (kv.key().clone(), Arc::clone(kv.value())))
610 .collect()
611 }
612
613 #[must_use]
617 pub fn iter_procedures(&self) -> Vec<(QName, Arc<ProcedureEntry>)> {
618 self.procedures
619 .iter()
620 .flat_map(|kv| {
621 let q = kv.key().clone();
622 kv.value()
623 .iter()
624 .map(move |e| (q.clone(), Arc::clone(e)))
625 .collect::<Vec<_>>()
626 })
627 .collect()
628 }
629
630 #[must_use]
632 pub fn iter_locy_aggregates(&self) -> Vec<(QName, Arc<LocyAggregateEntry>)> {
633 self.locy_aggregates
634 .iter()
635 .map(|kv| (kv.key().clone(), Arc::clone(kv.value())))
636 .collect()
637 }
638
639 #[must_use]
641 pub fn iter_locy_predicates(&self) -> Vec<(QName, Arc<LocyPredicateEntry>)> {
642 self.locy_predicates
643 .iter()
644 .map(|kv| (kv.key().clone(), Arc::clone(kv.value())))
645 .collect()
646 }
647
648 #[must_use]
650 pub fn iter_locy_generators(&self) -> Vec<(QName, Arc<LocyGeneratorEntry>)> {
651 self.locy_generators
652 .iter()
653 .map(|kv| (kv.key().clone(), Arc::clone(kv.value())))
654 .collect()
655 }
656
657 #[must_use]
659 pub fn iter_algorithms(&self) -> Vec<(QName, Arc<dyn AlgorithmProvider>)> {
660 self.algorithms
661 .iter()
662 .map(|kv| (kv.key().clone(), Arc::clone(&kv.value().provider)))
663 .collect()
664 }
665
666 #[must_use]
668 pub fn iter_index_kinds(&self) -> Vec<(IndexKind, Arc<dyn IndexKindProvider>)> {
669 self.index_kinds
670 .iter()
671 .map(|kv| (kv.key().clone(), Arc::clone(kv.value())))
672 .collect()
673 }
674
675 #[must_use]
680 pub fn catalogs(&self) -> Vec<Arc<dyn CatalogProvider>> {
681 self.catalogs
682 .iter()
683 .map(|kv| Arc::clone(kv.value()))
684 .collect()
685 }
686
687 #[must_use]
689 pub fn aggregate(&self, q: &QName) -> Option<Arc<AggregateEntry>> {
690 self.aggregates.get(q).map(|e| Arc::clone(e.value()))
691 }
692
693 #[must_use]
695 pub fn window(&self, q: &QName) -> Option<Arc<WindowEntry>> {
696 self.windows.get(q).map(|e| Arc::clone(e.value()))
697 }
698
699 #[must_use]
706 pub fn procedure(&self, q: &QName) -> Option<Arc<ProcedureEntry>> {
707 self.procedures
708 .get(q)
709 .and_then(|e| e.value().first().map(Arc::clone))
710 }
711
712 #[must_use]
723 pub fn procedure_with_arity(&self, q: &QName, arity: usize) -> Option<Arc<ProcedureEntry>> {
724 self.procedures.get(q).and_then(|e| {
725 e.value()
726 .iter()
727 .find(|entry| entry.signature.args.len() == arity)
728 .map(Arc::clone)
729 })
730 }
731
732 #[must_use]
738 pub fn procedure_overloads(&self, q: &QName) -> Vec<Arc<ProcedureEntry>> {
739 self.procedures
740 .get(q)
741 .map(|e| e.value().iter().map(Arc::clone).collect())
742 .unwrap_or_default()
743 }
744
745 #[must_use]
747 pub fn locy_aggregate(&self, q: &QName) -> Option<Arc<LocyAggregateEntry>> {
748 self.locy_aggregates.get(q).map(|e| Arc::clone(e.value()))
749 }
750
751 #[must_use]
753 pub fn locy_predicate(&self, q: &QName) -> Option<Arc<LocyPredicateEntry>> {
754 self.locy_predicates.get(q).map(|e| Arc::clone(e.value()))
755 }
756
757 #[must_use]
759 pub fn locy_generator(&self, q: &QName) -> Option<Arc<LocyGeneratorEntry>> {
760 self.locy_generators.get(q).map(|e| Arc::clone(e.value()))
761 }
762
763 #[must_use]
769 pub fn lookup_label_storage(
770 &self,
771 label: &str,
772 ) -> Option<Arc<dyn crate::traits::storage::Storage>> {
773 self.label_storages
774 .get(&SmolStr::new(label))
775 .map(|e| Arc::clone(e.value()))
776 }
777
778 #[must_use]
780 pub fn index_kind(&self, k: &IndexKind) -> Option<Arc<dyn IndexKindProvider>> {
781 self.index_kinds.get(k).map(|e| Arc::clone(e.value()))
782 }
783
784 pub fn register_index_handle(
793 &self,
794 name: impl Into<SmolStr>,
795 kind: IndexKind,
796 handle: Arc<dyn IndexHandle>,
797 ) {
798 self.index_handles
799 .insert(name.into(), IndexHandleEntry { kind, handle });
800 }
801
802 #[must_use]
805 pub fn index_handle(&self, name: &str) -> Option<IndexHandleEntry> {
806 self.index_handles
807 .get(&SmolStr::new(name))
808 .map(|e| e.value().clone())
809 }
810
811 pub fn deregister_index_handle(&self, name: &str) -> Option<IndexHandleEntry> {
814 self.index_handles
815 .remove(&SmolStr::new(name))
816 .map(|(_, v)| v)
817 }
818
819 pub fn register_virtual_label(
831 &self,
832 name: impl Into<SmolStr>,
833 table: Arc<dyn crate::traits::catalog::CatalogTable>,
834 ) -> Result<u16, PluginError> {
835 self.virtual_labels.lock().register(name.into(), table)
836 }
837
838 #[must_use]
842 pub fn virtual_label_by_name(&self, name: &str) -> Option<u16> {
843 let inner = self.virtual_labels.lock();
844 inner.name_to_id.get(&SmolStr::new(name)).copied()
845 }
846
847 #[must_use]
850 pub fn virtual_label_by_id(&self, id: u16) -> Option<VirtualEntry> {
851 self.virtual_labels.lock().id_to_entry.get(&id).cloned()
852 }
853
854 pub fn register_virtual_edge_type(
858 &self,
859 name: impl Into<SmolStr>,
860 table: Arc<dyn crate::traits::catalog::CatalogTable>,
861 ) -> Result<u32, PluginError> {
862 self.virtual_edge_types.lock().register(name.into(), table)
863 }
864
865 #[must_use]
867 pub fn virtual_edge_type_by_name(&self, name: &str) -> Option<u32> {
868 let inner = self.virtual_edge_types.lock();
869 inner.name_to_id.get(&SmolStr::new(name)).copied()
870 }
871
872 #[must_use]
874 pub fn virtual_edge_type_by_id(&self, id: u32) -> Option<VirtualEntry> {
875 self.virtual_edge_types.lock().id_to_entry.get(&id).cloned()
876 }
877
878 #[must_use]
880 pub fn algorithm(&self, q: &QName) -> Option<Arc<dyn AlgorithmProvider>> {
881 self.algorithms
882 .get(q)
883 .map(|e| Arc::clone(&e.value().provider))
884 }
885
886 #[must_use]
892 pub fn algorithm_entry(&self, q: &QName) -> Option<Arc<AlgorithmEntry>> {
893 self.algorithms.get(q).map(|e| Arc::clone(e.value()))
894 }
895
896 #[must_use]
898 pub fn crdt_kind(&self, k: &CrdtKind) -> Option<Arc<dyn CrdtKindProvider>> {
899 self.crdt_kinds.get(k).map(|e| Arc::clone(e.value()))
900 }
901
902 #[must_use]
904 pub fn logical_type(&self, name: &SmolStr) -> Option<Arc<dyn LogicalTypeProvider>> {
905 self.logical_types.get(name).map(|e| Arc::clone(e.value()))
906 }
907
908 #[must_use]
910 pub fn hooks(&self) -> Arc<Vec<Arc<dyn SessionHook>>> {
911 Self::project_append(&self.hooks)
912 }
913
914 #[must_use]
916 pub fn optimizer_rules(&self) -> Arc<Vec<Arc<dyn OptimizerRuleProvider>>> {
917 Self::project_append(&self.optimizer_rules)
918 }
919
920 #[must_use]
922 pub fn triggers(&self) -> Arc<Vec<Arc<dyn TriggerPlugin>>> {
923 Self::project_append(&self.triggers)
924 }
925
926 #[must_use]
931 pub fn cdc_outputs_snapshot(&self) -> Vec<(SmolStr, Arc<dyn CdcOutputProvider>)> {
932 self.cdc_outputs
933 .iter()
934 .map(|e| (e.key().clone(), Arc::clone(e.value())))
935 .collect()
936 }
937
938 #[must_use]
944 pub fn cdc_outputs_is_empty(&self) -> bool {
945 self.cdc_outputs.is_empty()
946 }
947
948 #[must_use]
950 pub fn auth_providers(&self) -> Arc<Vec<Arc<dyn AuthProvider>>> {
951 Self::project_append(&self.auth_providers)
952 }
953
954 #[must_use]
956 pub fn authz_policies(&self) -> Arc<Vec<Arc<dyn AuthzPolicy>>> {
957 Self::project_append(&self.authz_policies)
958 }
959
960 #[must_use]
962 pub fn replacement_scans(&self) -> Arc<Vec<Arc<dyn ReplacementScanProvider>>> {
963 Self::project_append(&self.replacement_scans)
964 }
965
966 pub(crate) fn apply_pending(
981 &self,
982 plugin_id: &PluginId,
983 pending: Vec<Box<dyn crate::surfaces::DynPendingRegistration>>,
984 ) -> Result<(), PluginError> {
985 let mut seen: std::collections::HashSet<QName> = std::collections::HashSet::new();
990 for reg in &pending {
991 reg.preflight(self)?;
992 if let Some(qname) = reg.dedup_key()
993 && !seen.insert(qname.clone())
994 {
995 return Err(PluginError::DuplicateRegistration(qname));
996 }
997 }
998
999 let mut record = PluginRecord::default();
1000 for reg in pending {
1001 reg.apply(self, plugin_id.clone(), &mut record);
1002 }
1003
1004 self.per_plugin
1007 .read()
1008 .entry(plugin_id.clone())
1009 .or_default()
1010 .merge(record);
1011
1012 Ok(())
1013 }
1014
1015 #[must_use]
1017 pub fn background_jobs(&self) -> Arc<Vec<Arc<dyn BackgroundJobProvider>>> {
1018 Self::project_append(&self.background_jobs)
1019 }
1020
1021 fn project_append<P: ?Sized>(
1033 slot: &ArcSwap<Vec<crate::surfaces::AppendEntry<P>>>,
1034 ) -> Arc<Vec<Arc<P>>> {
1035 let snap = slot.load();
1036 let v: Vec<Arc<P>> = snap.iter().map(|e| Arc::clone(&e.provider)).collect();
1037 Arc::new(v)
1038 }
1039
1040 #[must_use]
1051 pub fn iter_for_plugin(&self, plugin: &PluginId) -> Option<PluginRecordSnapshot> {
1052 let guard = self.per_plugin.read();
1053 guard.get(plugin).map(|r| PluginRecordSnapshot::from(&*r))
1054 }
1055
1056 pub fn remove_named_unique(&self, plugin: &PluginId, qname: &QName) -> bool {
1067 use crate::surfaces::{AggregateSurface, NamedUniqueOps, ScalarSurface};
1068 let mut removed = false;
1069 if let Some(mut rec) = self.per_plugin.read().get_mut(plugin) {
1070 if let Some(pos) = rec.scalars.iter().position(|q| q == qname) {
1071 rec.scalars.remove(pos);
1072 <ScalarSurface as NamedUniqueOps>::remove(self, qname);
1073 removed = true;
1074 }
1075 if let Some(pos) = rec.aggregates.iter().position(|q| q == qname) {
1076 rec.aggregates.remove(pos);
1077 <AggregateSurface as NamedUniqueOps>::remove(self, qname);
1078 removed = true;
1079 }
1080 }
1081 removed
1082 }
1083
1084 pub fn remove_plugin(&self, plugin: &PluginId) {
1092 use crate::surfaces::{
1093 AggregateSurface, AlgorithmSurface, AppendOps, AuthSurface, AuthzSurface,
1094 BackgroundJobSurface, CatalogSurface, CdcSurface, CollationSurface, CrdtSurface,
1095 Discriminator, HookSurface, IndexKindSurface, KeyedUniqueOps, LabelStorageSurface,
1096 LocyAggregateSurface, LocyGeneratorSurface, LocyPredicateSurface, LogicalTypeSurface,
1097 NamedUniqueOps, OptimizerRuleSurface, ProcedureSurface, ReplacementScanSurface,
1098 ScalarSurface, TriggerSurface, VersionedOps, WindowSurface,
1099 };
1100
1101 let record = self.per_plugin.read().remove(plugin).map(|(_, r)| r);
1102 let Some(record) = record else { return };
1103
1104 for q in record.scalars {
1105 <ScalarSurface as NamedUniqueOps>::remove(self, &q);
1106 }
1107 for q in record.aggregates {
1108 <AggregateSurface as NamedUniqueOps>::remove(self, &q);
1109 }
1110 for q in record.windows {
1111 <WindowSurface as NamedUniqueOps>::remove(self, &q);
1112 }
1113 for (q, arity) in record.procedures {
1114 <ProcedureSurface as VersionedOps>::remove(self, &q, Discriminator::Arity(arity));
1115 }
1116 for q in record.locy_aggregates {
1117 <LocyAggregateSurface as NamedUniqueOps>::remove(self, &q);
1118 }
1119 for q in record.locy_predicates {
1120 <LocyPredicateSurface as NamedUniqueOps>::remove(self, &q);
1121 }
1122 for q in record.locy_generators {
1123 <LocyGeneratorSurface as NamedUniqueOps>::remove(self, &q);
1124 }
1125 for q in record.algorithms {
1126 <AlgorithmSurface as NamedUniqueOps>::remove(self, &q);
1127 }
1128 for k in record.index_kinds {
1129 <IndexKindSurface as KeyedUniqueOps>::remove(self, &k);
1130 }
1131 for l in record.label_storages {
1132 <LabelStorageSurface as KeyedUniqueOps>::remove(self, &l);
1133 }
1134 for k in record.crdt_kinds {
1135 <CrdtSurface as KeyedUniqueOps>::remove(self, &k);
1136 }
1137 for k in record.logical_types {
1138 <LogicalTypeSurface as KeyedUniqueOps>::remove(self, &k);
1139 }
1140 for k in record.collations {
1141 <CollationSurface as KeyedUniqueOps>::remove(self, &k);
1142 }
1143 for k in record.cdc_outputs {
1144 <CdcSurface as KeyedUniqueOps>::remove(self, &k);
1145 }
1146 for k in record.catalogs {
1147 <CatalogSurface as KeyedUniqueOps>::remove(self, &k);
1148 }
1149
1150 <OptimizerRuleSurface as AppendOps>::remove_plugin(self, plugin);
1151 <HookSurface as AppendOps>::remove_plugin(self, plugin);
1152 <AuthSurface as AppendOps>::remove_plugin(self, plugin);
1153 <AuthzSurface as AppendOps>::remove_plugin(self, plugin);
1154 <TriggerSurface as AppendOps>::remove_plugin(self, plugin);
1155 <ReplacementScanSurface as AppendOps>::remove_plugin(self, plugin);
1156 <BackgroundJobSurface as AppendOps>::remove_plugin(self, plugin);
1157 }
1158}
1159
1160#[cfg(test)]
1161mod tests {
1162 use super::*;
1163
1164 #[test]
1165 fn registry_default_is_empty() {
1166 let r = PluginRegistry::new();
1167 assert!(r.scalar_fn(&QName::builtin("anything")).is_none());
1168 assert!(r.procedure(&QName::builtin("anything")).is_none());
1169 assert_eq!(r.hooks().len(), 0);
1170 }
1171
1172 #[test]
1173 fn debug_smoke() {
1174 let r = PluginRegistry::new();
1175 let s = format!("{r:?}");
1176 assert!(s.contains("PluginRegistry"));
1177 }
1178}