Skip to main content

connectorx/sources/mssql/
driver.rs

1//! Internal MSSQL driver seam.
2//!
3//! ConnectorX can talk to SQL Server through either `tiberius` + `bb8-tiberius`
4//! (the original implementation) or `mssql-tds` (Microsoft's own TDS
5//! client), following the phased migration plan tracked against
6//! sfu-db/connector-x#942.
7//!
8//! - When only one of `src_mssql_tiberius` / `src_mssql_tds` is enabled,
9//!   [`active_driver`] is a compile-time constant: that's the only backend
10//!   linked in, and there is no runtime switch.
11//! - When both are enabled (this is what the Python bindings build with, as
12//!   of Phase 3), both backends are linked into the same binary and
13//!   [`active_driver`] reads a process-wide atomic that [`set_active_driver`]
14//!   can flip at runtime. `mssql-tds` is the default; switching to
15//!   `tiberius` is the opt-in.
16//!
17//! Switching the driver only affects [`super::MsSQLSource`]s constructed
18//! *after* the switch — an in-flight source/partition/parser keeps using
19//! whichever backend it was built with.
20
21/// Which MSSQL wire-protocol driver ConnectorX uses for a given source.
22#[derive(Copy, Clone, Eq, PartialEq, Debug)]
23pub enum MsSQLDriverKind {
24    /// `tiberius` + `bb8-tiberius`.
25    Tiberius,
26    /// `mssql-tds`.
27    MssqlTds,
28}
29
30impl MsSQLDriverKind {
31    /// The name used on the Rust/Python opt-in surfaces (`"tiberius"` /
32    /// `"mssql-tds"`).
33    pub fn as_str(self) -> &'static str {
34        match self {
35            MsSQLDriverKind::Tiberius => "tiberius",
36            MsSQLDriverKind::MssqlTds => "mssql-tds",
37        }
38    }
39
40    /// Parses the opt-in name back into a [`MsSQLDriverKind`]. Returns
41    /// `None` for anything other than `"tiberius"` / `"mssql-tds"`.
42    pub fn from_name(name: &str) -> Option<Self> {
43        match name {
44            "tiberius" => Some(MsSQLDriverKind::Tiberius),
45            "mssql-tds" => Some(MsSQLDriverKind::MssqlTds),
46            _ => None,
47        }
48    }
49}
50
51/// Returns the MSSQL driver ConnectorX was compiled with.
52#[cfg(all(feature = "src_mssql_tiberius", not(feature = "src_mssql_tds")))]
53pub(crate) fn active_driver() -> MsSQLDriverKind {
54    MsSQLDriverKind::Tiberius
55}
56
57/// Returns the MSSQL driver ConnectorX was compiled with.
58#[cfg(all(feature = "src_mssql_tds", not(feature = "src_mssql_tiberius")))]
59pub(crate) fn active_driver() -> MsSQLDriverKind {
60    MsSQLDriverKind::MssqlTds
61}
62
63// Both backends linked in: a real runtime switch, defaulting to mssql-tds
64// (Phase 3 of sfu-db/connector-x#942).
65#[cfg(all(feature = "src_mssql_tiberius", feature = "src_mssql_tds"))]
66mod runtime_switch {
67    use super::MsSQLDriverKind;
68    use std::sync::atomic::{AtomicU8, Ordering};
69
70    const TIBERIUS: u8 = 0;
71    const MSSQL_TDS: u8 = 1;
72
73    static ACTIVE_DRIVER: AtomicU8 = AtomicU8::new(MSSQL_TDS);
74
75    pub fn active_driver() -> MsSQLDriverKind {
76        match ACTIVE_DRIVER.load(Ordering::SeqCst) {
77            TIBERIUS => MsSQLDriverKind::Tiberius,
78            _ => MsSQLDriverKind::MssqlTds,
79        }
80    }
81
82    /// Switches the MSSQL driver used by `MsSQLSource`s constructed from now
83    /// on. Only available when both `src_mssql_tiberius` and `src_mssql_tds`
84    /// are compiled in.
85    pub fn set_active_driver(kind: MsSQLDriverKind) {
86        let v = match kind {
87            MsSQLDriverKind::Tiberius => TIBERIUS,
88            MsSQLDriverKind::MssqlTds => MSSQL_TDS,
89        };
90        ACTIVE_DRIVER.store(v, Ordering::SeqCst);
91    }
92}
93
94#[cfg(all(feature = "src_mssql_tiberius", feature = "src_mssql_tds"))]
95pub(crate) use runtime_switch::active_driver;
96#[cfg(all(feature = "src_mssql_tiberius", feature = "src_mssql_tds"))]
97pub use runtime_switch::set_active_driver;
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    #[cfg(all(feature = "src_mssql_tiberius", not(feature = "src_mssql_tds")))]
105    fn active_driver_is_tiberius() {
106        assert_eq!(active_driver(), MsSQLDriverKind::Tiberius);
107    }
108
109    #[test]
110    #[cfg(all(feature = "src_mssql_tds", not(feature = "src_mssql_tiberius")))]
111    fn active_driver_is_mssql_tds() {
112        assert_eq!(active_driver(), MsSQLDriverKind::MssqlTds);
113    }
114
115    #[test]
116    #[cfg(all(feature = "src_mssql_tiberius", feature = "src_mssql_tds"))]
117    fn runtime_switch_defaults_to_mssql_tds_and_round_trips() {
118        assert_eq!(active_driver(), MsSQLDriverKind::MssqlTds);
119        set_active_driver(MsSQLDriverKind::Tiberius);
120        assert_eq!(active_driver(), MsSQLDriverKind::Tiberius);
121        // restore the default so other tests in this process observe it
122        set_active_driver(MsSQLDriverKind::MssqlTds);
123        assert_eq!(active_driver(), MsSQLDriverKind::MssqlTds);
124    }
125
126    #[test]
127    #[cfg(all(feature = "src_mssql_tiberius", feature = "src_mssql_tds"))]
128    fn driver_kind_name_round_trips() {
129        assert_eq!(
130            MsSQLDriverKind::from_name("tiberius"),
131            Some(MsSQLDriverKind::Tiberius)
132        );
133        assert_eq!(
134            MsSQLDriverKind::from_name("mssql-tds"),
135            Some(MsSQLDriverKind::MssqlTds)
136        );
137        assert_eq!(MsSQLDriverKind::from_name("nonsense"), None);
138        assert_eq!(MsSQLDriverKind::Tiberius.as_str(), "tiberius");
139        assert_eq!(MsSQLDriverKind::MssqlTds.as_str(), "mssql-tds");
140    }
141}