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 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 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 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 Text = 0x23,
92 Image = 0x22,
93 NText = 0x63,
94 Variant = 0x62,
95}
96
97#[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 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 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 let len = read_u64_le(input)?;
398
399 let mut data = match len {
400 0xffff_ffff_ffff_ffff => return Ok(None),
402 0xffff_ffff_ffff_fffe => Vec::new(),
404 _ => 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}