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