1mod binary;
2mod bit;
3mod bytes_mut_with_type_info;
4#[cfg(feature = "tds73")]
5mod date;
6#[cfg(feature = "tds73")]
7mod datetime2;
8mod datetimen;
9#[cfg(feature = "tds73")]
10mod datetimeoffsetn;
11mod fixed_len;
12mod float;
13mod guid;
14mod image;
15mod int;
16mod money;
17mod plp;
18mod sql_variant;
19mod string;
20mod text;
21#[cfg(feature = "tds73")]
22mod time;
23mod udt;
24mod var_len;
25mod xml;
26
27pub(crate) const MAX_PREALLOC: usize = 8192; pub(crate) const MAX_PLP_SIZE: usize = i32::MAX as usize;
46
47use super::{Encode, FixedLenType, TypeInfo, VarLenContext, VarLenType};
48use crate::tds::collation::Collation;
49#[cfg(feature = "tds73")]
50use crate::tds::time::{Date, DateTime2, DateTimeOffset, Time};
51use crate::{
52 tds::{time::DateTime, time::SmallDateTime, xml::XmlData, Numeric},
53 FromSql, FromSqlOwned, IntoSql, SqlReadBytes, ToSql,
54};
55use bytes::{BufMut, BytesMut};
56pub(crate) use bytes_mut_with_type_info::BytesMutWithTypeInfo;
57use std::borrow::{BorrowMut, Cow};
58use uuid::Uuid;
59
60const NVARCHAR_MAX_UNITS: usize = i32::MAX as usize / 2;
62
63const NUMERIC_PARAM_PRECISION: u8 = 38;
65
66fn fits_nvarchar_4000(s: &str) -> bool {
70 s.len() <= 4000 || (s.len() <= 12000 && s.encode_utf16().count() <= 4000)
71}
72
73const PLP_NULL: u64 = 0xffffffffffffffff;
74
75fn utf16_units_over(s: &str, max: usize) -> Option<usize> {
78 if s.len() <= max {
79 return None;
80 }
81 Some(s.encode_utf16().count()).filter(|&units| units > max)
82}
83
84fn lob_len(len: usize) -> Option<u32> {
87 i32::try_from(len).ok().map(i32::unsigned_abs)
88}
89
90pub(crate) fn put_plp_utf16(dst: &mut BytesMut, s: &str) -> crate::Result<()> {
93 let too_long = |units: usize| {
94 crate::Error::Conversion(
95 format!(
96 "a string of {units} UTF-16 code units is longer than nvarchar(max) and xml \
97 hold ({NVARCHAR_MAX_UNITS})"
98 )
99 .into(),
100 )
101 };
102 if let Some(units) = utf16_units_over(s, NVARCHAR_MAX_UNITS) {
104 return Err(too_long(units));
105 }
106
107 dst.put_u64_le(0xfffffffffffffffe); let len_pos = dst.len();
109 dst.put_u32_le(0);
110 for unit in s.encode_utf16() {
111 dst.put_u16_le(unit);
112 }
113
114 let bytes = dst.len() - len_pos - 4;
115 let len = lob_len(bytes).ok_or_else(|| too_long(bytes / 2))?;
116 dst[len_pos..len_pos + 4].copy_from_slice(&len.to_le_bytes());
117
118 if len > 0 {
120 dst.put_u32_le(0);
121 }
122
123 Ok(())
124}
125
126fn put_varchar(
132 dst: &mut BytesMutWithTypeInfo<'_>,
133 s: Option<&str>,
134 collation: Option<Collation>,
135 max: bool,
136) -> crate::Result<()> {
137 let collation = collation.ok_or_else(|| {
138 crate::Error::Encoding(
139 "a varchar parameter needs the session collation, which the server has not sent".into(),
140 )
141 })?;
142 let bytes = s
143 .map(|s| string::encode_varchar(s, &collation))
144 .transpose()?;
145
146 dst.put_u8(VarLenType::BigVarChar as u8);
147 dst.put_u16_le(if max { 0xffff } else { 8000 });
148 dst.put_u32_le(collation.info());
149 dst.put_u8(collation.sort_id());
150
151 match (bytes, max) {
152 (Some(bytes), false) => {
153 let len = u16::try_from(bytes.len()).map_err(|_| {
155 crate::Error::Encoding(
156 format!("{} bytes do not fit varchar(8000)", bytes.len()).into(),
157 )
158 })?;
159 dst.put_u16_le(len);
160 dst.extend_from_slice(&bytes);
161 }
162 (Some(bytes), true) => put_plp(dst, &bytes),
163 (None, false) => dst.put_u16_le(0xffff),
164 (None, true) => dst.put_u64_le(PLP_NULL),
165 }
166
167 Ok(())
168}
169
170fn put_nvarchar_max(dst: &mut BytesMut, s: Option<&str>) -> crate::Result<()> {
171 dst.put_u8(VarLenType::NVarchar as u8);
173 dst.extend_from_slice(&[0xff_u8; 2]);
174 dst.extend_from_slice(&[0u8; 5]);
175
176 match s {
177 Some(s) => put_plp_utf16(dst, s),
178 None => {
179 dst.put_u64_le(PLP_NULL);
180 Ok(())
181 }
182 }
183}
184
185fn put_varbinary_max(dst: &mut BytesMut, bytes: Option<&[u8]>) -> crate::Result<()> {
187 dst.put_u8(VarLenType::BigVarBin as u8);
188 dst.put_u16_le(0xffff_u16);
190
191 let Some(bytes) = bytes else {
192 dst.put_u64_le(PLP_NULL);
193 return Ok(());
194 };
195 let len = lob_len(bytes.len()).ok_or_else(|| {
196 crate::Error::Conversion(
197 format!(
198 "{} bytes are more than varbinary(max) holds ({})",
199 bytes.len(),
200 i32::MAX
201 )
202 .into(),
203 )
204 })?;
205
206 dst.put_u64_le(0xfffffffffffffffe_u64);
208 dst.put_u32_le(len);
209
210 if len > 0 {
211 dst.extend_from_slice(bytes);
212 dst.put_u32_le(0);
214 }
215
216 Ok(())
217}
218
219#[cfg(feature = "tds73")]
222const DAYS_YEAR_1_TO_1900: u32 = 693_595;
223
224const UNITS_PER_SECOND: u64 = 3_000_000_000;
227const UNITS_PER_DAY: u64 = 86_400 * UNITS_PER_SECOND;
228
229fn not_null_error() -> crate::Error {
230 crate::Error::BulkInput("NULL for a NOT NULL column".into())
231}
232
233fn encode_number(
236 data: &ColumnData<'_>,
237 ti: &TypeInfo,
238 dst: &mut BytesMutWithTypeInfo<'_>,
239) -> Option<crate::Result<()>> {
240 let (float, width, nullable) = match ti {
241 TypeInfo::FixedLen(FixedLenType::Int1) => (false, 1, false),
242 TypeInfo::FixedLen(FixedLenType::Int2) => (false, 2, false),
243 TypeInfo::FixedLen(FixedLenType::Int4) => (false, 4, false),
244 TypeInfo::FixedLen(FixedLenType::Int8) => (false, 8, false),
245 TypeInfo::FixedLen(FixedLenType::Float4) => (true, 4, false),
246 TypeInfo::FixedLen(FixedLenType::Float8) => (true, 8, false),
247 TypeInfo::VarLenSized(vlc) if vlc.r#type() == VarLenType::Intn => (false, vlc.len(), true),
248 TypeInfo::VarLenSized(vlc) if vlc.r#type() == VarLenType::Floatn => (true, vlc.len(), true),
249 _ => return None,
250 };
251
252 let bytes = if float {
253 let value = match *data {
254 ColumnData::F32(v) => v.map(f64::from),
255 ColumnData::F64(v) => v,
256 _ => return None,
257 };
258 value.map(|v| float_bytes(v, width))
259 } else {
260 let value = match *data {
261 ColumnData::U8(v) => v.map(i64::from),
262 ColumnData::I16(v) => v.map(i64::from),
263 ColumnData::I32(v) => v.map(i64::from),
264 ColumnData::I64(v) => v,
265 _ => return None,
266 };
267 value.map(|v| int_bytes(v, width))
268 };
269
270 Some(match bytes.transpose() {
271 Err(e) => Err(e),
272 Ok(None) if nullable => {
273 dst.put_u8(0);
274 Ok(())
275 }
276 Ok(None) => Err(not_null_error()),
277 Ok(Some(bytes)) => {
278 if nullable {
279 dst.put_u8(width as u8);
280 }
281 dst.extend_from_slice(&bytes[..width]);
282 Ok(())
283 }
284 })
285}
286
287fn int_bytes(value: i64, width: usize) -> crate::Result<[u8; 8]> {
288 let (name, fits) = match width {
289 1 => ("tinyint", (0..=255).contains(&value)),
290 2 => ("smallint", i16::try_from(value).is_ok()),
291 4 => ("int", i32::try_from(value).is_ok()),
292 8 => ("bigint", true),
293 _ => {
294 return Err(crate::Error::Protocol(
295 format!("integer column of {width} bytes").into(),
296 ))
297 }
298 };
299
300 if !fits {
301 return Err(crate::Error::Conversion(
302 format!("{value} is out of range for {name}").into(),
303 ));
304 }
305
306 Ok(value.to_le_bytes())
307}
308
309fn finite(value: f64) -> crate::Result<f64> {
312 if value.is_finite() {
313 Ok(value)
314 } else {
315 Err(crate::Error::Conversion(
316 format!("float: SQL Server has no NaN or infinity, got {value}").into(),
317 ))
318 }
319}
320
321fn float_bytes(value: f64, width: usize) -> crate::Result<[u8; 8]> {
322 let value = finite(value)?;
323 let mut bytes = [0u8; 8];
324
325 match width {
326 8 => bytes = value.to_le_bytes(),
327 4 => {
328 let narrowed = value as f32;
329 if value.is_finite() && narrowed.is_infinite() {
330 return Err(crate::Error::Conversion(
331 format!("{value:e} is out of range for real").into(),
332 ));
333 }
334 bytes[..4].copy_from_slice(&narrowed.to_le_bytes());
335 }
336 _ => {
337 return Err(crate::Error::Protocol(
338 format!("float column of {width} bytes").into(),
339 ))
340 }
341 }
342
343 Ok(bytes)
344}
345
346fn encode_legacy_datetime(
349 data: &ColumnData<'_>,
350 ti: &TypeInfo,
351 dst: &mut BytesMutWithTypeInfo<'_>,
352) -> Option<crate::Result<()>> {
353 let (small, nullable) = match ti {
354 TypeInfo::FixedLen(FixedLenType::Datetime) => (false, false),
355 TypeInfo::FixedLen(FixedLenType::Datetime4) => (true, false),
356 TypeInfo::VarLenSized(vlc) if vlc.r#type() == VarLenType::Datetimen => {
357 (vlc.len() == 4, true)
358 }
359 _ => return None,
360 };
361
362 let parts = match *data {
364 ColumnData::DateTime(v) => v.map(|v| {
366 v.validate()?;
367 Ok((
368 i64::from(v.days()),
369 u64::from(v.seconds_fragments()) * (UNITS_PER_SECOND / 300),
370 ))
371 }),
372 ColumnData::SmallDateTime(v) => v.map(|v| {
374 v.validate()?;
375 Ok((
376 i64::from(v.days()),
377 u64::from(v.seconds_fragments()) * 60 * UNITS_PER_SECOND,
378 ))
379 }),
380 #[cfg(feature = "tds73")]
381 ColumnData::DateTime2(v) => v.map(datetime2_parts),
382 _ => return None,
383 };
384
385 Some(match parts.transpose() {
386 Err(e) => Err(e),
387 Ok(None) if nullable => {
388 dst.put_u8(0);
389 Ok(())
390 }
391 Ok(None) => Err(not_null_error()),
392 Ok(Some((days, units))) if small => to_smalldatetime(days, units).and_then(|v| {
393 if nullable {
394 dst.put_u8(4);
395 }
396 v.encode(dst)
397 }),
398 Ok(Some((days, units))) => to_datetime(days, units).and_then(|v| {
399 if nullable {
400 dst.put_u8(8);
401 }
402 v.encode(dst)
403 }),
404 })
405}
406
407fn to_datetime(days: i64, units: u64) -> crate::Result<DateTime> {
409 const PER_FRAGMENT: u64 = UNITS_PER_SECOND / 300;
410 const FRAGMENTS_PER_DAY: u64 = UNITS_PER_DAY / PER_FRAGMENT;
411
412 let fragments = (units + PER_FRAGMENT / 2) / PER_FRAGMENT;
413 let days = days + (fragments / FRAGMENTS_PER_DAY) as i64;
414
415 if !(-53_690..=2_958_463).contains(&days) {
417 return Err(crate::Error::Conversion(
418 "value is outside the datetime range of 1753-01-01 to 9999-12-31".into(),
419 ));
420 }
421
422 Ok(DateTime::new(
423 days as i32,
424 (fragments % FRAGMENTS_PER_DAY) as u32,
425 ))
426}
427
428fn to_smalldatetime(days: i64, units: u64) -> crate::Result<SmallDateTime> {
430 const PER_MINUTE: u64 = 60 * UNITS_PER_SECOND;
431
432 let minutes = (units + PER_MINUTE / 2) / PER_MINUTE;
433 let days = days + (minutes / 1440) as i64;
434
435 let days = u16::try_from(days).map_err(|_| {
437 crate::Error::Conversion(
438 "value is outside the smalldatetime range of 1900-01-01 to 2079-06-06".into(),
439 )
440 })?;
441
442 Ok(SmallDateTime::new(days, (minutes % 1440) as u16))
443}
444
445#[cfg(feature = "tds73")]
446fn datetime2_parts(dt2: DateTime2) -> crate::Result<(i64, u64)> {
447 let time = rescale_time(dt2.time(), 7)?;
448 let days = i64::from(dt2.date().days()) - i64::from(DAYS_YEAR_1_TO_1900);
449
450 Ok((days, time.increments() * (UNITS_PER_SECOND / 10_000_000)))
451}
452
453#[cfg(feature = "tds73")]
460fn datetime2_for_column<'a>(data: ColumnData<'a>, ti: Option<&TypeInfo>) -> ColumnData<'a> {
461 match (data, ti) {
462 (ColumnData::DateTime2(v), Some(TypeInfo::VarLenSized(vlc)))
463 if vlc.r#type() == VarLenType::DatetimeOffsetn =>
464 {
465 ColumnData::DateTimeOffset(v.map(|v| DateTimeOffset::new(v, 0)))
466 }
467 (ColumnData::DateTime2(v), Some(TypeInfo::VarLenSized(vlc)))
468 if vlc.r#type() == VarLenType::Daten =>
469 {
470 ColumnData::Date(v.map(DateTime2::date))
471 }
472 (data, _) => data,
473 }
474}
475
476#[cfg(feature = "tds73")]
481fn rescale_time(time: Time, scale: u8) -> crate::Result<Time> {
482 time.validate()?;
483
484 if scale > 7 {
485 return Err(crate::Error::Conversion(
486 format!("time scale {scale} is invalid").into(),
487 ));
488 }
489
490 let increments = if scale >= time.scale() {
492 time.increments() * 10u64.pow(u32::from(scale - time.scale()))
493 } else {
494 time.increments() / 10u64.pow(u32::from(time.scale() - scale))
495 };
496
497 Ok(Time::new(increments, scale))
498}
499
500fn rescale_numeric(num: Numeric, precision: u8, scale: u8) -> crate::Result<Numeric> {
506 let out_of_range = || {
508 crate::Error::Conversion(
509 format!(
510 "{} at scale {} is out of range for decimal({precision},{scale})",
511 num.value(),
512 num.scale()
513 )
514 .into(),
515 )
516 };
517
518 let magnitude = num.value().unsigned_abs();
520 if magnitude >= 10u128.pow(38) || scale > 38 {
521 return Err(out_of_range());
522 }
523
524 let magnitude = match scale.checked_sub(num.scale()) {
525 Some(up) => magnitude.checked_mul(10u128.pow(u32::from(up))),
526 None => 10u128
527 .checked_pow(u32::from(num.scale() - scale))
528 .map(|f| magnitude / f + u128::from(magnitude % f >= f / 2)),
529 }
530 .filter(|&m| m < 10u128.pow(u32::from(precision.min(38))))
531 .ok_or_else(out_of_range)?;
532
533 let value = magnitude as i128;
535 Ok(Numeric::new_with_scale(
536 if num.value() < 0 { -value } else { value },
537 scale,
538 ))
539}
540
541fn column_limit(vlc: &VarLenContext) -> usize {
543 if vlc.len() < 0xffff {
544 vlc.len()
545 } else {
546 MAX_PLP_SIZE
547 }
548}
549
550fn put_plp(dst: &mut BytesMutWithTypeInfo<'_>, bytes: &[u8]) {
552 dst.put_u64_le(0xfffffffffffffffe); dst.put_u32_le(bytes.len() as u32);
554
555 if !bytes.is_empty() {
556 dst.extend_from_slice(bytes);
557 dst.put_u32_le(0); }
559}
560
561fn encode_in_collation(s: &str, vlc: &VarLenContext) -> crate::Result<Vec<u8>> {
563 let collation = vlc
564 .collation()
565 .ok_or_else(|| crate::Error::BulkInput("character column without a collation".into()))?;
566 string::encode_varchar(s, &collation)
567}
568
569#[derive(Clone, Debug, PartialEq)]
570#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
571pub enum ColumnData<'a> {
573 U8(Option<u8>),
575 I16(Option<i16>),
577 I32(Option<i32>),
579 I64(Option<i64>),
581 F32(Option<f32>),
583 F64(Option<f64>),
585 Bit(Option<bool>),
587 String(Option<Cow<'a, str>>),
590 VarChar(Option<Cow<'a, str>>),
601 Guid(Option<Uuid>),
603 Binary(Option<Cow<'a, [u8]>>),
605 Numeric(Option<Numeric>),
607 Xml(Option<Cow<'a, XmlData>>),
609 DateTime(Option<DateTime>),
611 SmallDateTime(Option<SmallDateTime>),
613 #[cfg(feature = "tds73")]
614 #[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
615 Time(Option<Time>),
617 #[cfg(feature = "tds73")]
618 #[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
619 Date(Option<Date>),
621 #[cfg(feature = "tds73")]
622 #[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
623 DateTime2(Option<DateTime2>),
625 #[cfg(feature = "tds73")]
626 #[cfg_attr(docsrs, doc(cfg(feature = "tds73")))]
627 DateTimeOffset(Option<DateTimeOffset>),
629}
630
631impl<'a> ColumnData<'a> {
632 pub(crate) fn type_name(&self) -> Cow<'static, str> {
633 match self {
634 ColumnData::U8(_) => "tinyint".into(),
635 ColumnData::I16(_) => "smallint".into(),
636 ColumnData::I32(_) => "int".into(),
637 ColumnData::I64(_) => "bigint".into(),
638 ColumnData::F32(_) => "float(24)".into(),
639 ColumnData::F64(_) => "float(53)".into(),
640 ColumnData::Bit(_) => "bit".into(),
641 ColumnData::String(None) => "nvarchar(4000)".into(),
642 ColumnData::String(Some(ref s)) if fits_nvarchar_4000(s) => "nvarchar(4000)".into(),
643 ColumnData::String(Some(_)) => "nvarchar(max)".into(),
645 ColumnData::VarChar(None) => "varchar(8000)".into(),
648 ColumnData::VarChar(Some(ref s)) if s.len() <= 8000 => "varchar(8000)".into(),
649 ColumnData::VarChar(Some(_)) => "varchar(max)".into(),
650 ColumnData::Guid(_) => "uniqueidentifier".into(),
651 ColumnData::Binary(None) => "varbinary(8000)".into(),
654 ColumnData::Binary(Some(ref b)) if b.len() <= 8000 => "varbinary(8000)".into(),
655 ColumnData::Binary(_) => "varbinary(max)".into(),
656 ColumnData::Numeric(n) => format!(
661 "numeric({},{})",
662 NUMERIC_PARAM_PRECISION,
663 n.map_or(0, Numeric::scale)
664 )
665 .into(),
666 ColumnData::Xml(_) => "xml".into(),
667 ColumnData::DateTime(_) => "datetime".into(),
668 ColumnData::SmallDateTime(_) => "smalldatetime".into(),
669 #[cfg(feature = "tds73")]
670 ColumnData::Time(_) => "time".into(),
671 #[cfg(feature = "tds73")]
672 ColumnData::Date(_) => "date".into(),
673 #[cfg(feature = "tds73")]
674 ColumnData::DateTime2(_) => "datetime2".into(),
675 #[cfg(feature = "tds73")]
676 ColumnData::DateTimeOffset(_) => "datetimeoffset".into(),
677 }
678 }
679
680 pub(crate) async fn decode<R>(src: &mut R, ctx: &TypeInfo) -> crate::Result<ColumnData<'a>>
681 where
682 R: SqlReadBytes + Unpin,
683 {
684 let res = match ctx {
685 TypeInfo::FixedLen(fixed_ty) => fixed_len::decode(src, fixed_ty).await?,
686 TypeInfo::VarLenSized(cx) => var_len::decode(src, cx).await?,
687 TypeInfo::VarLenSizedPrecision { ty, scale, .. } => match ty {
688 VarLenType::Decimaln | VarLenType::Numericn => {
689 ColumnData::Numeric(Numeric::decode(src, *scale).await?)
690 }
691 ty => {
694 return Err(crate::Error::Protocol(
695 format!("{ty:?} with precision and scale").into(),
696 ))
697 }
698 },
699 TypeInfo::Xml { schema, size } => xml::decode(src, *size, schema.clone()).await?,
700 TypeInfo::Udt(_) => udt::decode(src).await?,
701 };
702
703 Ok(res)
704 }
705}
706
707impl<'a> ColumnData<'a> {
708 pub(crate) fn encode_rpc_param(
718 self,
719 dst: &mut BytesMut,
720 out: bool,
721 collation: Option<Collation>,
722 ) -> crate::Result<()> {
723 match self {
724 ColumnData::String(s) if out => put_nvarchar_max(dst, s.as_deref())?,
725 ColumnData::VarChar(s) if out => put_varchar(
726 &mut BytesMutWithTypeInfo::new(dst),
727 s.as_deref(),
728 collation,
729 true,
730 )?,
731 ColumnData::Binary(b) if out => put_varbinary_max(dst, b.as_deref())?,
732 value => {
733 return value.encode(&mut BytesMutWithTypeInfo::new(dst).with_collation(collation))
734 }
735 }
736
737 Ok(())
738 }
739}
740
741impl<'a> Encode<BytesMutWithTypeInfo<'a>> for ColumnData<'a> {
742 fn encode(self, dst: &mut BytesMutWithTypeInfo<'a>) -> crate::Result<()> {
743 if let Some(ti) = dst.type_info() {
744 if let Some(res) = encode_number(&self, ti, dst) {
745 return res;
746 }
747 if let Some(res) = encode_legacy_datetime(&self, ti, dst) {
748 return res;
749 }
750 }
751
752 #[cfg(feature = "tds73")]
753 let data = datetime2_for_column(self, dst.type_info());
754 #[cfg(not(feature = "tds73"))]
755 let data = self;
756
757 let data = match (data, dst.type_info()) {
760 (ColumnData::VarChar(s), Some(_)) => ColumnData::String(s),
761 (data, _) => data,
762 };
763
764 match (data, dst.type_info()) {
765 (ColumnData::VarChar(opt), None) => {
766 let max = opt.as_ref().is_some_and(|s| s.len() > 8000);
767 put_varchar(dst, opt.as_deref(), dst.collation(), max)?;
768 }
769 (ColumnData::Bit(opt), Some(TypeInfo::VarLenSized(vlc)))
770 if vlc.r#type() == VarLenType::Bitn =>
771 {
772 if let Some(val) = opt {
773 dst.put_u8(1);
774 dst.put_u8(val as u8);
775 } else {
776 dst.put_u8(0);
777 }
778 }
779 (ColumnData::Bit(Some(val)), Some(TypeInfo::FixedLen(FixedLenType::Bit))) => {
780 dst.put_u8(val as u8);
781 }
782 (ColumnData::Bit(opt), None) => {
783 let header = [VarLenType::Bitn as u8, 1];
786 dst.extend_from_slice(&header);
787 if let Some(val) = opt {
788 dst.put_u8(1);
790 dst.put_u8(val as u8);
791 } else {
792 dst.put_u8(0);
793 }
794 }
795 (ColumnData::U8(opt), None) => {
796 let header = [VarLenType::Intn as u8, 1];
797 dst.extend_from_slice(&header);
798 if let Some(val) = opt {
799 dst.put_u8(1);
800 dst.put_u8(val);
801 } else {
802 dst.put_u8(0);
803 }
804 }
805 (ColumnData::I16(opt), None) => {
806 let header = [VarLenType::Intn as u8, 2];
807 dst.extend_from_slice(&header);
808 if let Some(val) = opt {
809 dst.put_u8(2);
810 dst.put_i16_le(val);
811 } else {
812 dst.put_u8(0);
813 }
814 }
815 (ColumnData::I32(opt), None) => {
816 let header = [VarLenType::Intn as u8, 4];
817 dst.extend_from_slice(&header);
818 if let Some(val) = opt {
819 dst.put_u8(4);
820 dst.put_i32_le(val);
821 } else {
822 dst.put_u8(0);
823 }
824 }
825 (ColumnData::I64(opt), None) => {
826 let header = [VarLenType::Intn as u8, 8];
827 dst.extend_from_slice(&header);
828 if let Some(val) = opt {
829 dst.put_u8(8);
830 dst.put_i64_le(val);
831 } else {
832 dst.put_u8(0);
833 }
834 }
835 (ColumnData::F32(opt), None) => {
836 let header = [VarLenType::Floatn as u8, 4];
837 dst.extend_from_slice(&header);
838 if let Some(val) = opt {
839 finite(val.into())?;
840 dst.put_u8(4);
841 dst.put_f32_le(val);
842 } else {
843 dst.put_u8(0);
844 }
845 }
846 (ColumnData::F64(opt), None) => {
847 let header = [VarLenType::Floatn as u8, 8];
848 dst.extend_from_slice(&header);
849 if let Some(val) = opt {
850 finite(val)?;
851 dst.put_u8(8);
852 dst.put_f64_le(val);
853 } else {
854 dst.put_u8(0);
855 }
856 }
857 (ColumnData::F64(opt), Some(TypeInfo::VarLenSized(vlc)))
858 if vlc.r#type() == VarLenType::Money =>
859 {
860 if let Some(val) = opt {
861 money::encode(dst, vlc.len(), val)?;
862 } else {
863 dst.put_u8(0);
864 }
865 }
866 (
867 ColumnData::F64(Some(val)),
868 Some(TypeInfo::FixedLen(ty @ (FixedLenType::Money | FixedLenType::Money4))),
869 ) => put_fixed_money(dst, *ty, |b, len| money::encode(b, len, val))?,
870 (
871 ColumnData::Numeric(Some(num)),
872 Some(TypeInfo::FixedLen(ty @ (FixedLenType::Money | FixedLenType::Money4))),
873 ) => put_fixed_money(dst, *ty, |b, len| money::encode_numeric(b, len, num))?,
874 (ColumnData::Guid(opt), Some(TypeInfo::VarLenSized(vlc)))
875 if vlc.r#type() == VarLenType::Guid =>
876 {
877 if let Some(uuid) = opt {
878 dst.put_u8(16);
879
880 let mut data = *uuid.as_bytes();
881 super::guid::reorder_bytes(&mut data);
882 dst.extend_from_slice(&data);
883 } else {
884 dst.put_u8(0);
885 }
886 }
887 (ColumnData::Guid(opt), None) => {
888 let header = [VarLenType::Guid as u8, 16];
889 dst.extend_from_slice(&header);
890 if let Some(uuid) = opt {
891 dst.put_u8(16);
892 let mut data = *uuid.as_bytes();
893 super::guid::reorder_bytes(&mut data);
894 dst.extend_from_slice(&data);
895 } else {
896 dst.put_u8(0);
897 }
898 }
899 (ColumnData::String(opt), Some(TypeInfo::VarLenSized(vlc)))
900 if vlc.r#type() == VarLenType::BigChar
901 || vlc.r#type() == VarLenType::BigVarChar =>
902 {
903 if let Some(str) = opt {
904 let bytes = encode_in_collation(&str, vlc)?;
905
906 if bytes.len() > column_limit(vlc) {
907 return Err(crate::Error::BulkInput(
908 format!(
909 "Encoded string length {} exceed column limit {}",
910 bytes.len(),
911 column_limit(vlc)
912 )
913 .into(),
914 ));
915 }
916
917 if vlc.len() < 0xffff {
918 dst.put_u16_le(bytes.len() as u16);
919 dst.extend_from_slice(bytes.as_slice());
920 } else {
921 put_plp(dst, &bytes);
922 }
923 } else if vlc.len() < 0xffff {
924 dst.put_u16_le(0xffff);
925 } else {
926 dst.put_u64_le(0xffffffffffffffff)
927 }
928 }
929 (ColumnData::String(opt), Some(TypeInfo::VarLenSized(vlc)))
930 if vlc.r#type() == VarLenType::NVarchar || vlc.r#type() == VarLenType::NChar =>
931 {
932 if let Some(str) = opt {
933 if vlc.len() < 0xffff {
934 let len_pos = dst.len();
935 dst.put_u16_le(0u16);
936
937 for chr in str.encode_utf16() {
938 dst.put_u16_le(chr);
939 }
940
941 let length = dst.len() - len_pos - 2;
942
943 if length > vlc.len() {
944 return Err(crate::Error::BulkInput(
945 format!(
946 "Encoded string length {} exceed column limit {}",
947 length,
948 vlc.len()
949 )
950 .into(),
951 ));
952 }
953
954 let dst: &mut [u8] = dst.borrow_mut();
955 let mut dst = &mut dst[len_pos..];
956 dst.put_u16_le(length as u16);
957 } else {
958 dst.put_u64_le(0xfffffffffffffffe);
960
961 let len_pos = dst.len();
962 dst.put_u32_le(0u32);
963
964 for chr in str.encode_utf16() {
965 dst.put_u16_le(chr);
966 }
967
968 let length = dst.len() - len_pos - 4;
969
970 if length > MAX_PLP_SIZE {
971 return Err(crate::Error::BulkInput(
972 format!(
973 "Encoded string length {} exceed column limit {}",
974 length, MAX_PLP_SIZE
975 )
976 .into(),
977 ));
978 }
979
980 if length > 0 {
981 dst.put_u32_le(0u32);
983 }
984
985 let dst: &mut [u8] = dst.borrow_mut();
986 let mut dst = &mut dst[len_pos..];
987 dst.put_u32_le(length as u32);
988 }
989 } else if vlc.len() < 0xffff {
990 dst.put_u16_le(0xffff);
991 } else {
992 dst.put_u64_le(0xffffffffffffffff)
993 }
994 }
995 (ColumnData::String(opt), Some(TypeInfo::VarLenSized(vlc)))
996 if vlc.r#type() == VarLenType::Text || vlc.r#type() == VarLenType::NText =>
997 {
998 if let Some(str) = opt {
999 if vlc.r#type() == VarLenType::Text {
1000 let bytes = encode_in_collation(&str, vlc)?;
1001 let len = lob_len(bytes.len()).ok_or_else(|| too_long(bytes.len()))?;
1002
1003 put_text_pointer(dst);
1004 dst.put_u32_le(len);
1005 dst.extend_from_slice(bytes.as_slice());
1006 } else {
1007 if let Some(units) = utf16_units_over(&str, NVARCHAR_MAX_UNITS) {
1009 return Err(too_long(2 * units));
1010 }
1011
1012 put_text_pointer(dst);
1013 let len_pos = dst.len();
1014 dst.put_u32_le(0u32);
1015 for chr in str.encode_utf16() {
1016 dst.put_u16_le(chr);
1017 }
1018
1019 let bytes = dst.len() - len_pos - 4;
1020 let len = lob_len(bytes).ok_or_else(|| too_long(bytes))?;
1021 dst[len_pos..len_pos + 4].copy_from_slice(&len.to_le_bytes());
1022 }
1023 } else {
1024 dst.put_u8(0);
1026 }
1027 }
1028 (ColumnData::Binary(opt), Some(TypeInfo::VarLenSized(vlc)))
1029 if vlc.r#type() == VarLenType::Image =>
1030 {
1031 if let Some(bytes) = opt {
1032 let len = lob_len(bytes.len()).ok_or_else(|| too_long(bytes.len()))?;
1033 put_text_pointer(dst);
1034 dst.put_u32_le(len);
1035 dst.extend_from_slice(&bytes);
1036 } else {
1037 dst.put_u8(0);
1038 }
1039 }
1040 (ColumnData::String(Some(ref s)), None) if fits_nvarchar_4000(s) => {
1041 dst.put_u8(VarLenType::NVarchar as u8);
1042 dst.put_u16_le(8000);
1043 dst.extend_from_slice(&[0u8; 5][..]);
1044
1045 let mut length = 0u16;
1046 let len_pos = dst.len();
1047
1048 dst.put_u16_le(length);
1049
1050 for chr in s.encode_utf16() {
1051 length += 1;
1052 dst.put_u16_le(chr);
1053 }
1054
1055 let dst: &mut [u8] = dst.borrow_mut();
1056 let bytes = (length * 2).to_le_bytes(); for (i, byte) in bytes.iter().enumerate() {
1059 dst[len_pos + i] = *byte;
1060 }
1061 }
1062 (ColumnData::String(Some(s)), None) => put_nvarchar_max(dst, Some(&s))?,
1063 (ColumnData::Binary(opt), Some(TypeInfo::VarLenSized(vlc)))
1064 if vlc.r#type() == VarLenType::BigBinary
1065 || vlc.r#type() == VarLenType::BigVarBin =>
1066 {
1067 if let Some(bytes) = opt {
1068 if bytes.len() > column_limit(vlc) {
1069 return Err(crate::Error::BulkInput(
1070 format!(
1071 "Binary length {} exceed column limit {}",
1072 bytes.len(),
1073 column_limit(vlc)
1074 )
1075 .into(),
1076 ));
1077 }
1078
1079 if vlc.len() < 0xffff {
1080 dst.put_u16_le(bytes.len() as u16);
1081 dst.extend_from_slice(&bytes);
1082 } else {
1083 put_plp(dst, &bytes);
1084 }
1085 } else if vlc.len() < 0xffff {
1086 dst.put_u16_le(0xffff);
1087 } else {
1088 dst.put_u64_le(0xffffffffffffffff);
1089 }
1090 }
1091 (ColumnData::Binary(Some(bytes)), None) if bytes.len() <= 8000 => {
1092 dst.put_u8(VarLenType::BigVarBin as u8);
1093 dst.put_u16_le(8000);
1094 dst.put_u16_le(bytes.len() as u16);
1095 dst.extend(bytes.into_owned());
1096 }
1097 (ColumnData::Binary(Some(bytes)), None) => put_varbinary_max(dst, Some(&bytes))?,
1098 (ColumnData::DateTime(Some(dt)), None) => {
1099 dst.extend_from_slice(&[VarLenType::Datetimen as u8, 8, 8]);
1100 dt.encode(&mut *dst)?;
1101 }
1102 (ColumnData::SmallDateTime(Some(dt)), None) => {
1103 dst.extend_from_slice(&[VarLenType::Datetimen as u8, 4, 4]);
1104 dt.encode(&mut *dst)?;
1105 }
1106 #[cfg(feature = "tds73")]
1107 (ColumnData::Date(opt), Some(TypeInfo::VarLenSized(vlc)))
1108 if vlc.r#type() == VarLenType::Daten =>
1109 {
1110 if let Some(dt) = opt {
1111 dst.put_u8(3);
1112 dt.encode(dst)?;
1113 } else {
1114 dst.put_u8(0);
1115 }
1116 }
1117 #[cfg(feature = "tds73")]
1118 (ColumnData::Date(Some(date)), None) => {
1119 dst.extend_from_slice(&[VarLenType::Daten as u8, 3]);
1120 date.encode(&mut *dst)?;
1121 }
1122 #[cfg(feature = "tds73")]
1123 (ColumnData::Time(opt), Some(TypeInfo::VarLenSized(vlc)))
1124 if vlc.r#type() == VarLenType::Timen =>
1125 {
1126 if let Some(time) = opt {
1127 let time = rescale_time(time, vlc.len() as u8)?;
1128 dst.put_u8(time.len()?);
1129 time.encode(dst)?;
1130 } else {
1131 dst.put_u8(0);
1132 }
1133 }
1134 #[cfg(feature = "tds73")]
1135 (ColumnData::Time(Some(time)), None) => {
1136 dst.extend_from_slice(&[VarLenType::Timen as u8, time.scale(), time.len()?]);
1137 time.encode(&mut *dst)?;
1138 }
1139 #[cfg(feature = "tds73")]
1140 (ColumnData::DateTime2(opt), Some(TypeInfo::VarLenSized(vlc)))
1141 if vlc.r#type() == VarLenType::Datetime2 =>
1142 {
1143 if let Some(dt2) = opt {
1144 let dt2 =
1145 DateTime2::new(dt2.date(), rescale_time(dt2.time(), vlc.len() as u8)?);
1146 dst.put_u8(dt2.time().len()? + 3);
1147 dt2.encode(dst)?;
1148 } else {
1149 dst.put_u8(0);
1150 }
1151 }
1152 #[cfg(feature = "tds73")]
1153 (ColumnData::DateTime2(Some(dt)), None) => {
1154 let len = dt.time().len()? + 3;
1155 dst.extend_from_slice(&[VarLenType::Datetime2 as u8, dt.time().scale(), len]);
1156 dt.encode(&mut *dst)?;
1157 }
1158 #[cfg(feature = "tds73")]
1159 (ColumnData::DateTimeOffset(opt), Some(TypeInfo::VarLenSized(vlc)))
1160 if vlc.r#type() == VarLenType::DatetimeOffsetn =>
1161 {
1162 if let Some(dto) = opt {
1163 let dt2 = dto.datetime2();
1164 let dt2 =
1165 DateTime2::new(dt2.date(), rescale_time(dt2.time(), vlc.len() as u8)?);
1166 let dto = DateTimeOffset::new(dt2, dto.offset());
1167 dst.put_u8(dto.datetime2().time().len()? + 5);
1168 dto.encode(dst)?;
1169 } else {
1170 dst.put_u8(0);
1171 }
1172 }
1173 #[cfg(feature = "tds73")]
1174 (ColumnData::DateTimeOffset(Some(dto)), None) => {
1175 let headers = [
1176 VarLenType::DatetimeOffsetn as u8,
1177 dto.datetime2().time().scale(),
1178 dto.datetime2().time().len()? + 5,
1179 ];
1180
1181 dst.extend_from_slice(&headers);
1182 dto.encode(&mut *dst)?;
1183 }
1184 (ColumnData::Xml(opt), Some(TypeInfo::Xml { .. })) => {
1185 if let Some(xml) = opt {
1186 xml.into_owned().encode(dst)?;
1187 } else {
1188 dst.put_u64_le(0xffffffffffffffff_u64);
1189 }
1190 }
1191 (ColumnData::String(opt), Some(TypeInfo::Xml { .. })) => match opt {
1194 Some(s) => put_plp_utf16(dst, &s)?,
1195 None => dst.put_u64_le(PLP_NULL),
1196 },
1197 (ColumnData::Xml(Some(xml)), None) => {
1198 dst.put_u8(VarLenType::Xml as u8);
1199 dst.put_u8(0);
1200 xml.into_owned().encode(&mut *dst)?;
1201 }
1202 (ColumnData::Numeric(opt), Some(TypeInfo::VarLenSized(vlc)))
1203 if vlc.r#type() == VarLenType::Money =>
1204 {
1205 if let Some(num) = opt {
1206 money::encode_numeric(dst, vlc.len(), num)?;
1207 } else {
1208 dst.put_u8(0);
1209 }
1210 }
1211 (
1212 ColumnData::Numeric(opt),
1213 Some(TypeInfo::VarLenSizedPrecision {
1214 ty,
1215 precision,
1216 scale,
1217 ..
1218 }),
1219 ) if ty == &VarLenType::Numericn || ty == &VarLenType::Decimaln => {
1220 if let Some(num) = opt {
1221 rescale_numeric(num, *precision, *scale)?.encode(&mut *dst)?;
1222 } else {
1223 dst.put_u8(0);
1224 }
1225 }
1226 (ColumnData::Numeric(Some(num)), None) => {
1227 let headers = &[
1230 VarLenType::Numericn as u8,
1231 17,
1232 NUMERIC_PARAM_PRECISION,
1233 num.scale(),
1234 ];
1235
1236 dst.extend_from_slice(headers);
1237 num.encode(&mut *dst)?;
1238 }
1239 (data, Some(TypeInfo::VarLenSized(vlc))) if vlc.r#type() == VarLenType::SSVariant => {
1240 sql_variant::encode(&mut *dst, data, vlc.collation())?;
1241 }
1242 (ColumnData::String(None), None) => {
1246 dst.put_u8(VarLenType::NVarchar as u8);
1247 dst.put_u16_le(8000);
1248 dst.extend_from_slice(&[0u8; 5]);
1249 dst.put_u16_le(0xffff);
1250 }
1251 (ColumnData::Binary(None), None) => {
1252 dst.put_u8(VarLenType::BigVarBin as u8);
1253 dst.put_u16_le(8000);
1254 dst.put_u16_le(0xffff);
1255 }
1256 (ColumnData::Numeric(None), None) => {
1257 dst.extend_from_slice(&[
1258 VarLenType::Numericn as u8,
1259 17,
1260 NUMERIC_PARAM_PRECISION,
1261 0,
1262 0,
1263 ]);
1264 }
1265 (ColumnData::Xml(None), None) => {
1266 dst.extend_from_slice(&[VarLenType::Xml as u8, 0]);
1267 dst.put_u64_le(PLP_NULL);
1268 }
1269 (ColumnData::DateTime(None), None) => {
1270 dst.extend_from_slice(&[VarLenType::Datetimen as u8, 8, 0]);
1271 }
1272 (ColumnData::SmallDateTime(None), None) => {
1273 dst.extend_from_slice(&[VarLenType::Datetimen as u8, 4, 0]);
1274 }
1275 #[cfg(feature = "tds73")]
1276 (ColumnData::Date(None), None) => {
1277 dst.extend_from_slice(&[VarLenType::Daten as u8, 0]);
1278 }
1279 #[cfg(feature = "tds73")]
1280 (ColumnData::Time(None), None) => {
1281 dst.extend_from_slice(&[VarLenType::Timen as u8, 7, 0]);
1282 }
1283 #[cfg(feature = "tds73")]
1284 (ColumnData::DateTime2(None), None) => {
1285 dst.extend_from_slice(&[VarLenType::Datetime2 as u8, 7, 0]);
1286 }
1287 #[cfg(feature = "tds73")]
1288 (ColumnData::DateTimeOffset(None), None) => {
1289 dst.extend_from_slice(&[VarLenType::DatetimeOffsetn as u8, 7, 0]);
1290 }
1291 (v, ref ti) => {
1292 return Err(crate::Error::BulkInput(
1293 format!("invalid data type, expecting {:?} but found {:?}", ti, v).into(),
1294 ));
1295 }
1296 }
1297
1298 Ok(())
1299 }
1300}
1301
1302fn put_fixed_money(
1304 dst: &mut BytesMutWithTypeInfo<'_>,
1305 ty: FixedLenType,
1306 encode: impl FnOnce(&mut BytesMut, usize) -> crate::Result<()>,
1307) -> crate::Result<()> {
1308 let mut moneyn = BytesMut::new();
1309 encode(&mut moneyn, if ty == FixedLenType::Money4 { 4 } else { 8 })?;
1310 dst.extend_from_slice(&moneyn[1..]);
1311 Ok(())
1312}
1313
1314fn too_long(len: usize) -> crate::Error {
1316 crate::Error::BulkInput(
1317 format!("Encoded length {len} exceeds the column limit {}", i32::MAX).into(),
1318 )
1319}
1320
1321fn put_text_pointer(dst: &mut BytesMutWithTypeInfo<'_>) {
1324 dst.put_u8(16);
1325 dst.extend_from_slice(&[0xff; 24]);
1326}
1327
1328impl<'a> FromSql<'a> for ColumnData<'a> {
1331 fn from_sql(value: &'a ColumnData<'static>) -> crate::Result<Option<Self>> {
1332 Ok(Some(value.clone()))
1333 }
1334}
1335
1336impl<'a> FromSqlOwned for ColumnData<'a> {
1339 fn from_sql_owned(value: ColumnData<'static>) -> crate::Result<Option<Self>> {
1340 Ok(Some(value))
1341 }
1342}
1343
1344impl<'a> ToSql for ColumnData<'a> {
1346 fn to_sql(&self) -> ColumnData<'_> {
1347 self.clone()
1348 }
1349}
1350
1351impl<'a> IntoSql<'a> for ColumnData<'a> {
1353 fn into_sql(self) -> ColumnData<'a> {
1354 self
1355 }
1356}
1357
1358#[cfg(test)]
1359mod tests {
1360 use super::*;
1361 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
1362 use crate::tds::Collation;
1363 use crate::{Error, VarLenContext};
1364 use bytes::BytesMut;
1365
1366 async fn test_round_trip(ti: TypeInfo, d: ColumnData<'_>) {
1367 let mut buf = BytesMut::new();
1368 let mut buf_with_ti = BytesMutWithTypeInfo::new(&mut buf).with_type_info(&ti);
1369
1370 d.clone()
1371 .encode(&mut buf_with_ti)
1372 .expect("encode must succeed");
1373
1374 let reader = &mut buf.into_sql_read_bytes();
1375 let nd = ColumnData::decode(reader, &ti)
1376 .await
1377 .expect("decode must succeed");
1378
1379 assert_eq!(nd, d);
1380
1381 reader
1382 .read_u8()
1383 .await
1384 .expect_err("decode must consume entire buffer");
1385 }
1386
1387 #[test]
1388 fn type_name_maps_each_variant() {
1389 assert_eq!(ColumnData::U8(Some(1)).type_name(), "tinyint");
1390 assert_eq!(ColumnData::I16(Some(1)).type_name(), "smallint");
1391 assert_eq!(ColumnData::I32(Some(1)).type_name(), "int");
1392 assert_eq!(ColumnData::I64(Some(1)).type_name(), "bigint");
1393 assert_eq!(ColumnData::F32(Some(1.0)).type_name(), "float(24)");
1394 assert_eq!(ColumnData::F64(Some(1.0)).type_name(), "float(53)");
1395 assert_eq!(ColumnData::Bit(Some(true)).type_name(), "bit");
1396 assert_eq!(ColumnData::Guid(None).type_name(), "uniqueidentifier");
1397 assert_eq!(ColumnData::Numeric(None).type_name(), "numeric(38,0)");
1398 assert_eq!(ColumnData::DateTime(None).type_name(), "datetime");
1399 assert_eq!(ColumnData::SmallDateTime(None).type_name(), "smalldatetime");
1400 }
1401
1402 #[test]
1403 fn type_name_string_length_thresholds() {
1404 assert_eq!(ColumnData::String(None).type_name(), "nvarchar(4000)");
1407 assert_eq!(
1408 ColumnData::String(Some("a".repeat(100).into())).type_name(),
1409 "nvarchar(4000)"
1410 );
1411 assert_eq!(
1412 ColumnData::String(Some("a".repeat(4000).into())).type_name(),
1413 "nvarchar(4000)"
1414 );
1415 assert_eq!(
1416 ColumnData::String(Some("a".repeat(4001).into())).type_name(),
1417 "nvarchar(max)"
1418 );
1419 }
1420
1421 async fn decode_rpc_param(buf: BytesMut) -> (TypeInfo, ColumnData<'static>) {
1423 let reader = &mut buf.into_sql_read_bytes();
1424 let ti = TypeInfo::decode(reader).await.unwrap();
1425 let data = ColumnData::decode(reader, &ti).await.unwrap();
1426 reader
1427 .read_u8()
1428 .await
1429 .expect_err("decode must consume entire buffer");
1430 (ti, data)
1431 }
1432
1433 #[tokio::test]
1434 async fn rpc_nulls_are_typed() {
1435 let nulls = vec![
1436 ColumnData::U8(None),
1437 ColumnData::I16(None),
1438 ColumnData::I32(None),
1439 ColumnData::I64(None),
1440 ColumnData::F32(None),
1441 ColumnData::F64(None),
1442 ColumnData::Bit(None),
1443 ColumnData::String(None),
1444 ColumnData::Guid(None),
1445 ColumnData::Binary(None),
1446 ColumnData::Numeric(None),
1447 ColumnData::Xml(None),
1448 ColumnData::DateTime(None),
1449 ColumnData::SmallDateTime(None),
1450 #[cfg(feature = "tds73")]
1451 ColumnData::Time(None),
1452 #[cfg(feature = "tds73")]
1453 ColumnData::Date(None),
1454 #[cfg(feature = "tds73")]
1455 ColumnData::DateTime2(None),
1456 #[cfg(feature = "tds73")]
1457 ColumnData::DateTimeOffset(None),
1458 ];
1459
1460 for null in nulls {
1461 for out in [false, true] {
1462 let mut buf = BytesMut::new();
1463 null.clone().encode_rpc_param(&mut buf, out, None).unwrap();
1464 assert_ne!(buf[0], FixedLenType::Null as u8, "{null:?}");
1465
1466 let (_, decoded) = decode_rpc_param(buf).await;
1467 assert_eq!(decoded, null);
1468 }
1469 }
1470 }
1471
1472 #[tokio::test]
1473 async fn varchar_params_go_in_the_session_collation() {
1474 let collation = Some(Collation::new(0x0409, 0));
1476 let text = |v: &ColumnData<'_>| match v {
1477 ColumnData::VarChar(s) | ColumnData::String(s) => s.clone().map(Cow::into_owned),
1478 _ => None,
1479 };
1480
1481 for (value, out, expected_len) in [
1482 (ColumnData::VarChar(Some("Väinämöinen".into())), false, 8000),
1483 (
1484 ColumnData::VarChar(Some("Väinämöinen".into())),
1485 true,
1486 0xffff,
1487 ),
1488 (ColumnData::VarChar(None), false, 8000),
1489 (ColumnData::VarChar(None), true, 0xffff),
1490 (
1491 ColumnData::VarChar(Some("a".repeat(8001).into())),
1492 false,
1493 0xffff,
1494 ),
1495 ] {
1496 let mut buf = BytesMut::new();
1497 value
1498 .clone()
1499 .encode_rpc_param(&mut buf, out, collation)
1500 .unwrap();
1501
1502 if expected_len == 8000 {
1505 let value_len = text(&value).map_or(0, |s| s.chars().count());
1506 assert_eq!(buf.len(), 1 + 2 + 5 + 2 + value_len, "{value:?}");
1507 }
1508
1509 let (ti, decoded) = decode_rpc_param(buf).await;
1510 match ti {
1511 TypeInfo::VarLenSized(vlc) => {
1512 assert_eq!(vlc.r#type(), VarLenType::BigVarChar, "{value:?}");
1513 assert_eq!(vlc.len(), expected_len, "{value:?}");
1514 assert_eq!(vlc.collation(), collation, "{value:?}");
1515 }
1516 other => panic!("{other:?}"),
1517 }
1518 assert_eq!(decoded, ColumnData::String(text(&value).map(Cow::Owned)));
1520 }
1521 }
1522
1523 #[test]
1524 fn a_varchar_param_needs_a_collation_and_a_character_it_holds() {
1525 let mut buf = BytesMut::new();
1526 let err = ColumnData::VarChar(Some("a".into()))
1527 .encode_rpc_param(&mut buf, false, None)
1528 .unwrap_err();
1529 assert!(matches!(err, Error::Encoding(_)), "{err:?}");
1530
1531 let collation = Some(Collation::new(0x0409, 0));
1532 let err = ColumnData::VarChar(Some("日本".into()))
1533 .encode_rpc_param(&mut buf, false, collation)
1534 .unwrap_err();
1535 assert!(matches!(err, Error::Encoding(_)), "{err:?}");
1536 assert!(buf.is_empty(), "nothing written before the error");
1537 }
1538
1539 #[test]
1540 fn varchar_params_are_declared_by_utf8_length() {
1541 assert_eq!(ColumnData::VarChar(None).type_name(), "varchar(8000)");
1542 assert_eq!(
1543 ColumnData::VarChar(Some("a".repeat(8000).into())).type_name(),
1544 "varchar(8000)"
1545 );
1546 assert_eq!(
1547 ColumnData::VarChar(Some("a".repeat(8001).into())).type_name(),
1548 "varchar(max)"
1549 );
1550 }
1551
1552 #[tokio::test]
1553 async fn out_strings_and_binaries_are_sent_as_max() {
1554 let values = [
1555 ColumnData::String(Some("a".into())),
1556 ColumnData::String(None),
1557 ColumnData::Binary(Some(vec![1u8].into())),
1558 ColumnData::Binary(None),
1559 ];
1560
1561 for value in values {
1562 for (out, max_len) in [(true, 0xffff), (false, 8000)] {
1563 let mut buf = BytesMut::new();
1564 value.clone().encode_rpc_param(&mut buf, out, None).unwrap();
1565
1566 let (ti, decoded) = decode_rpc_param(buf).await;
1567 match ti {
1568 TypeInfo::VarLenSized(vlc) => assert_eq!(vlc.len(), max_len, "{value:?}"),
1569 other => panic!("{other:?}"),
1570 }
1571 assert_eq!(decoded, value);
1572 }
1573 }
1574 }
1575
1576 #[tokio::test]
1577 async fn nulls_are_declared_like_values_of_their_type() {
1578 let pairs = vec![
1581 (
1582 ColumnData::Binary(None),
1583 ColumnData::Binary(Some(vec![1].into())),
1584 ),
1585 (
1586 ColumnData::Numeric(None),
1587 ColumnData::Numeric(Some(Numeric::new_with_scale(5, 0))),
1588 ),
1589 (
1590 ColumnData::String(None),
1591 ColumnData::String(Some("a".into())),
1592 ),
1593 (ColumnData::U8(None), ColumnData::U8(Some(1))),
1594 (ColumnData::I16(None), ColumnData::I16(Some(1))),
1595 (ColumnData::I32(None), ColumnData::I32(Some(1))),
1596 (ColumnData::I64(None), ColumnData::I64(Some(1))),
1597 (ColumnData::F32(None), ColumnData::F32(Some(1.0))),
1598 (ColumnData::F64(None), ColumnData::F64(Some(1.0))),
1599 (ColumnData::Bit(None), ColumnData::Bit(Some(true))),
1600 (ColumnData::Guid(None), ColumnData::Guid(Some(Uuid::nil()))),
1601 (
1602 ColumnData::Xml(None),
1603 ColumnData::Xml(Some(Cow::Owned(XmlData::new("<a/>")))),
1604 ),
1605 (
1606 ColumnData::DateTime(None),
1607 ColumnData::DateTime(Some(DateTime::new(0, 0))),
1608 ),
1609 (
1610 ColumnData::SmallDateTime(None),
1611 ColumnData::SmallDateTime(Some(SmallDateTime::new(0, 0))),
1612 ),
1613 #[cfg(feature = "tds73")]
1614 (
1615 ColumnData::Time(None),
1616 ColumnData::Time(Some(Time::new(0, 7))),
1617 ),
1618 #[cfg(feature = "tds73")]
1619 (ColumnData::Date(None), ColumnData::Date(Some(Date::new(0)))),
1620 #[cfg(feature = "tds73")]
1621 (
1622 ColumnData::DateTime2(None),
1623 ColumnData::DateTime2(Some(DateTime2::new(Date::new(0), Time::new(0, 7)))),
1624 ),
1625 #[cfg(feature = "tds73")]
1626 (
1627 ColumnData::DateTimeOffset(None),
1628 ColumnData::DateTimeOffset(Some(DateTimeOffset::new(
1629 DateTime2::new(Date::new(0), Time::new(0, 7)),
1630 0,
1631 ))),
1632 ),
1633 ];
1634
1635 for (null, value) in pairs {
1636 assert_eq!(null.type_name(), value.type_name(), "{null:?}");
1637
1638 let ti = |data: ColumnData<'static>| async {
1640 let mut buf = BytesMut::new();
1641 data.encode_rpc_param(&mut buf, false, None).unwrap();
1642 format!("{:?}", decode_rpc_param(buf).await.0)
1643 };
1644 assert_eq!(ti(null.clone()).await, ti(value).await, "{null:?}");
1645 }
1646 }
1647
1648 #[test]
1649 fn numeric_parameters_are_declared_at_precision_38() {
1650 for (value, scale) in [
1651 (15i128, 1u8),
1652 (123_455, 1),
1653 (10i128.pow(38) - 1, 38),
1654 (-7, 0),
1655 ] {
1656 let data = ColumnData::Numeric(Some(Numeric::new_with_scale(value, scale)));
1657 assert_eq!(data.type_name(), format!("numeric(38,{scale})"));
1658
1659 let mut buf = BytesMut::new();
1661 data.encode(&mut BytesMutWithTypeInfo::new(&mut buf))
1662 .unwrap();
1663 assert_eq!(&buf[..4], &[VarLenType::Numericn as u8, 17, 38, scale]);
1664 }
1665 }
1666
1667 #[test]
1668 fn nvarchar_4000_threshold_counts_utf16_units() {
1669 let cases = [
1672 ("\u{20AC}".repeat(1500), true),
1673 ("\u{20AC}".repeat(4000), true),
1674 ("\u{20AC}".repeat(4001), false),
1675 ("\u{1F600}".repeat(2000), true),
1676 ("\u{1F600}".repeat(2001), false),
1677 ];
1678
1679 for (s, fits) in cases {
1680 let data = ColumnData::String(Some(s.into()));
1681 let expected = if fits {
1682 "nvarchar(4000)"
1683 } else {
1684 "nvarchar(max)"
1685 };
1686 assert_eq!(data.type_name(), expected);
1687
1688 let mut buf = BytesMut::new();
1690 data.encode(&mut BytesMutWithTypeInfo::new(&mut buf))
1691 .unwrap();
1692 assert_eq!(buf[0], VarLenType::NVarchar as u8);
1693 let max_len = u16::from_le_bytes([buf[1], buf[2]]);
1694 assert_eq!(max_len, if fits { 8000 } else { 0xFFFF }, "{expected}");
1695 }
1696 }
1697
1698 fn euros_over_1_gib() -> String {
1701 "\u{20AC}".repeat((1 << 30) / 3 + 1)
1702 }
1703
1704 #[test]
1705 fn strings_over_1_gib_of_utf8() {
1706 let euros = euros_over_1_gib();
1707 assert_eq!(
1708 ColumnData::String(Some(euros.into())).type_name(),
1709 "nvarchar(max)"
1710 );
1711
1712 let err = ColumnData::String(Some("a".repeat(1 << 30).into()))
1714 .encode_rpc_param(&mut BytesMut::new(), false, None)
1715 .unwrap_err();
1716 assert!(matches!(err, Error::Conversion(_)), "{err:?}");
1717 }
1718
1719 #[test]
1720 #[ignore = "writes 716 MB, about 40 s in a debug build"]
1721 fn string_over_1_gib_of_utf8_encodes() {
1722 let euros = euros_over_1_gib();
1723 let units = euros.len() / 3;
1724
1725 let mut buf = BytesMut::new();
1726 ColumnData::String(Some(euros.into()))
1727 .encode_rpc_param(&mut buf, false, None)
1728 .unwrap();
1729
1730 assert_eq!(buf[..3], [VarLenType::NVarchar as u8, 0xff, 0xff]);
1732 let chunk = u32::from_le_bytes(buf[16..20].try_into().unwrap());
1733 assert_eq!(chunk as usize, units * 2);
1734 assert_eq!(buf.len(), 20 + units * 2 + 4);
1735 }
1736
1737 #[test]
1738 fn type_name_binary_length_threshold() {
1739 assert_eq!(
1740 ColumnData::Binary(Some(vec![0u8; 8000].into())).type_name(),
1741 "varbinary(8000)"
1742 );
1743 assert_eq!(
1744 ColumnData::Binary(Some(vec![0u8; 8001].into())).type_name(),
1745 "varbinary(max)"
1746 );
1747 assert_eq!(ColumnData::Binary(None).type_name(), "varbinary(8000)");
1748 }
1749
1750 #[test]
1751 #[cfg(feature = "serde")]
1752 fn serde_json_round_trip() {
1753 let value = ColumnData::I32(Some(1234));
1754 let json = serde_json::to_string(&value).expect("serialize");
1755 let back: ColumnData<'static> = serde_json::from_str(&json).expect("deserialize");
1756 assert_eq!(value, back);
1757 }
1758
1759 #[tokio::test]
1760 async fn i32_with_varlen_int() {
1761 test_round_trip(
1762 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 4, None)),
1763 ColumnData::I32(Some(42)),
1764 )
1765 .await;
1766 }
1767
1768 #[tokio::test]
1769 async fn none_with_varlen_int() {
1770 test_round_trip(
1771 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 4, None)),
1772 ColumnData::I32(None),
1773 )
1774 .await;
1775 }
1776
1777 #[tokio::test]
1778 async fn i32_with_fixedlen_int() {
1779 test_round_trip(
1780 TypeInfo::FixedLen(FixedLenType::Int4),
1781 ColumnData::I32(Some(42)),
1782 )
1783 .await;
1784 }
1785
1786 #[tokio::test]
1787 async fn bit_with_varlen_bit() {
1788 test_round_trip(
1789 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Bitn, 1, None)),
1790 ColumnData::Bit(Some(true)),
1791 )
1792 .await;
1793 }
1794
1795 #[tokio::test]
1796 async fn none_with_varlen_bit() {
1797 test_round_trip(
1798 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Bitn, 1, None)),
1799 ColumnData::Bit(None),
1800 )
1801 .await;
1802 }
1803
1804 #[tokio::test]
1805 async fn bit_with_fixedlen_bit() {
1806 test_round_trip(
1807 TypeInfo::FixedLen(FixedLenType::Bit),
1808 ColumnData::Bit(Some(true)),
1809 )
1810 .await;
1811 }
1812
1813 #[tokio::test]
1814 async fn u8_with_varlen_int() {
1815 test_round_trip(
1816 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 1, None)),
1817 ColumnData::U8(Some(8u8)),
1818 )
1819 .await;
1820 }
1821
1822 #[tokio::test]
1823 async fn none_u8_with_varlen_int() {
1824 test_round_trip(
1825 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 1, None)),
1826 ColumnData::U8(None),
1827 )
1828 .await;
1829 }
1830
1831 #[tokio::test]
1832 async fn u8_with_fixedlen_int() {
1833 test_round_trip(
1834 TypeInfo::FixedLen(FixedLenType::Int1),
1835 ColumnData::U8(Some(8u8)),
1836 )
1837 .await;
1838 }
1839
1840 #[tokio::test]
1841 async fn i16_with_varlen_intn() {
1842 test_round_trip(
1843 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 2, None)),
1844 ColumnData::I16(Some(8i16)),
1845 )
1846 .await;
1847 }
1848
1849 #[tokio::test]
1850 async fn none_i16_with_varlen_intn() {
1851 test_round_trip(
1852 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 2, None)),
1853 ColumnData::I16(None),
1854 )
1855 .await;
1856 }
1857
1858 #[tokio::test]
1859 async fn none_with_varlen_intn() {
1860 test_round_trip(
1861 TypeInfo::FixedLen(FixedLenType::Int2),
1862 ColumnData::I16(Some(8i16)),
1863 )
1864 .await;
1865 }
1866
1867 #[tokio::test]
1868 async fn i64_with_varlen_intn() {
1869 test_round_trip(
1870 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 8, None)),
1871 ColumnData::I64(Some(8i64)),
1872 )
1873 .await;
1874 }
1875
1876 #[tokio::test]
1877 async fn i64_none_with_varlen_intn() {
1878 test_round_trip(
1879 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 8, None)),
1880 ColumnData::I64(None),
1881 )
1882 .await;
1883 }
1884
1885 #[tokio::test]
1886 async fn i64_with_fixedlen_int8() {
1887 test_round_trip(
1888 TypeInfo::FixedLen(FixedLenType::Int8),
1889 ColumnData::I64(Some(8i64)),
1890 )
1891 .await;
1892 }
1893
1894 #[tokio::test]
1895 async fn f32_with_varlen_floatn() {
1896 test_round_trip(
1897 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 4, None)),
1898 ColumnData::F32(Some(8f32)),
1899 )
1900 .await;
1901 }
1902
1903 #[tokio::test]
1904 async fn null_f32_with_varlen_floatn() {
1905 test_round_trip(
1906 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 4, None)),
1907 ColumnData::F32(None),
1908 )
1909 .await;
1910 }
1911
1912 #[tokio::test]
1913 async fn f32_with_fixedlen_float4() {
1914 test_round_trip(
1915 TypeInfo::FixedLen(FixedLenType::Float4),
1916 ColumnData::F32(Some(8f32)),
1917 )
1918 .await;
1919 }
1920
1921 #[tokio::test]
1922 async fn f64_with_varlen_floatn() {
1923 test_round_trip(
1924 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 8, None)),
1925 ColumnData::F64(Some(8f64)),
1926 )
1927 .await;
1928 }
1929
1930 #[tokio::test]
1931 async fn none_f64_with_varlen_floatn() {
1932 test_round_trip(
1933 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 8, None)),
1934 ColumnData::F64(None),
1935 )
1936 .await;
1937 }
1938
1939 #[tokio::test]
1940 async fn f64_with_fixedlen_float8() {
1941 test_round_trip(
1942 TypeInfo::FixedLen(FixedLenType::Float8),
1943 ColumnData::F64(Some(8f64)),
1944 )
1945 .await;
1946 }
1947
1948 #[tokio::test]
1949 async fn guid_with_varlen_guid() {
1950 test_round_trip(
1951 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Guid, 16, None)),
1952 ColumnData::Guid(Some(Uuid::new_v4())),
1953 )
1954 .await;
1955 }
1956
1957 #[tokio::test]
1958 async fn none_guid_with_varlen_guid() {
1959 test_round_trip(
1960 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Guid, 16, None)),
1961 ColumnData::Guid(None),
1962 )
1963 .await;
1964 }
1965
1966 #[tokio::test]
1967 async fn numeric_with_varlen_sized_precision() {
1968 test_round_trip(
1969 TypeInfo::VarLenSizedPrecision {
1970 ty: VarLenType::Numericn,
1971 size: 17,
1972 precision: 18,
1973 scale: 0,
1974 },
1975 ColumnData::Numeric(Some(Numeric::new_with_scale(23, 0))),
1976 )
1977 .await;
1978 }
1979
1980 #[tokio::test]
1981 async fn none_numeric_with_varlen_sized_precision() {
1982 test_round_trip(
1983 TypeInfo::VarLenSizedPrecision {
1984 ty: VarLenType::Numericn,
1985 size: 17,
1986 precision: 18,
1987 scale: 0,
1988 },
1989 ColumnData::Numeric(None),
1990 )
1991 .await;
1992 }
1993
1994 #[tokio::test]
1995 async fn string_with_varlen_bigchar() {
1996 test_round_trip(
1997 TypeInfo::VarLenSized(VarLenContext::new(
1998 VarLenType::BigChar,
1999 40,
2000 Some(Collation::new(13632521, 52)),
2001 )),
2002 ColumnData::String(Some("aaa".into())),
2003 )
2004 .await;
2005 }
2006
2007 #[tokio::test]
2008 async fn long_string_with_varlen_bigchar() {
2009 test_round_trip(
2010 TypeInfo::VarLenSized(VarLenContext::new(
2011 VarLenType::BigChar,
2012 0x8ffff,
2013 Some(Collation::new(13632521, 52)),
2014 )),
2015 ColumnData::String(Some("aaa".into())),
2016 )
2017 .await;
2018 }
2019
2020 #[tokio::test]
2021 async fn none_long_string_with_varlen_bigchar() {
2022 test_round_trip(
2023 TypeInfo::VarLenSized(VarLenContext::new(
2024 VarLenType::BigChar,
2025 0x8ffff,
2026 Some(Collation::new(13632521, 52)),
2027 )),
2028 ColumnData::String(None),
2029 )
2030 .await;
2031 }
2032
2033 #[tokio::test]
2034 async fn none_string_with_varlen_bigchar() {
2035 test_round_trip(
2036 TypeInfo::VarLenSized(VarLenContext::new(
2037 VarLenType::BigChar,
2038 40,
2039 Some(Collation::new(13632521, 52)),
2040 )),
2041 ColumnData::String(None),
2042 )
2043 .await;
2044 }
2045
2046 #[tokio::test]
2047 async fn string_with_varlen_bigvarchar() {
2048 test_round_trip(
2049 TypeInfo::VarLenSized(VarLenContext::new(
2050 VarLenType::BigVarChar,
2051 40,
2052 Some(Collation::new(13632521, 52)),
2053 )),
2054 ColumnData::String(Some("aaa".into())),
2055 )
2056 .await;
2057 }
2058
2059 #[tokio::test]
2060 async fn none_string_with_varlen_bigvarchar() {
2061 test_round_trip(
2062 TypeInfo::VarLenSized(VarLenContext::new(
2063 VarLenType::BigVarChar,
2064 40,
2065 Some(Collation::new(13632521, 52)),
2066 )),
2067 ColumnData::String(None),
2068 )
2069 .await;
2070 }
2071
2072 #[tokio::test]
2073 async fn empty_string_with_varlen_bigvarchar() {
2074 test_round_trip(
2075 TypeInfo::VarLenSized(VarLenContext::new(
2076 VarLenType::BigVarChar,
2077 0x8ffff,
2078 Some(Collation::new(13632521, 52)),
2079 )),
2080 ColumnData::String(Some("".into())),
2081 )
2082 .await;
2083 }
2084
2085 #[tokio::test]
2086 async fn string_with_varlen_nvarchar() {
2087 test_round_trip(
2088 TypeInfo::VarLenSized(VarLenContext::new(
2089 VarLenType::NVarchar,
2090 40,
2091 Some(Collation::new(13632521, 52)),
2092 )),
2093 ColumnData::String(Some("hhh".into())),
2094 )
2095 .await;
2096 }
2097
2098 #[tokio::test]
2099 async fn none_string_with_varlen_nvarchar() {
2100 test_round_trip(
2101 TypeInfo::VarLenSized(VarLenContext::new(
2102 VarLenType::NVarchar,
2103 40,
2104 Some(Collation::new(13632521, 52)),
2105 )),
2106 ColumnData::String(None),
2107 )
2108 .await;
2109 }
2110
2111 #[tokio::test]
2112 async fn empty_string_with_varlen_nvarchar() {
2113 test_round_trip(
2114 TypeInfo::VarLenSized(VarLenContext::new(
2115 VarLenType::NVarchar,
2116 0x8ffff,
2117 Some(Collation::new(13632521, 52)),
2118 )),
2119 ColumnData::String(Some("".into())),
2120 )
2121 .await;
2122 }
2123
2124 #[tokio::test]
2125 async fn string_with_varlen_nchar() {
2126 test_round_trip(
2127 TypeInfo::VarLenSized(VarLenContext::new(
2128 VarLenType::NChar,
2129 40,
2130 Some(Collation::new(13632521, 52)),
2131 )),
2132 ColumnData::String(Some("hhh".into())),
2133 )
2134 .await;
2135 }
2136
2137 #[tokio::test]
2138 async fn long_string_with_varlen_nchar() {
2139 test_round_trip(
2140 TypeInfo::VarLenSized(VarLenContext::new(
2141 VarLenType::NChar,
2142 0x8ffff,
2143 Some(Collation::new(13632521, 52)),
2144 )),
2145 ColumnData::String(Some("hhh".into())),
2146 )
2147 .await;
2148 }
2149
2150 #[tokio::test]
2151 async fn none_long_string_with_varlen_nchar() {
2152 test_round_trip(
2153 TypeInfo::VarLenSized(VarLenContext::new(
2154 VarLenType::NChar,
2155 0x8ffff,
2156 Some(Collation::new(13632521, 52)),
2157 )),
2158 ColumnData::String(None),
2159 )
2160 .await;
2161 }
2162
2163 #[tokio::test]
2164 async fn none_string_with_varlen_nchar() {
2165 test_round_trip(
2166 TypeInfo::VarLenSized(VarLenContext::new(
2167 VarLenType::NChar,
2168 40,
2169 Some(Collation::new(13632521, 52)),
2170 )),
2171 ColumnData::String(None),
2172 )
2173 .await;
2174 }
2175
2176 #[tokio::test]
2177 async fn binary_with_varlen_bigbinary() {
2178 test_round_trip(
2179 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigBinary, 40, None)),
2180 ColumnData::Binary(Some(b"aaa".as_slice().into())),
2181 )
2182 .await;
2183 }
2184
2185 #[tokio::test]
2186 async fn long_binary_with_varlen_bigbinary() {
2187 test_round_trip(
2188 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigBinary, 0x8ffff, None)),
2189 ColumnData::Binary(Some(b"aaa".as_slice().into())),
2190 )
2191 .await;
2192 }
2193
2194 #[tokio::test]
2195 async fn none_binary_with_varlen_bigbinary() {
2196 test_round_trip(
2197 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigBinary, 40, None)),
2198 ColumnData::Binary(None),
2199 )
2200 .await;
2201 }
2202
2203 #[tokio::test]
2204 async fn none_long_binary_with_varlen_bigbinary() {
2205 test_round_trip(
2206 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigBinary, 0x8ffff, None)),
2207 ColumnData::Binary(None),
2208 )
2209 .await;
2210 }
2211
2212 #[tokio::test]
2213 async fn binary_with_varlen_bigvarbin() {
2214 test_round_trip(
2215 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarBin, 40, None)),
2216 ColumnData::Binary(Some(b"aaa".as_slice().into())),
2217 )
2218 .await;
2219 }
2220
2221 #[tokio::test]
2222 async fn none_binary_with_varlen_bigvarbin() {
2223 test_round_trip(
2224 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarBin, 40, None)),
2225 ColumnData::Binary(None),
2226 )
2227 .await;
2228 }
2229
2230 #[tokio::test]
2231 async fn empty_binary_with_varlen_bigvarbin() {
2232 test_round_trip(
2233 TypeInfo::VarLenSized(VarLenContext::new(
2234 VarLenType::BigVarBin,
2235 0x8ffff,
2236 Some(Collation::new(13632521, 52)),
2237 )),
2238 ColumnData::Binary(Some(b"".as_slice().into())),
2239 )
2240 .await;
2241 }
2242
2243 #[tokio::test]
2244 async fn datetime_with_varlen_datetimen() {
2245 test_round_trip(
2246 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 8, None)),
2247 ColumnData::DateTime(Some(DateTime::new(200, 3000))),
2248 )
2249 .await;
2250 }
2251
2252 #[tokio::test]
2255 async fn none_datetime_with_varlen_datetimen() {
2256 test_round_trip(
2257 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 8, None)),
2258 ColumnData::DateTime(None),
2259 )
2260 .await;
2261 }
2262
2263 #[tokio::test]
2264 async fn datetime_with_fixedlen_datetime() {
2265 test_round_trip(
2266 TypeInfo::FixedLen(FixedLenType::Datetime),
2267 ColumnData::DateTime(Some(DateTime::new(200, 3000))),
2268 )
2269 .await;
2270 }
2271
2272 #[tokio::test]
2273 async fn smalldatetime_with_varlen_datetimen() {
2274 test_round_trip(
2275 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 4, None)),
2276 ColumnData::SmallDateTime(Some(SmallDateTime::new(200, 1439))),
2277 )
2278 .await;
2279 }
2280
2281 #[tokio::test]
2282 async fn none_smalldatetime_with_varlen_datetimen() {
2283 test_round_trip(
2284 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 4, None)),
2285 ColumnData::SmallDateTime(None),
2286 )
2287 .await;
2288 }
2289
2290 #[tokio::test]
2291 async fn smalldatetime_with_fixedlen_datetime4() {
2292 test_round_trip(
2293 TypeInfo::FixedLen(FixedLenType::Datetime4),
2294 ColumnData::SmallDateTime(Some(SmallDateTime::new(200, 1439))),
2295 )
2296 .await;
2297 }
2298
2299 #[cfg(feature = "tds73")]
2300 #[tokio::test]
2301 async fn date_with_varlen_daten() {
2302 test_round_trip(
2303 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Daten, 3, None)),
2304 ColumnData::Date(Some(Date::new(200))),
2305 )
2306 .await;
2307 }
2308
2309 #[cfg(feature = "tds73")]
2310 #[tokio::test]
2311 async fn none_date_with_varlen_daten() {
2312 test_round_trip(
2313 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Daten, 3, None)),
2314 ColumnData::Date(None),
2315 )
2316 .await;
2317 }
2318
2319 #[cfg(feature = "tds73")]
2320 #[tokio::test]
2321 async fn time_with_varlen_timen() {
2322 test_round_trip(
2323 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Timen, 7, None)),
2324 ColumnData::Time(Some(Time::new(55, 7))),
2325 )
2326 .await;
2327 }
2328
2329 #[cfg(feature = "tds73")]
2330 #[tokio::test]
2331 async fn none_time_with_varlen_timen() {
2332 test_round_trip(
2333 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Timen, 7, None)),
2334 ColumnData::Time(None),
2335 )
2336 .await;
2337 }
2338
2339 #[cfg(feature = "tds73")]
2340 #[tokio::test]
2341 async fn datetime2_with_varlen_datetime2() {
2342 test_round_trip(
2343 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetime2, 7, None)),
2344 ColumnData::DateTime2(Some(DateTime2::new(Date::new(55), Time::new(222, 7)))),
2345 )
2346 .await;
2347 }
2348
2349 #[cfg(feature = "tds73")]
2350 #[tokio::test]
2351 async fn none_datetime2_with_varlen_datetime2() {
2352 test_round_trip(
2353 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetime2, 7, None)),
2354 ColumnData::DateTime2(None),
2355 )
2356 .await;
2357 }
2358
2359 #[cfg(feature = "tds73")]
2360 #[tokio::test]
2361 async fn datetimeoffset_with_varlen_datetimeoffsetn() {
2362 test_round_trip(
2363 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::DatetimeOffsetn, 7, None)),
2364 ColumnData::DateTimeOffset(Some(DateTimeOffset::new(
2365 DateTime2::new(Date::new(55), Time::new(222, 7)),
2366 -8,
2367 ))),
2368 )
2369 .await;
2370 }
2371
2372 #[cfg(feature = "tds73")]
2373 #[tokio::test]
2374 async fn none_datetimeoffset_with_varlen_datetimeoffsetn() {
2375 test_round_trip(
2376 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::DatetimeOffsetn, 7, None)),
2377 ColumnData::DateTimeOffset(None),
2378 )
2379 .await;
2380 }
2381
2382 #[cfg(feature = "tds73")]
2383 #[tokio::test]
2384 async fn xml_with_xml() {
2385 test_round_trip(
2386 TypeInfo::Xml {
2387 schema: None,
2388 size: 0xfffffffffffffffe_usize,
2389 },
2390 ColumnData::Xml(Some(Cow::Owned(XmlData::new("<a>ddd</a>")))),
2391 )
2392 .await;
2393 }
2394
2395 #[cfg(feature = "tds73")]
2396 #[tokio::test]
2397 async fn none_xml_with_xml() {
2398 test_round_trip(
2399 TypeInfo::Xml {
2400 schema: None,
2401 size: 0xfffffffffffffffe_usize,
2402 },
2403 ColumnData::Xml(None),
2404 )
2405 .await;
2406 }
2407
2408 #[tokio::test]
2409 async fn invalid_type_fails() {
2410 let data = vec![
2411 (
2412 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 4, None)),
2413 ColumnData::I32(Some(42)),
2414 ),
2415 (
2416 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 4, None)),
2417 ColumnData::I32(None),
2418 ),
2419 (
2420 TypeInfo::FixedLen(FixedLenType::Int4),
2421 ColumnData::I32(None),
2422 ),
2423 ];
2424
2425 for (ti, d) in data {
2426 let mut buf = BytesMut::new();
2427 let mut buf_ti = BytesMutWithTypeInfo::new(&mut buf).with_type_info(&ti);
2428
2429 let err = d.encode(&mut buf_ti).expect_err("encode should fail");
2430
2431 if let Error::BulkInput(_) = err {
2432 } else {
2433 panic!("Expected: Error::BulkInput, got: {:?}", err);
2434 }
2435 }
2436 }
2437
2438 #[test]
2439 fn column_data_from_sql_clones_by_reference() {
2440 let value: ColumnData<'static> = ColumnData::I32(Some(42));
2441 let out = ColumnData::from_sql(&value).unwrap();
2442 assert_eq!(out, Some(ColumnData::I32(Some(42))));
2443 }
2444
2445 #[test]
2446 fn column_data_from_sql_owned_passes_through() {
2447 let value: ColumnData<'static> = ColumnData::String(Some(Cow::Borrowed("hello")));
2448 let out = ColumnData::from_sql_owned(value).unwrap();
2449 assert_eq!(out, Some(ColumnData::String(Some(Cow::Borrowed("hello")))));
2450 }
2451
2452 #[test]
2453 fn column_data_to_sql_clones_by_reference() {
2454 let value = ColumnData::Bit(Some(true));
2455 assert_eq!(value.to_sql(), ColumnData::Bit(Some(true)));
2456 assert_eq!(value, ColumnData::Bit(Some(true)));
2458 }
2459
2460 #[test]
2461 fn column_data_into_sql_passes_through() {
2462 let value = ColumnData::F64(Some(1.5));
2463 assert_eq!(value.into_sql(), ColumnData::F64(Some(1.5)));
2464 }
2465
2466 fn encode_with_ti(ti: &TypeInfo, d: ColumnData<'_>) -> crate::Result<BytesMut> {
2469 let mut buf = BytesMut::new();
2470 {
2471 let mut b = BytesMutWithTypeInfo::new(&mut buf).with_type_info(ti);
2472 d.encode(&mut b)?;
2473 }
2474 Ok(buf)
2475 }
2476
2477 fn encode_without_ti(d: ColumnData<'_>) -> crate::Result<BytesMut> {
2478 let mut buf = BytesMut::new();
2479 {
2480 let mut b = BytesMutWithTypeInfo::new(&mut buf);
2481 d.encode(&mut b)?;
2482 }
2483 Ok(buf)
2484 }
2485
2486 fn expect_bulk_input(ti: TypeInfo, d: ColumnData<'_>) {
2487 let mut buf = BytesMut::new();
2488 let mut b = BytesMutWithTypeInfo::new(&mut buf).with_type_info(&ti);
2489 let err = d.encode(&mut b).expect_err("encode should fail");
2490 assert!(matches!(err, Error::BulkInput(_)), "got {:?}", err);
2491 }
2492
2493 #[tokio::test]
2494 async fn decode_varlen_sized_precision_non_decimal_is_an_error() {
2495 let ti = TypeInfo::VarLenSizedPrecision {
2496 ty: VarLenType::Money,
2497 size: 8,
2498 precision: 0,
2499 scale: 0,
2500 };
2501 let buf = BytesMut::new();
2502 let reader = &mut buf.into_sql_read_bytes();
2503 let res = ColumnData::decode(reader, &ti).await;
2504 assert!(matches!(res, Err(Error::Protocol(_))), "{res:?}");
2505 }
2506
2507 #[tokio::test]
2510 async fn decode_udt_type_info() {
2511 use bytes::BufMut;
2512
2513 let ti = TypeInfo::Udt(crate::tds::codec::type_info::UdtInfo {
2514 max_byte_size: 0xffff,
2515 db_name: "db".into(),
2516 schema_name: "dbo".into(),
2517 type_name: "geometry".into(),
2518 assembly_qualified_name: String::new(),
2519 });
2520
2521 let mut buf = BytesMut::new();
2522 buf.put_u64_le(0xfffffffffffffffe);
2524 buf.put_u32_le(4);
2525 buf.extend_from_slice(&[1, 2, 3, 4]);
2526 buf.put_u32_le(0);
2527
2528 let reader = &mut buf.into_sql_read_bytes();
2529 let nd = ColumnData::decode(reader, &ti).await.unwrap();
2530 assert_eq!(nd, ColumnData::Binary(Some(vec![1, 2, 3, 4].into())));
2531 }
2532
2533 #[tokio::test]
2536 async fn f64_with_varlen_money() {
2537 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 8, None));
2538 let buf = encode_with_ti(&ti, ColumnData::F64(Some(3.5))).unwrap();
2539
2540 let nd = ColumnData::decode(&mut buf.into_sql_read_bytes(), &ti)
2542 .await
2543 .unwrap();
2544 assert_eq!(
2545 nd,
2546 ColumnData::Numeric(Some(Numeric::new_with_scale(35000, 4)))
2547 );
2548 }
2549
2550 #[tokio::test]
2551 async fn none_f64_with_varlen_money() {
2552 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 8, None));
2553 let buf = encode_with_ti(&ti, ColumnData::F64(None)).unwrap();
2554
2555 let nd = ColumnData::decode(&mut buf.into_sql_read_bytes(), &ti)
2556 .await
2557 .unwrap();
2558 assert_eq!(nd, ColumnData::Numeric(None));
2559 }
2560
2561 #[tokio::test]
2564 async fn bigchar_unrepresentable_character_errors() {
2565 let ti = TypeInfo::VarLenSized(VarLenContext::new(
2566 VarLenType::BigChar,
2567 40,
2568 Some(Collation::new(13632521, 52)),
2569 ));
2570 let mut buf = BytesMut::new();
2571 let mut b = BytesMutWithTypeInfo::new(&mut buf).with_type_info(&ti);
2572 let err = ColumnData::String(Some("\u{1F600}".into()))
2574 .encode(&mut b)
2575 .expect_err("encode should fail");
2576 assert!(matches!(err, Error::Encoding(_)), "got {:?}", err);
2577 }
2578
2579 #[tokio::test]
2580 async fn bigchar_too_long_errors() {
2581 expect_bulk_input(
2582 TypeInfo::VarLenSized(VarLenContext::new(
2583 VarLenType::BigChar,
2584 2,
2585 Some(Collation::new(13632521, 52)),
2586 )),
2587 ColumnData::String(Some("aaa".into())),
2588 );
2589 }
2590
2591 #[tokio::test]
2594 async fn nvarchar_too_long_small_errors() {
2595 expect_bulk_input(
2596 TypeInfo::VarLenSized(VarLenContext::new(
2597 VarLenType::NVarchar,
2598 2,
2599 Some(Collation::new(13632521, 52)),
2600 )),
2601 ColumnData::String(Some("aaa".into())),
2602 );
2603 }
2604
2605 #[tokio::test]
2608 async fn string_with_varlen_text() {
2609 test_round_trip(
2610 TypeInfo::VarLenSized(VarLenContext::new(
2611 VarLenType::Text,
2612 40,
2613 Some(Collation::new(13632521, 52)),
2614 )),
2615 ColumnData::String(Some("hello".into())),
2616 )
2617 .await;
2618 }
2619
2620 #[tokio::test]
2621 async fn none_string_with_varlen_text() {
2622 test_round_trip(
2623 TypeInfo::VarLenSized(VarLenContext::new(
2624 VarLenType::Text,
2625 40,
2626 Some(Collation::new(13632521, 52)),
2627 )),
2628 ColumnData::String(None),
2629 )
2630 .await;
2631 }
2632
2633 #[tokio::test]
2634 async fn string_with_varlen_ntext() {
2635 test_round_trip(
2636 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NText, 40, None)),
2637 ColumnData::String(Some("hi".into())),
2638 )
2639 .await;
2640 }
2641
2642 #[tokio::test]
2643 async fn none_string_with_varlen_ntext() {
2644 test_round_trip(
2645 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NText, 40, None)),
2646 ColumnData::String(None),
2647 )
2648 .await;
2649 }
2650
2651 #[tokio::test]
2654 async fn binary_too_long_errors() {
2655 expect_bulk_input(
2656 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarBin, 2, None)),
2657 ColumnData::Binary(Some(b"aaa".as_slice().into())),
2658 );
2659 }
2660
2661 #[tokio::test]
2664 async fn datetime_encode_without_type_info() {
2665 let buf = encode_without_ti(ColumnData::DateTime(Some(DateTime::new(200, 3000)))).unwrap();
2666 assert_eq!(buf[0], VarLenType::Datetimen as u8);
2667 assert_eq!(buf[1], 8);
2668 assert_eq!(buf[2], 8);
2669 }
2670
2671 #[tokio::test]
2672 async fn smalldatetime_encode_without_type_info() {
2673 let buf = encode_without_ti(ColumnData::SmallDateTime(Some(SmallDateTime::new(
2674 200, 600,
2675 ))))
2676 .unwrap();
2677 assert_eq!(buf[0], VarLenType::Datetimen as u8);
2678 assert_eq!(buf[1], 4);
2679 assert_eq!(buf[2], 4);
2680 }
2681
2682 #[cfg(feature = "tds73")]
2685 #[tokio::test]
2686 async fn datetime2_with_varlen_datetimen() {
2687 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 8, None));
2688 let dt2 = DateTime2::new(Date::new(737_425), Time::new(0, 7));
2690 let buf = encode_with_ti(&ti, ColumnData::DateTime2(Some(dt2))).unwrap();
2691
2692 let reader = &mut buf.into_sql_read_bytes();
2693 let nd = ColumnData::decode(reader, &ti).await.unwrap();
2694 assert!(matches!(nd, ColumnData::DateTime(Some(_))), "got {:?}", nd);
2695 }
2696
2697 #[cfg(feature = "tds73")]
2698 #[tokio::test]
2699 async fn none_datetime2_with_varlen_datetimen() {
2700 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 8, None));
2701 let buf = encode_with_ti(&ti, ColumnData::DateTime2(None)).unwrap();
2702
2703 let reader = &mut buf.into_sql_read_bytes();
2704 ColumnData::decode(reader, &ti).await.unwrap();
2706 }
2707
2708 #[tokio::test]
2711 async fn numeric_with_varlen_money() {
2712 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 8, None));
2713 let buf = encode_with_ti(
2715 &ti,
2716 ColumnData::Numeric(Some(Numeric::new_with_scale(35000, 4))),
2717 )
2718 .unwrap();
2719
2720 let reader = &mut buf.into_sql_read_bytes();
2721 let nd = ColumnData::decode(reader, &ti).await.unwrap();
2722 assert_eq!(
2723 nd,
2724 ColumnData::Numeric(Some(Numeric::new_with_scale(35000, 4)))
2725 );
2726 }
2727
2728 #[tokio::test]
2729 async fn none_numeric_with_varlen_money() {
2730 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 8, None));
2731 let buf = encode_with_ti(&ti, ColumnData::Numeric(None)).unwrap();
2732
2733 let reader = &mut buf.into_sql_read_bytes();
2734 let nd = ColumnData::decode(reader, &ti).await.unwrap();
2735 assert_eq!(nd, ColumnData::Numeric(None));
2736 }
2737
2738 #[tokio::test]
2741 async fn numeric_scaled_up_to_column_scale() {
2742 let ti = TypeInfo::VarLenSizedPrecision {
2744 ty: VarLenType::Numericn,
2745 size: 17,
2746 precision: 18,
2747 scale: 2,
2748 };
2749 let buf = encode_with_ti(
2750 &ti,
2751 ColumnData::Numeric(Some(Numeric::new_with_scale(23, 0))),
2752 )
2753 .unwrap();
2754
2755 let reader = &mut buf.into_sql_read_bytes();
2756 let nd = ColumnData::decode(reader, &ti).await.unwrap();
2757 assert_eq!(
2758 nd,
2759 ColumnData::Numeric(Some(Numeric::new_with_scale(2300, 2)))
2760 );
2761 }
2762
2763 #[tokio::test]
2764 async fn numeric_scaled_down_to_column_scale() {
2765 let ti = TypeInfo::VarLenSizedPrecision {
2767 ty: VarLenType::Numericn,
2768 size: 17,
2769 precision: 18,
2770 scale: 2,
2771 };
2772 let buf = encode_with_ti(
2773 &ti,
2774 ColumnData::Numeric(Some(Numeric::new_with_scale(12345, 4))),
2775 )
2776 .unwrap();
2777
2778 let reader = &mut buf.into_sql_read_bytes();
2779 let nd = ColumnData::decode(reader, &ti).await.unwrap();
2780 assert_eq!(
2781 nd,
2782 ColumnData::Numeric(Some(Numeric::new_with_scale(123, 2)))
2783 );
2784 }
2785
2786 #[tokio::test]
2787 async fn numeric_the_column_cannot_hold_is_an_error() {
2788 let decimal = |precision, scale| TypeInfo::VarLenSizedPrecision {
2789 ty: VarLenType::Decimaln,
2790 size: 17,
2791 precision,
2792 scale,
2793 };
2794 let num = |v, s| ColumnData::Numeric(Some(Numeric::new_with_scale(v, s)));
2795
2796 for (ti, data) in [
2799 (decimal(38, 2), num(i128::MAX, 38)),
2800 (decimal(38, 2), num(i128::MIN, 38)),
2801 (decimal(5, 2), num(999_995, 3)),
2802 (decimal(5, 2), num(-999_995, 3)),
2803 (decimal(5, 2), num(12_345, 0)),
2804 (decimal(5, 2), num(123_456, 2)),
2805 (decimal(38, 0), num(10i128.pow(38), 0)),
2806 ] {
2807 let err = encode_with_ti(&ti, data.clone()).unwrap_err();
2808 assert!(matches!(err, Error::Conversion(_)), "{data:?}: {err:?}");
2809 }
2810
2811 for (ti, data, expected) in [
2812 (decimal(5, 2), num(999_994, 3), (99_999, 2)),
2813 (decimal(5, 2), num(-999_994, 3), (-99_999, 2)),
2814 (decimal(5, 2), num(-999, 0), (-99_900, 2)),
2815 (
2816 decimal(38, 0),
2817 num(10i128.pow(38) - 1, 0),
2818 (10i128.pow(38) - 1, 0),
2819 ),
2820 (decimal(38, 37), num(-15, 38), (-2, 37)),
2821 ] {
2822 let buf = encode_with_ti(&ti, data).unwrap();
2823 let nd = ColumnData::decode(&mut buf.into_sql_read_bytes(), &ti)
2824 .await
2825 .unwrap();
2826 assert_eq!(nd, num(expected.0, expected.1));
2827 }
2828 }
2829
2830 #[tokio::test]
2833 async fn ssvariant_round_trip_i32() {
2834 test_round_trip(
2835 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::SSVariant, 0, None)),
2836 ColumnData::I32(Some(42)),
2837 )
2838 .await;
2839 }
2840
2841 fn int_ti(len: usize) -> TypeInfo {
2842 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, len, None))
2843 }
2844
2845 fn float_ti(len: usize) -> TypeInfo {
2846 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, len, None))
2847 }
2848
2849 async fn decode_with(ti: &TypeInfo, buf: BytesMut) -> ColumnData<'static> {
2850 ColumnData::decode(&mut buf.into_sql_read_bytes(), ti)
2851 .await
2852 .unwrap()
2853 }
2854
2855 #[tokio::test]
2856 async fn bulk_integer_is_written_at_the_column_width() {
2857 let buf = encode_with_ti(&int_ti(8), ColumnData::I32(Some(-5))).unwrap();
2858 assert_eq!(
2859 &buf[..],
2860 &[8, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
2861 );
2862 assert_eq!(
2863 decode_with(&int_ti(8), buf).await,
2864 ColumnData::I64(Some(-5))
2865 );
2866
2867 let ti = TypeInfo::FixedLen(FixedLenType::Int4);
2868 let buf = encode_with_ti(&ti, ColumnData::I16(Some(-1))).unwrap();
2869 assert_eq!(&buf[..], &[0xff; 4]);
2870
2871 let buf = encode_with_ti(&int_ti(1), ColumnData::I64(Some(200))).unwrap();
2872 assert_eq!(
2873 decode_with(&int_ti(1), buf).await,
2874 ColumnData::U8(Some(200))
2875 );
2876
2877 let buf = encode_with_ti(&int_ti(2), ColumnData::I32(None)).unwrap();
2878 assert_eq!(&buf[..], &[0]);
2879 }
2880
2881 #[test]
2882 fn bulk_integer_out_of_the_column_range_errors() {
2883 for (len, value) in [
2884 (4, ColumnData::I64(Some(5_000_000_000))),
2885 (2, ColumnData::I32(Some(70_000))),
2886 (1, ColumnData::I32(Some(300))),
2887 (1, ColumnData::I16(Some(-1))),
2888 ] {
2889 let err = encode_with_ti(&int_ti(len), value).unwrap_err();
2890 assert!(matches!(err, Error::Conversion(_)), "got {err:?}");
2891 }
2892 }
2893
2894 #[tokio::test]
2895 async fn bulk_float_is_written_at_the_column_width() {
2896 let buf = encode_with_ti(&float_ti(8), ColumnData::F32(Some(1.5))).unwrap();
2897 assert_eq!(
2898 decode_with(&float_ti(8), buf).await,
2899 ColumnData::F64(Some(1.5))
2900 );
2901
2902 let ti = TypeInfo::FixedLen(FixedLenType::Float4);
2903 let buf = encode_with_ti(&ti, ColumnData::F64(Some(1.5))).unwrap();
2904 assert_eq!(&buf[..], &1.5f32.to_le_bytes());
2905
2906 let err = encode_with_ti(&float_ti(4), ColumnData::F64(Some(1e300))).unwrap_err();
2907 assert!(matches!(err, Error::Conversion(_)), "got {err:?}");
2908 }
2909
2910 #[test]
2911 fn a_non_finite_float_is_rejected_before_it_is_sent() {
2912 for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
2913 for data in [
2915 ColumnData::F64(Some(value)),
2916 ColumnData::F32(Some(value as f32)),
2917 ] {
2918 let mut buf = BytesMut::new();
2919 let err = data
2920 .clone()
2921 .encode(&mut BytesMutWithTypeInfo::new(&mut buf))
2922 .unwrap_err();
2923 assert!(matches!(err, Error::Conversion(_)), "{data:?}: {err:?}");
2924
2925 let err = encode_with_ti(&float_ti(8), data.clone()).unwrap_err();
2926 assert!(matches!(err, Error::Conversion(_)), "{data:?}: {err:?}");
2927 }
2928 }
2929 }
2930
2931 #[test]
2932 fn bulk_null_into_not_null_column_errors() {
2933 let ti = TypeInfo::FixedLen(FixedLenType::Int4);
2934 let err = encode_with_ti(&ti, ColumnData::I32(None)).unwrap_err();
2935 assert!(matches!(err, Error::BulkInput(_)), "got {err:?}");
2936 }
2937
2938 #[tokio::test]
2939 async fn bulk_not_null_money() {
2940 let money = TypeInfo::FixedLen(FixedLenType::Money);
2941 let money4 = TypeInfo::FixedLen(FixedLenType::Money4);
2942 let exact = |v| ColumnData::Numeric(Some(Numeric::new_with_scale(v, 4)));
2943
2944 let buf = encode_with_ti(&money, ColumnData::F64(Some(-1234.5))).unwrap();
2945 assert_eq!(decode_with(&money, buf).await, exact(-12_345_000));
2946 let buf = encode_with_ti(&money4, ColumnData::F64(Some(1.25))).unwrap();
2947 assert_eq!(decode_with(&money4, buf).await, exact(12_500));
2948
2949 let max_but_one = i64::MAX as i128 - 1;
2951 let buf = encode_with_ti(&money, exact(max_but_one)).unwrap();
2952 assert_eq!(decode_with(&money, buf).await, exact(max_but_one));
2953
2954 assert!(encode_with_ti(&money4, ColumnData::F64(Some(300_000.0))).is_err());
2955 }
2956
2957 #[cfg(feature = "tds73")]
2959 fn dt2(h: u64, m: u64, s: u64, increments: u64) -> ColumnData<'static> {
2960 let time = ((h * 60 + m) * 60 + s) * 10_000_000 + increments;
2961 ColumnData::DateTime2(Some(DateTime2::new(Date::new(739_011), Time::new(time, 7))))
2962 }
2963
2964 #[cfg(feature = "tds73")]
2965 async fn as_datetime(ti: &TypeInfo, value: ColumnData<'static>) -> ColumnData<'static> {
2966 decode_with(ti, encode_with_ti(ti, value).unwrap()).await
2967 }
2968
2969 #[cfg(feature = "tds73")]
2970 #[tokio::test]
2971 async fn bulk_datetime2_into_datetime_rounds_like_cast() {
2972 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 8, None));
2973 let day = 739_011 - DAYS_YEAR_1_TO_1900 as i32;
2974
2975 assert_eq!(
2978 as_datetime(&ti, dt2(13, 14, 15, 1_234_567)).await,
2979 ColumnData::DateTime(Some(DateTime::new(day, 47_655 * 300 + 37)))
2980 );
2981 assert_eq!(
2982 as_datetime(&ti, dt2(23, 59, 59, 9_983_333)).await,
2983 ColumnData::DateTime(Some(DateTime::new(day, 86_400 * 300 - 1)))
2984 );
2985 assert_eq!(
2986 as_datetime(&ti, dt2(23, 59, 59, 9_983_334)).await,
2987 ColumnData::DateTime(Some(DateTime::new(day + 1, 0)))
2988 );
2989 }
2990
2991 #[cfg(feature = "tds73")]
2992 #[tokio::test]
2993 async fn bulk_datetime_range_starts_in_1753() {
2994 let ti = TypeInfo::FixedLen(FixedLenType::Datetime);
2995 let at =
2996 |days| ColumnData::DateTime2(Some(DateTime2::new(Date::new(days), Time::new(0, 7))));
2997
2998 assert_eq!(
3000 as_datetime(&ti, at(639_905)).await,
3001 ColumnData::DateTime(Some(DateTime::new(-53_690, 0)))
3002 );
3003 let err = encode_with_ti(&ti, at(639_904)).unwrap_err();
3004 assert!(matches!(err, Error::Conversion(_)), "got {err:?}");
3005 }
3006
3007 #[cfg(feature = "tds73")]
3008 #[tokio::test]
3009 async fn bulk_smalldatetime_rounds_like_cast() {
3010 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 4, None));
3011 let day = (739_011 - DAYS_YEAR_1_TO_1900) as u16;
3012
3013 assert_eq!(
3015 as_datetime(&ti, dt2(10, 0, 29, 9_990_000)).await,
3016 ColumnData::SmallDateTime(Some(SmallDateTime::new(day, 600)))
3017 );
3018 assert_eq!(
3019 as_datetime(&ti, dt2(10, 0, 30, 0)).await,
3020 ColumnData::SmallDateTime(Some(SmallDateTime::new(day, 601)))
3021 );
3022 assert_eq!(
3023 as_datetime(&ti, dt2(23, 59, 30, 0)).await,
3024 ColumnData::SmallDateTime(Some(SmallDateTime::new(day + 1, 0)))
3025 );
3026
3027 let ti = TypeInfo::FixedLen(FixedLenType::Datetime4);
3028 let buf = encode_with_ti(&ti, ColumnData::DateTime(Some(DateTime::new(5, 9_000)))).unwrap();
3029 assert_eq!(
3030 decode_with(&ti, buf).await,
3031 ColumnData::SmallDateTime(Some(SmallDateTime::new(5, 1)))
3032 );
3033 }
3034
3035 #[cfg(feature = "tds73")]
3036 #[tokio::test]
3037 async fn bulk_time_is_written_at_the_column_scale() {
3038 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Timen, 3, None));
3039 let buf = encode_with_ti(&ti, ColumnData::Time(Some(Time::new(1_234_567, 7)))).unwrap();
3040 assert_eq!(&buf[..], &[4, 123, 0, 0, 0]);
3041
3042 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::DatetimeOffsetn, 0, None));
3043 let dto = DateTimeOffset::new(
3044 DateTime2::new(Date::new(739_011), Time::new(15_000_000, 7)),
3045 60,
3046 );
3047 let buf = encode_with_ti(&ti, ColumnData::DateTimeOffset(Some(dto))).unwrap();
3048 assert_eq!(buf[0], 8);
3050 assert_eq!(&buf[1..4], &[1, 0, 0]);
3051
3052 assert_eq!(
3053 rescale_time(Time::new(5, 0), 7).unwrap().increments(),
3054 50_000_000
3055 );
3056 assert!(rescale_time(Time::new(5, 9), 7).is_err());
3057 }
3058
3059 #[cfg(feature = "tds73")]
3060 #[test]
3061 fn bulk_times_from_24_hours_up_are_errors() {
3062 let column = |ty, len| TypeInfo::VarLenSized(VarLenContext::new(ty, len, None));
3063 let at = |increments| DateTime2::new(Date::new(739_011), Time::new(increments, 7));
3064
3065 for increments in [u64::MAX, Time::LEAP_SECOND, 864_000_000_000] {
3067 for (ti, data) in [
3068 (
3069 column(VarLenType::Timen, 3),
3070 ColumnData::Time(Some(Time::new(increments, 7))),
3071 ),
3072 (
3073 column(VarLenType::Datetime2, 7),
3074 ColumnData::DateTime2(Some(at(increments))),
3075 ),
3076 (
3077 column(VarLenType::Datetimen, 8),
3078 ColumnData::DateTime2(Some(at(increments))),
3079 ),
3080 (
3081 column(VarLenType::Datetimen, 4),
3082 ColumnData::DateTime2(Some(at(increments))),
3083 ),
3084 ] {
3085 let err = encode_with_ti(&ti, data).unwrap_err();
3086 assert!(matches!(err, Error::Conversion(_)), "{ti:?}: {err:?}");
3087 }
3088 }
3089 }
3090
3091 #[tokio::test]
3092 async fn bulk_max_values_over_64k() {
3093 let collation = Some(Collation::new(13632521, 52));
3094 test_round_trip(
3095 TypeInfo::VarLenSized(VarLenContext::new(
3096 VarLenType::BigVarChar,
3097 0xffff,
3098 collation,
3099 )),
3100 ColumnData::String(Some("a".repeat(70_000).into())),
3101 )
3102 .await;
3103 test_round_trip(
3104 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NVarchar, 0xffff, collation)),
3105 ColumnData::String(Some("a".repeat(40_000).into())),
3106 )
3107 .await;
3108 test_round_trip(
3109 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarBin, 0xffff, None)),
3110 ColumnData::Binary(Some(vec![7u8; 70_000].into())),
3111 )
3112 .await;
3113 }
3114
3115 #[tokio::test]
3116 async fn bulk_image() {
3117 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Image, 0x7fff_ffff, None));
3118 test_round_trip(ti.clone(), ColumnData::Binary(Some(vec![1u8, 2, 3].into()))).await;
3119
3120 let buf = encode_with_ti(&ti, ColumnData::Binary(None)).unwrap();
3121 assert_eq!(&buf[..], &[0]);
3122 }
3123
3124 #[test]
3125 fn bulk_varchar_uses_the_column_code_page() {
3126 let ti = TypeInfo::VarLenSized(VarLenContext::new(
3128 VarLenType::BigVarChar,
3129 10,
3130 Some(Collation::new(0x00d0_0408, 0)),
3131 ));
3132 let buf = encode_with_ti(&ti, ColumnData::String(Some("\u{3b1}\u{3b2}".into()))).unwrap();
3133 assert_eq!(&buf[..], &[2, 0, 0xe1, 0xe2]);
3134 }
3135
3136 #[test]
3139 fn utf16_values_over_2_gib_are_errors() {
3140 let s = "a".repeat(1 << 30);
3141 let collation = Some(Collation::new(0x00d0_0409, 52));
3142
3143 let ntext = TypeInfo::VarLenSized(VarLenContext::new(
3144 VarLenType::NText,
3145 0x7fff_fffe,
3146 collation,
3147 ));
3148 let err = encode_with_ti(&ntext, ColumnData::String(Some(s.as_str().into())))
3149 .map(drop)
3150 .unwrap_err();
3151 assert!(matches!(err, Error::BulkInput(_)), "{err:?}");
3152
3153 let xml = TypeInfo::Xml {
3154 schema: None,
3155 size: 0xfffffffffffffffe_usize,
3156 };
3157 let err = encode_with_ti(&xml, ColumnData::String(Some(s.as_str().into())))
3158 .map(drop)
3159 .unwrap_err();
3160 assert!(matches!(err, Error::Conversion(_)), "{err:?}");
3161
3162 let err = ColumnData::Xml(Some(Cow::Owned(XmlData::new(s))))
3163 .encode_rpc_param(&mut BytesMut::new(), false, None)
3164 .unwrap_err();
3165 assert!(matches!(err, Error::Conversion(_)), "{err:?}");
3166 }
3167
3168 #[test]
3171 fn binaries_over_2_gib_are_errors() {
3172 let big = vec![0u8; 1 << 31];
3173
3174 for out in [false, true] {
3175 let err = ColumnData::Binary(Some(big.as_slice().into()))
3176 .encode_rpc_param(&mut BytesMut::new(), out, None)
3177 .unwrap_err();
3178 assert!(matches!(err, Error::Conversion(_)), "{out}: {err:?}");
3179 }
3180
3181 let image = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Image, 0x7fff_ffff, None));
3182 let err = encode_with_ti(&image, ColumnData::Binary(Some(big.as_slice().into())))
3183 .map(drop)
3184 .unwrap_err();
3185 assert!(matches!(err, Error::BulkInput(_)), "{err:?}");
3186 }
3187
3188 #[test]
3189 #[ignore = "allocates 4 GiB"]
3190 fn text_over_2_gib_is_an_error() {
3191 let utf8 = Some(Collation::new(0x0400_0409, 0));
3192 let text = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Text, 0x7fff_ffff, utf8));
3193 let s = "a".repeat(1 << 31);
3194 let err = encode_with_ti(&text, ColumnData::String(Some(s.into())))
3195 .map(drop)
3196 .unwrap_err();
3197 assert!(matches!(err, Error::BulkInput(_)), "{err:?}");
3198 }
3199
3200 #[test]
3203 fn bulk_datetimes_past_midnight_are_errors() {
3204 let datetimen =
3205 |len| TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, len, None));
3206 let past = ColumnData::DateTime(Some(DateTime::new(0, 25_920_150)));
3207 let leap = ColumnData::DateTime(Some(DateTime::new(0, u32::MAX)));
3208 let small = ColumnData::SmallDateTime(Some(SmallDateTime::new(0, 1440)));
3209
3210 for (ti, data) in [
3211 (TypeInfo::FixedLen(FixedLenType::Datetime), past.clone()),
3212 (datetimen(8), leap.clone()),
3213 (TypeInfo::FixedLen(FixedLenType::Datetime4), past),
3214 (datetimen(4), leap),
3215 (TypeInfo::FixedLen(FixedLenType::Datetime), small.clone()),
3216 (datetimen(4), small),
3217 ] {
3218 let err = encode_with_ti(&ti, data).unwrap_err();
3219 assert!(matches!(err, Error::Conversion(_)), "{ti:?}: {err:?}");
3220 }
3221 }
3222}