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    /// Non-Unicode text such as `varchar`.
29    VarChar,
30    /// Binary data such as `varbinary`.
31    VarBinary,
32    /// A type not yet mapped by this skeleton.
33    Other(String),
34}
35
36/// SQL Server type information.
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct MssqlTypeInfo {
39    kind: MssqlType,
40    variable_length: bool,
41    size: Option<u16>,
42    protocol_type_info: Option<protocol::TypeInfo>,
43}
44
45impl MssqlTypeInfo {
46    /// Creates type information from a known SQL Server type family.
47    pub const fn new(kind: MssqlType) -> Self {
48        Self {
49            kind,
50            variable_length: false,
51            size: None,
52            protocol_type_info: None,
53        }
54    }
55
56    pub(crate) const fn with_size(kind: MssqlType, size: u16) -> Self {
57        Self {
58            kind,
59            variable_length: true,
60            size: Some(size),
61            protocol_type_info: None,
62        }
63    }
64
65    /// Returns the known SQL Server type family.
66    pub fn kind(&self) -> &MssqlType {
67        &self.kind
68    }
69
70    pub(crate) const fn size(&self) -> Option<u16> {
71        self.size
72    }
73
74    pub(crate) const fn protocol_type_info(&self) -> Option<&protocol::TypeInfo> {
75        self.protocol_type_info.as_ref()
76    }
77
78    /// SQL `NULL` type information.
79    pub const NULL: Self = Self::new(MssqlType::Null);
80    /// SQL Server `bit` type information.
81    pub const BIT: Self = Self::new(MssqlType::Bit);
82    /// SQL Server `tinyint` type information.
83    pub const TINYINT: Self = Self::new(MssqlType::TinyInt);
84    /// SQL Server `smallint` type information.
85    pub const SMALLINT: Self = Self::new(MssqlType::SmallInt);
86    /// SQL Server `int` type information.
87    pub const INT: Self = Self::new(MssqlType::Int);
88    /// SQL Server `bigint` type information.
89    pub const BIGINT: Self = Self::new(MssqlType::BigInt);
90    /// SQL Server `real` type information.
91    pub const REAL: Self = Self::new(MssqlType::Real);
92    /// SQL Server `float` type information.
93    pub const FLOAT: Self = Self::new(MssqlType::Float);
94    /// SQL Server Unicode text type information.
95    pub const NVARCHAR: Self = Self::new(MssqlType::NVarChar);
96    /// SQL Server non-Unicode text type information.
97    pub const VARCHAR: Self = Self::new(MssqlType::VarChar);
98    /// SQL Server binary type information.
99    pub const VARBINARY: Self = Self::new(MssqlType::VarBinary);
100
101    pub(crate) fn from_protocol(type_info: &protocol::TypeInfo) -> Self {
102        let kind = match type_info.ty {
103            protocol::DataType::Null => MssqlType::Null,
104            protocol::DataType::Bit | protocol::DataType::BitN => MssqlType::Bit,
105            protocol::DataType::TinyInt => MssqlType::TinyInt,
106            protocol::DataType::SmallInt => MssqlType::SmallInt,
107            protocol::DataType::Int => MssqlType::Int,
108            protocol::DataType::BigInt => MssqlType::BigInt,
109            protocol::DataType::Real => MssqlType::Real,
110            protocol::DataType::Float => MssqlType::Float,
111            protocol::DataType::IntN => match type_info.size {
112                1 => MssqlType::TinyInt,
113                2 => MssqlType::SmallInt,
114                4 => MssqlType::Int,
115                8 => MssqlType::BigInt,
116                _ => MssqlType::Other(type_info.name().to_owned()),
117            },
118            protocol::DataType::FloatN => match type_info.size {
119                4 => MssqlType::Real,
120                8 => MssqlType::Float,
121                _ => MssqlType::Other(type_info.name().to_owned()),
122            },
123            protocol::DataType::NVarChar | protocol::DataType::NChar => MssqlType::NVarChar,
124            protocol::DataType::VarChar
125            | protocol::DataType::Char
126            | protocol::DataType::BigVarChar
127            | protocol::DataType::BigChar => MssqlType::VarChar,
128            protocol::DataType::VarBinary
129            | protocol::DataType::Binary
130            | protocol::DataType::BigVarBinary
131            | protocol::DataType::BigBinary => MssqlType::VarBinary,
132            _ => MssqlType::Other(type_info.name().to_owned()),
133        };
134
135        Self {
136            kind,
137            variable_length: type_info.is_nullable_or_variable_length(),
138            size: u16::try_from(type_info.size).ok(),
139            protocol_type_info: Some(type_info.clone()),
140        }
141    }
142}
143
144impl TypeInfo for MssqlTypeInfo {
145    fn is_null(&self) -> bool {
146        matches!(self.kind, MssqlType::Null)
147    }
148
149    fn name(&self) -> &str {
150        match &self.kind {
151            MssqlType::Null => "NULL",
152            MssqlType::Bit => "BIT",
153            MssqlType::TinyInt => "TINYINT",
154            MssqlType::SmallInt => "SMALLINT",
155            MssqlType::Int => "INT",
156            MssqlType::BigInt => "BIGINT",
157            MssqlType::Real => "REAL",
158            MssqlType::Float => "FLOAT",
159            MssqlType::NVarChar => "NVARCHAR",
160            MssqlType::VarChar => "VARCHAR",
161            MssqlType::VarBinary => "VARBINARY",
162            MssqlType::Other(name) => name,
163        }
164    }
165
166    fn type_compatible(&self, other: &Self) -> bool {
167        self.kind == other.kind || self.is_null() || other.is_null()
168    }
169}
170
171impl Display for MssqlTypeInfo {
172    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
173        f.write_str(self.name())
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180
181    #[test]
182    fn exposes_sql_server_type_names() {
183        assert_eq!("INT", MssqlTypeInfo::INT.name());
184        assert_eq!("NVARCHAR", MssqlTypeInfo::NVARCHAR.to_string());
185        assert_eq!("VARCHAR", MssqlTypeInfo::VARCHAR.to_string());
186    }
187
188    #[test]
189    fn null_is_compatible_with_known_types() {
190        assert!(MssqlTypeInfo::NULL.type_compatible(&MssqlTypeInfo::INT));
191        assert!(MssqlTypeInfo::NVARCHAR.type_compatible(&MssqlTypeInfo::NULL));
192        assert!(!MssqlTypeInfo::INT.type_compatible(&MssqlTypeInfo::BIGINT));
193    }
194
195    #[test]
196    fn maps_unicode_and_non_unicode_protocol_text_separately() {
197        assert_eq!(
198            MssqlType::NVarChar,
199            MssqlTypeInfo::from_protocol(&protocol::TypeInfo::new(protocol::DataType::NVarChar, 8))
200                .kind
201        );
202        assert_eq!(
203            MssqlType::VarChar,
204            MssqlTypeInfo::from_protocol(&protocol::TypeInfo::new(protocol::DataType::VarChar, 8))
205                .kind
206        );
207        assert_eq!(
208            MssqlType::VarChar,
209            MssqlTypeInfo::from_protocol(&protocol::TypeInfo::new(
210                protocol::DataType::BigVarChar,
211                8,
212            ))
213            .kind
214        );
215    }
216}