sqlx_sqlserver/
type_info.rs1use std::fmt::{self, Display, Formatter};
2
3use sqlx_core::type_info::TypeInfo;
4
5use crate::protocol::type_info as protocol;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum MssqlType {
10 Null,
12 Bit,
14 TinyInt,
16 SmallInt,
18 Int,
20 BigInt,
22 Real,
24 Float,
26 NVarChar,
28 VarChar,
30 VarBinary,
32 Other(String),
34}
35
36#[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 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 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 pub const NULL: Self = Self::new(MssqlType::Null);
80 pub const BIT: Self = Self::new(MssqlType::Bit);
82 pub const TINYINT: Self = Self::new(MssqlType::TinyInt);
84 pub const SMALLINT: Self = Self::new(MssqlType::SmallInt);
86 pub const INT: Self = Self::new(MssqlType::Int);
88 pub const BIGINT: Self = Self::new(MssqlType::BigInt);
90 pub const REAL: Self = Self::new(MssqlType::Real);
92 pub const FLOAT: Self = Self::new(MssqlType::Float);
94 pub const NVARCHAR: Self = Self::new(MssqlType::NVarChar);
96 pub const VARCHAR: Self = Self::new(MssqlType::VarChar);
98 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}