1use super::protocol::{Reader, decode_ucs2};
22use crate::dialect::{Dialect, SqlServer};
23use crate::value::Value;
24use rustlavel_core::{Error, Json, Result};
25
26pub const NULLTYPE: u8 = 0x1F;
28pub const INT1TYPE: u8 = 0x30;
29pub const BITTYPE: u8 = 0x32;
30pub const INT2TYPE: u8 = 0x34;
31pub const INT4TYPE: u8 = 0x38;
32pub const DATETIM4TYPE: u8 = 0x3A;
33pub const FLT4TYPE: u8 = 0x3B;
34pub const MONEYTYPE: u8 = 0x3C;
35pub const DATETIMETYPE: u8 = 0x3D;
36pub const FLT8TYPE: u8 = 0x3E;
37pub const MONEY4TYPE: u8 = 0x7A;
38pub const INT8TYPE: u8 = 0x7F;
39
40pub const GUIDTYPE: u8 = 0x24;
42pub const INTNTYPE: u8 = 0x26;
43pub const DECIMALTYPE: u8 = 0x37;
44pub const NUMERICTYPE: u8 = 0x3F;
45pub const BITNTYPE: u8 = 0x68;
46pub const DECIMALNTYPE: u8 = 0x6A;
47pub const NUMERICNTYPE: u8 = 0x6C;
48pub const FLTNTYPE: u8 = 0x6D;
49pub const MONEYNTYPE: u8 = 0x6E;
50pub const DATETIMNTYPE: u8 = 0x6F;
51pub const DATENTYPE: u8 = 0x28;
52pub const TIMENTYPE: u8 = 0x29;
53pub const DATETIME2NTYPE: u8 = 0x2A;
54pub const DATETIMEOFFSETNTYPE: u8 = 0x2B;
55pub const CHARTYPE: u8 = 0x2F;
56pub const VARCHARTYPE: u8 = 0x27;
57pub const BINARYTYPE: u8 = 0x2D;
58pub const VARBINARYTYPE: u8 = 0x25;
59
60pub const BIGVARBINARYTYPE: u8 = 0xA5;
63pub const BIGVARCHARTYPE: u8 = 0xA7;
64pub const BIGBINARYTYPE: u8 = 0xAD;
65pub const BIGCHARTYPE: u8 = 0xAF;
66pub const NVARCHARTYPE: u8 = 0xE7;
67pub const NCHARTYPE: u8 = 0xEF;
68
69pub const IMAGETYPE: u8 = 0x22;
71pub const TEXTTYPE: u8 = 0x23;
72pub const NTEXTTYPE: u8 = 0x63;
73pub const SSVARIANTTYPE: u8 = 0x62;
74pub const XMLTYPE: u8 = 0xF1;
75
76const MAX_LENGTH: usize = 0xFFFF;
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub enum LengthStyle {
82 Fixed,
84 Byte,
86 Short,
88 Long,
90 Chunked,
94}
95
96#[derive(Debug, Clone, PartialEq, Eq)]
98pub struct TypeInfo {
99 pub kind: u8,
100 pub size: usize,
102 pub precision: u8,
103 pub scale: u8,
104 pub collation: Option<Collation>,
105 pub length_style: LengthStyle,
106}
107
108impl TypeInfo {
109 fn fixed(kind: u8, size: usize) -> TypeInfo {
110 TypeInfo {
111 kind,
112 size,
113 precision: 0,
114 scale: 0,
115 collation: None,
116 length_style: LengthStyle::Fixed,
117 }
118 }
119
120 fn with_style(kind: u8, size: usize, length_style: LengthStyle) -> TypeInfo {
121 TypeInfo { kind, size, precision: 0, scale: 0, collation: None, length_style }
122 }
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub struct Collation {
132 pub lcid: u32,
133 pub flags: u32,
134 pub sort_id: u8,
135}
136
137impl Collation {
138 fn parse(bytes: &[u8]) -> Collation {
139 let word = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
140 Collation { lcid: word & 0x000F_FFFF, flags: word, sort_id: bytes[4] }
141 }
142
143 pub fn is_utf8(&self) -> bool {
145 self.flags & 0x0400_0000 != 0
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Eq)]
151pub struct Column {
152 pub name: String,
153 pub type_info: TypeInfo,
154}
155
156pub fn parse_column_metadata(reader: &mut Reader<'_>) -> Result<Vec<Column>> {
158 let count = reader.u16()?;
159 if count == 0xFFFF {
161 return Ok(Vec::new());
162 }
163
164 let mut columns = Vec::with_capacity(count as usize);
165 for _ in 0..count {
166 reader.u32()?; reader.u16()?; let type_info = parse_type_info(reader)?;
169
170 if matches!(type_info.kind, TEXTTYPE | NTEXTTYPE | IMAGETYPE) {
173 let parts = reader.u8()?;
174 for _ in 0..parts {
175 reader.us_varchar()?;
176 }
177 }
178
179 columns.push(Column { name: reader.b_varchar()?, type_info });
180 }
181
182 Ok(columns)
183}
184
185pub fn parse_type_info(reader: &mut Reader<'_>) -> Result<TypeInfo> {
187 let kind = reader.u8()?;
188
189 Ok(match kind {
190 NULLTYPE => TypeInfo::fixed(kind, 0),
191 INT1TYPE | BITTYPE => TypeInfo::fixed(kind, 1),
192 INT2TYPE => TypeInfo::fixed(kind, 2),
193 INT4TYPE | FLT4TYPE | MONEY4TYPE | DATETIM4TYPE => TypeInfo::fixed(kind, 4),
194 INT8TYPE | FLT8TYPE | MONEYTYPE | DATETIMETYPE => TypeInfo::fixed(kind, 8),
195
196 GUIDTYPE | INTNTYPE | BITNTYPE | FLTNTYPE | MONEYNTYPE | DATETIMNTYPE | CHARTYPE
197 | VARCHARTYPE | BINARYTYPE | VARBINARYTYPE => {
198 let size = reader.u8()? as usize;
199 TypeInfo::with_style(kind, size, LengthStyle::Byte)
200 }
201
202 DECIMALTYPE | NUMERICTYPE | DECIMALNTYPE | NUMERICNTYPE => {
203 let size = reader.u8()? as usize;
204 TypeInfo {
205 kind,
206 size,
207 precision: reader.u8()?,
208 scale: reader.u8()?,
209 collation: None,
210 length_style: LengthStyle::Byte,
211 }
212 }
213
214 DATENTYPE => TypeInfo::with_style(kind, 3, LengthStyle::Byte),
217
218 TIMENTYPE | DATETIME2NTYPE | DATETIMEOFFSETNTYPE => TypeInfo {
219 kind,
220 size: 0,
221 precision: 0,
222 scale: reader.u8()?,
223 collation: None,
224 length_style: LengthStyle::Byte,
225 },
226
227 BIGVARBINARYTYPE | BIGBINARYTYPE => {
228 let size = reader.u16()? as usize;
229 TypeInfo::with_style(kind, size, length_for(size))
230 }
231
232 BIGVARCHARTYPE | BIGCHARTYPE | NVARCHARTYPE | NCHARTYPE => {
233 let size = reader.u16()? as usize;
234 let collation = Collation::parse(reader.take(5)?);
235 TypeInfo {
236 kind,
237 size,
238 precision: 0,
239 scale: 0,
240 collation: Some(collation),
241 length_style: length_for(size),
242 }
243 }
244
245 TEXTTYPE | NTEXTTYPE => {
246 let size = reader.u32()? as usize;
247 let collation = Collation::parse(reader.take(5)?);
248 TypeInfo {
249 kind,
250 size,
251 precision: 0,
252 scale: 0,
253 collation: Some(collation),
254 length_style: LengthStyle::Long,
255 }
256 }
257
258 IMAGETYPE => {
259 let size = reader.u32()? as usize;
260 TypeInfo::with_style(kind, size, LengthStyle::Long)
261 }
262
263 SSVARIANTTYPE => {
264 let size = reader.u32()? as usize;
265 TypeInfo::with_style(kind, size, LengthStyle::Long)
266 }
267
268 XMLTYPE => {
269 if reader.u8()? == 1 {
271 reader.b_varchar()?; reader.b_varchar()?; reader.us_varchar()?; }
275 TypeInfo::with_style(kind, MAX_LENGTH, LengthStyle::Chunked)
276 }
277
278 other => {
279 return Err(Error::Protocol(format!(
280 "TDS type 0x{other:02X} is not one this driver decodes"
281 )));
282 }
283 })
284}
285
286fn length_for(size: usize) -> LengthStyle {
287 if size == MAX_LENGTH { LengthStyle::Chunked } else { LengthStyle::Short }
288}
289
290pub fn read_row(reader: &mut Reader<'_>, columns: &[Column]) -> Result<Vec<Value>> {
292 columns.iter().map(|column| read_value(reader, &column.type_info)).collect()
293}
294
295pub fn read_nbc_row(reader: &mut Reader<'_>, columns: &[Column]) -> Result<Vec<Value>> {
302 let bitmap = reader.take(columns.len().div_ceil(8))?;
303
304 let mut values = Vec::with_capacity(columns.len());
305 for (index, column) in columns.iter().enumerate() {
306 let is_null = bitmap[index / 8] & (1 << (index % 8)) != 0;
308 values.push(if is_null {
309 Value::Null
310 } else {
311 read_value(reader, &column.type_info)?
312 });
313 }
314
315 Ok(values)
316}
317
318pub fn read_value(reader: &mut Reader<'_>, type_info: &TypeInfo) -> Result<Value> {
320 let bytes = match type_info.length_style {
321 LengthStyle::Fixed => {
322 if type_info.size == 0 {
323 return Ok(Value::Null);
324 }
325 reader.take(type_info.size)?.to_vec()
326 }
327 LengthStyle::Byte => match reader.u8()? {
328 0 => return Ok(Value::Null),
329 length => reader.take(length as usize)?.to_vec(),
330 },
331 LengthStyle::Short => match reader.u16()? {
332 0xFFFF => return Ok(Value::Null),
333 length => reader.take(length as usize)?.to_vec(),
334 },
335 LengthStyle::Long => match read_long(reader)? {
336 None => return Ok(Value::Null),
337 Some(bytes) => bytes,
338 },
339 LengthStyle::Chunked => match read_chunked(reader)? {
340 None => return Ok(Value::Null),
341 Some(bytes) => bytes,
342 },
343 };
344
345 decode(type_info, &bytes)
346}
347
348fn read_long(reader: &mut Reader<'_>) -> Result<Option<Vec<u8>>> {
350 let pointer = reader.u8()? as usize;
351 if pointer == 0 {
352 return Ok(None);
353 }
354 reader.skip(pointer)?;
355 reader.skip(8)?; let length = reader.u32()? as usize;
358 Ok(Some(reader.take(length)?.to_vec()))
359}
360
361fn read_chunked(reader: &mut Reader<'_>) -> Result<Option<Vec<u8>>> {
367 const NULL: u64 = 0xFFFF_FFFF_FFFF_FFFF;
368 const UNKNOWN: u64 = 0xFFFF_FFFF_FFFF_FFFE;
369
370 let total = reader.u64()?;
371 if total == NULL {
372 return Ok(None);
373 }
374
375 let mut out = if total == UNKNOWN {
376 Vec::new()
377 } else {
378 Vec::with_capacity(total.min(1 << 20) as usize)
379 };
380
381 loop {
382 if total == 0 && reader.remaining() < 4 {
387 return Ok(Some(out));
388 }
389 let length = reader.u32()? as usize;
390 if length == 0 {
391 return Ok(Some(out));
392 }
393 out.extend_from_slice(reader.take(length)?);
394 }
395}
396
397fn decode(type_info: &TypeInfo, bytes: &[u8]) -> Result<Value> {
399 Ok(match type_info.kind {
400 BITTYPE | BITNTYPE => boolean(bytes.first().copied().unwrap_or(0)),
401
402 INT1TYPE => Value::Int(bytes[0] as i64),
403 INT2TYPE => Value::Int(i16::from_le_bytes(fixed(bytes)?) as i64),
404 INT4TYPE => Value::Int(i32::from_le_bytes(fixed(bytes)?) as i64),
405 INT8TYPE => Value::Int(i64::from_le_bytes(fixed(bytes)?)),
406 INTNTYPE => match bytes.len() {
408 1 => Value::Int(bytes[0] as i64),
409 2 => Value::Int(i16::from_le_bytes(fixed(bytes)?) as i64),
410 4 => Value::Int(i32::from_le_bytes(fixed(bytes)?) as i64),
411 8 => Value::Int(i64::from_le_bytes(fixed(bytes)?)),
412 other => return Err(width("int", other)),
413 },
414
415 FLT4TYPE => Value::Float(f32::from_le_bytes(fixed(bytes)?) as f64),
416 FLT8TYPE => Value::Float(f64::from_le_bytes(fixed(bytes)?)),
417 FLTNTYPE => match bytes.len() {
418 4 => Value::Float(f32::from_le_bytes(fixed(bytes)?) as f64),
419 8 => Value::Float(f64::from_le_bytes(fixed(bytes)?)),
420 other => return Err(width("float", other)),
421 },
422
423 MONEYTYPE | MONEY4TYPE | MONEYNTYPE => Value::Text(money(bytes)?),
426
427 DECIMALTYPE | NUMERICTYPE | DECIMALNTYPE | NUMERICNTYPE => {
428 Value::Text(decimal(bytes, type_info.scale)?)
429 }
430
431 GUIDTYPE => Value::Text(uuid(bytes)?),
432
433 CHARTYPE | VARCHARTYPE | BIGCHARTYPE | BIGVARCHARTYPE | TEXTTYPE => {
434 Value::Text(decode_char(type_info.collation, bytes))
435 }
436
437 NCHARTYPE | NVARCHARTYPE | NTEXTTYPE | XMLTYPE => Value::Text(decode_ucs2(bytes)),
438
439 BINARYTYPE | VARBINARYTYPE | BIGBINARYTYPE | BIGVARBINARYTYPE | IMAGETYPE => {
440 Value::Bytes(bytes.to_vec())
441 }
442
443 DATENTYPE => Value::Text(date_text(days(bytes))),
444 TIMENTYPE => Value::Text(time_text(time_units(bytes), type_info.scale)),
445 DATETIME2NTYPE => Value::Text(datetime2_text(bytes, type_info.scale)?),
446 DATETIMEOFFSETNTYPE => Value::Text(datetimeoffset_text(bytes, type_info.scale)?),
447 DATETIMETYPE | DATETIM4TYPE | DATETIMNTYPE => Value::Text(legacy_datetime_text(bytes)?),
448
449 SSVARIANTTYPE => variant(bytes)?,
450
451 NULLTYPE => Value::Null,
452
453 other => {
454 return Err(Error::Protocol(format!(
455 "TDS type 0x{other:02X} arrived with no decoder"
456 )));
457 }
458 })
459}
460
461fn boolean(byte: u8) -> Value {
467 if SqlServer.booleans_are_integers() {
468 Value::Bool(byte != 0)
469 } else {
470 Value::Int(byte as i64)
471 }
472}
473
474fn fixed<const N: usize>(bytes: &[u8]) -> Result<[u8; N]> {
475 bytes
476 .get(..N)
477 .and_then(|slice| slice.try_into().ok())
478 .ok_or_else(|| Error::Protocol(format!("expected {N} bytes for a fixed-width value")))
479}
480
481fn width(name: &str, got: usize) -> Error {
482 Error::Protocol(format!("a {name} column arrived with an impossible width of {got} bytes"))
483}
484
485fn money(bytes: &[u8]) -> Result<String> {
487 let units: i64 = match bytes.len() {
488 4 => i32::from_le_bytes(fixed(bytes)?) as i64,
489 8 => {
490 let high = i32::from_le_bytes(fixed::<4>(&bytes[..4])?) as i64;
491 let low = u32::from_le_bytes(fixed::<4>(&bytes[4..])?) as i64;
492 (high << 32) | low
493 }
494 other => return Err(width("money", other)),
495 };
496
497 Ok(scaled(units as i128, 4))
498}
499
500fn decimal(bytes: &[u8], scale: u8) -> Result<String> {
503 if bytes.is_empty() {
504 return Err(Error::Protocol("a decimal value arrived with no sign byte".into()));
505 }
506
507 let positive = bytes[0] == 1;
508 let mut magnitude: i128 = 0;
509 for (index, byte) in bytes[1..].iter().enumerate() {
510 if index >= 16 {
511 return Err(width("decimal", bytes.len()));
512 }
513 magnitude |= (*byte as i128) << (index * 8);
514 }
515
516 Ok(scaled(if positive { magnitude } else { -magnitude }, scale))
517}
518
519fn scaled(units: i128, scale: u8) -> String {
521 if scale == 0 {
522 return units.to_string();
523 }
524
525 let divisor = 10i128.pow(scale as u32);
526 let sign = if units < 0 { "-" } else { "" };
527 let magnitude = units.unsigned_abs();
528
529 format!(
530 "{sign}{}.{:0width$}",
531 magnitude / divisor as u128,
532 magnitude % divisor as u128,
533 width = scale as usize
534 )
535}
536
537fn uuid(bytes: &[u8]) -> Result<String> {
541 if bytes.len() != 16 {
542 return Err(width("uniqueidentifier", bytes.len()));
543 }
544
545 Ok(format!(
546 "{:02X}{:02X}{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{}",
547 bytes[3],
548 bytes[2],
549 bytes[1],
550 bytes[0],
551 bytes[5],
552 bytes[4],
553 bytes[7],
554 bytes[6],
555 bytes[8],
556 bytes[9],
557 bytes[10..].iter().map(|b| format!("{b:02X}")).collect::<String>()
558 ))
559}
560
561fn decode_char(collation: Option<Collation>, bytes: &[u8]) -> String {
570 if collation.is_some_and(|c| c.is_utf8()) {
571 return String::from_utf8_lossy(bytes).into_owned();
572 }
573 match std::str::from_utf8(bytes) {
574 Ok(text) => text.to_string(),
575 Err(_) => bytes.iter().map(|byte| windows_1252(*byte)).collect(),
576 }
577}
578
579fn windows_1252(byte: u8) -> char {
582 const HIGH: [char; 32] = [
583 '€', '\u{81}', '‚', 'ƒ', '„', '…', '†', '‡', 'ˆ', '‰', 'Š', '‹', 'Œ', '\u{8D}', 'Ž',
584 '\u{8F}', '\u{90}', '‘', '’', '“', '”', '•', '–', '—', '˜', '™', 'š', '›', 'œ', '\u{9D}',
585 'ž', 'Ÿ',
586 ];
587
588 match byte {
589 0x80..=0x9F => HIGH[(byte - 0x80) as usize],
590 other => other as char,
591 }
592}
593
594const DAYS_0001_TO_1970: i64 = 719_162;
599
600const DAYS_1900_TO_1970: i64 = 25_567;
602
603fn days(bytes: &[u8]) -> i64 {
604 let mut total = 0i64;
605 for (index, byte) in bytes.iter().take(3).enumerate() {
606 total |= (*byte as i64) << (index * 8);
607 }
608 total
609}
610
611fn time_units(bytes: &[u8]) -> u64 {
613 let mut total = 0u64;
614 for (index, byte) in bytes.iter().enumerate() {
615 total |= (*byte as u64) << (index * 8);
616 }
617 total
618}
619
620fn civil_from_days(days_since_epoch: i64) -> (i64, u32, u32) {
623 let z = days_since_epoch + 719_468;
624 let era = z.div_euclid(146_097);
625 let day_of_era = z.rem_euclid(146_097);
626 let year_of_era =
627 (day_of_era - day_of_era / 1460 + day_of_era / 36_524 - day_of_era / 146_096) / 365;
628 let year = year_of_era + era * 400;
629 let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
630 let month_prime = (5 * day_of_year + 2) / 153;
631 let day = (day_of_year - (153 * month_prime + 2) / 5 + 1) as u32;
632 let month = if month_prime < 10 { month_prime + 3 } else { month_prime - 9 } as u32;
633
634 (if month <= 2 { year + 1 } else { year }, month, day)
635}
636
637fn date_text(day_count: i64) -> String {
638 let (year, month, day) = civil_from_days(day_count - DAYS_0001_TO_1970);
639 format!("{year:04}-{month:02}-{day:02}")
640}
641
642fn time_text(units: u64, scale: u8) -> String {
643 let per_second = 10u64.pow(scale as u32);
644 let seconds = units / per_second;
645 let fraction = units % per_second;
646
647 let base = format!(
648 "{:02}:{:02}:{:02}",
649 seconds / 3600,
650 (seconds / 60) % 60,
651 seconds % 60
652 );
653
654 if scale == 0 {
655 base
656 } else {
657 format!("{base}.{fraction:0width$}", width = scale as usize)
658 }
659}
660
661fn time_width(scale: u8) -> usize {
663 match scale {
664 0..=2 => 3,
665 3..=4 => 4,
666 _ => 5,
667 }
668}
669
670fn datetime2_text(bytes: &[u8], scale: u8) -> Result<String> {
671 let split = time_width(scale);
672 if bytes.len() < split + 3 {
673 return Err(width("datetime2", bytes.len()));
674 }
675
676 Ok(format!(
677 "{} {}",
678 date_text(days(&bytes[split..])),
679 time_text(time_units(&bytes[..split]), scale)
680 ))
681}
682
683fn datetimeoffset_text(bytes: &[u8], scale: u8) -> Result<String> {
684 let time_len = time_width(scale);
685 let split = time_len + 3;
686 if bytes.len() < split + 2 {
687 return Err(width("datetimeoffset", bytes.len()));
688 }
689
690 let offset = i16::from_le_bytes([bytes[split], bytes[split + 1]]) as i64;
695 let per_second = 10i64.pow(scale as u32);
696 let day = per_second * 86_400;
697
698 let shifted = time_units(&bytes[..time_len]) as i64 + offset * 60 * per_second;
699 let local_day = days(&bytes[time_len..split]) + shifted.div_euclid(day);
700 let local_time = shifted.rem_euclid(day) as u64;
701
702 let sign = if offset < 0 { '-' } else { '+' };
703 let minutes = offset.unsigned_abs();
704
705 Ok(format!(
706 "{} {} {sign}{:02}:{:02}",
707 date_text(local_day),
708 time_text(local_time, scale),
709 minutes / 60,
710 minutes % 60
711 ))
712}
713
714fn legacy_datetime_text(bytes: &[u8]) -> Result<String> {
717 match bytes.len() {
718 4 => {
719 let day = u16::from_le_bytes(fixed::<2>(&bytes[..2])?) as i64;
720 let minutes = u16::from_le_bytes(fixed::<2>(&bytes[2..])?) as u64;
721 Ok(format!(
722 "{} {}",
723 date_text(day + DAYS_0001_TO_1970 - DAYS_1900_TO_1970),
724 time_text(minutes * 60, 0)
725 ))
726 }
727 8 => {
728 let day = i32::from_le_bytes(fixed::<4>(&bytes[..4])?) as i64;
729 let ticks = u32::from_le_bytes(fixed::<4>(&bytes[4..])?) as u64;
730 let milliseconds = ticks * 1000 / 300;
733 Ok(format!(
734 "{} {}",
735 date_text(day + DAYS_0001_TO_1970 - DAYS_1900_TO_1970),
736 time_text(milliseconds, 3)
737 ))
738 }
739 other => Err(width("datetime", other)),
740 }
741}
742
743fn variant(bytes: &[u8]) -> Result<Value> {
749 if bytes.len() < 2 {
750 return Ok(Value::Null);
751 }
752
753 let kind = bytes[0];
754 let properties = bytes[1] as usize;
755 if bytes.len() < 2 + properties {
756 return Ok(Value::Null);
757 }
758 let (properties, data) = (&bytes[2..2 + properties], &bytes[2 + properties..]);
759
760 let type_info = match kind {
761 BITTYPE | INT1TYPE | INT2TYPE | INT4TYPE | INT8TYPE | FLT4TYPE | FLT8TYPE | MONEYTYPE
762 | MONEY4TYPE | GUIDTYPE | DATETIMETYPE | DATETIM4TYPE => {
763 TypeInfo::fixed(kind, data.len())
764 }
765 DECIMALNTYPE | NUMERICNTYPE if properties.len() >= 2 => TypeInfo {
766 kind,
767 size: data.len(),
768 precision: properties[0],
769 scale: properties[1],
770 collation: None,
771 length_style: LengthStyle::Byte,
772 },
773 BIGVARCHARTYPE | BIGCHARTYPE if properties.len() >= 5 => TypeInfo {
774 kind,
775 size: data.len(),
776 precision: 0,
777 scale: 0,
778 collation: Some(Collation::parse(properties)),
779 length_style: LengthStyle::Short,
780 },
781 NVARCHARTYPE | NCHARTYPE => TypeInfo::with_style(kind, data.len(), LengthStyle::Short),
782 BIGVARBINARYTYPE | BIGBINARYTYPE => {
783 TypeInfo::with_style(kind, data.len(), LengthStyle::Short)
784 }
785 DATENTYPE => TypeInfo::with_style(kind, 3, LengthStyle::Byte),
786 _ => return Ok(Value::Null),
787 };
788
789 decode(&type_info, data)
790}
791
792fn declared_type(value: &Value) -> &'static str {
799 match value {
800 Value::Bool(_) => "bit",
801 Value::Int(_) => "bigint",
802 Value::Float(_) => "float",
803 Value::Bytes(_) => "varbinary(max)",
804 Value::Null | Value::Text(_) | Value::Json(_) => "nvarchar(max)",
807 }
808}
809
810pub fn declare(params: &[Value]) -> String {
812 params
813 .iter()
814 .enumerate()
815 .map(|(index, value)| format!("@P{} {}", index + 1, declared_type(value)))
816 .collect::<Vec<_>>()
817 .join(", ")
818}
819
820pub fn encode(value: &Value) -> Vec<u8> {
822 match value {
823 Value::Bool(flag) => vec![BITNTYPE, 1, 1, u8::from(*flag)],
824
825 Value::Int(number) => {
826 let mut out = vec![INTNTYPE, 8, 8];
827 out.extend_from_slice(&number.to_le_bytes());
828 out
829 }
830
831 Value::Float(number) => {
832 let mut out = vec![FLTNTYPE, 8, 8];
833 out.extend_from_slice(&number.to_le_bytes());
834 out
835 }
836
837 Value::Bytes(bytes) => {
838 let mut out = vec![BIGVARBINARYTYPE];
839 out.extend_from_slice(&(MAX_LENGTH as u16).to_le_bytes());
840 out.extend_from_slice(&chunked(Some(bytes)));
841 out
842 }
843
844 Value::Null => nvarchar_max(None),
845 Value::Text(text) => nvarchar_max(Some(&utf16(text))),
846 Value::Json(json) => nvarchar_max(Some(&utf16(&json.to_string()))),
847 }
848}
849
850fn nvarchar_max(bytes: Option<&[u8]>) -> Vec<u8> {
851 let mut out = vec![NVARCHARTYPE];
852 out.extend_from_slice(&(MAX_LENGTH as u16).to_le_bytes());
853 out.extend_from_slice(&[0u8; 5]);
856 out.extend_from_slice(&chunked(bytes));
857 out
858}
859
860fn utf16(text: &str) -> Vec<u8> {
861 text.encode_utf16().flat_map(u16::to_le_bytes).collect()
862}
863
864fn chunked(bytes: Option<&[u8]>) -> Vec<u8> {
866 let Some(bytes) = bytes else {
867 return 0xFFFF_FFFF_FFFF_FFFFu64.to_le_bytes().to_vec();
868 };
869
870 let mut out = Vec::with_capacity(bytes.len() + 16);
871 out.extend_from_slice(&(bytes.len() as u64).to_le_bytes());
872 if !bytes.is_empty() {
873 out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
874 out.extend_from_slice(bytes);
875 }
876 out.extend_from_slice(&0u32.to_le_bytes()); out
878}
879
880pub fn as_json(value: &Value) -> Option<Json> {
885 match value {
886 Value::Text(text) => Json::parse(text).ok(),
887 _ => None,
888 }
889}
890
891#[cfg(test)]
892mod tests {
893 use super::*;
894
895 fn read(type_info_and_value: &[u8]) -> Value {
897 let mut reader = Reader::new(type_info_and_value);
898 let type_info = parse_type_info(&mut reader).expect("a type this driver knows");
899 read_value(&mut reader, &type_info).expect("a decodable value")
900 }
901
902 #[test]
903 fn decodes_every_width_of_integer() {
904 assert_eq!(read(&[INTNTYPE, 1, 1, 7]), Value::Int(7));
905 assert_eq!(read(&[INTNTYPE, 2, 2, 0xFF, 0xFF]), Value::Int(-1));
906 assert_eq!(read(&[INTNTYPE, 4, 4, 0x2A, 0, 0, 0]), Value::Int(42));
907 assert_eq!(
908 read(&[INTNTYPE, 8, 8, 0x00, 0x1A, 0x71, 0x18, 0x02, 0, 0, 0]),
909 Value::Int(9_000_000_000)
910 );
911 assert_eq!(read(&[INT4TYPE, 5, 0, 0, 0]), Value::Int(5));
913 }
914
915 #[test]
916 fn a_bit_column_comes_back_as_a_boolean_because_the_dialect_says_so() {
917 assert!(SqlServer.booleans_are_integers());
920 assert_eq!(read(&[BITNTYPE, 1, 1, 1]), Value::Bool(true));
921 assert_eq!(read(&[BITNTYPE, 1, 1, 0]), Value::Bool(false));
922 assert_eq!(read(&[BITTYPE, 1]), Value::Bool(true));
923 }
924
925 #[test]
926 fn decodes_floats_at_both_widths() {
927 let mut single = vec![FLTNTYPE, 8, 4];
928 single.extend_from_slice(&1.5f32.to_le_bytes());
929 assert_eq!(read(&single), Value::Float(1.5));
930
931 let mut double = vec![FLTNTYPE, 8, 8];
932 double.extend_from_slice(&(-0.25f64).to_le_bytes());
933 assert_eq!(read(&double), Value::Float(-0.25));
934 }
935
936 #[test]
937 fn a_zero_length_value_is_null_whatever_its_type() {
938 assert_eq!(read(&[INTNTYPE, 8, 0]), Value::Null);
939 assert_eq!(read(&[BITNTYPE, 1, 0]), Value::Null);
940
941 let mut nvarchar = vec![NVARCHARTYPE, 0x40, 0x00, 0, 0, 0, 0, 0];
942 nvarchar.extend_from_slice(&0xFFFFu16.to_le_bytes());
943 assert_eq!(read(&nvarchar), Value::Null);
944 }
945
946 #[test]
947 fn decodes_an_nvarchar_and_the_max_variant_that_arrives_in_chunks() {
948 let text: Vec<u8> = "héllo".encode_utf16().flat_map(u16::to_le_bytes).collect();
949
950 let mut plain = vec![NVARCHARTYPE, 0x40, 0x00, 0, 0, 0, 0, 0];
951 plain.extend_from_slice(&(text.len() as u16).to_le_bytes());
952 plain.extend_from_slice(&text);
953 assert_eq!(read(&plain), Value::Text("héllo".into()));
954
955 let mut chunked = vec![NVARCHARTYPE, 0xFF, 0xFF, 0, 0, 0, 0, 0];
957 chunked.extend_from_slice(&(text.len() as u64).to_le_bytes());
958 chunked.extend_from_slice(&4u32.to_le_bytes());
959 chunked.extend_from_slice(&text[..4]);
960 chunked.extend_from_slice(&((text.len() - 4) as u32).to_le_bytes());
961 chunked.extend_from_slice(&text[4..]);
962 chunked.extend_from_slice(&0u32.to_le_bytes());
963 assert_eq!(read(&chunked), Value::Text("héllo".into()));
964 }
965
966 #[test]
967 fn a_varchar_is_decoded_against_its_collation() {
968 let mut latin = vec![BIGVARCHARTYPE, 0x40, 0x00, 0x09, 0x04, 0xD0, 0x00, 0x34];
970 latin.extend_from_slice(&2u16.to_le_bytes());
971 latin.extend_from_slice(&[b'a', 0xE9]);
972 assert_eq!(read(&latin), Value::Text("aé".into()));
973
974 let mut utf8 = vec![BIGVARCHARTYPE, 0x40, 0x00, 0x09, 0x04, 0x00, 0x04, 0x34];
976 let bytes = "aé".as_bytes();
977 utf8.extend_from_slice(&(bytes.len() as u16).to_le_bytes());
978 utf8.extend_from_slice(bytes);
979 assert_eq!(read(&utf8), Value::Text("aé".into()));
980 }
981
982 #[test]
983 fn decodes_binary_including_the_max_variant() {
984 let mut plain = vec![BIGVARBINARYTYPE, 0x10, 0x00];
985 plain.extend_from_slice(&4u16.to_le_bytes());
986 plain.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
987 assert_eq!(read(&plain), Value::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF]));
988
989 let mut max = vec![BIGVARBINARYTYPE, 0xFF, 0xFF];
990 max.extend_from_slice(&2u64.to_le_bytes());
991 max.extend_from_slice(&2u32.to_le_bytes());
992 max.extend_from_slice(&[0x01, 0x02]);
993 max.extend_from_slice(&0u32.to_le_bytes());
994 assert_eq!(read(&max), Value::Bytes(vec![0x01, 0x02]));
995 }
996
997 #[test]
998 fn decimals_stay_text_so_the_precision_they_exist_for_survives() {
999 let mut bytes = vec![DECIMALNTYPE, 17, 38, 6, 17, 1];
1001 let mut magnitude = 12_345_678_901_234_567_890u128.to_le_bytes().to_vec();
1002 magnitude.truncate(16);
1003 bytes.extend_from_slice(&magnitude);
1004 assert_eq!(read(&bytes), Value::Text("12345678901234.567890".into()));
1005
1006 let mut negative = vec![DECIMALNTYPE, 5, 10, 4, 5, 0];
1008 negative.extend_from_slice(&25u32.to_le_bytes());
1009 assert_eq!(read(&negative), Value::Text("-0.0025".into()));
1010 }
1011
1012 #[test]
1013 fn money_keeps_its_four_decimal_places() {
1014 let mut bytes = vec![MONEYNTYPE, 8, 8];
1015 bytes.extend_from_slice(&0i32.to_le_bytes());
1017 bytes.extend_from_slice(&123_400u32.to_le_bytes());
1018 assert_eq!(read(&bytes), Value::Text("12.3400".into()));
1019 }
1020
1021 #[test]
1022 fn a_uniqueidentifier_swaps_the_first_three_groups() {
1023 let mut bytes = vec![GUIDTYPE, 16, 16];
1024 bytes.extend_from_slice(&[
1025 0x78, 0x56, 0x34, 0x12, 0x34, 0x12, 0x78, 0x56, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34,
1026 0x56, 0x78,
1027 ]);
1028
1029 assert_eq!(
1030 read(&bytes),
1031 Value::Text("12345678-1234-5678-9ABC-DEF012345678".into())
1032 );
1033 }
1034
1035 #[test]
1036 fn dates_and_times_come_back_as_iso_text() {
1037 let day = 739_856u32.to_le_bytes();
1039 assert_eq!(
1040 read(&[DATENTYPE, 3, day[0], day[1], day[2]]),
1041 Value::Text("2026-08-29".into())
1042 );
1043
1044 let units = (37_800u64 * 10_000_000).to_le_bytes();
1046 let mut time = vec![TIMENTYPE, 7, 5];
1047 time.extend_from_slice(&units[..5]);
1048 assert_eq!(read(&time), Value::Text("10:30:00.0000000".into()));
1049 }
1050
1051 #[test]
1052 fn a_datetime2_puts_the_time_before_the_date_on_the_wire() {
1053 let mut bytes = vec![DATETIME2NTYPE, 3, 7];
1054 let units = (3_723_004u64).to_le_bytes();
1056 bytes.extend_from_slice(&units[..4]);
1057 bytes.extend_from_slice(&739_856u32.to_le_bytes()[..3]);
1058
1059 assert_eq!(read(&bytes), Value::Text("2026-08-29 01:02:03.004".into()));
1060 }
1061
1062 #[test]
1063 fn a_datetimeoffset_is_rendered_in_the_zone_it_was_written_in() {
1064 let mut bytes = vec![DATETIMEOFFSETNTYPE, 0, 8];
1067 bytes.extend_from_slice(&23_523u64.to_le_bytes()[..3]);
1068 bytes.extend_from_slice(&739_856u32.to_le_bytes()[..3]);
1069 bytes.extend_from_slice(&(-330i16).to_le_bytes());
1070
1071 assert_eq!(read(&bytes), Value::Text("2026-08-29 01:02:03 -05:30".into()));
1072 }
1073
1074 #[test]
1075 fn a_datetimeoffset_that_crosses_midnight_moves_the_date_with_it() {
1076 let mut bytes = vec![DATETIMEOFFSETNTYPE, 0, 8];
1078 bytes.extend_from_slice(&1_800u64.to_le_bytes()[..3]);
1079 bytes.extend_from_slice(&739_856u32.to_le_bytes()[..3]);
1080 bytes.extend_from_slice(&(-330i16).to_le_bytes());
1081
1082 assert_eq!(read(&bytes), Value::Text("2026-08-28 19:00:00 -05:30".into()));
1083 }
1084
1085 #[test]
1086 fn the_legacy_datetime_types_are_decoded_too() {
1087 let mut long = vec![DATETIMNTYPE, 8, 8];
1089 long.extend_from_slice(&46_261i32.to_le_bytes());
1090 long.extend_from_slice(&(300u32 * 3600).to_le_bytes());
1091 assert_eq!(read(&long), Value::Text("2026-08-29 01:00:00.000".into()));
1092
1093 let mut short = vec![DATETIMNTYPE, 8, 4];
1095 short.extend_from_slice(&46_261u16.to_le_bytes());
1096 short.extend_from_slice(&90u16.to_le_bytes());
1097 assert_eq!(read(&short), Value::Text("2026-08-29 01:30:00".into()));
1098 }
1099
1100 #[test]
1101 fn a_row_decodes_every_column_in_order() {
1102 let columns = vec![
1103 Column { name: "id".into(), type_info: TypeInfo::with_style(INTNTYPE, 8, LengthStyle::Byte) },
1104 Column {
1105 name: "flag".into(),
1106 type_info: TypeInfo::with_style(BITNTYPE, 1, LengthStyle::Byte),
1107 },
1108 ];
1109
1110 let mut body = vec![8];
1111 body.extend_from_slice(&7i64.to_le_bytes());
1112 body.extend_from_slice(&[1, 1]);
1113
1114 let values = read_row(&mut Reader::new(&body), &columns).unwrap();
1115 assert_eq!(values, vec![Value::Int(7), Value::Bool(true)]);
1116 }
1117
1118 #[test]
1119 fn an_nbcrow_reads_its_null_bitmap_and_skips_the_columns_it_marks() {
1120 let columns: Vec<Column> = (0..9)
1123 .map(|index| Column {
1124 name: format!("c{index}"),
1125 type_info: TypeInfo::with_style(INTNTYPE, 8, LengthStyle::Byte),
1126 })
1127 .collect();
1128
1129 let mut body = vec![0b0000_0010, 0b0000_0001];
1130 for value in [0i64, 2, 3, 4, 5, 6, 7] {
1131 body.push(8);
1132 body.extend_from_slice(&value.to_le_bytes());
1133 }
1134
1135 let values = read_nbc_row(&mut Reader::new(&body), &columns).unwrap();
1136
1137 assert_eq!(values.len(), 9);
1138 assert_eq!(values[1], Value::Null);
1139 assert_eq!(values[8], Value::Null);
1140 assert_eq!(values[0], Value::Int(0));
1141 assert_eq!(values[7], Value::Int(7));
1142 }
1143
1144 #[test]
1145 fn column_metadata_names_every_column_and_its_type() {
1146 let mut body = Vec::new();
1147 body.extend_from_slice(&2u16.to_le_bytes());
1148
1149 body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&0u16.to_le_bytes()); body.extend_from_slice(&[INTNTYPE, 8]);
1152 body.push(2);
1153 body.extend("id".encode_utf16().flat_map(u16::to_le_bytes));
1154
1155 body.extend_from_slice(&0u32.to_le_bytes());
1156 body.extend_from_slice(&0u16.to_le_bytes());
1157 body.extend_from_slice(&[NVARCHARTYPE, 0xFF, 0xFF, 0, 0, 0, 0, 0]);
1158 body.push(4);
1159 body.extend("name".encode_utf16().flat_map(u16::to_le_bytes));
1160
1161 let columns = parse_column_metadata(&mut Reader::new(&body)).unwrap();
1162
1163 assert_eq!(columns.len(), 2);
1164 assert_eq!(columns[0].name, "id");
1165 assert_eq!(columns[1].name, "name");
1166 assert_eq!(columns[1].type_info.length_style, LengthStyle::Chunked);
1168 }
1169
1170 #[test]
1171 fn metadata_for_a_statement_that_returns_nothing_has_no_columns() {
1172 let body = 0xFFFFu16.to_le_bytes();
1173 assert!(parse_column_metadata(&mut Reader::new(&body)).unwrap().is_empty());
1174 }
1175
1176 #[test]
1177 fn a_type_this_driver_cannot_decode_is_named_in_the_error() {
1178 let error = parse_type_info(&mut Reader::new(&[0xF0])).unwrap_err().to_string();
1179 assert!(error.contains("0xF0"), "{error}");
1180 }
1181
1182 #[test]
1183 fn parameters_are_declared_with_the_widest_type_of_their_kind() {
1184 let declaration = declare(&[
1185 Value::Int(1),
1186 Value::Text("a".into()),
1187 Value::Bool(true),
1188 Value::Float(1.0),
1189 Value::Bytes(vec![1]),
1190 Value::Null,
1191 ]);
1192
1193 assert_eq!(
1194 declaration,
1195 "@P1 bigint, @P2 nvarchar(max), @P3 bit, @P4 float, \
1196 @P5 varbinary(max), @P6 nvarchar(max)"
1197 );
1198 assert_eq!(declare(&[]), "");
1199 }
1200
1201 #[test]
1202 fn an_encoded_parameter_round_trips_back_through_the_decoder() {
1203 for value in [
1206 Value::Int(-9_000_000_000),
1207 Value::Bool(true),
1208 Value::Bool(false),
1209 Value::Float(1.5),
1210 Value::Text("'; drop table users; --".into()),
1211 Value::Text(String::new()),
1212 Value::Bytes(vec![0xDE, 0xAD]),
1213 Value::Null,
1214 ] {
1215 let encoded = encode(&value);
1216 let mut reader = Reader::new(&encoded);
1217 let type_info = parse_type_info(&mut reader).unwrap();
1218 let decoded = read_value(&mut reader, &type_info).unwrap();
1219
1220 assert_eq!(decoded, value, "{value:?} did not survive encoding");
1221 assert!(reader.is_empty(), "{value:?} left bytes behind");
1222 }
1223 }
1224
1225 #[test]
1226 fn a_null_parameter_is_sent_as_a_typed_null_not_as_an_empty_string() {
1227 let encoded = encode(&Value::Null);
1228
1229 assert_eq!(encoded[0], NVARCHARTYPE);
1230 assert_eq!(&encoded[encoded.len() - 8..], &[0xFF; 8]);
1232 }
1233
1234 #[test]
1235 fn json_is_bound_as_the_nvarchar_sql_server_stores_it_in() {
1236 let json = Json::parse(r#"{"a":1}"#).unwrap();
1237 let encoded = encode(&Value::from(json.clone()));
1238
1239 assert_eq!(encoded[0], NVARCHARTYPE);
1240 match read(&encoded) {
1241 Value::Text(text) => assert_eq!(as_json(&Value::Text(text)).unwrap(), json),
1242 other => panic!("expected text, got {other:?}"),
1243 }
1244 }
1245
1246 #[test]
1247 fn civil_dates_are_exact_across_leap_years_and_centuries() {
1248 assert_eq!(civil_from_days(0), (1970, 1, 1));
1249 assert_eq!(civil_from_days(-1), (1969, 12, 31));
1250
1251 assert_eq!(date_text(DAYS_0001_TO_1970 + 11_016), "2000-02-29");
1254 assert_eq!(date_text(DAYS_0001_TO_1970 - 25_508), "1900-03-01");
1255 assert_eq!(date_text(0), "0001-01-01");
1257 }
1258}