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 #[allow(dead_code, reason = "Diagnostic surface for future error formatting.")]
1073 fn debug_label(&self) -> String;
1074
1075 fn dedup_key(&self) -> Option<QName> {
1081 None
1082 }
1083}
1084
1085pub(crate) struct NamedUniqueReg<S: NamedUniqueOps> {
1087 pub q: QName,
1089 pub sig: S::Sig,
1091 pub provider: Arc<S::Provider>,
1093}
1094
1095impl<S> DynPendingRegistration for NamedUniqueReg<S>
1096where
1097 S: NamedUniqueOps + 'static,
1098 S::Sig: Send + Sync,
1099{
1100 fn kind(&self) -> SurfaceKind {
1101 S::KIND
1102 }
1103 fn preflight(&self, registry: &PluginRegistry) -> Result<(), PluginError> {
1104 S::preflight(registry, &self.q)
1105 }
1106 fn apply(
1107 self: Box<Self>,
1108 registry: &PluginRegistry,
1109 plugin: PluginId,
1110 record: &mut PluginRecord,
1111 ) {
1112 S::insert(registry, plugin, self.q, self.sig, self.provider, record);
1113 }
1114 fn debug_label(&self) -> String {
1115 format!("{:?}({})", S::KIND, self.q)
1116 }
1117 fn dedup_key(&self) -> Option<QName> {
1118 Some(self.q.clone())
1120 }
1121}
1122
1123pub(crate) struct VersionedReg<S: VersionedOps> {
1125 pub q: QName,
1127 pub sig: S::Sig,
1129 pub provider: Arc<S::Provider>,
1131}
1132
1133impl<S> DynPendingRegistration for VersionedReg<S>
1134where
1135 S: VersionedOps + 'static,
1136 S::Sig: Send + Sync,
1137{
1138 fn kind(&self) -> SurfaceKind {
1139 S::KIND
1140 }
1141 fn preflight(&self, registry: &PluginRegistry) -> Result<(), PluginError> {
1142 S::preflight(registry, &self.q, &self.sig)
1143 }
1144 fn apply(
1145 self: Box<Self>,
1146 registry: &PluginRegistry,
1147 plugin: PluginId,
1148 record: &mut PluginRecord,
1149 ) {
1150 S::insert(registry, plugin, self.q, self.sig, self.provider, record);
1151 }
1152 fn debug_label(&self) -> String {
1153 format!("{:?}({})", S::KIND, self.q)
1154 }
1155}
1156
1157pub(crate) struct KeyedUniqueReg<S: KeyedUniqueOps> {
1164 pub key_override: Option<S::Key>,
1167 pub provider: Arc<S::Provider>,
1169}
1170
1171impl<S> KeyedUniqueReg<S>
1172where
1173 S: KeyedUniqueOps,
1174{
1175 pub fn resolve_key(&self) -> Result<S::Key, PluginError> {
1182 if let Some(ref k) = self.key_override {
1183 return Ok(k.clone());
1184 }
1185 S::key_of(&*self.provider).ok_or_else(|| {
1186 PluginError::internal(format!(
1187 "{:?} registration missing explicit key (provider does not self-identify)",
1188 S::KIND
1189 ))
1190 })
1191 }
1192}
1193
1194impl<S> DynPendingRegistration for KeyedUniqueReg<S>
1195where
1196 S: KeyedUniqueOps + 'static,
1197{
1198 fn kind(&self) -> SurfaceKind {
1199 S::KIND
1200 }
1201 fn preflight(&self, registry: &PluginRegistry) -> Result<(), PluginError> {
1202 let key = self.resolve_key()?;
1203 S::preflight(registry, &key)
1204 }
1205 fn apply(
1206 self: Box<Self>,
1207 registry: &PluginRegistry,
1208 _plugin: PluginId,
1209 record: &mut PluginRecord,
1210 ) {
1211 let key = match self.resolve_key() {
1215 Ok(k) => k,
1216 Err(_) => return, };
1218 S::insert(registry, key, self.provider, record);
1219 }
1220 fn debug_label(&self) -> String {
1221 let k = self
1222 .resolve_key()
1223 .map(|k| format!("{k:?}"))
1224 .unwrap_or_else(|_| "<unresolved>".into());
1225 format!("{:?}({k})", S::KIND)
1226 }
1227}
1228
1229pub(crate) struct AppendReg<S: AppendOps> {
1231 pub provider: Arc<S::Provider>,
1233}
1234
1235impl<S> DynPendingRegistration for AppendReg<S>
1236where
1237 S: AppendOps + 'static,
1238{
1239 fn kind(&self) -> SurfaceKind {
1240 S::KIND
1241 }
1242 fn preflight(&self, _registry: &PluginRegistry) -> Result<(), PluginError> {
1243 Ok(())
1244 }
1245 fn apply(
1246 self: Box<Self>,
1247 registry: &PluginRegistry,
1248 plugin: PluginId,
1249 record: &mut PluginRecord,
1250 ) {
1251 S::insert(registry, plugin, self.provider, record);
1252 }
1253 fn debug_label(&self) -> String {
1254 format!("{:?}", S::KIND)
1255 }
1256}
1257
1258#[cfg(test)]
1259mod tests {
1260 use super::*;
1261
1262 #[test]
1263 fn surface_kind_count_matches_design() {
1264 let kinds = [
1266 <ScalarSurface as NamedUniqueSurface>::KIND,
1267 <AggregateSurface as NamedUniqueSurface>::KIND,
1268 <WindowSurface as NamedUniqueSurface>::KIND,
1269 <LocyAggregateSurface as NamedUniqueSurface>::KIND,
1270 <LocyPredicateSurface as NamedUniqueSurface>::KIND,
1271 <LocyGeneratorSurface as NamedUniqueSurface>::KIND,
1272 <AlgorithmSurface as NamedUniqueSurface>::KIND,
1273 <ProcedureSurface as VersionedSurface>::KIND,
1274 <IndexKindSurface as KeyedUniqueSurface>::KIND,
1275 <LabelStorageSurface as KeyedUniqueSurface>::KIND,
1276 <CrdtSurface as KeyedUniqueSurface>::KIND,
1277 <LogicalTypeSurface as KeyedUniqueSurface>::KIND,
1278 <CollationSurface as KeyedUniqueSurface>::KIND,
1279 <CdcSurface as KeyedUniqueSurface>::KIND,
1280 <CatalogSurface as KeyedUniqueSurface>::KIND,
1281 <OptimizerRuleSurface as AppendSurface>::KIND,
1282 <HookSurface as AppendSurface>::KIND,
1283 <AuthSurface as AppendSurface>::KIND,
1284 <AuthzSurface as AppendSurface>::KIND,
1285 <TriggerSurface as AppendSurface>::KIND,
1286 <ReplacementScanSurface as AppendSurface>::KIND,
1287 <BackgroundJobSurface as AppendSurface>::KIND,
1288 ];
1289 assert_eq!(kinds.len(), 22);
1296 let mut sorted: Vec<_> = kinds.iter().collect();
1297 sorted.sort_by_key(|k| format!("{k:?}"));
1298 sorted.dedup();
1299 assert_eq!(sorted.len(), 22, "duplicate SurfaceKind in markers");
1300 }
1301
1302 #[test]
1303 fn keyed_unique_default_duplicate_error_is_internal() {
1304 let err = <LogicalTypeSurface as KeyedUniqueSurface>::duplicate_error(&SmolStr::new("x"));
1305 assert!(matches!(err, PluginError::Internal(_)));
1306 }
1307
1308 struct NoopHook;
1311 impl crate::traits::hook::SessionHook for NoopHook {}
1312
1313 fn pid(s: &str) -> PluginId {
1314 PluginId::new(s)
1315 }
1316
1317 #[test]
1318 fn append_ops_insert_and_remove_round_trip() {
1319 let registry = PluginRegistry::new();
1323 let mut record_a = PluginRecord::default();
1324 let mut record_b = PluginRecord::default();
1325 <HookSurface as AppendOps>::insert(®istry, pid("a"), Arc::new(NoopHook), &mut record_a);
1326 <HookSurface as AppendOps>::insert(®istry, pid("b"), Arc::new(NoopHook), &mut record_b);
1327 assert_eq!(registry.hooks().len(), 2);
1328 assert_eq!(record_a.hook_count, 1);
1329 assert_eq!(record_b.hook_count, 1);
1330
1331 <HookSurface as AppendOps>::remove_plugin(®istry, &pid("a"));
1332 assert_eq!(
1333 registry.hooks().len(),
1334 1,
1335 "remove_plugin should drop plugin a's entry"
1336 );
1337 <HookSurface as AppendOps>::remove_plugin(®istry, &pid("b"));
1338 assert_eq!(registry.hooks().len(), 0);
1339 }
1340
1341 #[test]
1342 fn append_ops_remove_plugin_is_noop_when_no_entries() {
1343 let registry = PluginRegistry::new();
1344 <HookSurface as AppendOps>::remove_plugin(®istry, &pid("ghost"));
1347 assert_eq!(registry.hooks().len(), 0);
1348 }
1349
1350 #[test]
1351 fn append_reg_dyn_dispatch_matches_static_dispatch() {
1352 let registry = PluginRegistry::new();
1355 let mut record = PluginRecord::default();
1356 let reg: Box<dyn DynPendingRegistration> = Box::new(AppendReg::<HookSurface> {
1357 provider: Arc::new(NoopHook),
1358 });
1359 assert_eq!(reg.kind(), SurfaceKind::Hook);
1360 reg.preflight(®istry).unwrap();
1361 reg.apply(®istry, pid("dyn"), &mut record);
1362 assert_eq!(registry.hooks().len(), 1);
1363 assert_eq!(record.hook_count, 1);
1364
1365 <HookSurface as AppendOps>::remove_plugin(®istry, &pid("dyn"));
1366 assert_eq!(registry.hooks().len(), 0);
1367 }
1368
1369 #[test]
1370 fn named_unique_ops_preflight_detects_duplicate() {
1371 let registry = PluginRegistry::new();
1373 let mut record = PluginRecord::default();
1374 let q = QName::builtin("scalar_dup");
1375 <ScalarSurface as NamedUniqueOps>::preflight(®istry, &q).unwrap();
1379 record.scalars.push(q.clone());
1382 }
1388
1389 struct StubCollation(&'static str);
1396 impl crate::traits::collation::CollationProvider for StubCollation {
1397 fn name(&self) -> &str {
1398 self.0
1399 }
1400 fn compare(&self, a: &str, b: &str) -> std::cmp::Ordering {
1401 a.cmp(b)
1402 }
1403 }
1404
1405 #[test]
1406 fn keyed_unique_collation_per_key_record_round_trip() {
1407 let registry = PluginRegistry::new();
1408 let mut record = PluginRecord::default();
1409 let key = SmolStr::new("test.case_fold");
1410 <CollationSurface as KeyedUniqueOps>::insert(
1411 ®istry,
1412 key.clone(),
1413 Arc::new(StubCollation("test.case_fold")),
1414 &mut record,
1415 );
1416 assert_eq!(record.collations, vec![key.clone()]);
1417 assert!(registry.collations.contains_key(&key));
1418
1419 <CollationSurface as KeyedUniqueOps>::remove(®istry, &key);
1420 assert!(
1421 !registry.collations.contains_key(&key),
1422 "remove must drop the keyed-unique slot entry; the legacy \
1423 count-only record could not"
1424 );
1425 }
1426}