1use std::fmt::Debug;
36use std::hash::Hash;
37use std::sync::Arc;
38
39use arc_swap::ArcSwap;
40use dashmap::DashMap;
41use smol_str::SmolStr;
42
43use crate::capability::CapabilitySet;
44use crate::errors::PluginError;
45use crate::plugin::PluginId;
46use crate::qname::QName;
47use crate::registry::{
48 AggregateEntry, AlgorithmEntry, LocyAggregateEntry, LocyGeneratorEntry, LocyPredicateEntry,
49 PluginRecord, PluginRegistry, ProcedureEntry, ScalarEntry, WindowEntry,
50};
51use crate::traits::crdt::CrdtKind;
52use crate::traits::index::IndexKind;
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
60pub enum Discriminator {
61 Arity(usize),
63}
64
65#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
70#[non_exhaustive]
71pub enum SurfaceKind {
72 Scalar,
74 Aggregate,
76 Window,
78 Procedure,
80 LocyAggregate,
82 LocyPredicate,
84 LocyGenerator,
86 OptimizerRule,
88 Algorithm,
90 IndexKind,
92 LabelStorage,
94 Crdt,
96 Hook,
98 LogicalType,
100 Auth,
102 Authz,
104 Trigger,
106 Collation,
108 Cdc,
110 Catalog,
112 ReplacementScan,
114 BackgroundJob,
116}
117
118pub trait NamedUniqueSurface: 'static {
126 type Sig: Send + Sync + 'static;
129 type Provider: ?Sized + Send + Sync + 'static;
131
132 const KIND: SurfaceKind;
134}
135
136pub trait VersionedSurface: 'static {
141 type Sig: Send + Sync + 'static;
143 type Provider: ?Sized + Send + Sync + 'static;
145
146 const KIND: SurfaceKind;
148
149 fn discriminator(sig: &Self::Sig) -> Discriminator;
152}
153
154pub trait KeyedUniqueSurface: 'static {
164 type Key: Clone + Eq + Hash + Debug + Send + Sync + 'static;
166 type Provider: ?Sized + Send + Sync + 'static;
168
169 const KIND: SurfaceKind;
171
172 fn duplicate_error(key: &Self::Key) -> PluginError {
182 PluginError::internal(format!("{:?} `{:?}` already registered", Self::KIND, key))
183 }
184
185 fn key_of(_provider: &Self::Provider) -> Option<Self::Key> {
199 None
200 }
201}
202
203pub trait AppendSurface: 'static {
210 type Provider: ?Sized + Send + Sync + 'static;
212
213 const KIND: SurfaceKind;
215}
216
217pub struct AppendEntry<P: ?Sized> {
224 pub plugin: PluginId,
226 pub provider: Arc<P>,
228}
229
230impl<P: ?Sized> Clone for AppendEntry<P> {
231 fn clone(&self) -> Self {
232 Self {
233 plugin: self.plugin.clone(),
234 provider: Arc::clone(&self.provider),
235 }
236 }
237}
238
239impl<P: ?Sized> Debug for AppendEntry<P> {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 f.debug_struct("AppendEntry")
242 .field("plugin", &self.plugin)
243 .finish_non_exhaustive()
244 }
245}
246
247use crate::traits::aggregate::{AggSignature, AggregatePluginFn};
253use crate::traits::algorithm::AlgorithmProvider;
254use crate::traits::background::BackgroundJobProvider;
255use crate::traits::catalog::{CatalogProvider, ReplacementScanProvider};
256use crate::traits::cdc::CdcOutputProvider;
257use crate::traits::collation::CollationProvider;
258use crate::traits::connector::{AuthProvider, AuthzPolicy};
259use crate::traits::crdt::CrdtKindProvider;
260use crate::traits::hook::SessionHook;
261use crate::traits::index::IndexKindProvider;
262use crate::traits::locy::{
263 GenSignature, LocyAggregate, LocyGenerator, LocyPredicate, PredSignature,
264};
265use crate::traits::operator::OptimizerRuleProvider;
266use crate::traits::procedure::{ProcedurePlugin, ProcedureSignature};
267use crate::traits::scalar::{FnSignature, ScalarPluginFn};
268use crate::traits::storage::Storage;
269use crate::traits::trigger::TriggerPlugin;
270use crate::traits::types::LogicalTypeProvider;
271use crate::traits::window::{WindowPluginFn, WindowSignature};
272
273macro_rules! marker {
274 ($(#[$attr:meta])* $name:ident) => {
275 $(#[$attr])*
276 #[derive(Debug, Clone, Copy)]
277 pub struct $name;
278 };
279}
280
281marker!(ScalarSurface);
284marker!(AggregateSurface);
286marker!(WindowSurface);
288marker!(LocyAggregateSurface);
290marker!(LocyPredicateSurface);
292marker!(LocyGeneratorSurface);
294marker!(AlgorithmSurface);
296
297marker!(ProcedureSurface);
300
301marker!(IndexKindSurface);
304marker!(LabelStorageSurface);
306marker!(CrdtSurface);
308marker!(LogicalTypeSurface);
310marker!(CollationSurface);
312marker!(CdcSurface);
314marker!(CatalogSurface);
316
317marker!(OptimizerRuleSurface);
320marker!(HookSurface);
322marker!(AuthSurface);
324marker!(AuthzSurface);
326marker!(TriggerSurface);
328marker!(ReplacementScanSurface);
330marker!(BackgroundJobSurface);
332
333impl NamedUniqueSurface for ScalarSurface {
336 type Sig = FnSignature;
337 type Provider = dyn ScalarPluginFn;
338 const KIND: SurfaceKind = SurfaceKind::Scalar;
339}
340
341impl NamedUniqueSurface for AggregateSurface {
342 type Sig = AggSignature;
343 type Provider = dyn AggregatePluginFn;
344 const KIND: SurfaceKind = SurfaceKind::Aggregate;
345}
346
347impl NamedUniqueSurface for WindowSurface {
348 type Sig = WindowSignature;
349 type Provider = dyn WindowPluginFn;
350 const KIND: SurfaceKind = SurfaceKind::Window;
351}
352
353impl NamedUniqueSurface for LocyAggregateSurface {
354 type Sig = ();
355 type Provider = dyn LocyAggregate;
356 const KIND: SurfaceKind = SurfaceKind::LocyAggregate;
357}
358
359impl NamedUniqueSurface for LocyPredicateSurface {
360 type Sig = PredSignature;
361 type Provider = dyn LocyPredicate;
362 const KIND: SurfaceKind = SurfaceKind::LocyPredicate;
363}
364
365impl NamedUniqueSurface for LocyGeneratorSurface {
366 type Sig = GenSignature;
367 type Provider = dyn LocyGenerator;
368 const KIND: SurfaceKind = SurfaceKind::LocyGenerator;
369}
370
371impl NamedUniqueSurface for AlgorithmSurface {
372 type Sig = CapabilitySet;
375 type Provider = dyn AlgorithmProvider;
376 const KIND: SurfaceKind = SurfaceKind::Algorithm;
377}
378
379impl VersionedSurface for ProcedureSurface {
382 type Sig = ProcedureSignature;
383 type Provider = dyn ProcedurePlugin;
384 const KIND: SurfaceKind = SurfaceKind::Procedure;
385
386 fn discriminator(sig: &Self::Sig) -> Discriminator {
387 Discriminator::Arity(sig.args.len())
388 }
389}
390
391impl KeyedUniqueSurface for IndexKindSurface {
394 type Key = IndexKind;
395 type Provider = dyn IndexKindProvider;
396 const KIND: SurfaceKind = SurfaceKind::IndexKind;
397
398 fn key_of(provider: &Self::Provider) -> Option<Self::Key> {
399 Some(provider.kind())
400 }
401}
402
403impl KeyedUniqueSurface for LabelStorageSurface {
404 type Key = SmolStr;
405 type Provider = dyn Storage;
406 const KIND: SurfaceKind = SurfaceKind::LabelStorage;
407
408 fn duplicate_error(key: &Self::Key) -> PluginError {
409 PluginError::internal(format!("label storage for `{key}` already registered"))
410 }
411
412 }
415
416impl KeyedUniqueSurface for CrdtSurface {
417 type Key = CrdtKind;
418 type Provider = dyn CrdtKindProvider;
419 const KIND: SurfaceKind = SurfaceKind::Crdt;
420
421 fn duplicate_error(key: &Self::Key) -> PluginError {
422 PluginError::internal(format!("CRDT kind `{}` already registered", key.0))
423 }
424
425 fn key_of(provider: &Self::Provider) -> Option<Self::Key> {
426 Some(provider.kind())
427 }
428}
429
430impl KeyedUniqueSurface for LogicalTypeSurface {
431 type Key = SmolStr;
432 type Provider = dyn LogicalTypeProvider;
433 const KIND: SurfaceKind = SurfaceKind::LogicalType;
434
435 fn key_of(provider: &Self::Provider) -> Option<Self::Key> {
436 Some(SmolStr::new(provider.name()))
437 }
438}
439
440impl KeyedUniqueSurface for CollationSurface {
441 type Key = SmolStr;
442 type Provider = dyn CollationProvider;
443 const KIND: SurfaceKind = SurfaceKind::Collation;
444
445 fn key_of(provider: &Self::Provider) -> Option<Self::Key> {
446 Some(SmolStr::new(provider.name()))
447 }
448}
449
450impl KeyedUniqueSurface for CdcSurface {
451 type Key = SmolStr;
452 type Provider = dyn CdcOutputProvider;
453 const KIND: SurfaceKind = SurfaceKind::Cdc;
454
455 fn key_of(provider: &Self::Provider) -> Option<Self::Key> {
456 Some(SmolStr::new(provider.name()))
457 }
458}
459
460impl KeyedUniqueSurface for CatalogSurface {
461 type Key = SmolStr;
462 type Provider = dyn CatalogProvider;
463 const KIND: SurfaceKind = SurfaceKind::Catalog;
464
465 fn key_of(provider: &Self::Provider) -> Option<Self::Key> {
466 Some(SmolStr::new(provider.name()))
467 }
468}
469
470impl AppendSurface for OptimizerRuleSurface {
473 type Provider = dyn OptimizerRuleProvider;
474 const KIND: SurfaceKind = SurfaceKind::OptimizerRule;
475}
476
477impl AppendSurface for HookSurface {
478 type Provider = dyn SessionHook;
479 const KIND: SurfaceKind = SurfaceKind::Hook;
480}
481
482impl AppendSurface for AuthSurface {
483 type Provider = dyn AuthProvider;
484 const KIND: SurfaceKind = SurfaceKind::Auth;
485}
486
487impl AppendSurface for AuthzSurface {
488 type Provider = dyn AuthzPolicy;
489 const KIND: SurfaceKind = SurfaceKind::Authz;
490}
491
492impl AppendSurface for TriggerSurface {
493 type Provider = dyn TriggerPlugin;
494 const KIND: SurfaceKind = SurfaceKind::Trigger;
495}
496
497impl AppendSurface for ReplacementScanSurface {
498 type Provider = dyn ReplacementScanProvider;
499 const KIND: SurfaceKind = SurfaceKind::ReplacementScan;
500}
501
502impl AppendSurface for BackgroundJobSurface {
503 type Provider = dyn BackgroundJobProvider;
504 const KIND: SurfaceKind = SurfaceKind::BackgroundJob;
505}
506
507pub(crate) trait NamedUniqueOps: NamedUniqueSurface {
523 type Stored: Clone + Send + Sync + 'static;
526
527 fn make_stored(plugin: PluginId, sig: Self::Sig, provider: Arc<Self::Provider>)
529 -> Self::Stored;
530
531 fn slot(registry: &PluginRegistry) -> &DashMap<QName, Self::Stored>;
533
534 fn record_slot(record: &mut PluginRecord) -> &mut Vec<QName>;
537
538 fn preflight(registry: &PluginRegistry, q: &QName) -> Result<(), PluginError> {
545 if Self::slot(registry).contains_key(q) {
546 return Err(PluginError::DuplicateRegistration(q.clone()));
547 }
548 Ok(())
549 }
550
551 fn insert(
554 registry: &PluginRegistry,
555 plugin: PluginId,
556 q: QName,
557 sig: Self::Sig,
558 provider: Arc<Self::Provider>,
559 record: &mut PluginRecord,
560 ) {
561 let stored = Self::make_stored(plugin, sig, provider);
562 Self::slot(registry).insert(q.clone(), stored);
563 Self::record_slot(record).push(q);
564 }
565
566 fn remove(registry: &PluginRegistry, q: &QName) {
568 Self::slot(registry).remove(q);
569 }
570}
571
572pub(crate) trait VersionedOps: VersionedSurface {
577 type Stored: Clone + Send + Sync + 'static;
579
580 fn make_stored(plugin: PluginId, sig: Self::Sig, provider: Arc<Self::Provider>)
582 -> Self::Stored;
583
584 fn entry_discriminator(stored: &Self::Stored) -> Discriminator;
587
588 fn signature_discriminator(sig: &Self::Sig) -> Discriminator {
590 Self::discriminator(sig)
591 }
592
593 fn slot(registry: &PluginRegistry) -> &DashMap<QName, Vec<Self::Stored>>;
595
596 fn record_slot(record: &mut PluginRecord) -> &mut Vec<(QName, usize)>;
599
600 fn discriminator_to_usize(d: Discriminator) -> usize {
602 match d {
603 Discriminator::Arity(n) => n,
604 }
605 }
606
607 fn preflight(registry: &PluginRegistry, q: &QName, sig: &Self::Sig) -> Result<(), PluginError> {
616 let d = Self::signature_discriminator(sig);
617 if let Some(slot) = Self::slot(registry).get(q)
618 && slot.iter().any(|e| Self::entry_discriminator(e) == d)
619 {
620 return Err(PluginError::DuplicateRegistration(q.clone()));
621 }
622 Ok(())
623 }
624
625 fn insert(
628 registry: &PluginRegistry,
629 plugin: PluginId,
630 q: QName,
631 sig: Self::Sig,
632 provider: Arc<Self::Provider>,
633 record: &mut PluginRecord,
634 ) {
635 let d = Self::signature_discriminator(&sig);
636 let stored = Self::make_stored(plugin, sig, provider);
637 let mut entry = Self::slot(registry).entry(q.clone()).or_default();
638 entry.push(stored);
639 drop(entry);
640 Self::record_slot(record).push((q, Self::discriminator_to_usize(d)));
641 }
642
643 fn remove(registry: &PluginRegistry, q: &QName, d: Discriminator) {
646 let slot = Self::slot(registry);
647 if let Some(mut entry) = slot.get_mut(q) {
648 entry.retain(|e| Self::entry_discriminator(e) != d);
649 let empty = entry.is_empty();
650 drop(entry);
651 if empty {
652 slot.remove(q);
653 }
654 }
655 }
656}
657
658pub(crate) trait KeyedUniqueOps: KeyedUniqueSurface {
664 fn slot(registry: &PluginRegistry) -> &DashMap<Self::Key, Arc<Self::Provider>>;
666
667 fn record_register(record: &mut PluginRecord, key: &Self::Key);
669
670 fn preflight(registry: &PluginRegistry, key: &Self::Key) -> Result<(), PluginError> {
677 if Self::slot(registry).contains_key(key) {
678 return Err(Self::duplicate_error(key));
679 }
680 Ok(())
681 }
682
683 fn insert(
686 registry: &PluginRegistry,
687 key: Self::Key,
688 provider: Arc<Self::Provider>,
689 record: &mut PluginRecord,
690 ) {
691 Self::slot(registry).insert(key.clone(), provider);
692 Self::record_register(record, &key);
693 }
694
695 fn remove(registry: &PluginRegistry, key: &Self::Key) {
697 Self::slot(registry).remove(key);
698 }
699}
700
701pub(crate) trait AppendOps: AppendSurface {
707 fn slot(registry: &PluginRegistry) -> &ArcSwap<Vec<AppendEntry<Self::Provider>>>;
709
710 fn record_register(record: &mut PluginRecord);
712
713 fn insert(
715 registry: &PluginRegistry,
716 plugin: PluginId,
717 provider: Arc<Self::Provider>,
718 record: &mut PluginRecord,
719 ) {
720 let slot = Self::slot(registry);
721 let mut v = (**slot.load()).clone();
722 v.push(AppendEntry { plugin, provider });
723 slot.store(Arc::new(v));
724 Self::record_register(record);
725 }
726
727 fn remove_plugin(registry: &PluginRegistry, plugin: &PluginId) {
729 let slot = Self::slot(registry);
730 let cur = slot.load();
731 if !cur.iter().any(|e| &e.plugin == plugin) {
732 return;
733 }
734 let v: Vec<AppendEntry<Self::Provider>> = cur
735 .iter()
736 .filter(|e| &e.plugin != plugin)
737 .cloned()
738 .collect();
739 slot.store(Arc::new(v));
740 }
741}
742
743impl NamedUniqueOps for ScalarSurface {
746 type Stored = Arc<ScalarEntry>;
747 fn make_stored(
748 plugin: PluginId,
749 sig: Self::Sig,
750 provider: Arc<Self::Provider>,
751 ) -> Self::Stored {
752 Arc::new(ScalarEntry {
753 plugin,
754 signature: sig,
755 function: provider,
756 })
757 }
758 fn slot(r: &PluginRegistry) -> &DashMap<QName, Self::Stored> {
759 &r.scalars
760 }
761 fn record_slot(rec: &mut PluginRecord) -> &mut Vec<QName> {
762 &mut rec.scalars
763 }
764}
765
766impl NamedUniqueOps for AggregateSurface {
767 type Stored = Arc<AggregateEntry>;
768 fn make_stored(
769 plugin: PluginId,
770 sig: Self::Sig,
771 provider: Arc<Self::Provider>,
772 ) -> Self::Stored {
773 Arc::new(AggregateEntry {
774 plugin,
775 signature: sig,
776 aggregate: provider,
777 })
778 }
779 fn slot(r: &PluginRegistry) -> &DashMap<QName, Self::Stored> {
780 &r.aggregates
781 }
782 fn record_slot(rec: &mut PluginRecord) -> &mut Vec<QName> {
783 &mut rec.aggregates
784 }
785}
786
787impl NamedUniqueOps for WindowSurface {
788 type Stored = Arc<WindowEntry>;
789 fn make_stored(
790 plugin: PluginId,
791 sig: Self::Sig,
792 provider: Arc<Self::Provider>,
793 ) -> Self::Stored {
794 Arc::new(WindowEntry {
795 plugin,
796 signature: sig,
797 window: provider,
798 })
799 }
800 fn slot(r: &PluginRegistry) -> &DashMap<QName, Self::Stored> {
801 &r.windows
802 }
803 fn record_slot(rec: &mut PluginRecord) -> &mut Vec<QName> {
804 &mut rec.windows
805 }
806}
807
808impl NamedUniqueOps for LocyAggregateSurface {
809 type Stored = Arc<LocyAggregateEntry>;
810 fn make_stored(
811 plugin: PluginId,
812 _sig: Self::Sig,
813 provider: Arc<Self::Provider>,
814 ) -> Self::Stored {
815 Arc::new(LocyAggregateEntry {
816 plugin,
817 aggregate: provider,
818 })
819 }
820 fn slot(r: &PluginRegistry) -> &DashMap<QName, Self::Stored> {
821 &r.locy_aggregates
822 }
823 fn record_slot(rec: &mut PluginRecord) -> &mut Vec<QName> {
824 &mut rec.locy_aggregates
825 }
826}
827
828impl NamedUniqueOps for LocyPredicateSurface {
829 type Stored = Arc<LocyPredicateEntry>;
830 fn make_stored(
831 plugin: PluginId,
832 sig: Self::Sig,
833 provider: Arc<Self::Provider>,
834 ) -> Self::Stored {
835 Arc::new(LocyPredicateEntry {
836 plugin,
837 signature: sig,
838 predicate: provider,
839 })
840 }
841 fn slot(r: &PluginRegistry) -> &DashMap<QName, Self::Stored> {
842 &r.locy_predicates
843 }
844 fn record_slot(rec: &mut PluginRecord) -> &mut Vec<QName> {
845 &mut rec.locy_predicates
846 }
847}
848
849impl NamedUniqueOps for LocyGeneratorSurface {
850 type Stored = Arc<LocyGeneratorEntry>;
851 fn make_stored(
852 plugin: PluginId,
853 sig: Self::Sig,
854 provider: Arc<Self::Provider>,
855 ) -> Self::Stored {
856 Arc::new(LocyGeneratorEntry {
857 plugin,
858 signature: sig,
859 generator: provider,
860 })
861 }
862 fn slot(r: &PluginRegistry) -> &DashMap<QName, Self::Stored> {
863 &r.locy_generators
864 }
865 fn record_slot(rec: &mut PluginRecord) -> &mut Vec<QName> {
866 &mut rec.locy_generators
867 }
868}
869
870impl NamedUniqueOps for AlgorithmSurface {
871 type Stored = Arc<AlgorithmEntry>;
872 fn make_stored(
873 plugin: PluginId,
874 sig: Self::Sig,
875 provider: Arc<Self::Provider>,
876 ) -> Self::Stored {
877 Arc::new(AlgorithmEntry {
878 plugin,
879 effective_caps: sig,
880 provider,
881 })
882 }
883 fn slot(r: &PluginRegistry) -> &DashMap<QName, Self::Stored> {
884 &r.algorithms
885 }
886 fn record_slot(rec: &mut PluginRecord) -> &mut Vec<QName> {
887 &mut rec.algorithms
888 }
889}
890
891impl VersionedOps for ProcedureSurface {
894 type Stored = Arc<ProcedureEntry>;
895 fn make_stored(
896 plugin: PluginId,
897 sig: Self::Sig,
898 provider: Arc<Self::Provider>,
899 ) -> Self::Stored {
900 Arc::new(ProcedureEntry {
901 plugin,
902 signature: sig,
903 procedure: provider,
904 })
905 }
906 fn entry_discriminator(stored: &Self::Stored) -> Discriminator {
907 Discriminator::Arity(stored.signature.args.len())
908 }
909 fn slot(r: &PluginRegistry) -> &DashMap<QName, Vec<Self::Stored>> {
910 &r.procedures
911 }
912 fn record_slot(rec: &mut PluginRecord) -> &mut Vec<(QName, usize)> {
913 &mut rec.procedures
914 }
915}
916
917impl KeyedUniqueOps for IndexKindSurface {
920 fn slot(r: &PluginRegistry) -> &DashMap<Self::Key, Arc<Self::Provider>> {
921 &r.index_kinds
922 }
923 fn record_register(rec: &mut PluginRecord, key: &Self::Key) {
924 rec.index_kinds.push(key.clone());
925 }
926}
927
928impl KeyedUniqueOps for LabelStorageSurface {
929 fn slot(r: &PluginRegistry) -> &DashMap<Self::Key, Arc<Self::Provider>> {
930 &r.label_storages
931 }
932 fn record_register(rec: &mut PluginRecord, key: &Self::Key) {
933 rec.label_storages.push(key.clone());
934 }
935}
936
937impl KeyedUniqueOps for CrdtSurface {
938 fn slot(r: &PluginRegistry) -> &DashMap<Self::Key, Arc<Self::Provider>> {
939 &r.crdt_kinds
940 }
941 fn record_register(rec: &mut PluginRecord, key: &Self::Key) {
942 rec.crdt_kinds.push(key.clone());
943 }
944}
945
946impl KeyedUniqueOps for LogicalTypeSurface {
947 fn slot(r: &PluginRegistry) -> &DashMap<Self::Key, Arc<Self::Provider>> {
948 &r.logical_types
949 }
950 fn record_register(rec: &mut PluginRecord, key: &Self::Key) {
951 rec.logical_types.push(key.clone());
952 }
953}
954
955impl KeyedUniqueOps for CollationSurface {
956 fn slot(r: &PluginRegistry) -> &DashMap<Self::Key, Arc<Self::Provider>> {
957 &r.collations
958 }
959 fn record_register(rec: &mut PluginRecord, key: &Self::Key) {
960 rec.collations.push(key.clone());
961 }
962}
963
964impl KeyedUniqueOps for CdcSurface {
965 fn slot(r: &PluginRegistry) -> &DashMap<Self::Key, Arc<Self::Provider>> {
966 &r.cdc_outputs
967 }
968 fn record_register(rec: &mut PluginRecord, key: &Self::Key) {
969 rec.cdc_outputs.push(key.clone());
970 }
971}
972
973impl KeyedUniqueOps for CatalogSurface {
974 fn slot(r: &PluginRegistry) -> &DashMap<Self::Key, Arc<Self::Provider>> {
975 &r.catalogs
976 }
977 fn record_register(rec: &mut PluginRecord, key: &Self::Key) {
978 rec.catalogs.push(key.clone());
979 }
980}
981
982impl AppendOps for OptimizerRuleSurface {
985 fn slot(r: &PluginRegistry) -> &ArcSwap<Vec<AppendEntry<Self::Provider>>> {
986 &r.optimizer_rules
987 }
988 fn record_register(rec: &mut PluginRecord) {
989 rec.optimizer_rule_count += 1;
990 }
991}
992impl AppendOps for HookSurface {
993 fn slot(r: &PluginRegistry) -> &ArcSwap<Vec<AppendEntry<Self::Provider>>> {
994 &r.hooks
995 }
996 fn record_register(rec: &mut PluginRecord) {
997 rec.hook_count += 1;
998 }
999}
1000impl AppendOps for AuthSurface {
1001 fn slot(r: &PluginRegistry) -> &ArcSwap<Vec<AppendEntry<Self::Provider>>> {
1002 &r.auth_providers
1003 }
1004 fn record_register(rec: &mut PluginRecord) {
1005 rec.auth_count += 1;
1006 }
1007}
1008impl AppendOps for AuthzSurface {
1009 fn slot(r: &PluginRegistry) -> &ArcSwap<Vec<AppendEntry<Self::Provider>>> {
1010 &r.authz_policies
1011 }
1012 fn record_register(rec: &mut PluginRecord) {
1013 rec.authz_count += 1;
1014 }
1015}
1016impl AppendOps for TriggerSurface {
1017 fn slot(r: &PluginRegistry) -> &ArcSwap<Vec<AppendEntry<Self::Provider>>> {
1018 &r.triggers
1019 }
1020 fn record_register(rec: &mut PluginRecord) {
1021 rec.trigger_count += 1;
1022 }
1023}
1024impl AppendOps for ReplacementScanSurface {
1025 fn slot(r: &PluginRegistry) -> &ArcSwap<Vec<AppendEntry<Self::Provider>>> {
1026 &r.replacement_scans
1027 }
1028 fn record_register(rec: &mut PluginRecord) {
1029 rec.replacement_scan_count += 1;
1030 }
1031}
1032impl AppendOps for BackgroundJobSurface {
1033 fn slot(r: &PluginRegistry) -> &ArcSwap<Vec<AppendEntry<Self::Provider>>> {
1034 &r.background_jobs
1035 }
1036 fn record_register(rec: &mut PluginRecord) {
1037 rec.background_job_count += 1;
1038 }
1039}
1040
1041pub(crate) trait DynPendingRegistration: Send + Sync {
1056 #[allow(
1058 dead_code,
1059 reason = "Diagnostic surface; exercised by tests and future debug paths."
1060 )]
1061 fn kind(&self) -> SurfaceKind;
1062 fn preflight(&self, registry: &PluginRegistry) -> Result<(), PluginError>;
1064 fn apply(
1066 self: Box<Self>,
1067 registry: &PluginRegistry,
1068 plugin: PluginId,
1069 record: &mut PluginRecord,
1070 );
1071
1072 fn dedup_key(&self) -> Option<QName> {
1078 None
1079 }
1080}
1081
1082pub(crate) struct NamedUniqueReg<S: NamedUniqueOps> {
1084 pub q: QName,
1086 pub sig: S::Sig,
1088 pub provider: Arc<S::Provider>,
1090}
1091
1092impl<S> DynPendingRegistration for NamedUniqueReg<S>
1093where
1094 S: NamedUniqueOps + 'static,
1095 S::Sig: Send + Sync,
1096{
1097 fn kind(&self) -> SurfaceKind {
1098 S::KIND
1099 }
1100 fn preflight(&self, registry: &PluginRegistry) -> Result<(), PluginError> {
1101 S::preflight(registry, &self.q)
1102 }
1103 fn apply(
1104 self: Box<Self>,
1105 registry: &PluginRegistry,
1106 plugin: PluginId,
1107 record: &mut PluginRecord,
1108 ) {
1109 S::insert(registry, plugin, self.q, self.sig, self.provider, record);
1110 }
1111 fn dedup_key(&self) -> Option<QName> {
1112 Some(self.q.clone())
1114 }
1115}
1116
1117pub(crate) struct VersionedReg<S: VersionedOps> {
1119 pub q: QName,
1121 pub sig: S::Sig,
1123 pub provider: Arc<S::Provider>,
1125}
1126
1127impl<S> DynPendingRegistration for VersionedReg<S>
1128where
1129 S: VersionedOps + 'static,
1130 S::Sig: Send + Sync,
1131{
1132 fn kind(&self) -> SurfaceKind {
1133 S::KIND
1134 }
1135 fn preflight(&self, registry: &PluginRegistry) -> Result<(), PluginError> {
1136 S::preflight(registry, &self.q, &self.sig)
1137 }
1138 fn apply(
1139 self: Box<Self>,
1140 registry: &PluginRegistry,
1141 plugin: PluginId,
1142 record: &mut PluginRecord,
1143 ) {
1144 S::insert(registry, plugin, self.q, self.sig, self.provider, record);
1145 }
1146}
1147
1148pub(crate) struct KeyedUniqueReg<S: KeyedUniqueOps> {
1155 pub key_override: Option<S::Key>,
1158 pub provider: Arc<S::Provider>,
1160}
1161
1162impl<S> KeyedUniqueReg<S>
1163where
1164 S: KeyedUniqueOps,
1165{
1166 pub fn resolve_key(&self) -> Result<S::Key, PluginError> {
1173 if let Some(ref k) = self.key_override {
1174 return Ok(k.clone());
1175 }
1176 S::key_of(&*self.provider).ok_or_else(|| {
1177 PluginError::internal(format!(
1178 "{:?} registration missing explicit key (provider does not self-identify)",
1179 S::KIND
1180 ))
1181 })
1182 }
1183}
1184
1185impl<S> DynPendingRegistration for KeyedUniqueReg<S>
1186where
1187 S: KeyedUniqueOps + 'static,
1188{
1189 fn kind(&self) -> SurfaceKind {
1190 S::KIND
1191 }
1192 fn preflight(&self, registry: &PluginRegistry) -> Result<(), PluginError> {
1193 let key = self.resolve_key()?;
1194 S::preflight(registry, &key)
1195 }
1196 fn apply(
1197 self: Box<Self>,
1198 registry: &PluginRegistry,
1199 _plugin: PluginId,
1200 record: &mut PluginRecord,
1201 ) {
1202 let key = match self.resolve_key() {
1206 Ok(k) => k,
1207 Err(_) => return, };
1209 S::insert(registry, key, self.provider, record);
1210 }
1211}
1212
1213pub(crate) struct AppendReg<S: AppendOps> {
1215 pub provider: Arc<S::Provider>,
1217}
1218
1219impl<S> DynPendingRegistration for AppendReg<S>
1220where
1221 S: AppendOps + 'static,
1222{
1223 fn kind(&self) -> SurfaceKind {
1224 S::KIND
1225 }
1226 fn preflight(&self, _registry: &PluginRegistry) -> Result<(), PluginError> {
1227 Ok(())
1228 }
1229 fn apply(
1230 self: Box<Self>,
1231 registry: &PluginRegistry,
1232 plugin: PluginId,
1233 record: &mut PluginRecord,
1234 ) {
1235 S::insert(registry, plugin, self.provider, record);
1236 }
1237}
1238
1239#[cfg(test)]
1240mod tests {
1241 use super::*;
1242
1243 #[test]
1244 fn surface_kind_count_matches_design() {
1245 let kinds = [
1247 <ScalarSurface as NamedUniqueSurface>::KIND,
1248 <AggregateSurface as NamedUniqueSurface>::KIND,
1249 <WindowSurface as NamedUniqueSurface>::KIND,
1250 <LocyAggregateSurface as NamedUniqueSurface>::KIND,
1251 <LocyPredicateSurface as NamedUniqueSurface>::KIND,
1252 <LocyGeneratorSurface as NamedUniqueSurface>::KIND,
1253 <AlgorithmSurface as NamedUniqueSurface>::KIND,
1254 <ProcedureSurface as VersionedSurface>::KIND,
1255 <IndexKindSurface as KeyedUniqueSurface>::KIND,
1256 <LabelStorageSurface as KeyedUniqueSurface>::KIND,
1257 <CrdtSurface as KeyedUniqueSurface>::KIND,
1258 <LogicalTypeSurface as KeyedUniqueSurface>::KIND,
1259 <CollationSurface as KeyedUniqueSurface>::KIND,
1260 <CdcSurface as KeyedUniqueSurface>::KIND,
1261 <CatalogSurface as KeyedUniqueSurface>::KIND,
1262 <OptimizerRuleSurface as AppendSurface>::KIND,
1263 <HookSurface as AppendSurface>::KIND,
1264 <AuthSurface as AppendSurface>::KIND,
1265 <AuthzSurface as AppendSurface>::KIND,
1266 <TriggerSurface as AppendSurface>::KIND,
1267 <ReplacementScanSurface as AppendSurface>::KIND,
1268 <BackgroundJobSurface as AppendSurface>::KIND,
1269 ];
1270 assert_eq!(kinds.len(), 22);
1277 let mut sorted: Vec<_> = kinds.iter().collect();
1278 sorted.sort_by_key(|k| format!("{k:?}"));
1279 sorted.dedup();
1280 assert_eq!(sorted.len(), 22, "duplicate SurfaceKind in markers");
1281 }
1282
1283 #[test]
1284 fn keyed_unique_default_duplicate_error_is_internal() {
1285 let err = <LogicalTypeSurface as KeyedUniqueSurface>::duplicate_error(&SmolStr::new("x"));
1286 assert!(matches!(err, PluginError::Internal(_)));
1287 }
1288
1289 struct NoopHook;
1292 impl crate::traits::hook::SessionHook for NoopHook {}
1293
1294 fn pid(s: &str) -> PluginId {
1295 PluginId::new(s)
1296 }
1297
1298 #[test]
1299 fn append_ops_insert_and_remove_round_trip() {
1300 let registry = PluginRegistry::new();
1304 let mut record_a = PluginRecord::default();
1305 let mut record_b = PluginRecord::default();
1306 <HookSurface as AppendOps>::insert(®istry, pid("a"), Arc::new(NoopHook), &mut record_a);
1307 <HookSurface as AppendOps>::insert(®istry, pid("b"), Arc::new(NoopHook), &mut record_b);
1308 assert_eq!(registry.hooks().len(), 2);
1309 assert_eq!(record_a.hook_count, 1);
1310 assert_eq!(record_b.hook_count, 1);
1311
1312 <HookSurface as AppendOps>::remove_plugin(®istry, &pid("a"));
1313 assert_eq!(
1314 registry.hooks().len(),
1315 1,
1316 "remove_plugin should drop plugin a's entry"
1317 );
1318 <HookSurface as AppendOps>::remove_plugin(®istry, &pid("b"));
1319 assert_eq!(registry.hooks().len(), 0);
1320 }
1321
1322 #[test]
1323 fn append_ops_remove_plugin_is_noop_when_no_entries() {
1324 let registry = PluginRegistry::new();
1325 <HookSurface as AppendOps>::remove_plugin(®istry, &pid("ghost"));
1328 assert_eq!(registry.hooks().len(), 0);
1329 }
1330
1331 #[test]
1332 fn append_reg_dyn_dispatch_matches_static_dispatch() {
1333 let registry = PluginRegistry::new();
1336 let mut record = PluginRecord::default();
1337 let reg: Box<dyn DynPendingRegistration> = Box::new(AppendReg::<HookSurface> {
1338 provider: Arc::new(NoopHook),
1339 });
1340 assert_eq!(reg.kind(), SurfaceKind::Hook);
1341 reg.preflight(®istry).unwrap();
1342 reg.apply(®istry, pid("dyn"), &mut record);
1343 assert_eq!(registry.hooks().len(), 1);
1344 assert_eq!(record.hook_count, 1);
1345
1346 <HookSurface as AppendOps>::remove_plugin(®istry, &pid("dyn"));
1347 assert_eq!(registry.hooks().len(), 0);
1348 }
1349
1350 #[test]
1351 fn named_unique_ops_preflight_detects_duplicate() {
1352 let registry = PluginRegistry::new();
1354 let mut record = PluginRecord::default();
1355 let q = QName::builtin("scalar_dup");
1356 <ScalarSurface as NamedUniqueOps>::preflight(®istry, &q).unwrap();
1360 record.scalars.push(q.clone());
1363 }
1369
1370 struct StubCollation(&'static str);
1377 impl crate::traits::collation::CollationProvider for StubCollation {
1378 fn name(&self) -> &str {
1379 self.0
1380 }
1381 fn compare(&self, a: &str, b: &str) -> std::cmp::Ordering {
1382 a.cmp(b)
1383 }
1384 }
1385
1386 #[test]
1387 fn keyed_unique_collation_per_key_record_round_trip() {
1388 let registry = PluginRegistry::new();
1389 let mut record = PluginRecord::default();
1390 let key = SmolStr::new("test.case_fold");
1391 <CollationSurface as KeyedUniqueOps>::insert(
1392 ®istry,
1393 key.clone(),
1394 Arc::new(StubCollation("test.case_fold")),
1395 &mut record,
1396 );
1397 assert_eq!(record.collations, vec![key.clone()]);
1398 assert!(registry.collations.contains_key(&key));
1399
1400 <CollationSurface as KeyedUniqueOps>::remove(®istry, &key);
1401 assert!(
1402 !registry.collations.contains_key(&key),
1403 "remove must drop the keyed-unique slot entry; the legacy \
1404 count-only record could not"
1405 );
1406 }
1407}