1use std::{
2 borrow::{BorrowMut, Cow},
3 fmt::Display,
4};
5
6use crate::{
7 tds::codec::{Encode, FixedLenType, TokenType, TypeInfo, VarLenType},
8 Column, ColumnData, ColumnType, SqlReadBytes,
9};
10use asynchronous_codec::BytesMut;
11use bytes::BufMut;
12use enumflags2::{bitflags, BitFlags};
13
14#[derive(Debug, Clone)]
15pub struct TokenColMetaData<'a> {
16 pub columns: Vec<MetaDataColumn<'a>>,
17}
18
19#[derive(Debug, Clone)]
22pub struct MetaDataColumn<'a> {
23 pub base: BaseMetaDataColumn,
25 pub col_name: Cow<'a, str>,
27}
28
29impl<'a> MetaDataColumn<'a> {
30 pub fn col_name(&self) -> &str {
32 self.col_name.as_ref()
33 }
34
35 pub fn base(&self) -> &BaseMetaDataColumn {
38 &self.base
39 }
40}
41
42impl<'a> Display for MetaDataColumn<'a> {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 write!(f, "[{}] ", self.col_name)?;
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")?,
71 #[cfg(feature = "tds73")]
72 VarLenType::Datetime2 => write!(f, "datetime2({})", ctx.len())?,
73 VarLenType::Datetimen => write!(f, "datetime")?,
74 VarLenType::Money => match ctx.len() {
75 4 => write!(f, "smallmoney")?,
76 _ => write!(f, "money")?,
77 },
78 #[cfg(feature = "tds73")]
79 VarLenType::DatetimeOffsetn => write!(f, "datetimeoffset")?,
80 VarLenType::BigVarBin => {
81 if ctx.len() <= 8000 {
82 write!(f, "varbinary({})", ctx.len())?
83 } else {
84 write!(f, "varbinary(max)")?
85 }
86 }
87 VarLenType::BigVarChar => {
88 if ctx.len() <= 8000 {
89 write!(f, "varchar({})", ctx.len())?
90 } else {
91 write!(f, "varchar(max)")?
92 }
93 }
94 VarLenType::BigBinary => write!(f, "binary({})", ctx.len())?,
95 VarLenType::BigChar => write!(f, "char({})", ctx.len())?,
96 VarLenType::NVarchar => {
97 if ctx.len() <= 4000 {
98 write!(f, "nvarchar({})", ctx.len())?
99 } else {
100 write!(f, "nvarchar(max)")?
101 }
102 }
103 VarLenType::NChar => write!(f, "nchar({})", ctx.len())?,
104 VarLenType::Text => write!(f, "text")?,
105 VarLenType::Image => write!(f, "image")?,
106 VarLenType::NText => write!(f, "ntext")?,
107 VarLenType::Intn => match ctx.len() {
108 1 => write!(f, "tinyint")?,
109 2 => write!(f, "smallint")?,
110 4 => write!(f, "int")?,
111 _ => write!(f, "bigint")?,
112 },
113 VarLenType::Floatn => match ctx.len() {
114 4 => write!(f, "real")?,
115 _ => write!(f, "float")?,
116 },
117 VarLenType::SSVariant => write!(f, "sql_variant")?,
118 other => write!(f, "{other:?}")?,
121 },
122 TypeInfo::VarLenSizedPrecision {
123 ty,
124 size: _,
125 precision,
126 scale,
127 } => match ty {
128 VarLenType::Numericn => write!(f, "numeric({},{})", precision, scale)?,
129 _ => write!(f, "decimal({},{})", precision, scale)?,
131 },
132 TypeInfo::Xml { .. } => write!(f, "xml")?,
133 TypeInfo::Udt(info) => write!(f, "{}.{}", info.schema_name, info.type_name)?,
134 }
135
136 Ok(())
137 }
138}
139
140#[derive(Debug, Clone)]
144pub struct BaseMetaDataColumn {
145 pub flags: BitFlags<ColumnFlag>,
148 pub ty: TypeInfo,
151}
152
153impl BaseMetaDataColumn {
154 pub fn ty(&self) -> &TypeInfo {
157 &self.ty
158 }
159
160 pub fn flags(&self) -> BitFlags<ColumnFlag> {
162 self.flags
163 }
164
165 pub fn is_nullable(&self) -> bool {
167 self.flags.contains(ColumnFlag::Nullable)
168 }
169
170 pub fn is_identity(&self) -> bool {
172 self.flags.contains(ColumnFlag::Identity)
173 }
174
175 pub fn is_updateable(&self) -> bool {
177 self.flags.contains(ColumnFlag::Updateable)
178 }
179
180 pub(crate) fn null_value(&self) -> ColumnData<'static> {
181 match &self.ty {
182 TypeInfo::FixedLen(ty) => match ty {
183 FixedLenType::Null => ColumnData::I32(None),
184 FixedLenType::Int1 => ColumnData::U8(None),
185 FixedLenType::Bit => ColumnData::Bit(None),
186 FixedLenType::Int2 => ColumnData::I16(None),
187 FixedLenType::Int4 => ColumnData::I32(None),
188 FixedLenType::Datetime4 => ColumnData::SmallDateTime(None),
189 FixedLenType::Float4 => ColumnData::F32(None),
190 FixedLenType::Money => ColumnData::F64(None),
191 FixedLenType::Datetime => ColumnData::DateTime(None),
192 FixedLenType::Float8 => ColumnData::F64(None),
193 FixedLenType::Money4 => ColumnData::F32(None),
194 FixedLenType::Int8 => ColumnData::I64(None),
195 },
196 TypeInfo::VarLenSized(cx) => match cx.r#type() {
197 VarLenType::Guid => ColumnData::Guid(None),
198 VarLenType::Intn => match cx.len() {
199 1 => ColumnData::U8(None),
200 2 => ColumnData::I16(None),
201 4 => ColumnData::I32(None),
202 _ => ColumnData::I64(None),
203 },
204 VarLenType::Bitn => ColumnData::Bit(None),
205 VarLenType::Decimaln => ColumnData::Numeric(None),
206 VarLenType::Numericn => ColumnData::Numeric(None),
207 VarLenType::Floatn => match cx.len() {
208 4 => ColumnData::F32(None),
209 _ => ColumnData::F64(None),
210 },
211 VarLenType::Money => ColumnData::F64(None),
212 VarLenType::Datetimen => ColumnData::DateTime(None),
213 #[cfg(feature = "tds73")]
214 VarLenType::Daten => ColumnData::Date(None),
215 #[cfg(feature = "tds73")]
216 VarLenType::Timen => ColumnData::Time(None),
217 #[cfg(feature = "tds73")]
218 VarLenType::Datetime2 => ColumnData::DateTime2(None),
219 #[cfg(feature = "tds73")]
220 VarLenType::DatetimeOffsetn => ColumnData::DateTimeOffset(None),
221 VarLenType::BigVarBin => ColumnData::Binary(None),
222 VarLenType::BigVarChar => ColumnData::String(None),
223 VarLenType::BigBinary => ColumnData::Binary(None),
224 VarLenType::BigChar => ColumnData::String(None),
225 VarLenType::NVarchar => ColumnData::String(None),
226 VarLenType::NChar => ColumnData::String(None),
227 VarLenType::Xml => ColumnData::Xml(None),
228 VarLenType::Udt => ColumnData::Binary(None),
233 VarLenType::Text => ColumnData::String(None),
234 VarLenType::Image => ColumnData::Binary(None),
235 VarLenType::NText => ColumnData::String(None),
236 VarLenType::SSVariant => ColumnData::String(None),
239 },
240 TypeInfo::VarLenSizedPrecision { ty, .. } => match ty {
241 VarLenType::Guid => ColumnData::Guid(None),
242 VarLenType::Intn => ColumnData::I32(None),
243 VarLenType::Bitn => ColumnData::Bit(None),
244 VarLenType::Decimaln => ColumnData::Numeric(None),
245 VarLenType::Numericn => ColumnData::Numeric(None),
246 VarLenType::Floatn => ColumnData::F32(None),
247 VarLenType::Money => ColumnData::F64(None),
248 VarLenType::Datetimen => ColumnData::DateTime(None),
249 #[cfg(feature = "tds73")]
250 VarLenType::Daten => ColumnData::Date(None),
251 #[cfg(feature = "tds73")]
252 VarLenType::Timen => ColumnData::Time(None),
253 #[cfg(feature = "tds73")]
254 VarLenType::Datetime2 => ColumnData::DateTime2(None),
255 #[cfg(feature = "tds73")]
256 VarLenType::DatetimeOffsetn => ColumnData::DateTimeOffset(None),
257 VarLenType::BigVarBin => ColumnData::Binary(None),
258 VarLenType::BigVarChar => ColumnData::String(None),
259 VarLenType::BigBinary => ColumnData::Binary(None),
260 VarLenType::BigChar => ColumnData::String(None),
261 VarLenType::NVarchar => ColumnData::String(None),
262 VarLenType::NChar => ColumnData::String(None),
263 VarLenType::Xml => ColumnData::Xml(None),
264 VarLenType::Udt => ColumnData::Binary(None),
269 VarLenType::Text => ColumnData::String(None),
270 VarLenType::Image => ColumnData::Binary(None),
271 VarLenType::NText => ColumnData::String(None),
272 VarLenType::SSVariant => ColumnData::String(None),
275 },
276 TypeInfo::Xml { .. } => ColumnData::Xml(None),
277 TypeInfo::Udt(_) => ColumnData::Binary(None),
278 }
279 }
280}
281
282impl<'a> Encode<BytesMut> for TokenColMetaData<'a> {
283 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
284 dst.put_u8(TokenType::ColMetaData as u8);
285 dst.put_u16_le(self.columns.len() as u16);
286
287 for col in self.columns.into_iter() {
288 col.encode(dst)?;
289 }
290
291 Ok(())
292 }
293}
294
295impl<'a> Encode<BytesMut> for MetaDataColumn<'a> {
296 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
297 dst.put_u32_le(0);
298 self.base.encode(dst)?;
299
300 let len_pos = dst.len();
301 let mut length = 0u8;
302
303 dst.put_u8(length);
304
305 for chr in self.col_name.encode_utf16() {
306 length += 1;
307 dst.put_u16_le(chr);
308 }
309
310 let dst: &mut [u8] = dst.borrow_mut();
311 dst[len_pos] = length;
312
313 Ok(())
314 }
315}
316
317impl Encode<BytesMut> for BaseMetaDataColumn {
318 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
319 dst.put_u16_le(BitFlags::bits(self.flags));
320 self.ty.encode(dst)?;
321
322 Ok(())
323 }
324}
325
326#[bitflags]
328#[repr(u16)]
329#[derive(Debug, Clone, Copy, PartialEq, Eq)]
330pub enum ColumnFlag {
331 Nullable = 1,
333 CaseSensitive = 1 << 1,
336 Updateable = 1 << 3,
338 UpdateableUnknown = 1 << 2,
340 Identity = 1 << 4,
342 Computed = 1 << 7,
344 FixedLenClrType = 1 << 10,
347 SparseColumnSet = 1 << 11,
349 Encrypted = 1 << 12,
353 Hidden = 1 << 13,
356 Key = 1 << 14,
359 NullableUnknown = 1 << 15,
361}
362
363impl TokenColMetaData<'static> {
364 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
365 where
366 R: SqlReadBytes + Unpin,
367 {
368 let column_count = src.read_u16_le().await?;
369 let mut columns = Vec::with_capacity(
374 (column_count as usize).min(crate::tds::codec::column_data::MAX_PREALLOC),
375 );
376
377 if column_count < 0xffff {
380 for _ in 0..column_count {
381 let base = BaseMetaDataColumn::decode(src).await?;
382 let col_name = Cow::from(src.read_b_varchar().await?);
383
384 columns.push(MetaDataColumn { base, col_name });
385 }
386 }
387
388 Ok(TokenColMetaData { columns })
389 }
390}
391
392impl<'a> TokenColMetaData<'a> {
393 pub(crate) fn columns(&self) -> impl Iterator<Item = Column> + '_ {
394 self.columns.iter().map(|x| Column {
395 name: x.col_name.to_string(),
396 column_type: ColumnType::from(&x.base.ty),
397 })
398 }
399}
400
401impl BaseMetaDataColumn {
402 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
403 where
404 R: SqlReadBytes + Unpin,
405 {
406 use VarLenType::*;
407
408 let _user_ty = src.read_u32_le().await?;
409
410 let flags = BitFlags::from_bits_truncate(src.read_u16_le().await?);
415
416 let ty = TypeInfo::decode(src).await?;
417
418 if let TypeInfo::VarLenSized(cx) = ty {
419 if let Text | NText | Image = cx.r#type() {
420 let num_of_parts = src.read_u8().await?;
421
422 for _ in 0..num_of_parts {
424 src.read_us_varchar().await?;
425 }
426 };
427 };
428
429 Ok(BaseMetaDataColumn { flags, ty })
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
437 use crate::tds::Collation;
438 use crate::VarLenContext;
439
440 fn meta(ty: TypeInfo, name: &'static str) -> MetaDataColumn<'static> {
441 MetaDataColumn {
442 base: BaseMetaDataColumn {
443 flags: ColumnFlag::Nullable.into(),
444 ty,
445 },
446 col_name: Cow::Borrowed(name),
447 }
448 }
449
450 #[tokio::test]
451 async fn round_trip_via_encode_decode() {
452 let cmd = TokenColMetaData {
453 columns: vec![
454 meta(TypeInfo::FixedLen(FixedLenType::Int4), "id"),
455 meta(
456 TypeInfo::VarLenSized(VarLenContext::new(
457 VarLenType::NVarchar,
458 4000,
459 Some(Collation::new(13632521, 52)),
460 )),
461 "name",
462 ),
463 ],
464 };
465
466 let mut buf = BytesMut::new();
470 buf.put_u16_le(cmd.columns.len() as u16);
471 for col in cmd.columns.iter().cloned() {
472 col.encode(&mut buf).unwrap();
473 }
474
475 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
476 .await
477 .unwrap();
478
479 assert_eq!(decoded.columns.len(), 2);
480 assert_eq!(decoded.columns[0].col_name, "id");
481 assert_eq!(decoded.columns[1].col_name, "name");
482
483 let columns: Vec<_> = decoded.columns().collect();
484 assert_eq!(columns.len(), 2);
485 assert_eq!(columns[0].name(), "id");
486 }
487
488 #[test]
489 fn encode_writes_token_header_and_column_count() {
490 let cmd = TokenColMetaData {
491 columns: vec![
492 meta(TypeInfo::FixedLen(FixedLenType::Int4), "id"),
493 meta(TypeInfo::FixedLen(FixedLenType::Bit), "flag"),
494 ],
495 };
496
497 let mut buf = BytesMut::new();
498 cmd.encode(&mut buf).unwrap();
499
500 assert_eq!(buf[0], TokenType::ColMetaData as u8);
502 assert_eq!(u16::from_le_bytes([buf[1], buf[2]]), 2);
503 assert!(buf.len() > 3);
505 }
506
507 #[tokio::test]
508 async fn zero_columns_yields_empty() {
509 let mut buf = BytesMut::new();
510 buf.put_u16_le(0);
511
512 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
513 .await
514 .unwrap();
515 assert!(decoded.columns.is_empty());
516 }
517
518 #[tokio::test]
519 async fn text_column_reads_table_name_parts() {
520 let mut buf = BytesMut::new();
521 buf.put_u16_le(1); buf.put_u32_le(0);
525 buf.put_u16_le(BitFlags::bits(BitFlags::from(ColumnFlag::Nullable)));
526
527 let ti = TypeInfo::VarLenSized(VarLenContext::new(
529 VarLenType::Text,
530 2147483647,
531 Some(Collation::new(13632521, 52)),
532 ));
533 ti.encode(&mut buf).unwrap();
534
535 buf.put_u8(1);
537 let part: Vec<u16> = "dbo".encode_utf16().collect();
538 buf.put_u16_le(part.len() as u16);
539 for c in part {
540 buf.put_u16_le(c);
541 }
542
543 let name: Vec<u16> = "body".encode_utf16().collect();
545 buf.put_u8(name.len() as u8);
546 for c in name {
547 buf.put_u16_le(c);
548 }
549
550 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
551 .await
552 .unwrap();
553
554 assert_eq!(decoded.columns.len(), 1);
555 assert_eq!(decoded.columns[0].col_name, "body");
556 }
557
558 #[test]
559 fn display_formats_various_types() {
560 let cases = vec![
561 (TypeInfo::FixedLen(FixedLenType::Int4), "c int"),
562 (TypeInfo::FixedLen(FixedLenType::Bit), "c bit"),
563 (TypeInfo::FixedLen(FixedLenType::Float8), "c float"),
564 (
565 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 1, None)),
566 "c tinyint",
567 ),
568 (
569 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 4, None)),
570 "c int",
571 ),
572 (
573 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 4, None)),
574 "c real",
575 ),
576 (
577 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Guid, 16, None)),
578 "c uniqueidentifier",
579 ),
580 (
581 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarBin, 100, None)),
582 "c varbinary(100)",
583 ),
584 (
585 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarBin, 100000, None)),
586 "c varbinary(max)",
587 ),
588 (
589 TypeInfo::VarLenSizedPrecision {
590 ty: VarLenType::Decimaln,
591 size: 17,
592 precision: 18,
593 scale: 2,
594 },
595 "c decimal(18,2)",
596 ),
597 (
600 TypeInfo::VarLenSizedPrecision {
601 ty: VarLenType::Numericn,
602 size: 9,
603 precision: 10,
604 scale: 4,
605 },
606 "c numeric(10,4)",
607 ),
608 (
609 TypeInfo::Xml {
610 schema: None,
611 size: 0,
612 },
613 "c xml",
614 ),
615 ];
616
617 for (ty, expected) in cases {
618 let expected = expected.replacen("c ", "[c] ", 1);
620 assert_eq!(format!("{}", meta(ty, "c")), expected);
621 }
622 }
623
624 #[test]
625 fn null_value_maps_types() {
626 let fixed = BaseMetaDataColumn {
627 flags: BitFlags::empty(),
628 ty: TypeInfo::FixedLen(FixedLenType::Int4),
629 };
630 assert_eq!(fixed.null_value(), ColumnData::I32(None));
631
632 let varlen = BaseMetaDataColumn {
633 flags: BitFlags::empty(),
634 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 2, None)),
635 };
636 assert_eq!(varlen.null_value(), ColumnData::I16(None));
637
638 let tinyint = BaseMetaDataColumn {
641 flags: BitFlags::empty(),
642 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 1, None)),
643 };
644 assert_eq!(tinyint.null_value(), ColumnData::U8(None));
645
646 let int4 = BaseMetaDataColumn {
647 flags: BitFlags::empty(),
648 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 4, None)),
649 };
650 assert_eq!(int4.null_value(), ColumnData::I32(None));
651
652 let int8 = BaseMetaDataColumn {
653 flags: BitFlags::empty(),
654 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 8, None)),
655 };
656 assert_eq!(int8.null_value(), ColumnData::I64(None));
657
658 let real = BaseMetaDataColumn {
660 flags: BitFlags::empty(),
661 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 4, None)),
662 };
663 assert_eq!(real.null_value(), ColumnData::F32(None));
664
665 let double = BaseMetaDataColumn {
666 flags: BitFlags::empty(),
667 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 8, None)),
668 };
669 assert_eq!(double.null_value(), ColumnData::F64(None));
670
671 let guid = BaseMetaDataColumn {
672 flags: BitFlags::empty(),
673 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Guid, 16, None)),
674 };
675 assert_eq!(guid.null_value(), ColumnData::Guid(None));
676 }
677
678 #[test]
679 fn null_value_maps_precision_and_xml_and_udt() {
680 let precision = BaseMetaDataColumn {
681 flags: BitFlags::empty(),
682 ty: TypeInfo::VarLenSizedPrecision {
683 ty: VarLenType::Numericn,
684 size: 17,
685 precision: 18,
686 scale: 2,
687 },
688 };
689 assert_eq!(precision.null_value(), ColumnData::Numeric(None));
690
691 let xml = BaseMetaDataColumn {
692 flags: BitFlags::empty(),
693 ty: TypeInfo::Xml {
694 schema: None,
695 size: 0,
696 },
697 };
698 assert_eq!(xml.null_value(), ColumnData::Xml(None));
699
700 let udt = BaseMetaDataColumn {
701 flags: BitFlags::empty(),
702 ty: TypeInfo::Udt(crate::tds::codec::type_info::UdtInfo {
703 max_byte_size: 0,
704 db_name: "db".into(),
705 schema_name: "dbo".into(),
706 type_name: "T".into(),
707 assembly_qualified_name: "A".into(),
708 }),
709 };
710 assert_eq!(udt.null_value(), ColumnData::Binary(None));
711 }
712
713 #[test]
714 fn base_meta_data_column_flag_accessors() {
715 let base = BaseMetaDataColumn {
716 flags: ColumnFlag::Nullable | ColumnFlag::Identity | ColumnFlag::Updateable,
717 ty: TypeInfo::FixedLen(FixedLenType::Int4),
718 };
719
720 assert!(base.is_nullable());
721 assert!(base.is_identity());
722 assert!(base.is_updateable());
723 assert_eq!(base.ty(), &TypeInfo::FixedLen(FixedLenType::Int4));
724 assert_eq!(base.flags(), base.flags);
725
726 let base2 = BaseMetaDataColumn {
727 flags: BitFlags::empty(),
728 ty: TypeInfo::FixedLen(FixedLenType::Int4),
729 };
730 assert!(!base2.is_nullable());
731 assert!(!base2.is_identity());
732 assert!(!base2.is_updateable());
733 }
734
735 #[test]
736 fn meta_data_column_accessors() {
737 let m = meta(TypeInfo::FixedLen(FixedLenType::Int4), "id");
738 assert_eq!(m.col_name(), "id");
739 assert!(m.base().is_nullable());
740 }
741
742 #[test]
743 fn display_formats_more_types() {
744 let cases = vec![
745 (
746 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Bitn, 1, None)),
747 "c bit",
748 ),
749 (
750 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 8, None)),
751 "c datetime",
752 ),
753 (
754 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 4, None)),
755 "c smallmoney",
756 ),
757 (
758 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 8, None)),
759 "c money",
760 ),
761 (
762 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarChar, 100, None)),
763 "c varchar(100)",
764 ),
765 (
766 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarChar, 100000, None)),
767 "c varchar(max)",
768 ),
769 (
770 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigBinary, 10, None)),
771 "c binary(10)",
772 ),
773 (
774 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigChar, 10, None)),
775 "c char(10)",
776 ),
777 (
778 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NVarchar, 100, None)),
779 "c nvarchar(100)",
780 ),
781 (
782 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NVarchar, 100000, None)),
783 "c nvarchar(max)",
784 ),
785 (
786 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NChar, 10, None)),
787 "c nchar(10)",
788 ),
789 (
790 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Text, 0, None)),
791 "c text",
792 ),
793 (
794 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Image, 0, None)),
795 "c image",
796 ),
797 (
798 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NText, 0, None)),
799 "c ntext",
800 ),
801 (
802 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 2, None)),
803 "c smallint",
804 ),
805 (
806 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 8, None)),
807 "c bigint",
808 ),
809 (
810 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 8, None)),
811 "c float",
812 ),
813 (
814 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::SSVariant, 0, None)),
815 "c sql_variant",
816 ),
817 ];
818
819 for (ty, expected) in cases {
820 let expected = expected.replacen("c ", "[c] ", 1);
821 assert_eq!(format!("{}", meta(ty, "c")), expected);
822 }
823 }
824
825 #[test]
826 fn display_formats_udt_and_decimaln() {
827 let udt = TypeInfo::Udt(crate::tds::codec::type_info::UdtInfo {
828 max_byte_size: 0,
829 db_name: "db".into(),
830 schema_name: "dbo".into(),
831 type_name: "MyType".into(),
832 assembly_qualified_name: "asm".into(),
833 });
834 assert_eq!(format!("{}", meta(udt, "c")), "[c] dbo.MyType");
835
836 let decimaln = TypeInfo::VarLenSizedPrecision {
837 ty: VarLenType::Decimaln,
838 size: 17,
839 precision: 10,
840 scale: 4,
841 };
842 assert_eq!(format!("{}", meta(decimaln, "c")), "[c] decimal(10,4)");
843 }
844
845 #[tokio::test]
846 async fn decode_all_ones_column_count_yields_empty() {
847 let mut buf = BytesMut::new();
850 buf.put_u16_le(0xffff);
851
852 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
853 .await
854 .unwrap();
855 assert!(decoded.columns.is_empty());
856 }
857
858 #[test]
859 fn display_formats_fixed_len_money_and_datetime_types() {
860 let cases = vec![
864 (TypeInfo::FixedLen(FixedLenType::Int1), "c tinyint"),
865 (TypeInfo::FixedLen(FixedLenType::Int2), "c smallint"),
866 (
867 TypeInfo::FixedLen(FixedLenType::Datetime4),
868 "c smalldatetime",
869 ),
870 (TypeInfo::FixedLen(FixedLenType::Float4), "c real"),
871 (TypeInfo::FixedLen(FixedLenType::Money), "c money"),
872 (TypeInfo::FixedLen(FixedLenType::Datetime), "c datetime"),
873 (TypeInfo::FixedLen(FixedLenType::Money4), "c smallmoney"),
874 (TypeInfo::FixedLen(FixedLenType::Int8), "c bigint"),
875 (TypeInfo::FixedLen(FixedLenType::Null), "c int"),
876 ];
877
878 for (ty, expected) in cases {
879 let expected = expected.replacen("c ", "[c] ", 1);
880 assert_eq!(format!("{}", meta(ty, "c")), expected);
881 }
882 }
883
884 #[cfg(feature = "tds73")]
885 #[test]
886 fn display_formats_tds73_date_time_types() {
887 let cases = vec![
889 (
890 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Daten, 3, None)),
891 "c date",
892 ),
893 (
894 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Timen, 7, None)),
895 "c time",
896 ),
897 (
898 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetime2, 7, None)),
899 "c datetime2(7)",
900 ),
901 (
902 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::DatetimeOffsetn, 7, None)),
903 "c datetimeoffset",
904 ),
905 ];
906
907 for (ty, expected) in cases {
908 let expected = expected.replacen("c ", "[c] ", 1);
909 assert_eq!(format!("{}", meta(ty, "c")), expected);
910 }
911 }
912
913 #[test]
914 fn display_var_len_other_fallback_uses_debug_name() {
915 let ty = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Decimaln, 17, None));
919 assert_eq!(format!("{}", meta(ty, "c")), "[c] Decimaln");
920 }
921
922 #[test]
923 fn null_value_all_fixed_len() {
924 use FixedLenType::*;
925 let cases = [
926 (Null, ColumnData::I32(None)),
927 (Int1, ColumnData::U8(None)),
928 (Bit, ColumnData::Bit(None)),
929 (Int2, ColumnData::I16(None)),
930 (Int4, ColumnData::I32(None)),
931 (Datetime4, ColumnData::SmallDateTime(None)),
932 (Float4, ColumnData::F32(None)),
933 (Money, ColumnData::F64(None)),
934 (Datetime, ColumnData::DateTime(None)),
935 (Float8, ColumnData::F64(None)),
936 (Money4, ColumnData::F32(None)),
937 (Int8, ColumnData::I64(None)),
938 ];
939
940 for (ty, expected) in cases {
941 let base = BaseMetaDataColumn {
942 flags: BitFlags::empty(),
943 ty: TypeInfo::FixedLen(ty),
944 };
945 assert_eq!(base.null_value(), expected);
946 }
947 }
948
949 fn vsize_null(ty: VarLenType, len: usize) -> ColumnData<'static> {
950 BaseMetaDataColumn {
951 flags: BitFlags::empty(),
952 ty: TypeInfo::VarLenSized(VarLenContext::new(ty, len, None)),
953 }
954 .null_value()
955 }
956
957 #[test]
958 fn null_value_all_var_len_sized() {
959 use VarLenType::*;
960 assert_eq!(vsize_null(Guid, 16), ColumnData::Guid(None));
961 assert_eq!(vsize_null(Bitn, 1), ColumnData::Bit(None));
962 assert_eq!(vsize_null(Decimaln, 17), ColumnData::Numeric(None));
963 assert_eq!(vsize_null(Numericn, 17), ColumnData::Numeric(None));
964 assert_eq!(vsize_null(Money, 8), ColumnData::F64(None));
965 assert_eq!(vsize_null(Datetimen, 8), ColumnData::DateTime(None));
966 assert_eq!(vsize_null(BigVarBin, 100), ColumnData::Binary(None));
967 assert_eq!(vsize_null(BigVarChar, 100), ColumnData::String(None));
968 assert_eq!(vsize_null(BigBinary, 10), ColumnData::Binary(None));
969 assert_eq!(vsize_null(BigChar, 10), ColumnData::String(None));
970 assert_eq!(vsize_null(NVarchar, 100), ColumnData::String(None));
971 assert_eq!(vsize_null(NChar, 10), ColumnData::String(None));
972 assert_eq!(vsize_null(Xml, 0), ColumnData::Xml(None));
973 assert_eq!(vsize_null(Udt, 0), ColumnData::Binary(None));
974 assert_eq!(vsize_null(Text, 0), ColumnData::String(None));
975 assert_eq!(vsize_null(Image, 0), ColumnData::Binary(None));
976 assert_eq!(vsize_null(NText, 0), ColumnData::String(None));
977 assert_eq!(vsize_null(SSVariant, 0), ColumnData::String(None));
978 }
979
980 #[cfg(feature = "tds73")]
981 #[test]
982 fn null_value_var_len_sized_tds73() {
983 use VarLenType::*;
984 assert_eq!(vsize_null(Daten, 3), ColumnData::Date(None));
985 assert_eq!(vsize_null(Timen, 7), ColumnData::Time(None));
986 assert_eq!(vsize_null(Datetime2, 7), ColumnData::DateTime2(None));
987 assert_eq!(
988 vsize_null(DatetimeOffsetn, 7),
989 ColumnData::DateTimeOffset(None)
990 );
991 }
992
993 fn vprec_null(ty: VarLenType) -> ColumnData<'static> {
994 BaseMetaDataColumn {
995 flags: BitFlags::empty(),
996 ty: TypeInfo::VarLenSizedPrecision {
997 ty,
998 size: 8,
999 precision: 18,
1000 scale: 2,
1001 },
1002 }
1003 .null_value()
1004 }
1005
1006 #[test]
1007 fn null_value_all_var_len_precision() {
1008 use VarLenType::*;
1009 assert_eq!(vprec_null(Guid), ColumnData::Guid(None));
1010 assert_eq!(vprec_null(Intn), ColumnData::I32(None));
1011 assert_eq!(vprec_null(Bitn), ColumnData::Bit(None));
1012 assert_eq!(vprec_null(Decimaln), ColumnData::Numeric(None));
1013 assert_eq!(vprec_null(Numericn), ColumnData::Numeric(None));
1014 assert_eq!(vprec_null(Floatn), ColumnData::F32(None));
1015 assert_eq!(vprec_null(Money), ColumnData::F64(None));
1016 assert_eq!(vprec_null(Datetimen), ColumnData::DateTime(None));
1017 assert_eq!(vprec_null(BigVarBin), ColumnData::Binary(None));
1018 assert_eq!(vprec_null(BigVarChar), ColumnData::String(None));
1019 assert_eq!(vprec_null(BigBinary), ColumnData::Binary(None));
1020 assert_eq!(vprec_null(BigChar), ColumnData::String(None));
1021 assert_eq!(vprec_null(NVarchar), ColumnData::String(None));
1022 assert_eq!(vprec_null(NChar), ColumnData::String(None));
1023 assert_eq!(vprec_null(Xml), ColumnData::Xml(None));
1024 assert_eq!(vprec_null(Udt), ColumnData::Binary(None));
1025 assert_eq!(vprec_null(Text), ColumnData::String(None));
1026 assert_eq!(vprec_null(Image), ColumnData::Binary(None));
1027 assert_eq!(vprec_null(NText), ColumnData::String(None));
1028 assert_eq!(vprec_null(SSVariant), ColumnData::String(None));
1029 }
1030
1031 #[cfg(feature = "tds73")]
1032 #[test]
1033 fn null_value_var_len_precision_tds73() {
1034 use VarLenType::*;
1035 assert_eq!(vprec_null(Daten), ColumnData::Date(None));
1036 assert_eq!(vprec_null(Timen), ColumnData::Time(None));
1037 assert_eq!(vprec_null(Datetime2), ColumnData::DateTime2(None));
1038 assert_eq!(
1039 vprec_null(DatetimeOffsetn),
1040 ColumnData::DateTimeOffset(None)
1041 );
1042 }
1043
1044 #[test]
1045 fn column_flag_bits_are_distinct() {
1046 let all = ColumnFlag::Nullable
1047 | ColumnFlag::CaseSensitive
1048 | ColumnFlag::Updateable
1049 | ColumnFlag::UpdateableUnknown
1050 | ColumnFlag::Identity
1051 | ColumnFlag::Computed
1052 | ColumnFlag::FixedLenClrType
1053 | ColumnFlag::SparseColumnSet
1054 | ColumnFlag::Encrypted
1055 | ColumnFlag::Hidden
1056 | ColumnFlag::Key
1057 | ColumnFlag::NullableUnknown;
1058
1059 assert!(all.contains(ColumnFlag::Nullable));
1060 assert!(all.contains(ColumnFlag::NullableUnknown));
1061 assert_eq!(BitFlags::bits(all).count_ones(), 12);
1062 }
1063}