Skip to main content

sqlx_sqlserver/
type_info.rs

1use std::fmt::{self, Display, Formatter};
2
3use sqlx_core::type_info::TypeInfo;
4
5use crate::protocol::type_info as protocol;
6
7/// SQL Server scalar type families known by the skeleton driver.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum MssqlType {
10    /// SQL `NULL`.
11    Null,
12    /// SQL Server `bit`.
13    Bit,
14    /// SQL Server `tinyint`.
15    TinyInt,
16    /// SQL Server `smallint`.
17    SmallInt,
18    /// SQL Server `int`.
19    Int,
20    /// SQL Server `bigint`.
21    BigInt,
22    /// SQL Server `real`.
23    Real,
24    /// SQL Server `float`.
25    Float,
26    /// Unicode text such as `nvarchar`.
27    NVarChar,
28    /// Binary data such as `varbinary`.
29    VarBinary,
30    /// A type not yet mapped by this skeleton.
31    Other(String),
32}
33
34/// SQL Server type information.
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct MssqlTypeInfo {
37    kind: MssqlType,
38    variable_length: bool,
39    size: Option<u16>,
40    protocol_type_info: Option<protocol::TypeInfo>,
41}
42
43impl MssqlTypeInfo {
44    /// Creates type information from a known SQL Server type family.
45    pub const fn new(kind: MssqlType) -> Self {
46        Self {
47            kind,
48            variable_length: false,
49            size: None,
50            protocol_type_info: None,
51        }
52    }
53
54    /// Returns the known SQL Server type family.
55    pub fn kind(&self) -> &MssqlType {
56        &self.kind
57    }
58
59    pub(crate) const fn protocol_type_info(&self) -> Option<&protocol::TypeInfo> {
60        self.protocol_type_info.as_ref()
61    }
62
63    /// SQL `NULL` type information.
64    pub const NULL: Self = Self::new(MssqlType::Null);
65    /// SQL Server `bit` type information.
66    pub const BIT: Self = Self::new(MssqlType::Bit);
67    /// SQL Server `tinyint` type information.
68    pub const TINYINT: Self = Self::new(MssqlType::TinyInt);
69    /// SQL Server `smallint` type information.
70    pub const SMALLINT: Self = Self::new(MssqlType::SmallInt);
71    /// SQL Server `int` type information.
72    pub const INT: Self = Self::new(MssqlType::Int);
73    /// SQL Server `bigint` type information.
74    pub const BIGINT: Self = Self::new(MssqlType::BigInt);
75    /// SQL Server `real` type information.
76    pub const REAL: Self = Self::new(MssqlType::Real);
77    /// SQL Server `float` type information.
78    pub const FLOAT: Self = Self::new(MssqlType::Float);
79    /// SQL Server Unicode text type information.
80    pub const NVARCHAR: Self = Self::new(MssqlType::NVarChar);
81    /// SQL Server binary type information.
82    pub const VARBINARY: Self = Self::new(MssqlType::VarBinary);
83
84    pub(crate) fn from_protocol(type_info: &protocol::TypeInfo) -> Self {
85        let kind = match type_info.ty {
86            protocol::DataType::Null => MssqlType::Null,
87            protocol::DataType::Bit | protocol::DataType::BitN => MssqlType::Bit,
88            protocol::DataType::TinyInt => MssqlType::TinyInt,
89            protocol::DataType::SmallInt => MssqlType::SmallInt,
90            protocol::DataType::Int => MssqlType::Int,
91            protocol::DataType::BigInt => MssqlType::BigInt,
92            protocol::DataType::Real => MssqlType::Real,
93            protocol::DataType::Float => MssqlType::Float,
94            protocol::DataType::IntN => match type_info.size {
95                1 => MssqlType::TinyInt,
96                2 => MssqlType::SmallInt,
97                4 => MssqlType::Int,
98                8 => MssqlType::BigInt,
99                _ => MssqlType::Other(type_info.name().to_owned()),
100            },
101            protocol::DataType::FloatN => match type_info.size {
102                4 => MssqlType::Real,
103                8 => MssqlType::Float,
104                _ => MssqlType::Other(type_info.name().to_owned()),
105            },
106            protocol::DataType::NVarChar
107            | protocol::DataType::NChar
108            | protocol::DataType::VarChar
109            | protocol::DataType::Char
110            | protocol::DataType::BigVarChar
111            | protocol::DataType::BigChar => MssqlType::NVarChar,
112            protocol::DataType::VarBinary
113            | protocol::DataType::Binary
114            | protocol::DataType::BigVarBinary
115            | protocol::DataType::BigBinary => MssqlType::VarBinary,
116            _ => MssqlType::Other(type_info.name().to_owned()),
117        };
118
119        Self {
120            kind,
121            variable_length: type_info.is_nullable_or_variable_length(),
122            size: u16::try_from(type_info.size).ok(),
123            protocol_type_info: Some(type_info.clone()),
124        }
125    }
126}
127
128impl TypeInfo for MssqlTypeInfo {
129    fn is_null(&self) -> bool {
130        matches!(self.kind, MssqlType::Null)
131    }
132
133    fn name(&self) -> &str {
134        match &self.kind {
135            MssqlType::Null => "NULL",
136            MssqlType::Bit => "BIT",
137            MssqlType::TinyInt => "TINYINT",
138            MssqlType::SmallInt => "SMALLINT",
139            MssqlType::Int => "INT",
140            MssqlType::BigInt => "BIGINT",
141            MssqlType::Real => "REAL",
142            MssqlType::Float => "FLOAT",
143            MssqlType::NVarChar => "NVARCHAR",
144            MssqlType::VarBinary => "VARBINARY",
145            MssqlType::Other(name) => name,
146        }
147    }
148
149    fn type_compatible(&self, other: &Self) -> bool {
150        self.kind == other.kind || self.is_null() || other.is_null()
151    }
152}
153
154impl Display for MssqlTypeInfo {
155    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
156        f.write_str(self.name())
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    fn exposes_sql_server_type_names() {
166        assert_eq!("INT", MssqlTypeInfo::INT.name());
167        assert_eq!("NVARCHAR", MssqlTypeInfo::NVARCHAR.to_string());
168    }
169
170    #[test]
171    fn null_is_compatible_with_known_types() {
172        assert!(MssqlTypeInfo::NULL.type_compatible(&MssqlTypeInfo::INT));
173        assert!(MssqlTypeInfo::NVARCHAR.type_compatible(&MssqlTypeInfo::NULL));
174        assert!(!MssqlTypeInfo::INT.type_compatible(&MssqlTypeInfo::BIGINT));
175    }
176}