1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9#[repr(u8)]
10pub enum TypeId {
11 Null = 0x1F,
14 Int1 = 0x30,
16 Bit = 0x32,
18 Int2 = 0x34,
20 Int4 = 0x38,
22 Int8 = 0x7F,
24 DateTimeN = 0x6F,
26 Float4 = 0x3B,
28 Float8 = 0x3E,
30 Money = 0x3C,
32 Money4 = 0x7A,
34 DateTime = 0x3D,
36 DateTime4 = 0x3A,
38
39 Guid = 0x24,
42 IntN = 0x26,
44 Decimal = 0x37,
46 Numeric = 0x3F,
48 BitN = 0x68,
50 DecimalN = 0x6A,
52 NumericN = 0x6C,
54 FloatN = 0x6D,
56 MoneyN = 0x6E,
58
59 Char = 0x2F,
62 VarChar = 0x27,
64 Binary = 0x2D,
66 VarBinary = 0x25,
68
69 BigVarChar = 0xA7,
72 BigVarBinary = 0xA5,
74 BigChar = 0xAF,
76 BigBinary = 0xAD,
78
79 NChar = 0xEF,
82 NVarChar = 0xE7,
84
85 Text = 0x23,
88 Image = 0x22,
90 NText = 0x63,
92
93 Date = 0x28,
96 Time = 0x29,
98 DateTime2 = 0x2A,
100 DateTimeOffset = 0x2B,
102
103 Variant = 0x62,
106 Udt = 0xF0,
108 Xml = 0xF1,
110 Tvp = 0xF3,
112}
113
114impl TypeId {
115 pub fn from_u8(value: u8) -> Option<Self> {
117 match value {
118 0x1F => Some(Self::Null),
119 0x30 => Some(Self::Int1),
120 0x32 => Some(Self::Bit),
121 0x34 => Some(Self::Int2),
122 0x38 => Some(Self::Int4),
123 0x7F => Some(Self::Int8),
124 0x6F => Some(Self::DateTimeN),
125 0x3B => Some(Self::Float4),
126 0x3E => Some(Self::Float8),
127 0x3C => Some(Self::Money),
128 0x7A => Some(Self::Money4),
129 0x3D => Some(Self::DateTime),
130 0x3A => Some(Self::DateTime4),
131 0x24 => Some(Self::Guid),
132 0x26 => Some(Self::IntN),
133 0x37 => Some(Self::Decimal),
134 0x3F => Some(Self::Numeric),
135 0x68 => Some(Self::BitN),
136 0x6A => Some(Self::DecimalN),
137 0x6C => Some(Self::NumericN),
138 0x6D => Some(Self::FloatN),
139 0x6E => Some(Self::MoneyN),
140 0x2F => Some(Self::Char),
141 0x27 => Some(Self::VarChar),
142 0x2D => Some(Self::Binary),
143 0x25 => Some(Self::VarBinary),
144 0xA7 => Some(Self::BigVarChar),
145 0xA5 => Some(Self::BigVarBinary),
146 0xAF => Some(Self::BigChar),
147 0xAD => Some(Self::BigBinary),
148 0xEF => Some(Self::NChar),
149 0xE7 => Some(Self::NVarChar),
150 0x23 => Some(Self::Text),
151 0x22 => Some(Self::Image),
152 0x63 => Some(Self::NText),
153 0x28 => Some(Self::Date),
154 0x29 => Some(Self::Time),
155 0x2A => Some(Self::DateTime2),
156 0x2B => Some(Self::DateTimeOffset),
157 0x62 => Some(Self::Variant),
158 0xF0 => Some(Self::Udt),
159 0xF1 => Some(Self::Xml),
160 0xF3 => Some(Self::Tvp),
161 _ => None,
162 }
163 }
164
165 #[must_use]
167 pub const fn is_fixed_length(&self) -> bool {
168 matches!(
169 self,
170 Self::Null
171 | Self::Int1
172 | Self::Bit
173 | Self::Int2
174 | Self::Int4
175 | Self::Int8
176 | Self::Float4
177 | Self::Float8
178 | Self::Money
179 | Self::Money4
180 | Self::DateTime
181 | Self::DateTime4
182 )
183 }
184
185 #[must_use]
187 pub const fn is_variable_length(&self) -> bool {
188 !self.is_fixed_length()
189 }
190
191 #[must_use]
193 pub const fn is_plp(&self) -> bool {
194 matches!(self, Self::Text | Self::Image | Self::NText | Self::Xml)
195 }
196
197 #[must_use]
199 pub const fn is_unicode(&self) -> bool {
200 matches!(self, Self::NChar | Self::NVarChar | Self::NText)
201 }
202
203 #[must_use]
205 pub const fn is_datetime(&self) -> bool {
206 matches!(
207 self,
208 Self::DateTime
209 | Self::DateTime4
210 | Self::DateTimeN
211 | Self::Date
212 | Self::Time
213 | Self::DateTime2
214 | Self::DateTimeOffset
215 )
216 }
217
218 #[must_use]
220 pub const fn fixed_size(&self) -> Option<usize> {
221 match self {
222 Self::Null => Some(0),
223 Self::Int1 => Some(1),
224 Self::Bit => Some(1),
225 Self::Int2 => Some(2),
226 Self::Int4 => Some(4),
227 Self::Int8 => Some(8),
228 Self::Float4 => Some(4),
229 Self::Float8 => Some(8),
230 Self::Money => Some(8),
231 Self::Money4 => Some(4),
232 Self::DateTime => Some(8),
233 Self::DateTime4 => Some(4),
234 Self::Date => Some(3),
235 _ => None,
236 }
237 }
238}
239
240#[derive(Debug, Clone, Copy, Default)]
242pub struct ColumnFlags {
243 pub nullable: bool,
245 pub case_sensitive: bool,
247 pub updateable: Updateable,
249 pub identity: bool,
251 pub computed: bool,
253 pub fixed_len_clr_type: bool,
255 pub sparse_column_set: bool,
257 pub encrypted: bool,
259 pub hidden: bool,
261 pub key: bool,
263 pub nullable_unknown: bool,
265}
266
267#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
269pub enum Updateable {
270 #[default]
272 ReadOnly,
273 ReadWrite,
275 Unknown,
277}
278
279impl ColumnFlags {
280 #[must_use]
282 pub fn from_bits(flags: u16) -> Self {
283 Self {
284 nullable: (flags & 0x0001) != 0,
285 case_sensitive: (flags & 0x0002) != 0,
286 updateable: match (flags >> 2) & 0x03 {
287 0 => Updateable::ReadOnly,
288 1 => Updateable::ReadWrite,
289 _ => Updateable::Unknown,
290 },
291 identity: (flags & 0x0010) != 0,
292 computed: (flags & 0x0020) != 0,
293 fixed_len_clr_type: (flags & 0x0100) != 0,
294 sparse_column_set: (flags & 0x0200) != 0,
295 encrypted: (flags & 0x0400) != 0,
296 hidden: (flags & 0x2000) != 0,
297 key: (flags & 0x4000) != 0,
298 nullable_unknown: (flags & 0x8000) != 0,
299 }
300 }
301
302 #[must_use]
304 pub fn to_bits(&self) -> u16 {
305 let mut flags = 0u16;
306 if self.nullable {
307 flags |= 0x0001;
308 }
309 if self.case_sensitive {
310 flags |= 0x0002;
311 }
312 flags |= match self.updateable {
313 Updateable::ReadOnly => 0,
314 Updateable::ReadWrite => 1 << 2,
315 Updateable::Unknown => 2 << 2,
316 };
317 if self.identity {
318 flags |= 0x0010;
319 }
320 if self.computed {
321 flags |= 0x0020;
322 }
323 if self.fixed_len_clr_type {
324 flags |= 0x0100;
325 }
326 if self.sparse_column_set {
327 flags |= 0x0200;
328 }
329 if self.encrypted {
330 flags |= 0x0400;
331 }
332 if self.hidden {
333 flags |= 0x2000;
334 }
335 if self.key {
336 flags |= 0x4000;
337 }
338 if self.nullable_unknown {
339 flags |= 0x8000;
340 }
341 flags
342 }
343}
344
345#[cfg(test)]
346#[allow(clippy::unwrap_used)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn test_type_id_from_u8() {
352 assert_eq!(TypeId::from_u8(0x38), Some(TypeId::Int4));
353 assert_eq!(TypeId::from_u8(0xE7), Some(TypeId::NVarChar));
354 assert_eq!(TypeId::from_u8(0x99), None);
355 }
356
357 #[test]
358 fn test_fixed_length_detection() {
359 assert!(TypeId::Int4.is_fixed_length());
360 assert!(TypeId::Float8.is_fixed_length());
361 assert!(!TypeId::NVarChar.is_fixed_length());
362 }
363
364 #[test]
365 fn test_column_flags_roundtrip() {
366 let flags = ColumnFlags {
367 nullable: true,
368 identity: true,
369 key: true,
370 ..Default::default()
371 };
372 let bits = flags.to_bits();
373 let restored = ColumnFlags::from_bits(bits);
374 assert_eq!(flags.nullable, restored.nullable);
375 assert_eq!(flags.identity, restored.identity);
376 assert_eq!(flags.key, restored.key);
377 }
378}