1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9#[repr(u8)]
10#[non_exhaustive]
11pub enum TypeId {
12 Null = 0x1F,
15 Int1 = 0x30,
17 Bit = 0x32,
19 Int2 = 0x34,
21 Int4 = 0x38,
23 Int8 = 0x7F,
25 DateTimeN = 0x6F,
27 Float4 = 0x3B,
29 Float8 = 0x3E,
31 Money = 0x3C,
33 Money4 = 0x7A,
35 DateTime = 0x3D,
37 DateTime4 = 0x3A,
39
40 Guid = 0x24,
43 IntN = 0x26,
45 Decimal = 0x37,
47 Numeric = 0x3F,
49 BitN = 0x68,
51 DecimalN = 0x6A,
53 NumericN = 0x6C,
55 FloatN = 0x6D,
57 MoneyN = 0x6E,
59
60 Char = 0x2F,
63 VarChar = 0x27,
65 Binary = 0x2D,
67 VarBinary = 0x25,
69
70 BigVarChar = 0xA7,
73 BigVarBinary = 0xA5,
75 BigChar = 0xAF,
77 BigBinary = 0xAD,
79
80 NChar = 0xEF,
83 NVarChar = 0xE7,
85
86 Text = 0x23,
89 Image = 0x22,
91 NText = 0x63,
93
94 Date = 0x28,
97 Time = 0x29,
99 DateTime2 = 0x2A,
101 DateTimeOffset = 0x2B,
103
104 Variant = 0x62,
107 Udt = 0xF0,
109 Xml = 0xF1,
111 Tvp = 0xF3,
113}
114
115impl TypeId {
116 pub fn from_u8(value: u8) -> Option<Self> {
118 match value {
119 0x1F => Some(Self::Null),
120 0x30 => Some(Self::Int1),
121 0x32 => Some(Self::Bit),
122 0x34 => Some(Self::Int2),
123 0x38 => Some(Self::Int4),
124 0x7F => Some(Self::Int8),
125 0x6F => Some(Self::DateTimeN),
126 0x3B => Some(Self::Float4),
127 0x3E => Some(Self::Float8),
128 0x3C => Some(Self::Money),
129 0x7A => Some(Self::Money4),
130 0x3D => Some(Self::DateTime),
131 0x3A => Some(Self::DateTime4),
132 0x24 => Some(Self::Guid),
133 0x26 => Some(Self::IntN),
134 0x37 => Some(Self::Decimal),
135 0x3F => Some(Self::Numeric),
136 0x68 => Some(Self::BitN),
137 0x6A => Some(Self::DecimalN),
138 0x6C => Some(Self::NumericN),
139 0x6D => Some(Self::FloatN),
140 0x6E => Some(Self::MoneyN),
141 0x2F => Some(Self::Char),
142 0x27 => Some(Self::VarChar),
143 0x2D => Some(Self::Binary),
144 0x25 => Some(Self::VarBinary),
145 0xA7 => Some(Self::BigVarChar),
146 0xA5 => Some(Self::BigVarBinary),
147 0xAF => Some(Self::BigChar),
148 0xAD => Some(Self::BigBinary),
149 0xEF => Some(Self::NChar),
150 0xE7 => Some(Self::NVarChar),
151 0x23 => Some(Self::Text),
152 0x22 => Some(Self::Image),
153 0x63 => Some(Self::NText),
154 0x28 => Some(Self::Date),
155 0x29 => Some(Self::Time),
156 0x2A => Some(Self::DateTime2),
157 0x2B => Some(Self::DateTimeOffset),
158 0x62 => Some(Self::Variant),
159 0xF0 => Some(Self::Udt),
160 0xF1 => Some(Self::Xml),
161 0xF3 => Some(Self::Tvp),
162 _ => None,
163 }
164 }
165
166 #[must_use]
168 pub const fn is_fixed_length(&self) -> bool {
169 matches!(
170 self,
171 Self::Null
172 | Self::Int1
173 | Self::Bit
174 | Self::Int2
175 | Self::Int4
176 | Self::Int8
177 | Self::Float4
178 | Self::Float8
179 | Self::Money
180 | Self::Money4
181 | Self::DateTime
182 | Self::DateTime4
183 )
184 }
185
186 #[must_use]
188 pub const fn is_variable_length(&self) -> bool {
189 !self.is_fixed_length()
190 }
191
192 #[must_use]
198 pub const fn is_plp(&self) -> bool {
199 matches!(self, Self::Text | Self::Image | Self::NText | Self::Xml)
200 }
201
202 #[must_use]
209 pub const fn can_be_max(&self) -> bool {
210 matches!(self, Self::NVarChar | Self::BigVarChar | Self::BigVarBinary)
211 }
212
213 #[must_use]
215 pub const fn is_unicode(&self) -> bool {
216 matches!(self, Self::NChar | Self::NVarChar | Self::NText)
217 }
218
219 #[must_use]
221 pub const fn is_datetime(&self) -> bool {
222 matches!(
223 self,
224 Self::DateTime
225 | Self::DateTime4
226 | Self::DateTimeN
227 | Self::Date
228 | Self::Time
229 | Self::DateTime2
230 | Self::DateTimeOffset
231 )
232 }
233
234 #[must_use]
236 pub const fn fixed_size(&self) -> Option<usize> {
237 match self {
238 Self::Null => Some(0),
239 Self::Int1 => Some(1),
240 Self::Bit => Some(1),
241 Self::Int2 => Some(2),
242 Self::Int4 => Some(4),
243 Self::Int8 => Some(8),
244 Self::Float4 => Some(4),
245 Self::Float8 => Some(8),
246 Self::Money => Some(8),
247 Self::Money4 => Some(4),
248 Self::DateTime => Some(8),
249 Self::DateTime4 => Some(4),
250 Self::Date => Some(3),
251 _ => None,
252 }
253 }
254}
255
256#[derive(Debug, Clone, Copy, Default)]
258#[non_exhaustive]
259pub struct ColumnFlags {
260 pub nullable: bool,
262 pub case_sensitive: bool,
264 pub updateable: Updateable,
266 pub identity: bool,
268 pub computed: bool,
270 pub fixed_len_clr_type: bool,
272 pub sparse_column_set: bool,
274 pub encrypted: bool,
276 pub hidden: bool,
278 pub key: bool,
280 pub nullable_unknown: bool,
282}
283
284#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
286#[non_exhaustive]
287pub enum Updateable {
288 #[default]
290 ReadOnly,
291 ReadWrite,
293 Unknown,
295}
296
297impl ColumnFlags {
298 #[must_use]
300 pub fn from_bits(flags: u16) -> Self {
301 Self {
302 nullable: (flags & 0x0001) != 0,
303 case_sensitive: (flags & 0x0002) != 0,
304 updateable: match (flags >> 2) & 0x03 {
305 0 => Updateable::ReadOnly,
306 1 => Updateable::ReadWrite,
307 _ => Updateable::Unknown,
308 },
309 identity: (flags & 0x0010) != 0,
310 computed: (flags & 0x0020) != 0,
311 fixed_len_clr_type: (flags & 0x0100) != 0,
312 sparse_column_set: (flags & 0x0200) != 0,
313 encrypted: (flags & 0x0400) != 0,
314 hidden: (flags & 0x2000) != 0,
315 key: (flags & 0x4000) != 0,
316 nullable_unknown: (flags & 0x8000) != 0,
317 }
318 }
319
320 #[must_use]
322 pub fn to_bits(&self) -> u16 {
323 let mut flags = 0u16;
324 if self.nullable {
325 flags |= 0x0001;
326 }
327 if self.case_sensitive {
328 flags |= 0x0002;
329 }
330 flags |= match self.updateable {
331 Updateable::ReadOnly => 0,
332 Updateable::ReadWrite => 1 << 2,
333 Updateable::Unknown => 2 << 2,
334 };
335 if self.identity {
336 flags |= 0x0010;
337 }
338 if self.computed {
339 flags |= 0x0020;
340 }
341 if self.fixed_len_clr_type {
342 flags |= 0x0100;
343 }
344 if self.sparse_column_set {
345 flags |= 0x0200;
346 }
347 if self.encrypted {
348 flags |= 0x0400;
349 }
350 if self.hidden {
351 flags |= 0x2000;
352 }
353 if self.key {
354 flags |= 0x4000;
355 }
356 if self.nullable_unknown {
357 flags |= 0x8000;
358 }
359 flags
360 }
361}
362
363#[cfg(test)]
364#[allow(clippy::unwrap_used)]
365mod tests {
366 use super::*;
367
368 #[test]
369 fn test_type_id_from_u8() {
370 assert_eq!(TypeId::from_u8(0x38), Some(TypeId::Int4));
371 assert_eq!(TypeId::from_u8(0xE7), Some(TypeId::NVarChar));
372 assert_eq!(TypeId::from_u8(0x99), None);
373 }
374
375 #[test]
376 fn test_fixed_length_detection() {
377 assert!(TypeId::Int4.is_fixed_length());
378 assert!(TypeId::Float8.is_fixed_length());
379 assert!(!TypeId::NVarChar.is_fixed_length());
380 }
381
382 #[test]
383 fn test_column_flags_roundtrip() {
384 let flags = ColumnFlags {
385 nullable: true,
386 identity: true,
387 key: true,
388 ..Default::default()
389 };
390 let bits = flags.to_bits();
391 let restored = ColumnFlags::from_bits(bits);
392 assert_eq!(flags.nullable, restored.nullable);
393 assert_eq!(flags.identity, restored.identity);
394 assert_eq!(flags.key, restored.key);
395 }
396}