1#![allow(clippy::expect_used)]
8
9use bytes::{Buf, Bytes};
10
11use crate::error::TypeError;
12use crate::value::SqlValue;
13
14pub trait TdsDecode: Sized {
16 fn decode(buf: &mut Bytes, type_info: &TypeInfo) -> Result<Self, TypeError>;
18}
19
20#[derive(Debug, Clone)]
22pub struct TypeInfo {
23 pub type_id: u8,
25 pub length: Option<u32>,
27 pub scale: Option<u8>,
29 pub precision: Option<u8>,
31 pub collation: Option<Collation>,
33}
34
35#[derive(Debug, Clone, Copy)]
37pub struct Collation {
38 pub lcid: u32,
40 pub flags: u8,
42}
43
44impl TypeInfo {
45 #[must_use]
47 pub fn int(type_id: u8) -> Self {
48 Self {
49 type_id,
50 length: None,
51 scale: None,
52 precision: None,
53 collation: None,
54 }
55 }
56
57 #[must_use]
59 pub fn varchar(length: u32) -> Self {
60 Self {
61 type_id: 0xE7, length: Some(length),
63 scale: None,
64 precision: None,
65 collation: None,
66 }
67 }
68
69 #[must_use]
71 pub fn decimal(precision: u8, scale: u8) -> Self {
72 Self {
73 type_id: 0x6C,
74 length: None,
75 scale: Some(scale),
76 precision: Some(precision),
77 collation: None,
78 }
79 }
80
81 #[must_use]
83 pub fn datetime_with_scale(type_id: u8, scale: u8) -> Self {
84 Self {
85 type_id,
86 length: None,
87 scale: Some(scale),
88 precision: None,
89 collation: None,
90 }
91 }
92}
93
94pub fn decode_value(buf: &mut Bytes, type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
96 match type_info.type_id {
97 0x1F => Ok(SqlValue::Null), 0x32 => decode_bit(buf), 0x30 => decode_tinyint(buf), 0x34 => decode_smallint(buf), 0x38 => decode_int(buf), 0x7F => decode_bigint(buf), 0x3B => decode_float(buf), 0x3E => decode_double(buf), 0x26 => decode_intn(buf, type_info),
109
110 0xE7 => decode_nvarchar(buf, type_info), 0xAF => decode_varchar(buf, type_info), 0xA7 => decode_varchar(buf, type_info), 0xA5 => decode_varbinary(buf, type_info), 0xAD => decode_varbinary(buf, type_info), 0x24 => decode_guid(buf),
121
122 0x6C | 0x6A => decode_decimal(buf, type_info),
124
125 0x28 => decode_date(buf), 0x29 => decode_time(buf, type_info), 0x2A => decode_datetime2(buf, type_info), 0x2B => decode_datetimeoffset(buf, type_info), 0x3D => decode_datetime(buf), 0x3F => decode_smalldatetime(buf), 0xF1 => decode_xml(buf),
135
136 _ => Err(TypeError::UnsupportedConversion {
137 from: format!("TDS type 0x{:02X}", type_info.type_id),
138 to: "SqlValue",
139 }),
140 }
141}
142
143fn decode_bit(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
144 if buf.remaining() < 1 {
145 return Err(TypeError::BufferTooSmall {
146 needed: 1,
147 available: buf.remaining(),
148 });
149 }
150 Ok(SqlValue::Bool(buf.get_u8() != 0))
151}
152
153fn decode_tinyint(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
154 if buf.remaining() < 1 {
155 return Err(TypeError::BufferTooSmall {
156 needed: 1,
157 available: buf.remaining(),
158 });
159 }
160 Ok(SqlValue::TinyInt(buf.get_u8()))
161}
162
163fn decode_smallint(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
164 if buf.remaining() < 2 {
165 return Err(TypeError::BufferTooSmall {
166 needed: 2,
167 available: buf.remaining(),
168 });
169 }
170 Ok(SqlValue::SmallInt(buf.get_i16_le()))
171}
172
173fn decode_int(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
174 if buf.remaining() < 4 {
175 return Err(TypeError::BufferTooSmall {
176 needed: 4,
177 available: buf.remaining(),
178 });
179 }
180 Ok(SqlValue::Int(buf.get_i32_le()))
181}
182
183fn decode_bigint(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
184 if buf.remaining() < 8 {
185 return Err(TypeError::BufferTooSmall {
186 needed: 8,
187 available: buf.remaining(),
188 });
189 }
190 Ok(SqlValue::BigInt(buf.get_i64_le()))
191}
192
193fn decode_float(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
194 if buf.remaining() < 4 {
195 return Err(TypeError::BufferTooSmall {
196 needed: 4,
197 available: buf.remaining(),
198 });
199 }
200 Ok(SqlValue::Float(buf.get_f32_le()))
201}
202
203fn decode_double(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
204 if buf.remaining() < 8 {
205 return Err(TypeError::BufferTooSmall {
206 needed: 8,
207 available: buf.remaining(),
208 });
209 }
210 Ok(SqlValue::Double(buf.get_f64_le()))
211}
212
213fn decode_intn(buf: &mut Bytes, _type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
214 if buf.remaining() < 1 {
215 return Err(TypeError::BufferTooSmall {
216 needed: 1,
217 available: buf.remaining(),
218 });
219 }
220
221 let actual_len = buf.get_u8() as usize;
222 if actual_len == 0 {
223 return Ok(SqlValue::Null);
224 }
225
226 if buf.remaining() < actual_len {
227 return Err(TypeError::BufferTooSmall {
228 needed: actual_len,
229 available: buf.remaining(),
230 });
231 }
232
233 match actual_len {
234 1 => Ok(SqlValue::TinyInt(buf.get_u8())),
235 2 => Ok(SqlValue::SmallInt(buf.get_i16_le())),
236 4 => Ok(SqlValue::Int(buf.get_i32_le())),
237 8 => Ok(SqlValue::BigInt(buf.get_i64_le())),
238 _ => Err(TypeError::InvalidBinary(format!(
239 "invalid INTN length: {actual_len}"
240 ))),
241 }
242}
243
244fn decode_nvarchar(buf: &mut Bytes, _type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
245 if buf.remaining() < 2 {
246 return Err(TypeError::BufferTooSmall {
247 needed: 2,
248 available: buf.remaining(),
249 });
250 }
251
252 let byte_len = buf.get_u16_le() as usize;
253
254 if byte_len == 0xFFFF {
256 return Ok(SqlValue::Null);
257 }
258
259 if buf.remaining() < byte_len {
260 return Err(TypeError::BufferTooSmall {
261 needed: byte_len,
262 available: buf.remaining(),
263 });
264 }
265
266 let utf16_data = buf.copy_to_bytes(byte_len);
267 let s = decode_utf16_string(&utf16_data)?;
268 Ok(SqlValue::String(s))
269}
270
271fn decode_varchar(buf: &mut Bytes, _type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
272 if buf.remaining() < 2 {
273 return Err(TypeError::BufferTooSmall {
274 needed: 2,
275 available: buf.remaining(),
276 });
277 }
278
279 let byte_len = buf.get_u16_le() as usize;
280
281 if byte_len == 0xFFFF {
283 return Ok(SqlValue::Null);
284 }
285
286 if buf.remaining() < byte_len {
287 return Err(TypeError::BufferTooSmall {
288 needed: byte_len,
289 available: buf.remaining(),
290 });
291 }
292
293 let data = buf.copy_to_bytes(byte_len);
294 let s =
295 String::from_utf8(data.to_vec()).map_err(|e| TypeError::InvalidEncoding(e.to_string()))?;
296 Ok(SqlValue::String(s))
297}
298
299fn decode_varbinary(buf: &mut Bytes, _type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
300 if buf.remaining() < 2 {
301 return Err(TypeError::BufferTooSmall {
302 needed: 2,
303 available: buf.remaining(),
304 });
305 }
306
307 let byte_len = buf.get_u16_le() as usize;
308
309 if byte_len == 0xFFFF {
311 return Ok(SqlValue::Null);
312 }
313
314 if buf.remaining() < byte_len {
315 return Err(TypeError::BufferTooSmall {
316 needed: byte_len,
317 available: buf.remaining(),
318 });
319 }
320
321 let data = buf.copy_to_bytes(byte_len);
322 Ok(SqlValue::Binary(data))
323}
324
325#[cfg(feature = "uuid")]
326fn decode_guid(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
327 if buf.remaining() < 1 {
328 return Err(TypeError::BufferTooSmall {
329 needed: 1,
330 available: buf.remaining(),
331 });
332 }
333
334 let len = buf.get_u8() as usize;
335 if len == 0 {
336 return Ok(SqlValue::Null);
337 }
338
339 if len != 16 {
340 return Err(TypeError::InvalidBinary(format!(
341 "invalid GUID length: {len}"
342 )));
343 }
344
345 if buf.remaining() < 16 {
346 return Err(TypeError::BufferTooSmall {
347 needed: 16,
348 available: buf.remaining(),
349 });
350 }
351
352 let mut bytes = [0u8; 16];
354
355 bytes[3] = buf.get_u8();
357 bytes[2] = buf.get_u8();
358 bytes[1] = buf.get_u8();
359 bytes[0] = buf.get_u8();
360
361 bytes[5] = buf.get_u8();
363 bytes[4] = buf.get_u8();
364
365 bytes[7] = buf.get_u8();
367 bytes[6] = buf.get_u8();
368
369 for byte in &mut bytes[8..16] {
371 *byte = buf.get_u8();
372 }
373
374 Ok(SqlValue::Uuid(uuid::Uuid::from_bytes(bytes)))
375}
376
377#[cfg(not(feature = "uuid"))]
378fn decode_guid(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
379 if buf.remaining() < 1 {
381 return Err(TypeError::BufferTooSmall {
382 needed: 1,
383 available: buf.remaining(),
384 });
385 }
386
387 let len = buf.get_u8() as usize;
388 if len == 0 {
389 return Ok(SqlValue::Null);
390 }
391
392 if buf.remaining() < len {
393 return Err(TypeError::BufferTooSmall {
394 needed: len,
395 available: buf.remaining(),
396 });
397 }
398
399 let data = buf.copy_to_bytes(len);
400 Ok(SqlValue::Binary(data))
401}
402
403#[cfg(feature = "decimal")]
404fn decode_decimal(buf: &mut Bytes, type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
405 use rust_decimal::Decimal;
406
407 if buf.remaining() < 1 {
408 return Err(TypeError::BufferTooSmall {
409 needed: 1,
410 available: buf.remaining(),
411 });
412 }
413
414 let len = buf.get_u8() as usize;
415 if len == 0 {
416 return Ok(SqlValue::Null);
417 }
418
419 if buf.remaining() < len {
420 return Err(TypeError::BufferTooSmall {
421 needed: len,
422 available: buf.remaining(),
423 });
424 }
425
426 let sign = buf.get_u8();
428 let remaining = len - 1;
429
430 let mut mantissa_bytes = [0u8; 16];
432 for byte in mantissa_bytes.iter_mut().take(remaining.min(16)) {
433 *byte = buf.get_u8();
434 }
435
436 let mantissa = u128::from_le_bytes(mantissa_bytes);
437 let scale = type_info.scale.unwrap_or(0) as u32;
438
439 let mut decimal = Decimal::from_i128_with_scale(mantissa as i128, scale);
440 if sign == 0 {
441 decimal.set_sign_negative(true);
442 }
443
444 Ok(SqlValue::Decimal(decimal))
445}
446
447#[cfg(not(feature = "decimal"))]
448fn decode_decimal(buf: &mut Bytes, _type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
449 if buf.remaining() < 1 {
451 return Err(TypeError::BufferTooSmall {
452 needed: 1,
453 available: buf.remaining(),
454 });
455 }
456
457 let len = buf.get_u8() as usize;
458 if len == 0 {
459 return Ok(SqlValue::Null);
460 }
461
462 if buf.remaining() < len {
463 return Err(TypeError::BufferTooSmall {
464 needed: len,
465 available: buf.remaining(),
466 });
467 }
468
469 buf.advance(len);
470 Ok(SqlValue::String("DECIMAL (feature disabled)".to_string()))
471}
472
473#[cfg(feature = "chrono")]
474fn decode_date(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
475 if buf.remaining() < 1 {
476 return Err(TypeError::BufferTooSmall {
477 needed: 1,
478 available: buf.remaining(),
479 });
480 }
481
482 let len = buf.get_u8() as usize;
483 if len == 0 {
484 return Ok(SqlValue::Null);
485 }
486
487 if len != 3 {
488 return Err(TypeError::InvalidDateTime(format!(
489 "invalid DATE length: {len}"
490 )));
491 }
492
493 if buf.remaining() < 3 {
494 return Err(TypeError::BufferTooSmall {
495 needed: 3,
496 available: buf.remaining(),
497 });
498 }
499
500 let days = buf.get_u8() as u32 | ((buf.get_u8() as u32) << 8) | ((buf.get_u8() as u32) << 16);
502
503 let base = chrono::NaiveDate::from_ymd_opt(1, 1, 1).expect("valid date");
504 let date = base + chrono::Duration::days(days as i64);
505
506 Ok(SqlValue::Date(date))
507}
508
509#[cfg(not(feature = "chrono"))]
510fn decode_date(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
511 if buf.remaining() < 1 {
512 return Err(TypeError::BufferTooSmall {
513 needed: 1,
514 available: buf.remaining(),
515 });
516 }
517
518 let len = buf.get_u8() as usize;
519 if len == 0 {
520 return Ok(SqlValue::Null);
521 }
522
523 if buf.remaining() < len {
524 return Err(TypeError::BufferTooSmall {
525 needed: len,
526 available: buf.remaining(),
527 });
528 }
529
530 buf.advance(len);
531 Ok(SqlValue::String("DATE (feature disabled)".to_string()))
532}
533
534#[cfg(feature = "chrono")]
535fn decode_time(buf: &mut Bytes, type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
536 let scale = type_info.scale.unwrap_or(7);
537 let time_len = time_bytes_for_scale(scale);
538
539 if buf.remaining() < 1 {
540 return Err(TypeError::BufferTooSmall {
541 needed: 1,
542 available: buf.remaining(),
543 });
544 }
545
546 let len = buf.get_u8() as usize;
547 if len == 0 {
548 return Ok(SqlValue::Null);
549 }
550
551 if buf.remaining() < len {
552 return Err(TypeError::BufferTooSmall {
553 needed: len,
554 available: buf.remaining(),
555 });
556 }
557
558 let mut time_bytes = [0u8; 8];
560 for byte in time_bytes.iter_mut().take(time_len) {
561 *byte = buf.get_u8();
562 }
563
564 let intervals = u64::from_le_bytes(time_bytes);
565 let time = intervals_to_time(intervals, scale);
566
567 Ok(SqlValue::Time(time))
568}
569
570#[cfg(not(feature = "chrono"))]
571fn decode_time(buf: &mut Bytes, _type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
572 if buf.remaining() < 1 {
573 return Err(TypeError::BufferTooSmall {
574 needed: 1,
575 available: buf.remaining(),
576 });
577 }
578
579 let len = buf.get_u8() as usize;
580 if len == 0 {
581 return Ok(SqlValue::Null);
582 }
583
584 if buf.remaining() < len {
585 return Err(TypeError::BufferTooSmall {
586 needed: len,
587 available: buf.remaining(),
588 });
589 }
590
591 buf.advance(len);
592 Ok(SqlValue::String("TIME (feature disabled)".to_string()))
593}
594
595#[cfg(feature = "chrono")]
596fn decode_datetime2(buf: &mut Bytes, type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
597 let scale = type_info.scale.unwrap_or(7);
598 let time_len = time_bytes_for_scale(scale);
599
600 if buf.remaining() < 1 {
601 return Err(TypeError::BufferTooSmall {
602 needed: 1,
603 available: buf.remaining(),
604 });
605 }
606
607 let len = buf.get_u8() as usize;
608 if len == 0 {
609 return Ok(SqlValue::Null);
610 }
611
612 if buf.remaining() < len {
613 return Err(TypeError::BufferTooSmall {
614 needed: len,
615 available: buf.remaining(),
616 });
617 }
618
619 let mut time_bytes = [0u8; 8];
621 for byte in time_bytes.iter_mut().take(time_len) {
622 *byte = buf.get_u8();
623 }
624 let intervals = u64::from_le_bytes(time_bytes);
625 let time = intervals_to_time(intervals, scale);
626
627 let days = buf.get_u8() as u32 | ((buf.get_u8() as u32) << 8) | ((buf.get_u8() as u32) << 16);
629 let base = chrono::NaiveDate::from_ymd_opt(1, 1, 1).expect("valid date");
630 let date = base + chrono::Duration::days(days as i64);
631
632 Ok(SqlValue::DateTime(date.and_time(time)))
633}
634
635#[cfg(not(feature = "chrono"))]
636fn decode_datetime2(buf: &mut Bytes, _type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
637 if buf.remaining() < 1 {
638 return Err(TypeError::BufferTooSmall {
639 needed: 1,
640 available: buf.remaining(),
641 });
642 }
643
644 let len = buf.get_u8() as usize;
645 if len == 0 {
646 return Ok(SqlValue::Null);
647 }
648
649 if buf.remaining() < len {
650 return Err(TypeError::BufferTooSmall {
651 needed: len,
652 available: buf.remaining(),
653 });
654 }
655
656 buf.advance(len);
657 Ok(SqlValue::String("DATETIME2 (feature disabled)".to_string()))
658}
659
660#[cfg(feature = "chrono")]
661fn decode_datetimeoffset(buf: &mut Bytes, type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
662 use chrono::TimeZone;
663
664 let scale = type_info.scale.unwrap_or(7);
665 let time_len = time_bytes_for_scale(scale);
666
667 if buf.remaining() < 1 {
668 return Err(TypeError::BufferTooSmall {
669 needed: 1,
670 available: buf.remaining(),
671 });
672 }
673
674 let len = buf.get_u8() as usize;
675 if len == 0 {
676 return Ok(SqlValue::Null);
677 }
678
679 if buf.remaining() < len {
680 return Err(TypeError::BufferTooSmall {
681 needed: len,
682 available: buf.remaining(),
683 });
684 }
685
686 let mut time_bytes = [0u8; 8];
688 for byte in time_bytes.iter_mut().take(time_len) {
689 *byte = buf.get_u8();
690 }
691 let intervals = u64::from_le_bytes(time_bytes);
692 let time = intervals_to_time(intervals, scale);
693
694 let days = buf.get_u8() as u32 | ((buf.get_u8() as u32) << 8) | ((buf.get_u8() as u32) << 16);
696 let base = chrono::NaiveDate::from_ymd_opt(1, 1, 1).expect("valid date");
697 let date = base + chrono::Duration::days(days as i64);
698
699 let offset_minutes = buf.get_i16_le();
701 let offset = chrono::FixedOffset::east_opt((offset_minutes as i32) * 60)
702 .ok_or_else(|| TypeError::InvalidDateTime(format!("invalid offset: {offset_minutes}")))?;
703
704 let datetime = offset
705 .from_local_datetime(&date.and_time(time))
706 .single()
707 .ok_or_else(|| TypeError::InvalidDateTime("ambiguous datetime".to_string()))?;
708
709 Ok(SqlValue::DateTimeOffset(datetime))
710}
711
712#[cfg(not(feature = "chrono"))]
713fn decode_datetimeoffset(buf: &mut Bytes, _type_info: &TypeInfo) -> Result<SqlValue, TypeError> {
714 if buf.remaining() < 1 {
715 return Err(TypeError::BufferTooSmall {
716 needed: 1,
717 available: buf.remaining(),
718 });
719 }
720
721 let len = buf.get_u8() as usize;
722 if len == 0 {
723 return Ok(SqlValue::Null);
724 }
725
726 if buf.remaining() < len {
727 return Err(TypeError::BufferTooSmall {
728 needed: len,
729 available: buf.remaining(),
730 });
731 }
732
733 buf.advance(len);
734 Ok(SqlValue::String(
735 "DATETIMEOFFSET (feature disabled)".to_string(),
736 ))
737}
738
739#[cfg(feature = "chrono")]
740fn decode_datetime(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
741 if buf.remaining() < 8 {
743 return Err(TypeError::BufferTooSmall {
744 needed: 8,
745 available: buf.remaining(),
746 });
747 }
748
749 let days = buf.get_i32_le();
750 let time_300ths = buf.get_u32_le();
751
752 let base = chrono::NaiveDate::from_ymd_opt(1900, 1, 1).expect("valid date");
753 let date = base + chrono::Duration::days(days as i64);
754
755 let total_ms = (time_300ths as u64 * 1000) / 300;
757 let secs = (total_ms / 1000) as u32;
758 let nanos = ((total_ms % 1000) * 1_000_000) as u32;
759
760 let time = chrono::NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos)
761 .ok_or_else(|| TypeError::InvalidDateTime("invalid DATETIME time".to_string()))?;
762
763 Ok(SqlValue::DateTime(date.and_time(time)))
764}
765
766#[cfg(not(feature = "chrono"))]
767fn decode_datetime(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
768 if buf.remaining() < 8 {
769 return Err(TypeError::BufferTooSmall {
770 needed: 8,
771 available: buf.remaining(),
772 });
773 }
774
775 buf.advance(8);
776 Ok(SqlValue::String("DATETIME (feature disabled)".to_string()))
777}
778
779#[cfg(feature = "chrono")]
780fn decode_smalldatetime(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
781 if buf.remaining() < 4 {
783 return Err(TypeError::BufferTooSmall {
784 needed: 4,
785 available: buf.remaining(),
786 });
787 }
788
789 let days = buf.get_u16_le();
790 let minutes = buf.get_u16_le();
791
792 let base = chrono::NaiveDate::from_ymd_opt(1900, 1, 1).expect("valid date");
793 let date = base + chrono::Duration::days(days as i64);
794
795 let time = chrono::NaiveTime::from_num_seconds_from_midnight_opt((minutes as u32) * 60, 0)
796 .ok_or_else(|| TypeError::InvalidDateTime("invalid SMALLDATETIME time".to_string()))?;
797
798 Ok(SqlValue::DateTime(date.and_time(time)))
799}
800
801#[cfg(not(feature = "chrono"))]
802fn decode_smalldatetime(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
803 if buf.remaining() < 4 {
804 return Err(TypeError::BufferTooSmall {
805 needed: 4,
806 available: buf.remaining(),
807 });
808 }
809
810 buf.advance(4);
811 Ok(SqlValue::String(
812 "SMALLDATETIME (feature disabled)".to_string(),
813 ))
814}
815
816fn decode_xml(buf: &mut Bytes) -> Result<SqlValue, TypeError> {
817 if buf.remaining() < 2 {
819 return Err(TypeError::BufferTooSmall {
820 needed: 2,
821 available: buf.remaining(),
822 });
823 }
824
825 let byte_len = buf.get_u16_le() as usize;
826
827 if byte_len == 0xFFFF {
828 return Ok(SqlValue::Null);
829 }
830
831 if buf.remaining() < byte_len {
832 return Err(TypeError::BufferTooSmall {
833 needed: byte_len,
834 available: buf.remaining(),
835 });
836 }
837
838 let utf16_data = buf.copy_to_bytes(byte_len);
839 let s = decode_utf16_string(&utf16_data)?;
840 Ok(SqlValue::Xml(s))
841}
842
843pub fn decode_utf16_string(data: &[u8]) -> Result<String, TypeError> {
845 if data.len() % 2 != 0 {
846 return Err(TypeError::InvalidEncoding(
847 "UTF-16 data must have even length".to_string(),
848 ));
849 }
850
851 let utf16: Vec<u16> = data
852 .chunks_exact(2)
853 .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
854 .collect();
855
856 String::from_utf16(&utf16).map_err(|e| TypeError::InvalidEncoding(e.to_string()))
857}
858
859#[cfg(feature = "chrono")]
861fn time_bytes_for_scale(scale: u8) -> usize {
862 match scale {
863 0..=2 => 3,
864 3..=4 => 4,
865 5..=7 => 5,
866 _ => 5, }
868}
869
870#[cfg(feature = "chrono")]
872fn intervals_to_time(intervals: u64, scale: u8) -> chrono::NaiveTime {
873 let nanos = match scale {
884 0 => intervals * 1_000_000_000,
885 1 => intervals * 100_000_000,
886 2 => intervals * 10_000_000,
887 3 => intervals * 1_000_000,
888 4 => intervals * 100_000,
889 5 => intervals * 10_000,
890 6 => intervals * 1_000,
891 7 => intervals * 100,
892 _ => intervals * 100,
893 };
894
895 let secs = (nanos / 1_000_000_000) as u32;
896 let nano_part = (nanos % 1_000_000_000) as u32;
897
898 chrono::NaiveTime::from_num_seconds_from_midnight_opt(secs, nano_part)
899 .unwrap_or_else(|| chrono::NaiveTime::from_hms_opt(0, 0, 0).expect("valid time"))
900}
901
902#[cfg(test)]
903#[allow(clippy::unwrap_used)]
904mod tests {
905 use super::*;
906
907 #[test]
908 fn test_decode_int() {
909 let mut buf = Bytes::from_static(&[42, 0, 0, 0]);
910 let type_info = TypeInfo::int(0x38);
911 let result = decode_value(&mut buf, &type_info).unwrap();
912 assert_eq!(result, SqlValue::Int(42));
913 }
914
915 #[test]
916 fn test_decode_utf16_string() {
917 let data = [0x41, 0x00, 0x42, 0x00];
919 let result = decode_utf16_string(&data).unwrap();
920 assert_eq!(result, "AB");
921 }
922
923 #[test]
924 fn test_decode_nvarchar() {
925 let mut buf = Bytes::from_static(&[4, 0, 0x41, 0x00, 0x42, 0x00]);
927 let type_info = TypeInfo::varchar(100);
928 let type_info = TypeInfo {
929 type_id: 0xE7,
930 ..type_info
931 };
932 let result = decode_value(&mut buf, &type_info).unwrap();
933 assert_eq!(result, SqlValue::String("AB".to_string()));
934 }
935
936 #[test]
937 fn test_decode_null_nvarchar() {
938 let mut buf = Bytes::from_static(&[0xFF, 0xFF]);
940 let type_info = TypeInfo {
941 type_id: 0xE7,
942 length: Some(100),
943 scale: None,
944 precision: None,
945 collation: None,
946 };
947 let result = decode_value(&mut buf, &type_info).unwrap();
948 assert_eq!(result, SqlValue::Null);
949 }
950}