Skip to main content

arrow_tiberius/mssql/
profile.rs

1//! SQL Server profile types.
2
3use crate::error::Result;
4
5/// SQL Server engine version targeted by planning.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7#[non_exhaustive]
8pub enum MssqlVersion {
9    /// SQL Server 2016.
10    SqlServer2016,
11}
12
13/// SQL Server database compatibility level.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
15pub struct CompatibilityLevel(u16);
16
17impl CompatibilityLevel {
18    /// SQL Server 2008 / 2008 R2 compatibility level.
19    pub const SQL_SERVER_2008: Self = Self(100);
20
21    /// Creates a validated compatibility level.
22    pub fn new(level: u16) -> Result<Self> {
23        if !Self::is_supported(level) {
24            return Err(crate::Error::InvalidCompatibilityLevel { level });
25        }
26
27        Ok(Self(level))
28    }
29
30    /// Returns the numeric compatibility level.
31    pub const fn as_u16(self) -> u16 {
32        self.0
33    }
34
35    const fn is_supported(level: u16) -> bool {
36        matches!(level, 100)
37    }
38}
39
40impl TryFrom<u16> for CompatibilityLevel {
41    type Error = crate::Error;
42
43    fn try_from(value: u16) -> Result<Self> {
44        Self::new(value)
45    }
46}
47
48/// SQL Server planning profile.
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
50pub struct MssqlProfile {
51    version: MssqlVersion,
52    compatibility_level: CompatibilityLevel,
53}
54
55impl MssqlProfile {
56    /// Creates the v0.1 SQL Server 2016 profile with database compatibility
57    /// level 100.
58    pub const fn sql_server_2016_compat_100() -> Self {
59        Self {
60            version: MssqlVersion::SqlServer2016,
61            compatibility_level: CompatibilityLevel::SQL_SERVER_2008,
62        }
63    }
64
65    /// Returns the SQL Server engine version.
66    pub const fn version(self) -> MssqlVersion {
67        self.version
68    }
69
70    /// Returns the database compatibility level.
71    pub const fn compatibility_level(self) -> CompatibilityLevel {
72        self.compatibility_level
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::{CompatibilityLevel, MssqlProfile, MssqlVersion};
79
80    #[test]
81    fn constructs_sql_server_2016_compat_100_profile() {
82        let profile = MssqlProfile::sql_server_2016_compat_100();
83
84        assert_eq!(profile.version(), MssqlVersion::SqlServer2016);
85        assert_eq!(profile.compatibility_level().as_u16(), 100);
86    }
87
88    #[test]
89    fn accepts_supported_compatibility_level() {
90        let level = CompatibilityLevel::new(100).unwrap();
91
92        assert_eq!(level.as_u16(), 100);
93    }
94
95    #[test]
96    fn try_from_accepts_supported_compatibility_level() {
97        let level = CompatibilityLevel::try_from(100).unwrap();
98
99        assert_eq!(level, CompatibilityLevel::SQL_SERVER_2008);
100    }
101
102    #[test]
103    fn rejects_unsupported_compatibility_level() {
104        let err = CompatibilityLevel::new(90).expect_err("level should be rejected");
105
106        assert!(err.to_string().contains("invalid compatibility level 90"));
107    }
108
109    #[test]
110    fn rejects_nearby_and_extreme_compatibility_levels() {
111        for level in [0, 99, 101, 150, u16::MAX] {
112            let err = CompatibilityLevel::new(level).expect_err("level should be rejected");
113
114            assert!(
115                err.to_string()
116                    .contains(&format!("invalid compatibility level {level}")),
117                "unexpected error: {err}"
118            );
119        }
120    }
121}