Skip to main content

sqlx_sqlserver/protocol/
type_info.rs

1use std::fmt::Write;
2
3use sqlx_core::encode::{Encode, IsNull};
4use sqlx_core::error::BoxDynError;
5
6use crate::Mssql;
7
8const PLP_NULL: u64 = 0xffff_ffff_ffff_ffff;
9const PLP_CHUNK_SIZE: usize = 8192;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub(crate) struct CollationFlags(u8);
13
14impl CollationFlags {
15    pub(crate) const IGNORE_CASE: Self = Self(1 << 0);
16    pub(crate) const IGNORE_ACCENT: Self = Self(1 << 1);
17    pub(crate) const IGNORE_WIDTH: Self = Self(1 << 2);
18    pub(crate) const IGNORE_KANA: Self = Self(1 << 3);
19    pub(crate) const BINARY: Self = Self(1 << 4);
20    pub(crate) const BINARY2: Self = Self(1 << 5);
21
22    pub(crate) const fn from_bits_truncate(bits: u8) -> Self {
23        Self(bits & 0x3f)
24    }
25
26    pub(crate) const fn bits(self) -> u8 {
27        self.0
28    }
29}
30
31#[derive(Debug, PartialEq, Eq, Clone, Copy)]
32pub(crate) struct Collation {
33    pub(crate) locale: u32,
34    pub(crate) flags: CollationFlags,
35    pub(crate) sort: u8,
36    pub(crate) version: u8,
37}
38
39#[derive(Debug, PartialEq, Eq, Clone, Copy)]
40#[repr(u8)]
41pub(crate) enum DataType {
42    // fixed-length data types
43    // https://docs.microsoft.com/en-us/openspecs/sql_server_protocols/ms-sstds/d33ef17b-7e53-4380-ad11-2ba42c8dda8d
44    Null = 0x1f,
45    TinyInt = 0x30,
46    Bit = 0x32,
47    SmallInt = 0x34,
48    Int = 0x38,
49    SmallDateTime = 0x3a,
50    Real = 0x3b,
51    Money = 0x3c,
52    DateTime = 0x3d,
53    Float = 0x3e,
54    SmallMoney = 0x7a,
55    BigInt = 0x7f,
56
57    // variable-length data types
58    // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/ce3183a6-9d89-47e8-a02f-de5a1a1303de
59
60    // byte length
61    Guid = 0x24,
62    IntN = 0x26,
63    Decimal = 0x37,
64    Numeric = 0x3f,
65    BitN = 0x68,
66    DecimalN = 0x6a,
67    NumericN = 0x6c,
68    FloatN = 0x6d,
69    MoneyN = 0x6e,
70    DateTimeN = 0x6f,
71    DateN = 0x28,
72    TimeN = 0x29,
73    DateTime2N = 0x2a,
74    DateTimeOffsetN = 0x2b,
75    Char = 0x2f,
76    VarChar = 0x27,
77    Binary = 0x2d,
78    VarBinary = 0x25,
79
80    // short length
81    BigVarBinary = 0xa5,
82    BigVarChar = 0xa7,
83    BigBinary = 0xad,
84    BigChar = 0xaf,
85    NVarChar = 0xe7,
86    NChar = 0xef,
87    Xml = 0xf1,
88    UserDefined = 0xf0,
89
90    // long length
91    Text = 0x23,
92    Image = 0x22,
93    NText = 0x63,
94    Variant = 0x62,
95}
96
97// http://msdn.microsoft.com/en-us/library/dd358284.aspx
98#[derive(Debug, Clone, Eq, PartialEq)]
99pub(crate) struct TypeInfo {
100    pub(crate) ty: DataType,
101    pub(crate) size: u32,
102    pub(crate) scale: u8,
103    pub(crate) precision: u8,
104    pub(crate) collation: Option<Collation>,
105}
106
107impl TypeInfo {
108    pub(crate) const fn new(ty: DataType, size: u32) -> Self {
109        Self {
110            ty,
111            size,
112            scale: 0,
113            precision: 0,
114            collation: None,
115        }
116    }
117
118    // reads a TYPE_INFO from the buffer
119    pub(crate) fn get(input: &mut &[u8]) -> Result<Self, TypeInfoError> {
120        let ty = DataType::get(input)?;
121
122        Ok(match ty {
123            DataType::Null => Self::new(ty, 0),
124
125            DataType::TinyInt | DataType::Bit => Self::new(ty, 1),
126
127            DataType::SmallInt => Self::new(ty, 2),
128
129            DataType::Int | DataType::SmallDateTime | DataType::Real | DataType::SmallMoney => {
130                Self::new(ty, 4)
131            }
132
133            DataType::BigInt | DataType::Money | DataType::DateTime | DataType::Float => {
134                Self::new(ty, 8)
135            }
136
137            DataType::DateN => Self::new(ty, 3),
138
139            DataType::TimeN | DataType::DateTime2N | DataType::DateTimeOffsetN => {
140                let scale = read_u8(input)?;
141
142                let mut size = match scale {
143                    0..=2 => 3,
144                    3..=4 => 4,
145                    5..=7 => 5,
146                    scale => {
147                        return Err(TypeInfoError::InvalidScale { ty, scale });
148                    }
149                };
150
151                match ty {
152                    DataType::DateTime2N => {
153                        size += 3;
154                    }
155                    DataType::DateTimeOffsetN => {
156                        size += 5;
157                    }
158                    _ => {}
159                }
160
161                Self {
162                    scale,
163                    size,
164                    ty,
165                    precision: 0,
166                    collation: None,
167                }
168            }
169
170            DataType::Guid
171            | DataType::IntN
172            | DataType::BitN
173            | DataType::FloatN
174            | DataType::MoneyN
175            | DataType::DateTimeN
176            | DataType::Char
177            | DataType::VarChar
178            | DataType::Binary
179            | DataType::VarBinary => Self::new(ty, u32::from(read_u8(input)?)),
180
181            DataType::Decimal | DataType::Numeric | DataType::DecimalN | DataType::NumericN => {
182                let size = u32::from(read_u8(input)?);
183                let precision = read_u8(input)?;
184                let scale = read_u8(input)?;
185
186                Self {
187                    size,
188                    precision,
189                    scale,
190                    ty,
191                    collation: None,
192                }
193            }
194
195            DataType::BigVarBinary | DataType::BigBinary => {
196                Self::new(ty, u32::from(read_u16_le(input)?))
197            }
198
199            DataType::BigVarChar | DataType::BigChar | DataType::NVarChar | DataType::NChar => {
200                let size = u32::from(read_u16_le(input)?);
201                let collation = Collation::get(input)?;
202
203                Self {
204                    ty,
205                    size,
206                    collation: Some(collation),
207                    scale: 0,
208                    precision: 0,
209                }
210            }
211
212            DataType::Xml
213            | DataType::UserDefined
214            | DataType::Text
215            | DataType::Image
216            | DataType::NText
217            | DataType::Variant => {
218                return Err(TypeInfoError::UnsupportedDataType(ty));
219            }
220        })
221    }
222
223    // writes a TYPE_INFO to the buffer
224    pub(crate) fn put(&self, out: &mut Vec<u8>) -> Result<(), BoxDynError> {
225        out.push(self.ty as u8);
226
227        match self.ty {
228            DataType::Null
229            | DataType::TinyInt
230            | DataType::Bit
231            | DataType::SmallInt
232            | DataType::Int
233            | DataType::SmallDateTime
234            | DataType::Real
235            | DataType::SmallMoney
236            | DataType::BigInt
237            | DataType::Money
238            | DataType::DateTime
239            | DataType::Float => {}
240
241            DataType::TimeN | DataType::DateTime2N | DataType::DateTimeOffsetN => {
242                out.push(self.scale);
243            }
244
245            DataType::Guid
246            | DataType::IntN
247            | DataType::BitN
248            | DataType::FloatN
249            | DataType::MoneyN
250            | DataType::DateTimeN
251            | DataType::DateN
252            | DataType::Char
253            | DataType::VarChar
254            | DataType::Binary
255            | DataType::VarBinary => {
256                out.push(u8::try_from(self.size)?);
257            }
258
259            DataType::Decimal | DataType::Numeric | DataType::DecimalN | DataType::NumericN => {
260                out.push(u8::try_from(self.size)?);
261                out.push(self.precision);
262                out.push(self.scale);
263            }
264
265            DataType::BigVarBinary | DataType::BigBinary => {
266                out.extend_from_slice(&u16::try_from(self.size)?.to_le_bytes());
267            }
268
269            DataType::BigVarChar | DataType::BigChar | DataType::NVarChar | DataType::NChar => {
270                out.extend_from_slice(&u16::try_from(self.size)?.to_le_bytes());
271
272                if let Some(collation) = &self.collation {
273                    collation.put(out);
274                } else {
275                    out.extend_from_slice(&0_u32.to_le_bytes());
276                    out.push(0);
277                }
278            }
279
280            DataType::Xml
281            | DataType::UserDefined
282            | DataType::Text
283            | DataType::Image
284            | DataType::NText
285            | DataType::Variant => {
286                log::error!("Unsupported mssql data type argument writing {:?}", self.ty);
287            }
288        }
289
290        Ok(())
291    }
292
293    pub(crate) fn is_null(&self) -> bool {
294        matches!(self.ty, DataType::Null)
295    }
296
297    pub(crate) fn is_nullable_or_variable_length(&self) -> bool {
298        !matches!(
299            self.ty,
300            DataType::Null
301                | DataType::TinyInt
302                | DataType::Bit
303                | DataType::SmallInt
304                | DataType::Int
305                | DataType::SmallDateTime
306                | DataType::Real
307                | DataType::Money
308                | DataType::DateTime
309                | DataType::Float
310                | DataType::SmallMoney
311                | DataType::BigInt
312        )
313    }
314
315    pub(crate) fn get_value(&self, input: &mut &[u8]) -> Result<Option<Vec<u8>>, TypeInfoError> {
316        Ok(match self.ty {
317            DataType::Null => None,
318
319            DataType::TinyInt
320            | DataType::Bit
321            | DataType::SmallInt
322            | DataType::Int
323            | DataType::SmallDateTime
324            | DataType::Real
325            | DataType::Money
326            | DataType::DateTime
327            | DataType::Float
328            | DataType::SmallMoney
329            | DataType::BigInt => Some(take(input, self.size as usize)?.to_vec()),
330
331            DataType::Guid
332            | DataType::IntN
333            | DataType::Decimal
334            | DataType::Numeric
335            | DataType::BitN
336            | DataType::DecimalN
337            | DataType::NumericN
338            | DataType::FloatN
339            | DataType::MoneyN
340            | DataType::DateN
341            | DataType::DateTimeN
342            | DataType::TimeN
343            | DataType::DateTime2N
344            | DataType::DateTimeOffsetN => {
345                let size = read_u8(input)?;
346
347                if size == 0 || size == 0xff {
348                    None
349                } else {
350                    Some(take(input, usize::from(size))?.to_vec())
351                }
352            }
353
354            DataType::Char | DataType::VarChar | DataType::Binary | DataType::VarBinary => {
355                let size = read_u8(input)?;
356                if size == 0xff {
357                    None
358                } else {
359                    Some(take(input, usize::from(size))?.to_vec())
360                }
361            }
362
363            DataType::BigVarBinary
364            | DataType::BigVarChar
365            | DataType::BigBinary
366            | DataType::BigChar
367            | DataType::NVarChar
368            | DataType::NChar
369            | DataType::Xml
370            | DataType::UserDefined => {
371                if self.size == 0xffff {
372                    self.get_big_blob(input)?
373                } else {
374                    let size = read_u16_le(input)?;
375                    if size == 0xffff {
376                        None
377                    } else {
378                        Some(take(input, usize::from(size))?.to_vec())
379                    }
380                }
381            }
382
383            DataType::Text | DataType::Image | DataType::NText | DataType::Variant => {
384                let size = read_u32_le(input)?;
385
386                if size == 0xffff_ffff {
387                    None
388                } else {
389                    Some(take(input, usize::try_from(size).unwrap())?.to_vec())
390                }
391            }
392        })
393    }
394
395    pub(crate) fn get_big_blob(&self, input: &mut &[u8]) -> Result<Option<Vec<u8>>, TypeInfoError> {
396        // Unknown size, length-prefixed blobs.
397        let len = read_u64_le(input)?;
398
399        let mut data = match len {
400            // NULL
401            0xffff_ffff_ffff_ffff => return Ok(None),
402            // Unknown size
403            0xffff_ffff_ffff_fffe => Vec::new(),
404            // Known size
405            _ => Vec::with_capacity(usize::try_from(len).unwrap()),
406        };
407
408        loop {
409            let chunk_size = read_u32_le(input)? as usize;
410
411            if chunk_size == 0 {
412                break;
413            }
414
415            data.extend_from_slice(take(input, chunk_size)?);
416        }
417
418        Ok(Some(data))
419    }
420
421    pub(crate) fn put_value<'q, T: Encode<'q, Mssql>>(
422        &self,
423        out: &mut Vec<u8>,
424        value: T,
425    ) -> Result<(), BoxDynError> {
426        match self.ty {
427            DataType::Null
428            | DataType::TinyInt
429            | DataType::Bit
430            | DataType::SmallInt
431            | DataType::Int
432            | DataType::SmallDateTime
433            | DataType::Real
434            | DataType::Money
435            | DataType::DateTime
436            | DataType::DateN
437            | DataType::Float
438            | DataType::SmallMoney
439            | DataType::BigInt => {
440                self.put_fixed_value(out, value)?;
441            }
442
443            DataType::Guid
444            | DataType::IntN
445            | DataType::Decimal
446            | DataType::Numeric
447            | DataType::BitN
448            | DataType::DecimalN
449            | DataType::NumericN
450            | DataType::FloatN
451            | DataType::MoneyN
452            | DataType::DateTimeN
453            | DataType::TimeN
454            | DataType::DateTime2N
455            | DataType::DateTimeOffsetN
456            | DataType::Char
457            | DataType::VarChar
458            | DataType::Binary
459            | DataType::VarBinary => {
460                self.put_byte_len_value(out, value)?;
461            }
462
463            DataType::BigVarBinary
464            | DataType::BigVarChar
465            | DataType::BigBinary
466            | DataType::BigChar
467            | DataType::NVarChar
468            | DataType::NChar
469            | DataType::Xml
470            | DataType::UserDefined => {
471                if self.size == 0xffff {
472                    self.put_big_blob(out, value)?;
473                } else {
474                    self.put_short_len_value(out, value)?;
475                }
476            }
477
478            DataType::Text | DataType::Image | DataType::NText | DataType::Variant => {
479                self.put_long_len_value(out, value)?;
480            }
481        }
482
483        Ok(())
484    }
485
486    pub(crate) fn put_fixed_value<'q, T: Encode<'q, Mssql>>(
487        &self,
488        out: &mut Vec<u8>,
489        value: T,
490    ) -> Result<(), BoxDynError> {
491        let _ = value.encode(out)?;
492        Ok(())
493    }
494
495    pub(crate) fn put_byte_len_value<'q, T: Encode<'q, Mssql>>(
496        &self,
497        out: &mut Vec<u8>,
498        value: T,
499    ) -> Result<(), BoxDynError> {
500        let offset = out.len();
501        out.push(0);
502
503        let size = if let IsNull::Yes = value.encode(out)? {
504            0xff
505        } else {
506            u8::try_from(out.len() - offset - 1)?
507        };
508
509        out[offset] = size;
510        Ok(())
511    }
512
513    pub(crate) fn put_short_len_value<'q, T: Encode<'q, Mssql>>(
514        &self,
515        out: &mut Vec<u8>,
516        value: T,
517    ) -> Result<(), BoxDynError> {
518        let offset = out.len();
519        out.extend_from_slice(&0_u16.to_le_bytes());
520
521        let size = if let IsNull::Yes = value.encode(out)? {
522            0xffff
523        } else {
524            u16::try_from(out.len() - offset - 2)?
525        };
526
527        out[offset..(offset + 2)].copy_from_slice(&size.to_le_bytes());
528        Ok(())
529    }
530
531    pub(crate) fn put_big_blob<'q, T: Encode<'q, Mssql>>(
532        &self,
533        out: &mut Vec<u8>,
534        value: T,
535    ) -> Result<(), BoxDynError> {
536        let mut value_buf = Vec::new();
537        if let IsNull::Yes = value.encode(&mut value_buf)? {
538            out.extend_from_slice(&PLP_NULL.to_le_bytes());
539            return Ok(());
540        }
541
542        out.extend_from_slice(&u64::try_from(value_buf.len())?.to_le_bytes());
543
544        for chunk in value_buf.chunks(PLP_CHUNK_SIZE) {
545            out.extend_from_slice(&u32::try_from(chunk.len())?.to_le_bytes());
546            out.extend_from_slice(chunk);
547        }
548
549        out.extend_from_slice(&0_u32.to_le_bytes());
550        Ok(())
551    }
552
553    pub(crate) fn put_long_len_value<'q, T: Encode<'q, Mssql>>(
554        &self,
555        out: &mut Vec<u8>,
556        value: T,
557    ) -> Result<(), BoxDynError> {
558        let offset = out.len();
559        out.extend_from_slice(&0_u32.to_le_bytes());
560
561        let size = if let IsNull::Yes = value.encode(out)? {
562            0xffff_ffff
563        } else {
564            u32::try_from(out.len() - offset - 4)?
565        };
566
567        out[offset..(offset + 4)].copy_from_slice(&size.to_le_bytes());
568        Ok(())
569    }
570
571    pub(crate) fn name(&self) -> &'static str {
572        match self.ty {
573            DataType::Null => "NULL",
574            DataType::TinyInt => "TINYINT",
575            DataType::SmallInt => "SMALLINT",
576            DataType::Int => "INT",
577            DataType::BigInt => "BIGINT",
578            DataType::Real => "REAL",
579            DataType::Float => "FLOAT",
580
581            DataType::IntN => match self.size {
582                1 => "TINYINT",
583                2 => "SMALLINT",
584                4 => "INT",
585                8 => "BIGINT",
586                n => unreachable!("invalid size {} for int", n),
587            },
588
589            DataType::FloatN => match self.size {
590                4 => "REAL",
591                8 => "FLOAT",
592                n => unreachable!("invalid size {} for float", n),
593            },
594
595            DataType::VarChar => "VARCHAR",
596            DataType::NVarChar => "NVARCHAR",
597            DataType::BigVarChar => "BIGVARCHAR",
598            DataType::Char => "CHAR",
599            DataType::BigChar => "BIGCHAR",
600            DataType::NChar => "NCHAR",
601            DataType::VarBinary => "VARBINARY",
602            DataType::BigVarBinary => "BIGVARBINARY",
603            DataType::Binary => "BINARY",
604            DataType::BigBinary => "BIGBINARY",
605            DataType::DateN => "DATE",
606            DataType::DateTimeN => "DATETIME",
607            DataType::DateTime2N => "DATETIME2",
608            DataType::DateTimeOffsetN => "DATETIMEOFFSET",
609
610            DataType::Bit => "BIT",
611            DataType::SmallDateTime => "SMALLDATETIME",
612            DataType::Money => "MONEY",
613            DataType::DateTime => "DATETIME",
614            DataType::SmallMoney => "SMALLMONEY",
615            DataType::Guid => "UNIQUEIDENTIFIER",
616            DataType::Decimal => "DECIMAL",
617            DataType::Numeric => "NUMERIC",
618            DataType::BitN => "BIT",
619            DataType::DecimalN => "DECIMAL",
620            DataType::NumericN => "NUMERIC",
621            DataType::MoneyN => "MONEY",
622            DataType::TimeN => "TIME",
623            DataType::Xml => "XML",
624            DataType::UserDefined => "USER_DEFINED_TYPE",
625            DataType::Text => "TEXT",
626            DataType::Image => "IMAGE",
627            DataType::NText => "NTEXT",
628            DataType::Variant => "SQL_VARIANT",
629        }
630    }
631
632    pub(crate) fn fmt(&self, out: &mut String) {
633        match self.ty {
634            DataType::Null => out.push_str("nvarchar(1)"),
635            DataType::TinyInt => out.push_str("tinyint"),
636            DataType::SmallInt => out.push_str("smallint"),
637            DataType::Int => out.push_str("int"),
638            DataType::BigInt => out.push_str("bigint"),
639            DataType::Real => out.push_str("real"),
640            DataType::Float => out.push_str("float"),
641            DataType::Bit => out.push_str("bit"),
642
643            DataType::IntN => out.push_str(match self.size {
644                1 => "tinyint",
645                2 => "smallint",
646                4 => "int",
647                8 => "bigint",
648                n => unreachable!("invalid size {} for int", n),
649            }),
650
651            DataType::FloatN => out.push_str(match self.size {
652                4 => "real",
653                8 => "float",
654                n => unreachable!("invalid size {} for float", n),
655            }),
656
657            DataType::NVarChar | DataType::NChar => {
658                out.push_str(match self.ty {
659                    DataType::NVarChar => "nvarchar",
660                    DataType::NChar => "nchar",
661                    _ => unreachable!(),
662                });
663
664                if self.size == 0xffff {
665                    out.push_str("(max)");
666                } else {
667                    let _ = write!(out, "({})", self.size / 2);
668                }
669            }
670
671            DataType::VarChar
672            | DataType::BigVarChar
673            | DataType::Char
674            | DataType::BigChar
675            | DataType::VarBinary
676            | DataType::BigVarBinary
677            | DataType::Binary
678            | DataType::BigBinary => {
679                out.push_str(match self.ty {
680                    DataType::VarChar => "varchar",
681                    DataType::BigVarChar => "bigvarchar",
682                    DataType::Char => "char",
683                    DataType::BigChar => "bigchar",
684                    DataType::VarBinary => "varbinary",
685                    DataType::BigVarBinary => "varbinary",
686                    DataType::Binary => "binary",
687                    DataType::BigBinary => "binary",
688                    _ => unreachable!(),
689                });
690
691                if self.size == 0xffff {
692                    out.push_str("(max)");
693                } else {
694                    let _ = write!(out, "({})", self.size);
695                }
696            }
697
698            DataType::BitN => {
699                out.push_str("bit");
700            }
701
702            DataType::DateN => {
703                out.push_str("date");
704            }
705
706            DataType::DateTime | DataType::DateTimeN => {
707                out.push_str("datetime");
708            }
709
710            DataType::DateTime2N => {
711                let _ = write!(out, "datetime2({})", self.scale);
712            }
713
714            DataType::DateTimeOffsetN => {
715                let _ = write!(out, "datetimeoffset({})", self.scale);
716            }
717
718            DataType::TimeN => {
719                let _ = write!(out, "time({})", self.scale);
720            }
721            DataType::SmallDateTime => out.push_str("smalldatetime"),
722            DataType::Money => out.push_str("money"),
723            DataType::SmallMoney => out.push_str("smallmoney"),
724            DataType::Guid => out.push_str("uniqueidentifier"),
725            DataType::Decimal => out.push_str("decimal"),
726            DataType::Numeric => out.push_str("numeric"),
727            DataType::DecimalN => {
728                let _ = write!(out, "decimal({},{})", self.precision, self.scale);
729            }
730            DataType::NumericN => {
731                let _ = write!(out, "numeric({},{})", self.precision, self.scale);
732            }
733            DataType::MoneyN => {
734                let _ = write!(out, "money({})", self.scale);
735            }
736            DataType::Xml => out.push_str("xml"),
737            DataType::UserDefined => out.push_str("user_defined_type"),
738            DataType::Text => out.push_str("text"),
739            DataType::Image => out.push_str("image"),
740            DataType::NText => out.push_str("ntext"),
741            DataType::Variant => out.push_str("sql_variant"),
742        }
743    }
744}
745
746impl DataType {
747    pub(crate) fn get(input: &mut &[u8]) -> Result<Self, TypeInfoError> {
748        Ok(match read_u8(input)? {
749            0x1f => DataType::Null,
750            0x30 => DataType::TinyInt,
751            0x32 => DataType::Bit,
752            0x34 => DataType::SmallInt,
753            0x38 => DataType::Int,
754            0x3a => DataType::SmallDateTime,
755            0x3b => DataType::Real,
756            0x3c => DataType::Money,
757            0x3d => DataType::DateTime,
758            0x3e => DataType::Float,
759            0x7a => DataType::SmallMoney,
760            0x7f => DataType::BigInt,
761            0x24 => DataType::Guid,
762            0x26 => DataType::IntN,
763            0x37 => DataType::Decimal,
764            0x3f => DataType::Numeric,
765            0x68 => DataType::BitN,
766            0x6a => DataType::DecimalN,
767            0x6c => DataType::NumericN,
768            0x6d => DataType::FloatN,
769            0x6e => DataType::MoneyN,
770            0x6f => DataType::DateTimeN,
771            0x28 => DataType::DateN,
772            0x29 => DataType::TimeN,
773            0x2a => DataType::DateTime2N,
774            0x2b => DataType::DateTimeOffsetN,
775            0x2f => DataType::Char,
776            0x27 => DataType::VarChar,
777            0x2d => DataType::Binary,
778            0x25 => DataType::VarBinary,
779            0xa5 => DataType::BigVarBinary,
780            0xa7 => DataType::BigVarChar,
781            0xad => DataType::BigBinary,
782            0xaf => DataType::BigChar,
783            0xe7 => DataType::NVarChar,
784            0xef => DataType::NChar,
785            0xf1 => DataType::Xml,
786            0xf0 => DataType::UserDefined,
787            0x23 => DataType::Text,
788            0x22 => DataType::Image,
789            0x63 => DataType::NText,
790            0x62 => DataType::Variant,
791            ty => return Err(TypeInfoError::UnknownDataType(ty)),
792        })
793    }
794}
795
796impl Collation {
797    pub(crate) fn get(input: &mut &[u8]) -> Result<Collation, TypeInfoError> {
798        let locale_sort_version = read_u32_le(input)?;
799        let locale = locale_sort_version & 0xfffff;
800        let flags = CollationFlags::from_bits_truncate(((locale_sort_version >> 20) & 0xff) as u8);
801        let version = (locale_sort_version >> 28) as u8;
802        let sort = read_u8(input)?;
803
804        Ok(Collation {
805            locale,
806            flags,
807            sort,
808            version,
809        })
810    }
811
812    pub(crate) fn put(&self, out: &mut Vec<u8>) {
813        let locale_sort_version =
814            self.locale | ((u32::from(self.flags.bits())) << 20) | ((self.version as u32) << 28);
815
816        out.extend_from_slice(&locale_sort_version.to_le_bytes());
817        out.push(self.sort);
818    }
819}
820
821#[derive(Debug, thiserror::Error, PartialEq, Eq)]
822pub(crate) enum TypeInfoError {
823    #[error("TDS TYPE_INFO ended unexpectedly")]
824    UnexpectedEof,
825    #[error("unknown TDS data type 0x{0:02x}")]
826    UnknownDataType(u8),
827    #[error("unsupported TDS data type {0:?}")]
828    UnsupportedDataType(DataType),
829    #[error("invalid scale {scale} for type {ty:?}")]
830    InvalidScale { ty: DataType, scale: u8 },
831}
832
833fn read_u8(input: &mut &[u8]) -> Result<u8, TypeInfoError> {
834    let bytes = take(input, 1)?;
835    Ok(bytes[0])
836}
837
838fn read_u16_le(input: &mut &[u8]) -> Result<u16, TypeInfoError> {
839    let bytes = take(input, 2)?;
840    Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
841}
842
843fn read_u32_le(input: &mut &[u8]) -> Result<u32, TypeInfoError> {
844    let bytes = take(input, 4)?;
845    Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
846}
847
848fn read_u64_le(input: &mut &[u8]) -> Result<u64, TypeInfoError> {
849    let bytes = take(input, 8)?;
850    Ok(u64::from_le_bytes([
851        bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
852    ]))
853}
854
855fn take<'a>(input: &mut &'a [u8], len: usize) -> Result<&'a [u8], TypeInfoError> {
856    let bytes = input.get(..len).ok_or(TypeInfoError::UnexpectedEof)?;
857    *input = &input[len..];
858    Ok(bytes)
859}
860
861#[cfg(test)]
862mod tests {
863    use super::*;
864
865    #[test]
866    fn parses_type_info_and_value_with_old_wire_constants() {
867        let mut input = &[
868            DataType::IntN as u8,
869            4,
870            4,
871            1,
872            0,
873            0,
874            0,
875            0xfe,
876            0,
877            0,
878            0xe0,
879            0,
880            0,
881            0,
882            0,
883            0,
884            0,
885            0,
886            0,
887            0,
888        ][..];
889
890        let type_info = TypeInfo::get(&mut input).unwrap();
891
892        assert_eq!(type_info, TypeInfo::new(DataType::IntN, 4));
893        assert_eq!(
894            Some(vec![1, 0, 0, 0]),
895            type_info.get_value(&mut input).unwrap()
896        );
897    }
898
899    #[test]
900    fn put_round_trips_collation_for_nvarchar_type_info() {
901        let type_info = TypeInfo {
902            ty: DataType::NVarChar,
903            size: 8,
904            scale: 0,
905            precision: 0,
906            collation: Some(Collation {
907                locale: 0x0409,
908                flags: CollationFlags::IGNORE_CASE,
909                sort: 52,
910                version: 0,
911            }),
912        };
913
914        let mut out = Vec::new();
915        type_info.put(&mut out).unwrap();
916        let mut input = out.as_slice();
917
918        assert_eq!(type_info, TypeInfo::get(&mut input).unwrap());
919        assert!(input.is_empty());
920    }
921
922    #[test]
923    fn put_value_uses_old_byte_length_null_sentinel() {
924        let type_info = TypeInfo::new(DataType::IntN, 4);
925        let mut out = Vec::new();
926
927        type_info.put_value(&mut out, Option::<i32>::None).unwrap();
928
929        assert_eq!([0xff], out.as_slice());
930    }
931
932    #[test]
933    fn put_value_and_get_value_round_trip_max_nvarchar_blob() {
934        let type_info = TypeInfo::new(DataType::NVarChar, 0xffff);
935        let mut out = Vec::new();
936
937        type_info.put_value(&mut out, "hi").unwrap();
938
939        assert_eq!(&[4, 0, 0, 0, 0, 0, 0, 0], &out[..8]);
940        assert_eq!(&[4, 0, 0, 0], &out[8..12]);
941        assert_eq!(&[0, 0, 0, 0], &out[out.len() - 4..]);
942
943        let mut input = out.as_slice();
944        assert_eq!(
945            Some(vec![b'h', 0, b'i', 0]),
946            type_info.get_value(&mut input).unwrap()
947        );
948        assert!(input.is_empty());
949    }
950
951    #[test]
952    fn put_value_chunks_large_max_nvarchar_blob() {
953        let type_info = TypeInfo::new(DataType::NVarChar, 0xffff);
954        let value = "x".repeat(PLP_CHUNK_SIZE + 1);
955        let expected_len = value.encode_utf16().count() * 2;
956        let mut out = Vec::new();
957
958        type_info.put_value(&mut out, value.as_str()).unwrap();
959
960        let mut input = out.as_slice();
961        assert_eq!(expected_len as u64, read_u64_le(&mut input).unwrap());
962        let mut chunk_sizes = Vec::new();
963        loop {
964            let chunk_size = read_u32_le(&mut input).unwrap() as usize;
965            if chunk_size == 0 {
966                break;
967            }
968
969            chunk_sizes.push(chunk_size);
970            let _ = take(&mut input, chunk_size).unwrap();
971        }
972
973        assert_eq!(vec![PLP_CHUNK_SIZE, PLP_CHUNK_SIZE, 2], chunk_sizes);
974        assert!(input.is_empty());
975
976        let mut input = out.as_slice();
977        assert_eq!(
978            expected_len,
979            type_info.get_value(&mut input).unwrap().unwrap().len()
980        );
981        assert!(input.is_empty());
982    }
983
984    #[test]
985    fn put_value_encodes_null_max_nvarchar_blob() {
986        let type_info = TypeInfo::new(DataType::NVarChar, 0xffff);
987        let mut out = Vec::new();
988
989        type_info
990            .put_value(&mut out, Option::<String>::None)
991            .unwrap();
992
993        assert_eq!(PLP_NULL.to_le_bytes(), out.as_slice());
994
995        let mut input = out.as_slice();
996        assert_eq!(None, type_info.get_value(&mut input).unwrap());
997        assert!(input.is_empty());
998    }
999
1000    #[test]
1001    fn formats_tds_type_declarations_like_old_protocol_module() {
1002        let mut out = String::new();
1003        TypeInfo::new(DataType::NVarChar, 12).fmt(&mut out);
1004
1005        assert_eq!("nvarchar(6)", out);
1006        assert_eq!("NVARCHAR", TypeInfo::new(DataType::NVarChar, 12).name());
1007    }
1008}