1use crate::{
2 error::Error,
3 tds::codec::{ColumnData, FixedLenType, TokenRow, TypeInfo, VarLenType},
4 FromSql,
5};
6use std::{fmt::Display, sync::Arc};
7
8#[derive(Debug, Clone)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct Column {
12 pub(crate) name: String,
13 pub(crate) column_type: ColumnType,
14}
15
16impl Column {
17 pub fn new(name: String, column_type: ColumnType) -> Self {
19 Self { name, column_type }
20 }
21
22 pub fn name(&self) -> &str {
24 &self.name
25 }
26
27 pub fn column_type(&self) -> ColumnType {
29 self.column_type
30 }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35pub enum ColumnType {
37 Null,
39 Bit,
41 Int1,
43 Int2,
45 Int4,
47 Int8,
49 Datetime4,
51 Float4,
53 Float8,
55 Money,
57 Datetime,
59 Money4,
61 Guid,
63 Intn,
65 Bitn,
67 Decimaln,
69 Numericn,
71 Floatn,
73 Datetimen,
75 Daten,
77 Timen,
79 Datetime2,
81 DatetimeOffsetn,
83 BigVarBin,
85 BigVarChar,
87 BigBinary,
89 BigChar,
91 NVarchar,
93 NChar,
95 Xml,
97 Udt,
99 Text,
101 Image,
103 NText,
105 SSVariant,
107}
108
109impl From<&TypeInfo> for ColumnType {
110 fn from(ti: &TypeInfo) -> Self {
111 match ti {
112 TypeInfo::FixedLen(flt) => match flt {
113 FixedLenType::Int1 => Self::Int1,
114 FixedLenType::Bit => Self::Bit,
115 FixedLenType::Int2 => Self::Int2,
116 FixedLenType::Int4 => Self::Int4,
117 FixedLenType::Datetime4 => Self::Datetime4,
118 FixedLenType::Float4 => Self::Float4,
119 FixedLenType::Money => Self::Money,
120 FixedLenType::Datetime => Self::Datetime,
121 FixedLenType::Float8 => Self::Float8,
122 FixedLenType::Money4 => Self::Money4,
123 FixedLenType::Int8 => Self::Int8,
124 FixedLenType::Null => Self::Null,
125 },
126 TypeInfo::VarLenSized(cx) => match cx.r#type() {
127 VarLenType::Guid => Self::Guid,
128 VarLenType::Intn => match cx.len() {
129 1 => Self::Int1,
130 2 => Self::Int2,
131 4 => Self::Int4,
132 8 => Self::Int8,
133 _ => Self::Intn,
134 },
135 VarLenType::Bitn => Self::Bitn,
136 VarLenType::Decimaln => Self::Decimaln,
137 VarLenType::Numericn => Self::Numericn,
138 VarLenType::Floatn => match cx.len() {
139 4 => Self::Float4,
140 8 => Self::Float8,
141 _ => Self::Floatn,
142 },
143 VarLenType::Money => Self::Money,
144 VarLenType::Datetimen => Self::Datetimen,
145 #[cfg(feature = "tds73")]
146 VarLenType::Daten => Self::Daten,
147 #[cfg(feature = "tds73")]
148 VarLenType::Timen => Self::Timen,
149 #[cfg(feature = "tds73")]
150 VarLenType::Datetime2 => Self::Datetime2,
151 #[cfg(feature = "tds73")]
152 VarLenType::DatetimeOffsetn => Self::DatetimeOffsetn,
153 VarLenType::BigVarBin => Self::BigVarBin,
154 VarLenType::BigVarChar => Self::BigVarChar,
155 VarLenType::BigBinary => Self::BigBinary,
156 VarLenType::BigChar => Self::BigChar,
157 VarLenType::NVarchar => Self::NVarchar,
158 VarLenType::NChar => Self::NChar,
159 VarLenType::Xml => Self::Xml,
160 VarLenType::Udt => Self::Udt,
161 VarLenType::Text => Self::Text,
162 VarLenType::Image => Self::Image,
163 VarLenType::NText => Self::NText,
164 VarLenType::SSVariant => Self::SSVariant,
165 },
166 TypeInfo::VarLenSizedPrecision { ty, .. } => match ty {
167 VarLenType::Guid => Self::Guid,
168 VarLenType::Intn => Self::Intn,
169 VarLenType::Bitn => Self::Bitn,
170 VarLenType::Decimaln => Self::Decimaln,
171 VarLenType::Numericn => Self::Numericn,
172 VarLenType::Floatn => Self::Floatn,
173 VarLenType::Money => Self::Money,
174 VarLenType::Datetimen => Self::Datetimen,
175 #[cfg(feature = "tds73")]
176 VarLenType::Daten => Self::Daten,
177 #[cfg(feature = "tds73")]
178 VarLenType::Timen => Self::Timen,
179 #[cfg(feature = "tds73")]
180 VarLenType::Datetime2 => Self::Datetime2,
181 #[cfg(feature = "tds73")]
182 VarLenType::DatetimeOffsetn => Self::DatetimeOffsetn,
183 VarLenType::BigVarBin => Self::BigVarBin,
184 VarLenType::BigVarChar => Self::BigVarChar,
185 VarLenType::BigBinary => Self::BigBinary,
186 VarLenType::BigChar => Self::BigChar,
187 VarLenType::NVarchar => Self::NVarchar,
188 VarLenType::NChar => Self::NChar,
189 VarLenType::Xml => Self::Xml,
190 VarLenType::Udt => Self::Udt,
191 VarLenType::Text => Self::Text,
192 VarLenType::Image => Self::Image,
193 VarLenType::NText => Self::NText,
194 VarLenType::SSVariant => Self::SSVariant,
195 },
196 TypeInfo::Xml { .. } => Self::Xml,
197 TypeInfo::Udt(_) => Self::Udt,
198 }
199 }
200}
201
202#[derive(Debug)]
252#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
253pub struct Row {
254 pub(crate) columns: Arc<Vec<Column>>,
255 pub(crate) data: TokenRow<'static>,
256 pub(crate) result_index: usize,
257}
258
259pub trait QueryIdx
265where
266 Self: Display,
267{
268 fn idx(&self, row: &Row) -> Option<usize>;
271}
272
273impl QueryIdx for usize {
274 fn idx(&self, row: &Row) -> Option<usize> {
275 if *self < row.columns.len() {
276 Some(*self)
277 } else {
278 None
279 }
280 }
281}
282
283impl QueryIdx for &str {
284 fn idx(&self, row: &Row) -> Option<usize> {
285 let name = self.strip_prefix("r#").unwrap_or(self);
288 row.columns.iter().position(|c| c.name() == name)
289 }
290}
291
292impl Row {
293 pub fn columns(&self) -> &[Column] {
324 &self.columns
325 }
326
327 pub fn cells(&self) -> impl Iterator<Item = (&Column, &ColumnData<'static>)> {
329 self.columns().iter().zip(self.data.iter())
330 }
331
332 pub fn result_index(&self) -> usize {
335 self.result_index
336 }
337
338 #[allow(clippy::len_without_is_empty)]
367 pub fn len(&self) -> usize {
368 self.data.len()
369 }
370
371 #[track_caller]
411 pub fn get<'a, R, I>(&'a self, idx: I) -> Option<R>
412 where
413 R: FromSql<'a>,
414 I: QueryIdx,
415 {
416 self.try_get(idx).unwrap()
417 }
418
419 #[track_caller]
421 pub fn try_get<'a, R, I>(&'a self, idx: I) -> crate::Result<Option<R>>
422 where
423 R: FromSql<'a>,
424 I: QueryIdx,
425 {
426 let data = self.get_column_data(idx)?;
427
428 R::from_sql(data)
429 }
430
431 #[track_caller]
433 pub fn get_column_data<I>(&self, idx: I) -> crate::Result<&ColumnData<'static>>
434 where
435 I: QueryIdx,
436 {
437 let idx = idx.idx(self).ok_or_else(|| {
438 Error::Conversion(format!("Could not find column with index {}", idx).into())
439 })?;
440
441 self.data.get(idx).ok_or_else(|| {
445 Error::Protocol(format!("row has no data for column index {idx}").into())
446 })
447 }
448
449 pub fn into_token_row(self) -> TokenRow<'static> {
458 self.data
459 }
460}
461
462impl IntoIterator for Row {
463 type Item = ColumnData<'static>;
464 type IntoIter = std::vec::IntoIter<Self::Item>;
465
466 fn into_iter(self) -> Self::IntoIter {
467 self.data.into_iter()
468 }
469}
470
471#[cfg(test)]
472mod tests {
473 use super::*;
474
475 fn make_row() -> Row {
476 let columns = Arc::new(vec![
477 Column::new("foo".to_string(), ColumnType::Int4),
478 Column::new("type".to_string(), ColumnType::Int4),
479 ]);
480
481 let mut data = TokenRow::new();
482 data.push(ColumnData::I32(Some(1)));
483 data.push(ColumnData::I32(Some(2)));
484
485 Row {
486 columns,
487 data,
488 result_index: 0,
489 }
490 }
491
492 #[test]
493 fn result_index_reflects_the_field() {
494 let columns = Arc::new(vec![Column::new("c".to_string(), ColumnType::Int4)]);
497 let mut data = TokenRow::new();
498 data.push(ColumnData::I32(Some(1)));
499 let row = Row {
500 columns,
501 data,
502 result_index: 3,
503 };
504 assert_eq!(row.result_index(), 3);
505 }
506
507 #[test]
509 fn try_get_out_of_range_index_returns_none() {
510 let row = make_row();
511
512 assert_eq!(None, 2usize.idx(&row));
513 assert_eq!(Some(0), 0usize.idx(&row));
514
515 let value: crate::Result<Option<i32>> = row.try_get(5usize);
516 assert!(value.is_err());
517 }
518
519 #[test]
522 fn raw_identifier_column_name_matches() {
523 let row = make_row();
524
525 assert_eq!(Some(1), "type".idx(&row));
526 assert_eq!(Some(1), "r#type".idx(&row));
527 assert_eq!(Some(0), "r#foo".idx(&row));
528 assert_eq!(None, "r#missing".idx(&row));
529
530 assert_eq!(Some(2i32), row.get::<i32, _>("r#type"));
531 }
532
533 #[test]
534 fn row_accessors() {
535 let row = make_row();
536
537 assert_eq!(2, row.columns().len());
538 assert_eq!(2, row.len());
539 assert_eq!(0, row.result_index());
540
541 let cells: Vec<_> = row.cells().collect();
542 assert_eq!(2, cells.len());
543 assert_eq!("foo", cells[0].0.name());
544
545 let token_row = row.into_token_row();
546 assert_eq!(2, token_row.len());
547 }
548
549 #[test]
550 fn row_into_iterator_yields_column_data() {
551 let row = make_row();
552 let values: Vec<_> = row.into_iter().collect();
553 assert_eq!(
554 vec![ColumnData::I32(Some(1)), ColumnData::I32(Some(2))],
555 values
556 );
557 }
558
559 #[test]
560 fn get_column_data_missing_cell_errors() {
561 let columns = Arc::new(vec![
562 Column::new("a".to_string(), ColumnType::Int4),
563 Column::new("b".to_string(), ColumnType::Int4),
564 ]);
565
566 let mut data = TokenRow::new();
568 data.push(ColumnData::I32(Some(1)));
569
570 let row = Row {
571 columns,
572 data,
573 result_index: 0,
574 };
575
576 let err = row.get_column_data(1usize).unwrap_err();
577 assert!(format!("{}", err).contains("row has no data for column index"));
578 }
579
580 #[test]
581 fn column_new_and_accessors() {
582 let column = Column::new("id".to_string(), ColumnType::Int8);
583 assert_eq!("id", column.name());
584 assert_eq!(ColumnType::Int8, column.column_type());
585 }
586
587 #[test]
588 fn column_type_from_fixed_len_type_info() {
589 use crate::tds::codec::FixedLenType;
590
591 let cases = [
592 (FixedLenType::Int1, ColumnType::Int1),
593 (FixedLenType::Bit, ColumnType::Bit),
594 (FixedLenType::Int2, ColumnType::Int2),
595 (FixedLenType::Int4, ColumnType::Int4),
596 (FixedLenType::Datetime4, ColumnType::Datetime4),
597 (FixedLenType::Float4, ColumnType::Float4),
598 (FixedLenType::Money, ColumnType::Money),
599 (FixedLenType::Datetime, ColumnType::Datetime),
600 (FixedLenType::Float8, ColumnType::Float8),
601 (FixedLenType::Money4, ColumnType::Money4),
602 (FixedLenType::Int8, ColumnType::Int8),
603 (FixedLenType::Null, ColumnType::Null),
604 ];
605
606 for (flt, expected) in cases {
607 let ti = TypeInfo::FixedLen(flt);
608 assert_eq!(ColumnType::from(&ti), expected);
609 }
610 }
611
612 #[test]
613 fn column_type_from_var_len_sized_type_info() {
614 use crate::tds::codec::VarLenType;
615 use crate::VarLenContext;
616
617 let cases = [
618 (VarLenType::Guid, 16, ColumnType::Guid),
619 (VarLenType::Intn, 1, ColumnType::Int1),
620 (VarLenType::Intn, 2, ColumnType::Int2),
621 (VarLenType::Intn, 4, ColumnType::Int4),
622 (VarLenType::Intn, 8, ColumnType::Int8),
623 (VarLenType::Intn, 3, ColumnType::Intn),
624 (VarLenType::Bitn, 1, ColumnType::Bitn),
625 (VarLenType::Decimaln, 17, ColumnType::Decimaln),
626 (VarLenType::Numericn, 17, ColumnType::Numericn),
627 (VarLenType::Floatn, 4, ColumnType::Float4),
628 (VarLenType::Floatn, 8, ColumnType::Float8),
629 (VarLenType::Floatn, 2, ColumnType::Floatn),
630 (VarLenType::Money, 8, ColumnType::Money),
631 (VarLenType::Datetimen, 8, ColumnType::Datetimen),
632 (VarLenType::BigVarBin, 8000, ColumnType::BigVarBin),
633 (VarLenType::BigVarChar, 8000, ColumnType::BigVarChar),
634 (VarLenType::BigBinary, 8000, ColumnType::BigBinary),
635 (VarLenType::BigChar, 8000, ColumnType::BigChar),
636 (VarLenType::NVarchar, 4000, ColumnType::NVarchar),
637 (VarLenType::NChar, 4000, ColumnType::NChar),
638 (VarLenType::Xml, 0, ColumnType::Xml),
639 (VarLenType::Udt, 0, ColumnType::Udt),
640 (VarLenType::Text, 0, ColumnType::Text),
641 (VarLenType::Image, 0, ColumnType::Image),
642 (VarLenType::NText, 0, ColumnType::NText),
643 (VarLenType::SSVariant, 0, ColumnType::SSVariant),
644 ];
645
646 for (ty, len, expected) in cases {
647 let ti = TypeInfo::VarLenSized(VarLenContext::new(ty, len, None));
648 assert_eq!(ColumnType::from(&ti), expected, "{:?} len {}", ty, len);
649 }
650 }
651
652 #[test]
653 fn column_type_from_var_len_sized_precision_type_info() {
654 use crate::tds::codec::VarLenType;
655
656 let cases = [
657 (VarLenType::Guid, ColumnType::Guid),
658 (VarLenType::Intn, ColumnType::Intn),
659 (VarLenType::Bitn, ColumnType::Bitn),
660 (VarLenType::Decimaln, ColumnType::Decimaln),
661 (VarLenType::Numericn, ColumnType::Numericn),
662 (VarLenType::Floatn, ColumnType::Floatn),
663 (VarLenType::Money, ColumnType::Money),
664 (VarLenType::Datetimen, ColumnType::Datetimen),
665 (VarLenType::BigVarBin, ColumnType::BigVarBin),
666 (VarLenType::BigVarChar, ColumnType::BigVarChar),
667 (VarLenType::BigBinary, ColumnType::BigBinary),
668 (VarLenType::BigChar, ColumnType::BigChar),
669 (VarLenType::NVarchar, ColumnType::NVarchar),
670 (VarLenType::NChar, ColumnType::NChar),
671 (VarLenType::Xml, ColumnType::Xml),
672 (VarLenType::Udt, ColumnType::Udt),
673 (VarLenType::Text, ColumnType::Text),
674 (VarLenType::Image, ColumnType::Image),
675 (VarLenType::NText, ColumnType::NText),
676 (VarLenType::SSVariant, ColumnType::SSVariant),
677 ];
678
679 for (ty, expected) in cases {
680 let ti = TypeInfo::VarLenSizedPrecision {
681 ty,
682 size: 38,
683 precision: 38,
684 scale: 2,
685 };
686 assert_eq!(ColumnType::from(&ti), expected, "{:?}", ty);
687 }
688 }
689
690 #[test]
691 fn column_type_from_xml_and_udt_type_info() {
692 use crate::tds::codec::UdtInfo;
693 use crate::tds::xml::XmlSchema;
694 use std::sync::Arc as StdArc;
695
696 let ti = TypeInfo::Xml {
697 schema: None::<StdArc<XmlSchema>>,
698 size: 0,
699 };
700 assert_eq!(ColumnType::from(&ti), ColumnType::Xml);
701
702 let ti = TypeInfo::Udt(UdtInfo {
703 max_byte_size: 0xffff,
704 db_name: "db".to_string(),
705 schema_name: "dbo".to_string(),
706 type_name: "geometry".to_string(),
707 assembly_qualified_name: "asm".to_string(),
708 });
709 assert_eq!(ColumnType::from(&ti), ColumnType::Udt);
710 }
711
712 #[cfg(feature = "tds73")]
713 #[test]
714 fn column_type_from_var_len_sized_tds73_type_info() {
715 use crate::tds::codec::VarLenType;
716 use crate::VarLenContext;
717
718 let cases = [
719 (VarLenType::Daten, ColumnType::Daten),
720 (VarLenType::Timen, ColumnType::Timen),
721 (VarLenType::Datetime2, ColumnType::Datetime2),
722 (VarLenType::DatetimeOffsetn, ColumnType::DatetimeOffsetn),
723 ];
724
725 for (ty, expected) in cases {
726 let ti = TypeInfo::VarLenSized(VarLenContext::new(ty, 8, None));
727 assert_eq!(ColumnType::from(&ti), expected, "{:?}", ty);
728 }
729 }
730
731 #[cfg(feature = "tds73")]
732 #[test]
733 fn column_type_from_var_len_sized_precision_tds73_type_info() {
734 use crate::tds::codec::VarLenType;
735
736 let cases = [
737 (VarLenType::Daten, ColumnType::Daten),
738 (VarLenType::Timen, ColumnType::Timen),
739 (VarLenType::Datetime2, ColumnType::Datetime2),
740 (VarLenType::DatetimeOffsetn, ColumnType::DatetimeOffsetn),
741 ];
742
743 for (ty, expected) in cases {
744 let ti = TypeInfo::VarLenSizedPrecision {
745 ty,
746 size: 8,
747 precision: 0,
748 scale: 7,
749 };
750 assert_eq!(ColumnType::from(&ti), expected, "{:?}", ty);
751 }
752 }
753}