1use std::{borrow::Cow, fmt::Display};
2
3use crate::{
4 tds::codec::{put_b_varchar, Encode, FixedLenType, TokenType, TypeInfo, VarLenType},
5 tds::Collation,
6 Column, ColumnData, ColumnType, SqlReadBytes, VarLenContext,
7};
8use asynchronous_codec::BytesMut;
9use bytes::BufMut;
10use enumflags2::{bitflags, BitFlags};
11
12#[derive(Debug, Clone)]
13pub struct TokenColMetaData<'a> {
14 pub columns: Vec<MetaDataColumn<'a>>,
15}
16
17#[derive(Debug, Clone)]
20pub struct MetaDataColumn<'a> {
21 pub base: BaseMetaDataColumn,
23 pub col_name: Cow<'a, str>,
25}
26
27impl<'a> MetaDataColumn<'a> {
28 pub fn col_name(&self) -> &str {
30 self.col_name.as_ref()
31 }
32
33 pub fn base(&self) -> &BaseMetaDataColumn {
36 &self.base
37 }
38}
39
40impl<'a> Display for MetaDataColumn<'a> {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 write!(f, "[{}] ", self.col_name.replace(']', "]]"))?;
45
46 match &self.base.ty {
47 TypeInfo::FixedLen(fixed) => match fixed {
48 FixedLenType::Int1 => write!(f, "tinyint")?,
49 FixedLenType::Bit => write!(f, "bit")?,
50 FixedLenType::Int2 => write!(f, "smallint")?,
51 FixedLenType::Int4 => write!(f, "int")?,
52 FixedLenType::Datetime4 => write!(f, "smalldatetime")?,
53 FixedLenType::Float4 => write!(f, "real")?,
54 FixedLenType::Money => write!(f, "money")?,
55 FixedLenType::Datetime => write!(f, "datetime")?,
56 FixedLenType::Float8 => write!(f, "float")?,
57 FixedLenType::Money4 => write!(f, "smallmoney")?,
58 FixedLenType::Int8 => write!(f, "bigint")?,
59 FixedLenType::Null => write!(f, "int")?,
63 },
64 TypeInfo::VarLenSized(ctx) => match ctx.r#type() {
65 VarLenType::Bitn => write!(f, "bit")?,
66 VarLenType::Guid => write!(f, "uniqueidentifier")?,
67 #[cfg(feature = "tds73")]
68 VarLenType::Daten => write!(f, "date")?,
69 #[cfg(feature = "tds73")]
70 VarLenType::Timen => write!(f, "time({})", ctx.len())?,
71 #[cfg(feature = "tds73")]
72 VarLenType::Datetime2 => write!(f, "datetime2({})", ctx.len())?,
73 VarLenType::Datetimen => match ctx.len() {
74 4 => write!(f, "smalldatetime")?,
75 _ => write!(f, "datetime")?,
76 },
77 VarLenType::Money => match ctx.len() {
78 4 => write!(f, "smallmoney")?,
79 _ => write!(f, "money")?,
80 },
81 #[cfg(feature = "tds73")]
82 VarLenType::DatetimeOffsetn => write!(f, "datetimeoffset({})", ctx.len())?,
83 VarLenType::BigVarBin => {
84 if ctx.len() <= 8000 {
85 write!(f, "varbinary({})", ctx.len())?
86 } else {
87 write!(f, "varbinary(max)")?
88 }
89 }
90 VarLenType::BigVarChar => {
91 if ctx.len() <= 8000 {
92 write!(f, "varchar({})", ctx.len())?
93 } else {
94 write!(f, "varchar(max)")?
95 }
96 }
97 VarLenType::BigBinary => write!(f, "binary({})", ctx.len())?,
98 VarLenType::BigChar => write!(f, "char({})", ctx.len())?,
99 VarLenType::NVarchar => {
101 if ctx.len() <= 8000 {
102 write!(f, "nvarchar({})", ctx.len() / 2)?
103 } else {
104 write!(f, "nvarchar(max)")?
105 }
106 }
107 VarLenType::NChar => write!(f, "nchar({})", ctx.len() / 2)?,
108 VarLenType::Text => write!(f, "text")?,
109 VarLenType::Image => write!(f, "image")?,
110 VarLenType::NText => write!(f, "ntext")?,
111 VarLenType::Intn => match ctx.len() {
112 1 => write!(f, "tinyint")?,
113 2 => write!(f, "smallint")?,
114 4 => write!(f, "int")?,
115 _ => write!(f, "bigint")?,
116 },
117 VarLenType::Floatn => match ctx.len() {
118 4 => write!(f, "real")?,
119 _ => write!(f, "float")?,
120 },
121 VarLenType::SSVariant => write!(f, "sql_variant")?,
122 other => write!(f, "{other:?}")?,
125 },
126 TypeInfo::VarLenSizedPrecision {
127 ty,
128 size: _,
129 precision,
130 scale,
131 } => match ty {
132 VarLenType::Numericn => write!(f, "numeric({},{})", precision, scale)?,
133 _ => write!(f, "decimal({},{})", precision, scale)?,
135 },
136 TypeInfo::Xml { .. } => write!(f, "xml")?,
137 TypeInfo::Udt(info) => write!(f, "{}.{}", info.schema_name, info.type_name)?,
138 }
139
140 Ok(())
141 }
142}
143
144#[derive(Debug, Clone)]
148pub struct BaseMetaDataColumn {
149 pub flags: BitFlags<ColumnFlag>,
152 pub ty: TypeInfo,
155}
156
157impl BaseMetaDataColumn {
158 pub fn ty(&self) -> &TypeInfo {
161 &self.ty
162 }
163
164 pub fn flags(&self) -> BitFlags<ColumnFlag> {
166 self.flags
167 }
168
169 pub fn is_nullable(&self) -> bool {
171 self.flags.contains(ColumnFlag::Nullable)
172 }
173
174 pub fn is_identity(&self) -> bool {
176 self.flags.contains(ColumnFlag::Identity)
177 }
178
179 pub fn is_updateable(&self) -> bool {
181 self.flags.contains(ColumnFlag::Updateable)
182 }
183
184 pub(crate) fn is_non_unicode_text(&self) -> bool {
187 matches!(&self.ty, TypeInfo::VarLenSized(cx)
188 if matches!(cx.r#type(), VarLenType::BigChar | VarLenType::BigVarChar | VarLenType::Text))
189 }
190
191 pub(crate) fn set_variant_collation(&mut self, collation: Option<Collation>) {
194 if let TypeInfo::VarLenSized(cx) = &mut self.ty {
195 if cx.r#type() == VarLenType::SSVariant {
196 *cx = VarLenContext::new(VarLenType::SSVariant, cx.len(), collation);
197 }
198 }
199 }
200
201 pub(crate) fn null_value(&self) -> ColumnData<'static> {
202 match &self.ty {
203 TypeInfo::FixedLen(ty) => match ty {
204 FixedLenType::Null => ColumnData::I32(None),
205 FixedLenType::Int1 => ColumnData::U8(None),
206 FixedLenType::Bit => ColumnData::Bit(None),
207 FixedLenType::Int2 => ColumnData::I16(None),
208 FixedLenType::Int4 => ColumnData::I32(None),
209 FixedLenType::Datetime4 => ColumnData::SmallDateTime(None),
210 FixedLenType::Float4 => ColumnData::F32(None),
211 FixedLenType::Money => ColumnData::Numeric(None),
212 FixedLenType::Datetime => ColumnData::DateTime(None),
213 FixedLenType::Float8 => ColumnData::F64(None),
214 FixedLenType::Money4 => ColumnData::Numeric(None),
215 FixedLenType::Int8 => ColumnData::I64(None),
216 },
217 TypeInfo::VarLenSized(cx) => match cx.r#type() {
218 VarLenType::Guid => ColumnData::Guid(None),
219 VarLenType::Intn => match cx.len() {
220 1 => ColumnData::U8(None),
221 2 => ColumnData::I16(None),
222 4 => ColumnData::I32(None),
223 _ => ColumnData::I64(None),
224 },
225 VarLenType::Bitn => ColumnData::Bit(None),
226 VarLenType::Decimaln => ColumnData::Numeric(None),
227 VarLenType::Numericn => ColumnData::Numeric(None),
228 VarLenType::Floatn => match cx.len() {
229 4 => ColumnData::F32(None),
230 _ => ColumnData::F64(None),
231 },
232 VarLenType::Money => ColumnData::Numeric(None),
233 VarLenType::Datetimen => match cx.len() {
235 4 => ColumnData::SmallDateTime(None),
236 _ => ColumnData::DateTime(None),
237 },
238 #[cfg(feature = "tds73")]
239 VarLenType::Daten => ColumnData::Date(None),
240 #[cfg(feature = "tds73")]
241 VarLenType::Timen => ColumnData::Time(None),
242 #[cfg(feature = "tds73")]
243 VarLenType::Datetime2 => ColumnData::DateTime2(None),
244 #[cfg(feature = "tds73")]
245 VarLenType::DatetimeOffsetn => ColumnData::DateTimeOffset(None),
246 VarLenType::BigVarBin => ColumnData::Binary(None),
247 VarLenType::BigVarChar => ColumnData::String(None),
248 VarLenType::BigBinary => ColumnData::Binary(None),
249 VarLenType::BigChar => ColumnData::String(None),
250 VarLenType::NVarchar => ColumnData::String(None),
251 VarLenType::NChar => ColumnData::String(None),
252 VarLenType::Xml => ColumnData::Xml(None),
253 VarLenType::Udt => ColumnData::Binary(None),
258 VarLenType::Text => ColumnData::String(None),
259 VarLenType::Image => ColumnData::Binary(None),
260 VarLenType::NText => ColumnData::String(None),
261 VarLenType::SSVariant => ColumnData::String(None),
264 },
265 TypeInfo::VarLenSizedPrecision { ty, .. } => match ty {
266 VarLenType::Guid => ColumnData::Guid(None),
267 VarLenType::Intn => ColumnData::I32(None),
268 VarLenType::Bitn => ColumnData::Bit(None),
269 VarLenType::Decimaln => ColumnData::Numeric(None),
270 VarLenType::Numericn => ColumnData::Numeric(None),
271 VarLenType::Floatn => ColumnData::F32(None),
272 VarLenType::Money => ColumnData::Numeric(None),
273 VarLenType::Datetimen => ColumnData::DateTime(None),
274 #[cfg(feature = "tds73")]
275 VarLenType::Daten => ColumnData::Date(None),
276 #[cfg(feature = "tds73")]
277 VarLenType::Timen => ColumnData::Time(None),
278 #[cfg(feature = "tds73")]
279 VarLenType::Datetime2 => ColumnData::DateTime2(None),
280 #[cfg(feature = "tds73")]
281 VarLenType::DatetimeOffsetn => ColumnData::DateTimeOffset(None),
282 VarLenType::BigVarBin => ColumnData::Binary(None),
283 VarLenType::BigVarChar => ColumnData::String(None),
284 VarLenType::BigBinary => ColumnData::Binary(None),
285 VarLenType::BigChar => ColumnData::String(None),
286 VarLenType::NVarchar => ColumnData::String(None),
287 VarLenType::NChar => ColumnData::String(None),
288 VarLenType::Xml => ColumnData::Xml(None),
289 VarLenType::Udt => ColumnData::Binary(None),
294 VarLenType::Text => ColumnData::String(None),
295 VarLenType::Image => ColumnData::Binary(None),
296 VarLenType::NText => ColumnData::String(None),
297 VarLenType::SSVariant => ColumnData::String(None),
300 },
301 TypeInfo::Xml { .. } => ColumnData::Xml(None),
302 TypeInfo::Udt(_) => ColumnData::Binary(None),
303 }
304 }
305}
306
307impl<'a> Encode<BytesMut> for TokenColMetaData<'a> {
308 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
309 dst.put_u8(TokenType::ColMetaData as u8);
310 dst.put_u16_le(self.columns.len() as u16);
311
312 for col in self.columns.into_iter() {
313 col.encode(dst)?;
314 }
315
316 Ok(())
317 }
318}
319
320impl<'a> Encode<BytesMut> for MetaDataColumn<'a> {
321 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
322 let long = matches!(&self.base.ty, TypeInfo::VarLenSized(cx)
323 if matches!(cx.r#type(), VarLenType::Text | VarLenType::NText | VarLenType::Image));
324
325 dst.put_u32_le(0);
326
327 match self.base.ty {
328 TypeInfo::Xml { .. } => {
333 dst.put_u16_le(self.base.flags.bits());
334 dst.extend_from_slice(&[VarLenType::NVarchar as u8, 0xff, 0xff, 0, 0, 0, 0, 0]);
335 }
336 _ => self.base.encode(dst)?,
337 }
338
339 if long {
340 dst.put_u16_le(0);
343 }
344
345 put_b_varchar(dst, &self.col_name, "column name")
346 }
347}
348
349impl Encode<BytesMut> for BaseMetaDataColumn {
350 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
351 dst.put_u16_le(BitFlags::bits(self.flags));
352 self.ty.encode(dst)?;
353
354 Ok(())
355 }
356}
357
358#[bitflags]
360#[repr(u16)]
361#[derive(Debug, Clone, Copy, PartialEq, Eq)]
362pub enum ColumnFlag {
363 Nullable = 1,
365 CaseSensitive = 1 << 1,
368 Updateable = 1 << 3,
370 UpdateableUnknown = 1 << 2,
372 Identity = 1 << 4,
374 Computed = 1 << 7,
376 FixedLenClrType = 1 << 10,
379 SparseColumnSet = 1 << 11,
381 Encrypted = 1 << 12,
385 Hidden = 1 << 13,
388 Key = 1 << 14,
391 NullableUnknown = 1 << 15,
393}
394
395impl TokenColMetaData<'static> {
396 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
397 where
398 R: SqlReadBytes + Unpin,
399 {
400 let column_count = src.read_u16_le().await?;
401 let mut columns = Vec::with_capacity(
406 (column_count as usize).min(crate::tds::codec::column_data::MAX_PREALLOC),
407 );
408
409 if column_count < 0xffff {
412 for _ in 0..column_count {
413 let base = BaseMetaDataColumn::decode(src).await?;
414 let col_name = Cow::from(src.read_b_varchar().await?);
415
416 columns.push(MetaDataColumn { base, col_name });
417 }
418 }
419
420 Ok(TokenColMetaData { columns })
421 }
422}
423
424impl<'a> TokenColMetaData<'a> {
425 pub(crate) fn columns(&self) -> impl Iterator<Item = Column> + '_ {
426 self.columns.iter().map(|x| Column {
427 name: x.col_name.to_string(),
428 column_type: ColumnType::from(&x.base.ty),
429 })
430 }
431}
432
433impl BaseMetaDataColumn {
434 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
435 where
436 R: SqlReadBytes + Unpin,
437 {
438 use VarLenType::*;
439
440 let _user_ty = src.read_u32_le().await?;
441
442 let flags = BitFlags::from_bits_truncate(src.read_u16_le().await?);
447
448 let ty = TypeInfo::decode(src).await?;
449
450 if let TypeInfo::VarLenSized(cx) = ty {
451 if let Text | NText | Image = cx.r#type() {
452 let num_of_parts = src.read_u8().await?;
453
454 for _ in 0..num_of_parts {
456 src.read_us_varchar().await?;
457 }
458 };
459 };
460
461 Ok(BaseMetaDataColumn { flags, ty })
462 }
463}
464
465#[cfg(test)]
466mod tests {
467 use super::*;
468 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
469 use crate::tds::Collation;
470 use crate::VarLenContext;
471
472 fn meta(ty: TypeInfo, name: &'static str) -> MetaDataColumn<'static> {
473 MetaDataColumn {
474 base: BaseMetaDataColumn {
475 flags: ColumnFlag::Nullable.into(),
476 ty,
477 },
478 col_name: Cow::Borrowed(name),
479 }
480 }
481
482 #[tokio::test]
483 async fn round_trip_via_encode_decode() {
484 let cmd = TokenColMetaData {
485 columns: vec![
486 meta(TypeInfo::FixedLen(FixedLenType::Int4), "id"),
487 meta(
488 TypeInfo::VarLenSized(VarLenContext::new(
489 VarLenType::NVarchar,
490 4000,
491 Some(Collation::new(13632521, 52)),
492 )),
493 "name",
494 ),
495 ],
496 };
497
498 let mut buf = BytesMut::new();
502 buf.put_u16_le(cmd.columns.len() as u16);
503 for col in cmd.columns.iter().cloned() {
504 col.encode(&mut buf).unwrap();
505 }
506
507 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
508 .await
509 .unwrap();
510
511 assert_eq!(decoded.columns.len(), 2);
512 assert_eq!(decoded.columns[0].col_name, "id");
513 assert_eq!(decoded.columns[1].col_name, "name");
514
515 let columns: Vec<_> = decoded.columns().collect();
516 assert_eq!(columns.len(), 2);
517 assert_eq!(columns[0].name(), "id");
518 }
519
520 #[test]
521 fn encode_writes_token_header_and_column_count() {
522 let cmd = TokenColMetaData {
523 columns: vec![
524 meta(TypeInfo::FixedLen(FixedLenType::Int4), "id"),
525 meta(TypeInfo::FixedLen(FixedLenType::Bit), "flag"),
526 ],
527 };
528
529 let mut buf = BytesMut::new();
530 cmd.encode(&mut buf).unwrap();
531
532 assert_eq!(buf[0], TokenType::ColMetaData as u8);
534 assert_eq!(u16::from_le_bytes([buf[1], buf[2]]), 2);
535 assert!(buf.len() > 3);
537 }
538
539 #[tokio::test]
540 async fn zero_columns_yields_empty() {
541 let mut buf = BytesMut::new();
542 buf.put_u16_le(0);
543
544 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
545 .await
546 .unwrap();
547 assert!(decoded.columns.is_empty());
548 }
549
550 #[tokio::test]
551 async fn text_column_reads_table_name_parts() {
552 let mut buf = BytesMut::new();
553 buf.put_u16_le(1); buf.put_u32_le(0);
557 buf.put_u16_le(BitFlags::bits(BitFlags::from(ColumnFlag::Nullable)));
558
559 let ti = TypeInfo::VarLenSized(VarLenContext::new(
561 VarLenType::Text,
562 2147483647,
563 Some(Collation::new(13632521, 52)),
564 ));
565 ti.encode(&mut buf).unwrap();
566
567 buf.put_u8(1);
569 let part: Vec<u16> = "dbo".encode_utf16().collect();
570 buf.put_u16_le(part.len() as u16);
571 for c in part {
572 buf.put_u16_le(c);
573 }
574
575 let name: Vec<u16> = "body".encode_utf16().collect();
577 buf.put_u8(name.len() as u8);
578 for c in name {
579 buf.put_u16_le(c);
580 }
581
582 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
583 .await
584 .unwrap();
585
586 assert_eq!(decoded.columns.len(), 1);
587 assert_eq!(decoded.columns[0].col_name, "body");
588 }
589
590 #[test]
591 fn display_formats_various_types() {
592 let cases = vec![
593 (TypeInfo::FixedLen(FixedLenType::Int4), "c int"),
594 (TypeInfo::FixedLen(FixedLenType::Bit), "c bit"),
595 (TypeInfo::FixedLen(FixedLenType::Float8), "c float"),
596 (
597 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 1, None)),
598 "c tinyint",
599 ),
600 (
601 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 4, None)),
602 "c int",
603 ),
604 (
605 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 4, None)),
606 "c real",
607 ),
608 (
609 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Guid, 16, None)),
610 "c uniqueidentifier",
611 ),
612 (
613 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarBin, 100, None)),
614 "c varbinary(100)",
615 ),
616 (
617 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarBin, 100000, None)),
618 "c varbinary(max)",
619 ),
620 (
621 TypeInfo::VarLenSizedPrecision {
622 ty: VarLenType::Decimaln,
623 size: 17,
624 precision: 18,
625 scale: 2,
626 },
627 "c decimal(18,2)",
628 ),
629 (
632 TypeInfo::VarLenSizedPrecision {
633 ty: VarLenType::Numericn,
634 size: 9,
635 precision: 10,
636 scale: 4,
637 },
638 "c numeric(10,4)",
639 ),
640 (
641 TypeInfo::Xml {
642 schema: None,
643 size: 0,
644 },
645 "c xml",
646 ),
647 ];
648
649 for (ty, expected) in cases {
650 let expected = expected.replacen("c ", "[c] ", 1);
652 assert_eq!(format!("{}", meta(ty, "c")), expected);
653 }
654 }
655
656 #[test]
657 fn null_value_maps_types() {
658 let fixed = BaseMetaDataColumn {
659 flags: BitFlags::empty(),
660 ty: TypeInfo::FixedLen(FixedLenType::Int4),
661 };
662 assert_eq!(fixed.null_value(), ColumnData::I32(None));
663
664 let varlen = BaseMetaDataColumn {
665 flags: BitFlags::empty(),
666 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 2, None)),
667 };
668 assert_eq!(varlen.null_value(), ColumnData::I16(None));
669
670 let tinyint = BaseMetaDataColumn {
673 flags: BitFlags::empty(),
674 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 1, None)),
675 };
676 assert_eq!(tinyint.null_value(), ColumnData::U8(None));
677
678 let int4 = BaseMetaDataColumn {
679 flags: BitFlags::empty(),
680 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 4, None)),
681 };
682 assert_eq!(int4.null_value(), ColumnData::I32(None));
683
684 let int8 = BaseMetaDataColumn {
685 flags: BitFlags::empty(),
686 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 8, None)),
687 };
688 assert_eq!(int8.null_value(), ColumnData::I64(None));
689
690 let real = BaseMetaDataColumn {
692 flags: BitFlags::empty(),
693 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 4, None)),
694 };
695 assert_eq!(real.null_value(), ColumnData::F32(None));
696
697 let double = BaseMetaDataColumn {
698 flags: BitFlags::empty(),
699 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 8, None)),
700 };
701 assert_eq!(double.null_value(), ColumnData::F64(None));
702
703 let guid = BaseMetaDataColumn {
704 flags: BitFlags::empty(),
705 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Guid, 16, None)),
706 };
707 assert_eq!(guid.null_value(), ColumnData::Guid(None));
708 }
709
710 #[test]
711 fn null_value_maps_precision_and_xml_and_udt() {
712 let precision = BaseMetaDataColumn {
713 flags: BitFlags::empty(),
714 ty: TypeInfo::VarLenSizedPrecision {
715 ty: VarLenType::Numericn,
716 size: 17,
717 precision: 18,
718 scale: 2,
719 },
720 };
721 assert_eq!(precision.null_value(), ColumnData::Numeric(None));
722
723 let xml = BaseMetaDataColumn {
724 flags: BitFlags::empty(),
725 ty: TypeInfo::Xml {
726 schema: None,
727 size: 0,
728 },
729 };
730 assert_eq!(xml.null_value(), ColumnData::Xml(None));
731
732 let udt = BaseMetaDataColumn {
733 flags: BitFlags::empty(),
734 ty: TypeInfo::Udt(crate::tds::codec::type_info::UdtInfo {
735 max_byte_size: 0,
736 db_name: "db".into(),
737 schema_name: "dbo".into(),
738 type_name: "T".into(),
739 assembly_qualified_name: "A".into(),
740 }),
741 };
742 assert_eq!(udt.null_value(), ColumnData::Binary(None));
743 }
744
745 #[test]
746 fn base_meta_data_column_flag_accessors() {
747 let base = BaseMetaDataColumn {
748 flags: ColumnFlag::Nullable | ColumnFlag::Identity | ColumnFlag::Updateable,
749 ty: TypeInfo::FixedLen(FixedLenType::Int4),
750 };
751
752 assert!(base.is_nullable());
753 assert!(base.is_identity());
754 assert!(base.is_updateable());
755 assert_eq!(base.ty(), &TypeInfo::FixedLen(FixedLenType::Int4));
756 assert_eq!(base.flags(), base.flags);
757
758 let base2 = BaseMetaDataColumn {
759 flags: BitFlags::empty(),
760 ty: TypeInfo::FixedLen(FixedLenType::Int4),
761 };
762 assert!(!base2.is_nullable());
763 assert!(!base2.is_identity());
764 assert!(!base2.is_updateable());
765 }
766
767 #[test]
768 fn meta_data_column_accessors() {
769 let m = meta(TypeInfo::FixedLen(FixedLenType::Int4), "id");
770 assert_eq!(m.col_name(), "id");
771 assert!(m.base().is_nullable());
772 }
773
774 #[test]
775 fn display_formats_more_types() {
776 let cases = vec![
777 (
778 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Bitn, 1, None)),
779 "c bit",
780 ),
781 (
782 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 8, None)),
783 "c datetime",
784 ),
785 (
786 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 4, None)),
787 "c smallmoney",
788 ),
789 (
790 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 8, None)),
791 "c money",
792 ),
793 (
794 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarChar, 100, None)),
795 "c varchar(100)",
796 ),
797 (
798 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarChar, 100000, None)),
799 "c varchar(max)",
800 ),
801 (
802 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigBinary, 10, None)),
803 "c binary(10)",
804 ),
805 (
806 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigChar, 10, None)),
807 "c char(10)",
808 ),
809 (
810 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NVarchar, 100, None)),
811 "c nvarchar(50)",
812 ),
813 (
814 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NVarchar, 8000, None)),
815 "c nvarchar(4000)",
816 ),
817 (
818 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NVarchar, 100000, None)),
819 "c nvarchar(max)",
820 ),
821 (
822 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NChar, 10, None)),
823 "c nchar(5)",
824 ),
825 (
826 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 4, None)),
827 "c smalldatetime",
828 ),
829 (
830 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Text, 0, None)),
831 "c text",
832 ),
833 (
834 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Image, 0, None)),
835 "c image",
836 ),
837 (
838 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NText, 0, None)),
839 "c ntext",
840 ),
841 (
842 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 2, None)),
843 "c smallint",
844 ),
845 (
846 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 8, None)),
847 "c bigint",
848 ),
849 (
850 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 8, None)),
851 "c float",
852 ),
853 (
854 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::SSVariant, 0, None)),
855 "c sql_variant",
856 ),
857 ];
858
859 for (ty, expected) in cases {
860 let expected = expected.replacen("c ", "[c] ", 1);
861 assert_eq!(format!("{}", meta(ty, "c")), expected);
862 }
863 }
864
865 #[test]
866 fn display_formats_udt_and_decimaln() {
867 let udt = TypeInfo::Udt(crate::tds::codec::type_info::UdtInfo {
868 max_byte_size: 0,
869 db_name: "db".into(),
870 schema_name: "dbo".into(),
871 type_name: "MyType".into(),
872 assembly_qualified_name: "asm".into(),
873 });
874 assert_eq!(format!("{}", meta(udt, "c")), "[c] dbo.MyType");
875
876 let decimaln = TypeInfo::VarLenSizedPrecision {
877 ty: VarLenType::Decimaln,
878 size: 17,
879 precision: 10,
880 scale: 4,
881 };
882 assert_eq!(format!("{}", meta(decimaln, "c")), "[c] decimal(10,4)");
883 }
884
885 #[tokio::test]
886 async fn decode_all_ones_column_count_yields_empty() {
887 let mut buf = BytesMut::new();
890 buf.put_u16_le(0xffff);
891
892 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
893 .await
894 .unwrap();
895 assert!(decoded.columns.is_empty());
896 }
897
898 #[test]
899 fn display_formats_fixed_len_money_and_datetime_types() {
900 let cases = vec![
904 (TypeInfo::FixedLen(FixedLenType::Int1), "c tinyint"),
905 (TypeInfo::FixedLen(FixedLenType::Int2), "c smallint"),
906 (
907 TypeInfo::FixedLen(FixedLenType::Datetime4),
908 "c smalldatetime",
909 ),
910 (TypeInfo::FixedLen(FixedLenType::Float4), "c real"),
911 (TypeInfo::FixedLen(FixedLenType::Money), "c money"),
912 (TypeInfo::FixedLen(FixedLenType::Datetime), "c datetime"),
913 (TypeInfo::FixedLen(FixedLenType::Money4), "c smallmoney"),
914 (TypeInfo::FixedLen(FixedLenType::Int8), "c bigint"),
915 (TypeInfo::FixedLen(FixedLenType::Null), "c int"),
916 ];
917
918 for (ty, expected) in cases {
919 let expected = expected.replacen("c ", "[c] ", 1);
920 assert_eq!(format!("{}", meta(ty, "c")), expected);
921 }
922 }
923
924 #[cfg(feature = "tds73")]
925 #[test]
926 fn display_formats_tds73_date_time_types() {
927 let cases = vec![
929 (
930 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Daten, 3, None)),
931 "c date",
932 ),
933 (
934 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Timen, 7, None)),
935 "c time(7)",
936 ),
937 (
938 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Timen, 3, None)),
939 "c time(3)",
940 ),
941 (
942 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetime2, 7, None)),
943 "c datetime2(7)",
944 ),
945 (
946 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::DatetimeOffsetn, 0, None)),
947 "c datetimeoffset(0)",
948 ),
949 ];
950
951 for (ty, expected) in cases {
952 let expected = expected.replacen("c ", "[c] ", 1);
953 assert_eq!(format!("{}", meta(ty, "c")), expected);
954 }
955 }
956
957 #[test]
958 fn display_quotes_the_column_name() {
959 let m = meta(TypeInfo::FixedLen(FixedLenType::Int4), "a]b");
960 assert_eq!(format!("{m}"), "[a]]b] int");
961 }
962
963 #[test]
964 fn text_column_metadata_carries_a_table_name() {
965 let text = meta(
966 TypeInfo::VarLenSized(VarLenContext::new(
967 VarLenType::Text,
968 2147483647,
969 Some(Collation::new(13632521, 52)),
970 )),
971 "c",
972 );
973 let mut buf = BytesMut::new();
974 text.encode(&mut buf).unwrap();
975
976 assert_eq!(buf.len(), 21);
979 assert_eq!(&buf[16..18], &[0, 0]);
980 }
981
982 #[test]
983 fn variant_collation_is_kept_off_the_wire() {
984 let mut base = BaseMetaDataColumn {
985 flags: BitFlags::empty(),
986 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::SSVariant, 8016, None)),
987 };
988 let mut plain = BytesMut::new();
989 base.clone().encode(&mut plain).unwrap();
990
991 base.set_variant_collation(Some(Collation::new(13632521, 52)));
992 let mut stamped = BytesMut::new();
993 base.clone().encode(&mut stamped).unwrap();
994
995 assert_eq!(plain, stamped);
996 match base.ty {
997 TypeInfo::VarLenSized(cx) => assert!(cx.collation().is_some()),
998 _ => unreachable!(),
999 }
1000 }
1001
1002 #[test]
1003 fn display_var_len_other_fallback_uses_debug_name() {
1004 let ty = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Decimaln, 17, None));
1008 assert_eq!(format!("{}", meta(ty, "c")), "[c] Decimaln");
1009 }
1010
1011 #[test]
1012 fn null_value_all_fixed_len() {
1013 use FixedLenType::*;
1014 let cases = [
1015 (Null, ColumnData::I32(None)),
1016 (Int1, ColumnData::U8(None)),
1017 (Bit, ColumnData::Bit(None)),
1018 (Int2, ColumnData::I16(None)),
1019 (Int4, ColumnData::I32(None)),
1020 (Datetime4, ColumnData::SmallDateTime(None)),
1021 (Float4, ColumnData::F32(None)),
1022 (Money, ColumnData::Numeric(None)),
1023 (Datetime, ColumnData::DateTime(None)),
1024 (Float8, ColumnData::F64(None)),
1025 (Money4, ColumnData::Numeric(None)),
1026 (Int8, ColumnData::I64(None)),
1027 ];
1028
1029 for (ty, expected) in cases {
1030 let base = BaseMetaDataColumn {
1031 flags: BitFlags::empty(),
1032 ty: TypeInfo::FixedLen(ty),
1033 };
1034 assert_eq!(base.null_value(), expected);
1035 }
1036 }
1037
1038 fn vsize_null(ty: VarLenType, len: usize) -> ColumnData<'static> {
1039 BaseMetaDataColumn {
1040 flags: BitFlags::empty(),
1041 ty: TypeInfo::VarLenSized(VarLenContext::new(ty, len, None)),
1042 }
1043 .null_value()
1044 }
1045
1046 #[test]
1047 fn null_value_all_var_len_sized() {
1048 use VarLenType::*;
1049 assert_eq!(vsize_null(Guid, 16), ColumnData::Guid(None));
1050 assert_eq!(vsize_null(Bitn, 1), ColumnData::Bit(None));
1051 assert_eq!(vsize_null(Decimaln, 17), ColumnData::Numeric(None));
1052 assert_eq!(vsize_null(Numericn, 17), ColumnData::Numeric(None));
1053 assert_eq!(vsize_null(Money, 4), ColumnData::Numeric(None));
1054 assert_eq!(vsize_null(Money, 8), ColumnData::Numeric(None));
1055 assert_eq!(vsize_null(Datetimen, 4), ColumnData::SmallDateTime(None));
1056 assert_eq!(vsize_null(Datetimen, 8), ColumnData::DateTime(None));
1057 assert_eq!(vsize_null(BigVarBin, 100), ColumnData::Binary(None));
1058 assert_eq!(vsize_null(BigVarChar, 100), ColumnData::String(None));
1059 assert_eq!(vsize_null(BigBinary, 10), ColumnData::Binary(None));
1060 assert_eq!(vsize_null(BigChar, 10), ColumnData::String(None));
1061 assert_eq!(vsize_null(NVarchar, 100), ColumnData::String(None));
1062 assert_eq!(vsize_null(NChar, 10), ColumnData::String(None));
1063 assert_eq!(vsize_null(Xml, 0), ColumnData::Xml(None));
1064 assert_eq!(vsize_null(Udt, 0), ColumnData::Binary(None));
1065 assert_eq!(vsize_null(Text, 0), ColumnData::String(None));
1066 assert_eq!(vsize_null(Image, 0), ColumnData::Binary(None));
1067 assert_eq!(vsize_null(NText, 0), ColumnData::String(None));
1068 assert_eq!(vsize_null(SSVariant, 0), ColumnData::String(None));
1069 }
1070
1071 #[cfg(feature = "tds73")]
1072 #[test]
1073 fn null_value_var_len_sized_tds73() {
1074 use VarLenType::*;
1075 assert_eq!(vsize_null(Daten, 3), ColumnData::Date(None));
1076 assert_eq!(vsize_null(Timen, 7), ColumnData::Time(None));
1077 assert_eq!(vsize_null(Datetime2, 7), ColumnData::DateTime2(None));
1078 assert_eq!(
1079 vsize_null(DatetimeOffsetn, 7),
1080 ColumnData::DateTimeOffset(None)
1081 );
1082 }
1083
1084 fn vprec_null(ty: VarLenType) -> ColumnData<'static> {
1085 BaseMetaDataColumn {
1086 flags: BitFlags::empty(),
1087 ty: TypeInfo::VarLenSizedPrecision {
1088 ty,
1089 size: 8,
1090 precision: 18,
1091 scale: 2,
1092 },
1093 }
1094 .null_value()
1095 }
1096
1097 #[test]
1098 fn null_value_all_var_len_precision() {
1099 use VarLenType::*;
1100 assert_eq!(vprec_null(Guid), ColumnData::Guid(None));
1101 assert_eq!(vprec_null(Intn), ColumnData::I32(None));
1102 assert_eq!(vprec_null(Bitn), ColumnData::Bit(None));
1103 assert_eq!(vprec_null(Decimaln), ColumnData::Numeric(None));
1104 assert_eq!(vprec_null(Numericn), ColumnData::Numeric(None));
1105 assert_eq!(vprec_null(Floatn), ColumnData::F32(None));
1106 assert_eq!(vprec_null(Money), ColumnData::Numeric(None));
1107 assert_eq!(vprec_null(Datetimen), ColumnData::DateTime(None));
1108 assert_eq!(vprec_null(BigVarBin), ColumnData::Binary(None));
1109 assert_eq!(vprec_null(BigVarChar), ColumnData::String(None));
1110 assert_eq!(vprec_null(BigBinary), ColumnData::Binary(None));
1111 assert_eq!(vprec_null(BigChar), ColumnData::String(None));
1112 assert_eq!(vprec_null(NVarchar), ColumnData::String(None));
1113 assert_eq!(vprec_null(NChar), ColumnData::String(None));
1114 assert_eq!(vprec_null(Xml), ColumnData::Xml(None));
1115 assert_eq!(vprec_null(Udt), ColumnData::Binary(None));
1116 assert_eq!(vprec_null(Text), ColumnData::String(None));
1117 assert_eq!(vprec_null(Image), ColumnData::Binary(None));
1118 assert_eq!(vprec_null(NText), ColumnData::String(None));
1119 assert_eq!(vprec_null(SSVariant), ColumnData::String(None));
1120 }
1121
1122 #[cfg(feature = "tds73")]
1123 #[test]
1124 fn null_value_var_len_precision_tds73() {
1125 use VarLenType::*;
1126 assert_eq!(vprec_null(Daten), ColumnData::Date(None));
1127 assert_eq!(vprec_null(Timen), ColumnData::Time(None));
1128 assert_eq!(vprec_null(Datetime2), ColumnData::DateTime2(None));
1129 assert_eq!(
1130 vprec_null(DatetimeOffsetn),
1131 ColumnData::DateTimeOffset(None)
1132 );
1133 }
1134
1135 #[test]
1136 fn column_flag_bits_are_distinct() {
1137 let all = ColumnFlag::Nullable
1138 | ColumnFlag::CaseSensitive
1139 | ColumnFlag::Updateable
1140 | ColumnFlag::UpdateableUnknown
1141 | ColumnFlag::Identity
1142 | ColumnFlag::Computed
1143 | ColumnFlag::FixedLenClrType
1144 | ColumnFlag::SparseColumnSet
1145 | ColumnFlag::Encrypted
1146 | ColumnFlag::Hidden
1147 | ColumnFlag::Key
1148 | ColumnFlag::NullableUnknown;
1149
1150 assert!(all.contains(ColumnFlag::Nullable));
1151 assert!(all.contains(ColumnFlag::NullableUnknown));
1152 assert_eq!(BitFlags::bits(all).count_ones(), 12);
1153 }
1154
1155 #[test]
1156 fn column_name_over_255_units_is_an_error() {
1157 let column = |name: String| MetaDataColumn {
1159 base: BaseMetaDataColumn {
1160 flags: ColumnFlag::Nullable.into(),
1161 ty: TypeInfo::FixedLen(FixedLenType::Int4),
1162 },
1163 col_name: name.into(),
1164 };
1165
1166 for name in ["a".repeat(256), "\u{1F600}".repeat(128)] {
1167 let err = column(name).encode(&mut BytesMut::new()).unwrap_err();
1168 assert!(matches!(err, crate::Error::Conversion(_)), "{err:?}");
1169 }
1170
1171 let mut buf = BytesMut::new();
1173 column("a".repeat(255)).encode(&mut buf).unwrap();
1174 assert_eq!(buf[7], 255);
1175 }
1176}