1use std::{borrow::Cow, fmt::Display};
2
3use crate::{
4 tds::codec::{encode_b_varchar, Encode, FixedLenType, TokenType, TypeInfo, VarLenType},
5 Column, ColumnData, ColumnType, Error, SqlReadBytes,
6};
7use asynchronous_codec::BytesMut;
8use bytes::BufMut;
9use enumflags2::{bitflags, BitFlags};
10
11#[derive(Debug, Clone)]
12pub struct TokenColMetaData<'a> {
13 pub columns: Vec<MetaDataColumn<'a>>,
14}
15
16#[derive(Debug, Clone)]
19pub struct MetaDataColumn<'a> {
20 pub base: BaseMetaDataColumn,
22 pub col_name: Cow<'a, str>,
24}
25
26impl<'a> MetaDataColumn<'a> {
27 pub fn col_name(&self) -> &str {
29 self.col_name.as_ref()
30 }
31
32 pub fn base(&self) -> &BaseMetaDataColumn {
35 &self.base
36 }
37}
38
39impl<'a> Display for MetaDataColumn<'a> {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 write!(f, "[{}] ", self.col_name.replace(']', "]]"))?;
46
47 match &self.base.ty {
48 TypeInfo::FixedLen(fixed) => match fixed {
49 FixedLenType::Int1 => write!(f, "tinyint")?,
50 FixedLenType::Bit => write!(f, "bit")?,
51 FixedLenType::Int2 => write!(f, "smallint")?,
52 FixedLenType::Int4 => write!(f, "int")?,
53 FixedLenType::Datetime4 => write!(f, "smalldatetime")?,
54 FixedLenType::Float4 => write!(f, "real")?,
55 FixedLenType::Money => write!(f, "money")?,
56 FixedLenType::Datetime => write!(f, "datetime")?,
57 FixedLenType::Float8 => write!(f, "float")?,
58 FixedLenType::Money4 => write!(f, "smallmoney")?,
59 FixedLenType::Int8 => write!(f, "bigint")?,
60 FixedLenType::Null => write!(f, "int")?,
64 },
65 TypeInfo::VarLenSized(ctx) => match ctx.r#type() {
66 VarLenType::Bitn => write!(f, "bit")?,
67 VarLenType::Guid => write!(f, "uniqueidentifier")?,
68 #[cfg(feature = "tds73")]
69 VarLenType::Daten => write!(f, "date")?,
70 #[cfg(feature = "tds73")]
71 VarLenType::Timen => write!(f, "time")?,
72 #[cfg(feature = "tds73")]
73 VarLenType::Datetime2 => write!(f, "datetime2({})", ctx.len())?,
74 VarLenType::Datetimen => write!(f, "datetime")?,
75 VarLenType::Money => match ctx.len() {
76 4 => write!(f, "smallmoney")?,
77 _ => write!(f, "money")?,
78 },
79 #[cfg(feature = "tds73")]
80 VarLenType::DatetimeOffsetn => write!(f, "datetimeoffset")?,
81 VarLenType::BigVarBin => {
82 if ctx.len() <= 8000 {
83 write!(f, "varbinary({})", ctx.len())?
84 } else {
85 write!(f, "varbinary(max)")?
86 }
87 }
88 VarLenType::BigVarChar => {
89 if ctx.len() <= 8000 {
90 write!(f, "varchar({})", ctx.len())?
91 } else {
92 write!(f, "varchar(max)")?
93 }
94 }
95 VarLenType::BigBinary => write!(f, "binary({})", ctx.len())?,
96 VarLenType::BigChar => write!(f, "char({})", ctx.len())?,
97 VarLenType::NVarchar => {
98 if ctx.len() <= 4000 {
99 write!(f, "nvarchar({})", ctx.len())?
100 } else {
101 write!(f, "nvarchar(max)")?
102 }
103 }
104 VarLenType::NChar => write!(f, "nchar({})", ctx.len())?,
105 VarLenType::Text => write!(f, "text")?,
106 VarLenType::Image => write!(f, "image")?,
107 VarLenType::NText => write!(f, "ntext")?,
108 VarLenType::Intn => match ctx.len() {
109 1 => write!(f, "tinyint")?,
110 2 => write!(f, "smallint")?,
111 4 => write!(f, "int")?,
112 _ => write!(f, "bigint")?,
113 },
114 VarLenType::Floatn => match ctx.len() {
115 4 => write!(f, "real")?,
116 _ => write!(f, "float")?,
117 },
118 VarLenType::SSVariant => write!(f, "sql_variant")?,
119 _ => return Err(std::fmt::Error),
128 },
129 TypeInfo::VarLenSizedPrecision {
130 ty,
131 size: _,
132 precision,
133 scale,
134 } => match ty {
135 VarLenType::Numericn => write!(f, "numeric({},{})", precision, scale)?,
136 _ => write!(f, "decimal({},{})", precision, scale)?,
138 },
139 TypeInfo::Xml { .. } => write!(f, "xml")?,
140 TypeInfo::Udt(info) => write!(f, "{}.{}", info.schema_name, info.type_name)?,
141 }
142
143 Ok(())
144 }
145}
146
147#[derive(Debug, Clone)]
151pub struct BaseMetaDataColumn {
152 pub flags: BitFlags<ColumnFlag>,
155 pub ty: TypeInfo,
158 pub table_name: Option<String>,
164}
165
166impl BaseMetaDataColumn {
167 pub fn ty(&self) -> &TypeInfo {
170 &self.ty
171 }
172
173 pub fn flags(&self) -> BitFlags<ColumnFlag> {
175 self.flags
176 }
177
178 pub fn is_nullable(&self) -> bool {
180 self.flags.contains(ColumnFlag::Nullable)
181 }
182
183 pub fn is_identity(&self) -> bool {
185 self.flags.contains(ColumnFlag::Identity)
186 }
187
188 pub fn is_updateable(&self) -> bool {
196 self.flags.contains(ColumnFlag::Updateable)
197 || self.flags.contains(ColumnFlag::UpdateableUnknown)
198 }
199
200 pub(crate) fn null_value(&self) -> ColumnData<'static> {
201 match &self.ty {
202 TypeInfo::FixedLen(ty) => match ty {
203 FixedLenType::Null => ColumnData::I32(None),
204 FixedLenType::Int1 => ColumnData::U8(None),
205 FixedLenType::Bit => ColumnData::Bit(None),
206 FixedLenType::Int2 => ColumnData::I16(None),
207 FixedLenType::Int4 => ColumnData::I32(None),
208 FixedLenType::Datetime4 => ColumnData::SmallDateTime(None),
209 FixedLenType::Float4 => ColumnData::F32(None),
210 FixedLenType::Money => ColumnData::F64(None),
211 FixedLenType::Datetime => ColumnData::DateTime(None),
212 FixedLenType::Float8 => ColumnData::F64(None),
213 FixedLenType::Money4 => ColumnData::F32(None),
214 FixedLenType::Int8 => ColumnData::I64(None),
215 },
216 TypeInfo::VarLenSized(cx) => match cx.r#type() {
217 VarLenType::Guid => ColumnData::Guid(None),
218 VarLenType::Intn => match cx.len() {
219 1 => ColumnData::U8(None),
220 2 => ColumnData::I16(None),
221 4 => ColumnData::I32(None),
222 _ => ColumnData::I64(None),
223 },
224 VarLenType::Bitn => ColumnData::Bit(None),
225 VarLenType::Decimaln => ColumnData::Numeric(None),
226 VarLenType::Numericn => ColumnData::Numeric(None),
227 VarLenType::Floatn => match cx.len() {
228 4 => ColumnData::F32(None),
229 _ => ColumnData::F64(None),
230 },
231 VarLenType::Money => ColumnData::F64(None),
232 VarLenType::Datetimen => ColumnData::DateTime(None),
233 #[cfg(feature = "tds73")]
234 VarLenType::Daten => ColumnData::Date(None),
235 #[cfg(feature = "tds73")]
236 VarLenType::Timen => ColumnData::Time(None),
237 #[cfg(feature = "tds73")]
238 VarLenType::Datetime2 => ColumnData::DateTime2(None),
239 #[cfg(feature = "tds73")]
240 VarLenType::DatetimeOffsetn => ColumnData::DateTimeOffset(None),
241 VarLenType::BigVarBin => ColumnData::Binary(None),
242 VarLenType::BigVarChar => ColumnData::String(None),
243 VarLenType::BigBinary => ColumnData::Binary(None),
244 VarLenType::BigChar => ColumnData::String(None),
245 VarLenType::NVarchar => ColumnData::String(None),
246 VarLenType::NChar => ColumnData::String(None),
247 VarLenType::Xml => ColumnData::Xml(None),
248 VarLenType::Udt => ColumnData::Binary(None),
252 VarLenType::Text => ColumnData::String(None),
253 VarLenType::Image => ColumnData::Binary(None),
254 VarLenType::NText => ColumnData::String(None),
255 VarLenType::SSVariant => ColumnData::String(None),
258 },
259 TypeInfo::VarLenSizedPrecision { ty, .. } => match ty {
260 VarLenType::Guid => ColumnData::Guid(None),
261 VarLenType::Intn => ColumnData::I32(None),
262 VarLenType::Bitn => ColumnData::Bit(None),
263 VarLenType::Decimaln => ColumnData::Numeric(None),
264 VarLenType::Numericn => ColumnData::Numeric(None),
265 VarLenType::Floatn => ColumnData::F32(None),
266 VarLenType::Money => ColumnData::F64(None),
267 VarLenType::Datetimen => ColumnData::DateTime(None),
268 #[cfg(feature = "tds73")]
269 VarLenType::Daten => ColumnData::Date(None),
270 #[cfg(feature = "tds73")]
271 VarLenType::Timen => ColumnData::Time(None),
272 #[cfg(feature = "tds73")]
273 VarLenType::Datetime2 => ColumnData::DateTime2(None),
274 #[cfg(feature = "tds73")]
275 VarLenType::DatetimeOffsetn => ColumnData::DateTimeOffset(None),
276 VarLenType::BigVarBin => ColumnData::Binary(None),
277 VarLenType::BigVarChar => ColumnData::String(None),
278 VarLenType::BigBinary => ColumnData::Binary(None),
279 VarLenType::BigChar => ColumnData::String(None),
280 VarLenType::NVarchar => ColumnData::String(None),
281 VarLenType::NChar => ColumnData::String(None),
282 VarLenType::Xml => ColumnData::Xml(None),
283 VarLenType::Udt => ColumnData::Binary(None),
287 VarLenType::Text => ColumnData::String(None),
288 VarLenType::Image => ColumnData::Binary(None),
289 VarLenType::NText => ColumnData::String(None),
290 VarLenType::SSVariant => ColumnData::String(None),
293 },
294 TypeInfo::Xml { .. } => ColumnData::Xml(None),
295 TypeInfo::Udt(_) => ColumnData::Binary(None),
296 }
297 }
298}
299
300impl<'a> Encode<BytesMut> for TokenColMetaData<'a> {
301 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
302 dst.put_u8(TokenType::ColMetaData as u8);
303 dst.put_u16_le(self.columns.len() as u16);
304
305 for col in self.columns.into_iter() {
306 col.encode(dst)?;
307 }
308
309 Ok(())
310 }
311}
312
313impl<'a> Encode<BytesMut> for MetaDataColumn<'a> {
314 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
315 dst.put_u32_le(0);
316 self.base.encode(dst)?;
317
318 encode_b_varchar(dst, &self.col_name)?;
321
322 Ok(())
323 }
324}
325
326impl Encode<BytesMut> for BaseMetaDataColumn {
327 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
328 dst.put_u16_le(BitFlags::bits(self.flags));
329
330 let emits_table_name = matches!(
351 &self.ty,
352 TypeInfo::VarLenSized(cx)
353 if matches!(cx.r#type(), VarLenType::Text | VarLenType::NText | VarLenType::Image)
354 );
355
356 self.ty.encode(dst)?;
357
358 if emits_table_name {
359 let table_name = self.table_name.as_deref().unwrap_or("");
360 encode_us_varchar(dst, table_name)?;
361 }
362
363 Ok(())
364 }
365}
366
367fn encode_us_varchar(dst: &mut BytesMut, s: &str) -> crate::Result<()> {
371 let units = s.encode_utf16().count();
372 if units > u16::MAX as usize {
373 return Err(Error::BulkInput(
374 format!("table name is too long ({units} UTF-16 code units, max 65535)").into(),
375 ));
376 }
377
378 dst.put_u16_le(units as u16);
379 for chr in s.encode_utf16() {
380 dst.put_u16_le(chr);
381 }
382
383 Ok(())
384}
385
386#[bitflags]
388#[repr(u16)]
389#[derive(Debug, Clone, Copy, PartialEq, Eq)]
390pub enum ColumnFlag {
391 Nullable = 1 << 0,
393 CaseSensitive = 1 << 1,
396 Updateable = 1 << 2,
400 UpdateableUnknown = 1 << 3,
403 Identity = 1 << 4,
405 Computed = 1 << 5,
409 FixedLenClrType = 1 << 8,
413 SparseColumnSet = 1 << 10,
417 Encrypted = 1 << 11,
423 Hidden = 1 << 13,
426 Key = 1 << 14,
429 NullableUnknown = 1 << 15,
431}
432
433impl TokenColMetaData<'static> {
434 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
435 where
436 R: SqlReadBytes + Unpin,
437 {
438 let column_count = src.read_u16_le().await?;
439 let mut columns = Vec::with_capacity(
444 (column_count as usize).min(crate::tds::codec::column_data::MAX_PREALLOC),
445 );
446
447 if column_count < 0xffff {
450 for _ in 0..column_count {
451 let base = BaseMetaDataColumn::decode(src).await?;
452 let col_name = Cow::from(src.read_b_varchar().await?);
453
454 columns.push(MetaDataColumn { base, col_name });
455 }
456 }
457
458 Ok(TokenColMetaData { columns })
459 }
460}
461
462impl<'a> TokenColMetaData<'a> {
463 pub(crate) fn columns(&self) -> impl Iterator<Item = Column> + '_ {
464 self.columns.iter().map(|x| Column {
465 name: x.col_name.to_string(),
466 column_type: ColumnType::from(&x.base.ty),
467 })
468 }
469}
470
471impl BaseMetaDataColumn {
472 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
473 where
474 R: SqlReadBytes + Unpin,
475 {
476 use VarLenType::*;
477
478 let _user_ty = src.read_u32_le().await?;
479
480 let flags = BitFlags::from_bits_truncate(src.read_u16_le().await?);
485
486 let ty = TypeInfo::decode(src).await?;
487
488 if let TypeInfo::VarLenSized(cx) = ty {
489 if let Text | NText | Image = cx.r#type() {
490 let num_of_parts = src.read_u8().await?;
491
492 for _ in 0..num_of_parts {
494 src.read_us_varchar().await?;
495 }
496 };
497 };
498
499 Ok(BaseMetaDataColumn {
500 flags,
501 ty,
502 table_name: None,
503 })
504 }
505}
506
507#[cfg(test)]
508mod tests {
509 use super::*;
510 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
511 use crate::tds::codec::type_info::VarLenContext;
512 use crate::tds::Collation;
513 use std::fmt::Write as _;
514
515 fn us_varchar_bytes(s: &str) -> Vec<u8> {
517 let mut out = Vec::new();
518 let units: Vec<u16> = s.encode_utf16().collect();
519 out.extend_from_slice(&(units.len() as u16).to_le_bytes());
520 for u in units {
521 out.extend_from_slice(&u.to_le_bytes());
522 }
523 out
524 }
525
526 fn text_column(table_name: Option<&str>) -> BaseMetaDataColumn {
527 BaseMetaDataColumn {
528 flags: ColumnFlag::Nullable.into(),
529 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Text, 2147483647, None)),
530 table_name: table_name.map(str::to_string),
531 }
532 }
533
534 #[test]
539 fn text_column_emits_dotted_name_as_bare_us_varchar() {
540 let mut buf = BytesMut::new();
541 text_column(Some("dbo.MyTable")).encode(&mut buf).unwrap();
542
543 let expected_tail = us_varchar_bytes("dbo.MyTable");
545
546 assert!(
547 buf.ends_with(&expected_tail),
548 "buffer {:02x?} did not end with bare US_VARCHAR TableName {:02x?}",
549 &buf[..],
550 expected_tail
551 );
552 }
553
554 #[test]
557 fn text_column_single_name_is_bare_us_varchar() {
558 let mut buf = BytesMut::new();
559 text_column(Some("##bulk_test")).encode(&mut buf).unwrap();
560
561 let expected_tail = us_varchar_bytes("##bulk_test");
562 assert!(buf.ends_with(&expected_tail), "got {:02x?}", &buf[..]);
563 }
564
565 #[test]
568 fn non_text_column_never_emits_table_name() {
569 let with = {
570 let mut buf = BytesMut::new();
571 BaseMetaDataColumn {
572 flags: ColumnFlag::Nullable.into(),
573 ty: TypeInfo::FixedLen(FixedLenType::Int4),
574 table_name: Some("dbo.MyTable".to_string()),
575 }
576 .encode(&mut buf)
577 .unwrap();
578 buf.to_vec()
579 };
580 let without = {
581 let mut buf = BytesMut::new();
582 BaseMetaDataColumn {
583 flags: ColumnFlag::Nullable.into(),
584 ty: TypeInfo::FixedLen(FixedLenType::Int4),
585 table_name: None,
586 }
587 .encode(&mut buf)
588 .unwrap();
589 buf.to_vec()
590 };
591 assert_eq!(with, without);
592 }
593
594 #[test]
597 fn text_column_rejects_over_long_table_name() {
598 let long = "a".repeat(u16::MAX as usize + 1);
599 let mut buf = BytesMut::new();
600 let err = text_column(Some(&long)).encode(&mut buf).unwrap_err();
601 assert!(matches!(err, Error::BulkInput(_)), "got {err:?}");
602 }
603
604 fn meta(ty: TypeInfo, name: &'static str) -> MetaDataColumn<'static> {
605 MetaDataColumn {
606 base: BaseMetaDataColumn {
607 flags: ColumnFlag::Nullable.into(),
608 ty,
609 table_name: None,
610 },
611 col_name: Cow::Borrowed(name),
612 }
613 }
614
615 #[test]
616 fn display_var_len_unknown_type_yields_err_not_panic() {
617 for ty in [VarLenType::Decimaln, VarLenType::Numericn] {
623 let col = meta(TypeInfo::VarLenSized(VarLenContext::new(ty, 17, None)), "c");
624 let mut out = String::new();
625 let result = write!(out, "{col}");
626 assert!(
627 result.is_err(),
628 "expected Err for unhandled var-len type {ty:?}, got Ok({out:?})"
629 );
630 }
631 }
632
633 #[test]
634 fn display_money_columns_never_panic_and_render_expected() {
635 let cases = vec![
642 (TypeInfo::FixedLen(FixedLenType::Money), " money"),
643 (TypeInfo::FixedLen(FixedLenType::Money4), " smallmoney"),
644 (
645 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 8, None)),
646 " money",
647 ),
648 (
649 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 4, None)),
650 " smallmoney",
651 ),
652 ];
653
654 for (ty, expected_suffix) in cases {
655 let rendered = format!("{}", meta(ty, "c"));
657 assert!(
658 rendered.ends_with(expected_suffix),
659 "expected {rendered:?} to end with {expected_suffix:?}"
660 );
661 }
662 }
663
664 #[test]
665 fn display_text_ntext_image_render_expected_type_names() {
666 let cases = [
671 (VarLenType::Text, " text"),
672 (VarLenType::NText, " ntext"),
673 (VarLenType::Image, " image"),
674 ];
675
676 for (ty, expected_suffix) in cases {
677 let rendered = format!(
678 "{}",
679 meta(
680 TypeInfo::VarLenSized(VarLenContext::new(ty, 2147483647, None)),
681 "c",
682 )
683 );
684 assert!(
685 rendered.ends_with(expected_suffix),
686 "expected {rendered:?} to end with {expected_suffix:?}",
687 );
688 }
689 }
690
691 fn column(name: &'static str) -> MetaDataColumn<'static> {
692 MetaDataColumn {
693 base: BaseMetaDataColumn {
694 flags: BitFlags::empty(),
695 ty: TypeInfo::FixedLen(FixedLenType::Int4),
696 table_name: None,
697 },
698 col_name: Cow::Borrowed(name),
699 }
700 }
701
702 #[test]
720 fn column_flag_bit_positions_match_ms_tds() {
721 assert_eq!(BitFlags::bits(BitFlags::from(ColumnFlag::Nullable)), 0x0001);
722 assert_eq!(
723 BitFlags::bits(BitFlags::from(ColumnFlag::CaseSensitive)),
724 0x0002
725 );
726 assert_eq!(
728 BitFlags::bits(BitFlags::from(ColumnFlag::Updateable)),
729 0x0004
730 );
731 assert_eq!(
732 BitFlags::bits(BitFlags::from(ColumnFlag::UpdateableUnknown)),
733 0x0008
734 );
735 assert_eq!(BitFlags::bits(BitFlags::from(ColumnFlag::Identity)), 0x0010);
736 }
737
738 #[test]
745 fn is_updateable_true_for_read_write_and_unknown_false_for_read_only() {
746 let read_write = BaseMetaDataColumn {
747 flags: BitFlags::from_bits_truncate(0x0004),
748 ty: TypeInfo::FixedLen(FixedLenType::Int4),
749 table_name: None,
750 };
751 assert!(read_write.is_updateable());
752
753 let unknown = BaseMetaDataColumn {
754 flags: BitFlags::from_bits_truncate(0x0008),
755 ty: TypeInfo::FixedLen(FixedLenType::Int4),
756 table_name: None,
757 };
758 assert!(unknown.is_updateable());
759
760 let read_only = BaseMetaDataColumn {
761 flags: BitFlags::from_bits_truncate(0x0000),
762 ty: TypeInfo::FixedLen(FixedLenType::Int4),
763 table_name: None,
764 };
765 assert!(!read_only.is_updateable());
766 }
767
768 #[test]
780 fn column_flags_decode_byte_exact_low_and_mid_bits() {
781 let wire: [u8; 2] = [0x35, 0x0D];
785 let flags = BitFlags::<ColumnFlag>::from_bits_truncate(u16::from_le_bytes(wire));
786
787 let col = BaseMetaDataColumn {
788 flags,
789 ty: TypeInfo::FixedLen(FixedLenType::Int4),
790 table_name: None,
791 };
792
793 assert!(col.is_nullable());
794 assert!(col.is_updateable()); assert!(col.is_identity());
796 assert!(flags.contains(ColumnFlag::Computed));
797 assert!(flags.contains(ColumnFlag::FixedLenClrType));
798 assert!(flags.contains(ColumnFlag::SparseColumnSet));
799 assert!(flags.contains(ColumnFlag::Encrypted));
800
801 assert!(!flags.contains(ColumnFlag::CaseSensitive));
803 assert!(!flags.contains(ColumnFlag::UpdateableUnknown));
804 assert!(!flags.contains(ColumnFlag::Hidden));
805 assert!(!flags.contains(ColumnFlag::Key));
806 assert!(!flags.contains(ColumnFlag::NullableUnknown));
807 }
808
809 #[test]
813 fn column_flags_decode_byte_exact_high_bits() {
814 let wire: [u8; 2] = [0x0A, 0xE0];
817 let flags = BitFlags::<ColumnFlag>::from_bits_truncate(u16::from_le_bytes(wire));
818
819 assert!(flags.contains(ColumnFlag::CaseSensitive));
820 assert!(flags.contains(ColumnFlag::UpdateableUnknown));
821 assert!(flags.contains(ColumnFlag::Hidden));
822 assert!(flags.contains(ColumnFlag::Key));
823 assert!(flags.contains(ColumnFlag::NullableUnknown));
824
825 assert!(!flags.contains(ColumnFlag::Updateable));
827 assert!(!flags.contains(ColumnFlag::Nullable));
828 assert!(!flags.contains(ColumnFlag::Identity));
829 assert!(!flags.contains(ColumnFlag::Computed));
830 }
831
832 #[test]
835 fn metadata_column_rejects_over_long_col_name() {
836 let col = MetaDataColumn {
837 base: BaseMetaDataColumn {
838 flags: BitFlags::empty(),
839 ty: TypeInfo::FixedLen(FixedLenType::Int4),
840 table_name: None,
841 },
842 col_name: Cow::Owned("a".repeat(256)),
843 };
844
845 let mut buf = BytesMut::new();
846 let err = col.encode(&mut buf).unwrap_err();
847 assert!(matches!(err, Error::Protocol(_)), "got {err:?}");
848 }
849
850 #[test]
853 fn metadata_column_accepts_max_length_col_name() {
854 let col = MetaDataColumn {
855 base: BaseMetaDataColumn {
856 flags: BitFlags::empty(),
857 ty: TypeInfo::FixedLen(FixedLenType::Int4),
858 table_name: None,
859 },
860 col_name: Cow::Owned("a".repeat(255)),
861 };
862
863 let mut buf = BytesMut::new();
864 col.encode(&mut buf).expect("255-unit col_name must encode");
865 assert_eq!(buf[7], 255);
868 }
869
870 #[test]
871 fn display_escapes_closing_bracket_in_column_name() {
872 assert_eq!(format!("{}", column("my]col")), "[my]]col] int");
875 }
876
877 #[test]
878 fn display_leaves_plain_column_name_unchanged() {
879 assert_eq!(format!("{}", column("foo")), "[foo] int");
880 }
881
882 #[tokio::test]
883 async fn round_trip_via_encode_decode() {
884 let cmd = TokenColMetaData {
885 columns: vec![
886 meta(TypeInfo::FixedLen(FixedLenType::Int4), "id"),
887 meta(
888 TypeInfo::VarLenSized(VarLenContext::new(
889 VarLenType::NVarchar,
890 4000,
891 Some(Collation::new(13632521, 52)),
892 )),
893 "name",
894 ),
895 ],
896 };
897
898 let mut buf = BytesMut::new();
902 buf.put_u16_le(cmd.columns.len() as u16);
903 for col in cmd.columns.iter().cloned() {
904 col.encode(&mut buf).unwrap();
905 }
906
907 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
908 .await
909 .unwrap();
910
911 assert_eq!(decoded.columns.len(), 2);
912 assert_eq!(decoded.columns[0].col_name, "id");
913 assert_eq!(decoded.columns[1].col_name, "name");
914
915 let columns: Vec<_> = decoded.columns().collect();
916 assert_eq!(columns.len(), 2);
917 assert_eq!(columns[0].name(), "id");
918 }
919
920 #[test]
921 fn encode_writes_token_header_and_column_count() {
922 let cmd = TokenColMetaData {
923 columns: vec![
924 meta(TypeInfo::FixedLen(FixedLenType::Int4), "id"),
925 meta(TypeInfo::FixedLen(FixedLenType::Bit), "flag"),
926 ],
927 };
928
929 let mut buf = BytesMut::new();
930 cmd.encode(&mut buf).unwrap();
931
932 assert_eq!(buf[0], TokenType::ColMetaData as u8);
934 assert_eq!(u16::from_le_bytes([buf[1], buf[2]]), 2);
935 assert!(buf.len() > 3);
937 }
938
939 #[tokio::test]
940 async fn zero_columns_yields_empty() {
941 let mut buf = BytesMut::new();
942 buf.put_u16_le(0);
943
944 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
945 .await
946 .unwrap();
947 assert!(decoded.columns.is_empty());
948 }
949
950 #[tokio::test]
951 async fn text_column_reads_table_name_parts() {
952 let mut buf = BytesMut::new();
953 buf.put_u16_le(1); buf.put_u32_le(0);
957 buf.put_u16_le(BitFlags::bits(BitFlags::from(ColumnFlag::Nullable)));
958
959 let ti = TypeInfo::VarLenSized(VarLenContext::new(
961 VarLenType::Text,
962 2147483647,
963 Some(Collation::new(13632521, 52)),
964 ));
965 ti.encode(&mut buf).unwrap();
966
967 buf.put_u8(1);
969 let part: Vec<u16> = "dbo".encode_utf16().collect();
970 buf.put_u16_le(part.len() as u16);
971 for c in part {
972 buf.put_u16_le(c);
973 }
974
975 let name: Vec<u16> = "body".encode_utf16().collect();
977 buf.put_u8(name.len() as u8);
978 for c in name {
979 buf.put_u16_le(c);
980 }
981
982 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
983 .await
984 .unwrap();
985
986 assert_eq!(decoded.columns.len(), 1);
987 assert_eq!(decoded.columns[0].col_name, "body");
988 }
989
990 #[test]
991 fn display_formats_various_types() {
992 let cases = vec![
993 (TypeInfo::FixedLen(FixedLenType::Int4), "c int"),
994 (TypeInfo::FixedLen(FixedLenType::Bit), "c bit"),
995 (TypeInfo::FixedLen(FixedLenType::Float8), "c float"),
996 (
997 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 1, None)),
998 "c tinyint",
999 ),
1000 (
1001 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 4, None)),
1002 "c int",
1003 ),
1004 (
1005 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 4, None)),
1006 "c real",
1007 ),
1008 (
1009 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Guid, 16, None)),
1010 "c uniqueidentifier",
1011 ),
1012 (
1013 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarBin, 100, None)),
1014 "c varbinary(100)",
1015 ),
1016 (
1017 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarBin, 100000, None)),
1018 "c varbinary(max)",
1019 ),
1020 (
1021 TypeInfo::VarLenSizedPrecision {
1022 ty: VarLenType::Decimaln,
1023 size: 17,
1024 precision: 18,
1025 scale: 2,
1026 },
1027 "c decimal(18,2)",
1028 ),
1029 (
1032 TypeInfo::VarLenSizedPrecision {
1033 ty: VarLenType::Numericn,
1034 size: 9,
1035 precision: 10,
1036 scale: 4,
1037 },
1038 "c numeric(10,4)",
1039 ),
1040 (
1041 TypeInfo::Xml {
1042 schema: None,
1043 size: 0,
1044 },
1045 "c xml",
1046 ),
1047 ];
1048
1049 for (ty, expected) in cases {
1050 let expected = expected.replacen("c ", "[c] ", 1);
1052 assert_eq!(format!("{}", meta(ty, "c")), expected);
1053 }
1054 }
1055
1056 #[test]
1057 fn null_value_maps_types() {
1058 let fixed = BaseMetaDataColumn {
1059 flags: BitFlags::empty(),
1060 ty: TypeInfo::FixedLen(FixedLenType::Int4),
1061 table_name: None,
1062 };
1063 assert_eq!(fixed.null_value(), ColumnData::I32(None));
1064
1065 let varlen = BaseMetaDataColumn {
1066 flags: BitFlags::empty(),
1067 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 2, None)),
1068 table_name: None,
1069 };
1070 assert_eq!(varlen.null_value(), ColumnData::I16(None));
1071
1072 let tinyint = BaseMetaDataColumn {
1075 flags: BitFlags::empty(),
1076 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 1, None)),
1077 table_name: None,
1078 };
1079 assert_eq!(tinyint.null_value(), ColumnData::U8(None));
1080
1081 let int4 = BaseMetaDataColumn {
1082 flags: BitFlags::empty(),
1083 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 4, None)),
1084 table_name: None,
1085 };
1086 assert_eq!(int4.null_value(), ColumnData::I32(None));
1087
1088 let int8 = BaseMetaDataColumn {
1089 flags: BitFlags::empty(),
1090 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 8, None)),
1091 table_name: None,
1092 };
1093 assert_eq!(int8.null_value(), ColumnData::I64(None));
1094
1095 let real = BaseMetaDataColumn {
1097 flags: BitFlags::empty(),
1098 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 4, None)),
1099 table_name: None,
1100 };
1101 assert_eq!(real.null_value(), ColumnData::F32(None));
1102
1103 let double = BaseMetaDataColumn {
1104 flags: BitFlags::empty(),
1105 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 8, None)),
1106 table_name: None,
1107 };
1108 assert_eq!(double.null_value(), ColumnData::F64(None));
1109
1110 let guid = BaseMetaDataColumn {
1111 flags: BitFlags::empty(),
1112 ty: TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Guid, 16, None)),
1113 table_name: None,
1114 };
1115 assert_eq!(guid.null_value(), ColumnData::Guid(None));
1116 }
1117
1118 #[test]
1119 fn null_value_maps_precision_and_xml_and_udt() {
1120 let precision = BaseMetaDataColumn {
1121 flags: BitFlags::empty(),
1122 ty: TypeInfo::VarLenSizedPrecision {
1123 ty: VarLenType::Numericn,
1124 size: 17,
1125 precision: 18,
1126 scale: 2,
1127 },
1128 table_name: None,
1129 };
1130 assert_eq!(precision.null_value(), ColumnData::Numeric(None));
1131
1132 let xml = BaseMetaDataColumn {
1133 flags: BitFlags::empty(),
1134 ty: TypeInfo::Xml {
1135 schema: None,
1136 size: 0,
1137 },
1138 table_name: None,
1139 };
1140 assert_eq!(xml.null_value(), ColumnData::Xml(None));
1141
1142 let udt = BaseMetaDataColumn {
1143 flags: BitFlags::empty(),
1144 ty: TypeInfo::Udt(crate::tds::codec::type_info::UdtInfo {
1145 max_byte_size: 0,
1146 db_name: "db".into(),
1147 schema_name: "dbo".into(),
1148 type_name: "T".into(),
1149 assembly_qualified_name: "A".into(),
1150 }),
1151 table_name: None,
1152 };
1153 assert_eq!(udt.null_value(), ColumnData::Binary(None));
1154 }
1155
1156 #[test]
1157 fn base_meta_data_column_flag_accessors() {
1158 let base = BaseMetaDataColumn {
1159 flags: ColumnFlag::Nullable | ColumnFlag::Identity | ColumnFlag::Updateable,
1160 ty: TypeInfo::FixedLen(FixedLenType::Int4),
1161 table_name: None,
1162 };
1163
1164 assert!(base.is_nullable());
1165 assert!(base.is_identity());
1166 assert!(base.is_updateable());
1167 assert_eq!(base.ty(), &TypeInfo::FixedLen(FixedLenType::Int4));
1168 assert_eq!(base.flags(), base.flags);
1169
1170 let base2 = BaseMetaDataColumn {
1171 flags: BitFlags::empty(),
1172 ty: TypeInfo::FixedLen(FixedLenType::Int4),
1173 table_name: None,
1174 };
1175 assert!(!base2.is_nullable());
1176 assert!(!base2.is_identity());
1177 assert!(!base2.is_updateable());
1178 }
1179
1180 #[test]
1181 fn meta_data_column_accessors() {
1182 let m = meta(TypeInfo::FixedLen(FixedLenType::Int4), "id");
1183 assert_eq!(m.col_name(), "id");
1184 assert!(m.base().is_nullable());
1185 }
1186
1187 #[test]
1188 fn display_formats_more_types() {
1189 let cases = vec![
1190 (
1191 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Bitn, 1, None)),
1192 "c bit",
1193 ),
1194 (
1195 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetimen, 8, None)),
1196 "c datetime",
1197 ),
1198 (
1199 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 4, None)),
1200 "c smallmoney",
1201 ),
1202 (
1203 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Money, 8, None)),
1204 "c money",
1205 ),
1206 (
1207 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarChar, 100, None)),
1208 "c varchar(100)",
1209 ),
1210 (
1211 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigVarChar, 100000, None)),
1212 "c varchar(max)",
1213 ),
1214 (
1215 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigBinary, 10, None)),
1216 "c binary(10)",
1217 ),
1218 (
1219 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::BigChar, 10, None)),
1220 "c char(10)",
1221 ),
1222 (
1223 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NVarchar, 100, None)),
1224 "c nvarchar(100)",
1225 ),
1226 (
1227 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NVarchar, 100000, None)),
1228 "c nvarchar(max)",
1229 ),
1230 (
1231 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NChar, 10, None)),
1232 "c nchar(10)",
1233 ),
1234 (
1235 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Text, 0, None)),
1236 "c text",
1237 ),
1238 (
1239 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Image, 0, None)),
1240 "c image",
1241 ),
1242 (
1243 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::NText, 0, None)),
1244 "c ntext",
1245 ),
1246 (
1247 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 2, None)),
1248 "c smallint",
1249 ),
1250 (
1251 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 8, None)),
1252 "c bigint",
1253 ),
1254 (
1255 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Floatn, 8, None)),
1256 "c float",
1257 ),
1258 (
1259 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::SSVariant, 0, None)),
1260 "c sql_variant",
1261 ),
1262 ];
1263
1264 for (ty, expected) in cases {
1265 let expected = expected.replacen("c ", "[c] ", 1);
1266 assert_eq!(format!("{}", meta(ty, "c")), expected);
1267 }
1268 }
1269
1270 #[test]
1271 fn display_formats_udt_and_decimaln() {
1272 let udt = TypeInfo::Udt(crate::tds::codec::type_info::UdtInfo {
1273 max_byte_size: 0,
1274 db_name: "db".into(),
1275 schema_name: "dbo".into(),
1276 type_name: "MyType".into(),
1277 assembly_qualified_name: "asm".into(),
1278 });
1279 assert_eq!(format!("{}", meta(udt, "c")), "[c] dbo.MyType");
1280
1281 let decimaln = TypeInfo::VarLenSizedPrecision {
1282 ty: VarLenType::Decimaln,
1283 size: 17,
1284 precision: 10,
1285 scale: 4,
1286 };
1287 assert_eq!(format!("{}", meta(decimaln, "c")), "[c] decimal(10,4)");
1288 }
1289
1290 #[tokio::test]
1291 async fn decode_all_ones_column_count_yields_empty() {
1292 let mut buf = BytesMut::new();
1295 buf.put_u16_le(0xffff);
1296
1297 let decoded = TokenColMetaData::decode(&mut buf.into_sql_read_bytes())
1298 .await
1299 .unwrap();
1300 assert!(decoded.columns.is_empty());
1301 }
1302
1303 #[test]
1304 fn display_formats_fixed_len_money_and_datetime_types() {
1305 let cases = vec![
1309 (TypeInfo::FixedLen(FixedLenType::Int1), "c tinyint"),
1310 (TypeInfo::FixedLen(FixedLenType::Int2), "c smallint"),
1311 (
1312 TypeInfo::FixedLen(FixedLenType::Datetime4),
1313 "c smalldatetime",
1314 ),
1315 (TypeInfo::FixedLen(FixedLenType::Float4), "c real"),
1316 (TypeInfo::FixedLen(FixedLenType::Money), "c money"),
1317 (TypeInfo::FixedLen(FixedLenType::Datetime), "c datetime"),
1318 (TypeInfo::FixedLen(FixedLenType::Money4), "c smallmoney"),
1319 (TypeInfo::FixedLen(FixedLenType::Int8), "c bigint"),
1320 (TypeInfo::FixedLen(FixedLenType::Null), "c int"),
1321 ];
1322
1323 for (ty, expected) in cases {
1324 let expected = expected.replacen("c ", "[c] ", 1);
1325 assert_eq!(format!("{}", meta(ty, "c")), expected);
1326 }
1327 }
1328
1329 #[cfg(feature = "tds73")]
1330 #[test]
1331 fn display_formats_tds73_date_time_types() {
1332 let cases = vec![
1334 (
1335 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Daten, 3, None)),
1336 "c date",
1337 ),
1338 (
1339 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Timen, 7, None)),
1340 "c time",
1341 ),
1342 (
1343 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Datetime2, 7, None)),
1344 "c datetime2(7)",
1345 ),
1346 (
1347 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::DatetimeOffsetn, 7, None)),
1348 "c datetimeoffset",
1349 ),
1350 ];
1351
1352 for (ty, expected) in cases {
1353 let expected = expected.replacen("c ", "[c] ", 1);
1354 assert_eq!(format!("{}", meta(ty, "c")), expected);
1355 }
1356 }
1357
1358 #[test]
1359 fn null_value_all_fixed_len() {
1360 use FixedLenType::*;
1361 let cases = [
1362 (Null, ColumnData::I32(None)),
1363 (Int1, ColumnData::U8(None)),
1364 (Bit, ColumnData::Bit(None)),
1365 (Int2, ColumnData::I16(None)),
1366 (Int4, ColumnData::I32(None)),
1367 (Datetime4, ColumnData::SmallDateTime(None)),
1368 (Float4, ColumnData::F32(None)),
1369 (Money, ColumnData::F64(None)),
1370 (Datetime, ColumnData::DateTime(None)),
1371 (Float8, ColumnData::F64(None)),
1372 (Money4, ColumnData::F32(None)),
1373 (Int8, ColumnData::I64(None)),
1374 ];
1375
1376 for (ty, expected) in cases {
1377 let base = BaseMetaDataColumn {
1378 flags: BitFlags::empty(),
1379 ty: TypeInfo::FixedLen(ty),
1380 table_name: None,
1381 };
1382 assert_eq!(base.null_value(), expected);
1383 }
1384 }
1385
1386 fn vsize_null(ty: VarLenType, len: usize) -> ColumnData<'static> {
1387 BaseMetaDataColumn {
1388 flags: BitFlags::empty(),
1389 ty: TypeInfo::VarLenSized(VarLenContext::new(ty, len, None)),
1390 table_name: None,
1391 }
1392 .null_value()
1393 }
1394
1395 #[test]
1396 fn null_value_all_var_len_sized() {
1397 use VarLenType::*;
1398 assert_eq!(vsize_null(Guid, 16), ColumnData::Guid(None));
1399 assert_eq!(vsize_null(Bitn, 1), ColumnData::Bit(None));
1400 assert_eq!(vsize_null(Decimaln, 17), ColumnData::Numeric(None));
1401 assert_eq!(vsize_null(Numericn, 17), ColumnData::Numeric(None));
1402 assert_eq!(vsize_null(Money, 8), ColumnData::F64(None));
1403 assert_eq!(vsize_null(Datetimen, 8), ColumnData::DateTime(None));
1404 assert_eq!(vsize_null(BigVarBin, 100), ColumnData::Binary(None));
1405 assert_eq!(vsize_null(BigVarChar, 100), ColumnData::String(None));
1406 assert_eq!(vsize_null(BigBinary, 10), ColumnData::Binary(None));
1407 assert_eq!(vsize_null(BigChar, 10), ColumnData::String(None));
1408 assert_eq!(vsize_null(NVarchar, 100), ColumnData::String(None));
1409 assert_eq!(vsize_null(NChar, 10), ColumnData::String(None));
1410 assert_eq!(vsize_null(Xml, 0), ColumnData::Xml(None));
1411 assert_eq!(vsize_null(Udt, 0), ColumnData::Binary(None));
1412 assert_eq!(vsize_null(Text, 0), ColumnData::String(None));
1413 assert_eq!(vsize_null(Image, 0), ColumnData::Binary(None));
1414 assert_eq!(vsize_null(NText, 0), ColumnData::String(None));
1415 assert_eq!(vsize_null(SSVariant, 0), ColumnData::String(None));
1416 }
1417
1418 #[cfg(feature = "tds73")]
1419 #[test]
1420 fn null_value_var_len_sized_tds73() {
1421 use VarLenType::*;
1422 assert_eq!(vsize_null(Daten, 3), ColumnData::Date(None));
1423 assert_eq!(vsize_null(Timen, 7), ColumnData::Time(None));
1424 assert_eq!(vsize_null(Datetime2, 7), ColumnData::DateTime2(None));
1425 assert_eq!(
1426 vsize_null(DatetimeOffsetn, 7),
1427 ColumnData::DateTimeOffset(None)
1428 );
1429 }
1430
1431 fn vprec_null(ty: VarLenType) -> ColumnData<'static> {
1432 BaseMetaDataColumn {
1433 flags: BitFlags::empty(),
1434 ty: TypeInfo::VarLenSizedPrecision {
1435 ty,
1436 size: 8,
1437 precision: 18,
1438 scale: 2,
1439 },
1440 table_name: None,
1441 }
1442 .null_value()
1443 }
1444
1445 #[test]
1446 fn null_value_all_var_len_precision() {
1447 use VarLenType::*;
1448 assert_eq!(vprec_null(Guid), ColumnData::Guid(None));
1449 assert_eq!(vprec_null(Intn), ColumnData::I32(None));
1450 assert_eq!(vprec_null(Bitn), ColumnData::Bit(None));
1451 assert_eq!(vprec_null(Decimaln), ColumnData::Numeric(None));
1452 assert_eq!(vprec_null(Numericn), ColumnData::Numeric(None));
1453 assert_eq!(vprec_null(Floatn), ColumnData::F32(None));
1454 assert_eq!(vprec_null(Money), ColumnData::F64(None));
1455 assert_eq!(vprec_null(Datetimen), ColumnData::DateTime(None));
1456 assert_eq!(vprec_null(BigVarBin), ColumnData::Binary(None));
1457 assert_eq!(vprec_null(BigVarChar), ColumnData::String(None));
1458 assert_eq!(vprec_null(BigBinary), ColumnData::Binary(None));
1459 assert_eq!(vprec_null(BigChar), ColumnData::String(None));
1460 assert_eq!(vprec_null(NVarchar), ColumnData::String(None));
1461 assert_eq!(vprec_null(NChar), ColumnData::String(None));
1462 assert_eq!(vprec_null(Xml), ColumnData::Xml(None));
1463 assert_eq!(vprec_null(Udt), ColumnData::Binary(None));
1464 assert_eq!(vprec_null(Text), ColumnData::String(None));
1465 assert_eq!(vprec_null(Image), ColumnData::Binary(None));
1466 assert_eq!(vprec_null(NText), ColumnData::String(None));
1467 assert_eq!(vprec_null(SSVariant), ColumnData::String(None));
1468 }
1469
1470 #[cfg(feature = "tds73")]
1471 #[test]
1472 fn null_value_var_len_precision_tds73() {
1473 use VarLenType::*;
1474 assert_eq!(vprec_null(Daten), ColumnData::Date(None));
1475 assert_eq!(vprec_null(Timen), ColumnData::Time(None));
1476 assert_eq!(vprec_null(Datetime2), ColumnData::DateTime2(None));
1477 assert_eq!(
1478 vprec_null(DatetimeOffsetn),
1479 ColumnData::DateTimeOffset(None)
1480 );
1481 }
1482
1483 #[test]
1484 fn column_flag_bits_are_distinct() {
1485 let all = ColumnFlag::Nullable
1486 | ColumnFlag::CaseSensitive
1487 | ColumnFlag::Updateable
1488 | ColumnFlag::UpdateableUnknown
1489 | ColumnFlag::Identity
1490 | ColumnFlag::Computed
1491 | ColumnFlag::FixedLenClrType
1492 | ColumnFlag::SparseColumnSet
1493 | ColumnFlag::Encrypted
1494 | ColumnFlag::Hidden
1495 | ColumnFlag::Key
1496 | ColumnFlag::NullableUnknown;
1497
1498 assert!(all.contains(ColumnFlag::Nullable));
1499 assert!(all.contains(ColumnFlag::NullableUnknown));
1500 assert_eq!(BitFlags::bits(all).count_ones(), 12);
1501 }
1502}