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 => match cx.len() {
144 4 => Self::Money4,
145 _ => Self::Money,
146 },
147 VarLenType::Datetimen => match cx.len() {
149 4 => Self::Datetime4,
150 _ => Self::Datetimen,
151 },
152 #[cfg(feature = "tds73")]
153 VarLenType::Daten => Self::Daten,
154 #[cfg(feature = "tds73")]
155 VarLenType::Timen => Self::Timen,
156 #[cfg(feature = "tds73")]
157 VarLenType::Datetime2 => Self::Datetime2,
158 #[cfg(feature = "tds73")]
159 VarLenType::DatetimeOffsetn => Self::DatetimeOffsetn,
160 VarLenType::BigVarBin => Self::BigVarBin,
161 VarLenType::BigVarChar => Self::BigVarChar,
162 VarLenType::BigBinary => Self::BigBinary,
163 VarLenType::BigChar => Self::BigChar,
164 VarLenType::NVarchar => Self::NVarchar,
165 VarLenType::NChar => Self::NChar,
166 VarLenType::Xml => Self::Xml,
167 VarLenType::Udt => Self::Udt,
168 VarLenType::Text => Self::Text,
169 VarLenType::Image => Self::Image,
170 VarLenType::NText => Self::NText,
171 VarLenType::SSVariant => Self::SSVariant,
172 },
173 TypeInfo::VarLenSizedPrecision { ty, .. } => match ty {
174 VarLenType::Guid => Self::Guid,
175 VarLenType::Intn => Self::Intn,
176 VarLenType::Bitn => Self::Bitn,
177 VarLenType::Decimaln => Self::Decimaln,
178 VarLenType::Numericn => Self::Numericn,
179 VarLenType::Floatn => Self::Floatn,
180 VarLenType::Money => Self::Money,
181 VarLenType::Datetimen => Self::Datetimen,
182 #[cfg(feature = "tds73")]
183 VarLenType::Daten => Self::Daten,
184 #[cfg(feature = "tds73")]
185 VarLenType::Timen => Self::Timen,
186 #[cfg(feature = "tds73")]
187 VarLenType::Datetime2 => Self::Datetime2,
188 #[cfg(feature = "tds73")]
189 VarLenType::DatetimeOffsetn => Self::DatetimeOffsetn,
190 VarLenType::BigVarBin => Self::BigVarBin,
191 VarLenType::BigVarChar => Self::BigVarChar,
192 VarLenType::BigBinary => Self::BigBinary,
193 VarLenType::BigChar => Self::BigChar,
194 VarLenType::NVarchar => Self::NVarchar,
195 VarLenType::NChar => Self::NChar,
196 VarLenType::Xml => Self::Xml,
197 VarLenType::Udt => Self::Udt,
198 VarLenType::Text => Self::Text,
199 VarLenType::Image => Self::Image,
200 VarLenType::NText => Self::NText,
201 VarLenType::SSVariant => Self::SSVariant,
202 },
203 TypeInfo::Xml { .. } => Self::Xml,
204 TypeInfo::Udt(_) => Self::Udt,
205 }
206 }
207}
208
209#[derive(Debug)]
259#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
260pub struct Row {
261 pub(crate) columns: Arc<Vec<Column>>,
262 pub(crate) data: TokenRow<'static>,
263 pub(crate) result_index: usize,
264}
265
266pub trait QueryIdx
272where
273 Self: Display,
274{
275 fn idx(&self, row: &Row) -> Option<usize>;
278}
279
280impl QueryIdx for usize {
281 fn idx(&self, row: &Row) -> Option<usize> {
282 if *self < row.columns.len() {
283 Some(*self)
284 } else {
285 None
286 }
287 }
288}
289
290impl QueryIdx for &str {
291 fn idx(&self, row: &Row) -> Option<usize> {
292 let name = self.strip_prefix("r#").unwrap_or(self);
295 row.columns.iter().position(|c| c.name() == name)
296 }
297}
298
299impl Row {
300 pub fn columns(&self) -> &[Column] {
331 &self.columns
332 }
333
334 pub fn cells(&self) -> impl Iterator<Item = (&Column, &ColumnData<'static>)> {
336 self.columns().iter().zip(self.data.iter())
337 }
338
339 pub fn result_index(&self) -> usize {
342 self.result_index
343 }
344
345 #[allow(clippy::len_without_is_empty)]
374 pub fn len(&self) -> usize {
375 self.data.len()
376 }
377
378 #[track_caller]
418 pub fn get<'a, R, I>(&'a self, idx: I) -> Option<R>
419 where
420 R: FromSql<'a>,
421 I: QueryIdx,
422 {
423 self.try_get(idx).unwrap()
424 }
425
426 #[track_caller]
428 pub fn try_get<'a, R, I>(&'a self, idx: I) -> crate::Result<Option<R>>
429 where
430 R: FromSql<'a>,
431 I: QueryIdx,
432 {
433 let data = self.get_column_data(idx)?;
434
435 R::from_sql(data)
436 }
437
438 #[track_caller]
440 pub fn get_column_data<I>(&self, idx: I) -> crate::Result<&ColumnData<'static>>
441 where
442 I: QueryIdx,
443 {
444 let idx = idx.idx(self).ok_or_else(|| {
445 Error::Conversion(format!("Could not find column with index {}", idx).into())
446 })?;
447
448 self.data.get(idx).ok_or_else(|| {
452 Error::Protocol(format!("row has no data for column index {idx}").into())
453 })
454 }
455
456 pub fn into_token_row(self) -> TokenRow<'static> {
465 self.data
466 }
467}
468
469impl IntoIterator for Row {
470 type Item = ColumnData<'static>;
471 type IntoIter = std::vec::IntoIter<Self::Item>;
472
473 fn into_iter(self) -> Self::IntoIter {
474 self.data.into_iter()
475 }
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481
482 fn make_row() -> Row {
483 let columns = Arc::new(vec![
484 Column::new("foo".to_string(), ColumnType::Int4),
485 Column::new("type".to_string(), ColumnType::Int4),
486 ]);
487
488 let mut data = TokenRow::new();
489 data.push(ColumnData::I32(Some(1)));
490 data.push(ColumnData::I32(Some(2)));
491
492 Row {
493 columns,
494 data,
495 result_index: 0,
496 }
497 }
498
499 #[test]
500 fn result_index_reflects_the_field() {
501 let columns = Arc::new(vec![Column::new("c".to_string(), ColumnType::Int4)]);
504 let mut data = TokenRow::new();
505 data.push(ColumnData::I32(Some(1)));
506 let row = Row {
507 columns,
508 data,
509 result_index: 3,
510 };
511 assert_eq!(row.result_index(), 3);
512 }
513
514 #[test]
516 fn try_get_out_of_range_index_returns_none() {
517 let row = make_row();
518
519 assert_eq!(None, 2usize.idx(&row));
520 assert_eq!(Some(0), 0usize.idx(&row));
521
522 let value: crate::Result<Option<i32>> = row.try_get(5usize);
523 assert!(value.is_err());
524 }
525
526 #[test]
529 fn raw_identifier_column_name_matches() {
530 let row = make_row();
531
532 assert_eq!(Some(1), "type".idx(&row));
533 assert_eq!(Some(1), "r#type".idx(&row));
534 assert_eq!(Some(0), "r#foo".idx(&row));
535 assert_eq!(None, "r#missing".idx(&row));
536
537 assert_eq!(Some(2i32), row.get::<i32, _>("r#type"));
538 }
539
540 #[test]
541 fn row_accessors() {
542 let row = make_row();
543
544 assert_eq!(2, row.columns().len());
545 assert_eq!(2, row.len());
546 assert_eq!(0, row.result_index());
547
548 let cells: Vec<_> = row.cells().collect();
549 assert_eq!(2, cells.len());
550 assert_eq!("foo", cells[0].0.name());
551
552 let token_row = row.into_token_row();
553 assert_eq!(2, token_row.len());
554 }
555
556 #[test]
557 fn row_into_iterator_yields_column_data() {
558 let row = make_row();
559 let values: Vec<_> = row.into_iter().collect();
560 assert_eq!(
561 vec![ColumnData::I32(Some(1)), ColumnData::I32(Some(2))],
562 values
563 );
564 }
565
566 #[test]
567 fn get_column_data_missing_cell_errors() {
568 let columns = Arc::new(vec![
569 Column::new("a".to_string(), ColumnType::Int4),
570 Column::new("b".to_string(), ColumnType::Int4),
571 ]);
572
573 let mut data = TokenRow::new();
575 data.push(ColumnData::I32(Some(1)));
576
577 let row = Row {
578 columns,
579 data,
580 result_index: 0,
581 };
582
583 let err = row.get_column_data(1usize).unwrap_err();
584 assert!(format!("{}", err).contains("row has no data for column index"));
585 }
586
587 #[test]
588 fn column_new_and_accessors() {
589 let column = Column::new("id".to_string(), ColumnType::Int8);
590 assert_eq!("id", column.name());
591 assert_eq!(ColumnType::Int8, column.column_type());
592 }
593
594 #[test]
595 fn column_type_from_fixed_len_type_info() {
596 use crate::tds::codec::FixedLenType;
597
598 let cases = [
599 (FixedLenType::Int1, ColumnType::Int1),
600 (FixedLenType::Bit, ColumnType::Bit),
601 (FixedLenType::Int2, ColumnType::Int2),
602 (FixedLenType::Int4, ColumnType::Int4),
603 (FixedLenType::Datetime4, ColumnType::Datetime4),
604 (FixedLenType::Float4, ColumnType::Float4),
605 (FixedLenType::Money, ColumnType::Money),
606 (FixedLenType::Datetime, ColumnType::Datetime),
607 (FixedLenType::Float8, ColumnType::Float8),
608 (FixedLenType::Money4, ColumnType::Money4),
609 (FixedLenType::Int8, ColumnType::Int8),
610 (FixedLenType::Null, ColumnType::Null),
611 ];
612
613 for (flt, expected) in cases {
614 let ti = TypeInfo::FixedLen(flt);
615 assert_eq!(ColumnType::from(&ti), expected);
616 }
617 }
618
619 #[test]
620 fn column_type_from_var_len_sized_type_info() {
621 use crate::tds::codec::VarLenType;
622 use crate::VarLenContext;
623
624 let cases = [
625 (VarLenType::Guid, 16, ColumnType::Guid),
626 (VarLenType::Intn, 1, ColumnType::Int1),
627 (VarLenType::Intn, 2, ColumnType::Int2),
628 (VarLenType::Intn, 4, ColumnType::Int4),
629 (VarLenType::Intn, 8, ColumnType::Int8),
630 (VarLenType::Intn, 3, ColumnType::Intn),
631 (VarLenType::Bitn, 1, ColumnType::Bitn),
632 (VarLenType::Decimaln, 17, ColumnType::Decimaln),
633 (VarLenType::Numericn, 17, ColumnType::Numericn),
634 (VarLenType::Floatn, 4, ColumnType::Float4),
635 (VarLenType::Floatn, 8, ColumnType::Float8),
636 (VarLenType::Floatn, 2, ColumnType::Floatn),
637 (VarLenType::Money, 4, ColumnType::Money4),
638 (VarLenType::Money, 8, ColumnType::Money),
639 (VarLenType::Datetimen, 4, ColumnType::Datetime4),
640 (VarLenType::Datetimen, 8, ColumnType::Datetimen),
641 (VarLenType::BigVarBin, 8000, ColumnType::BigVarBin),
642 (VarLenType::BigVarChar, 8000, ColumnType::BigVarChar),
643 (VarLenType::BigBinary, 8000, ColumnType::BigBinary),
644 (VarLenType::BigChar, 8000, ColumnType::BigChar),
645 (VarLenType::NVarchar, 4000, ColumnType::NVarchar),
646 (VarLenType::NChar, 4000, ColumnType::NChar),
647 (VarLenType::Xml, 0, ColumnType::Xml),
648 (VarLenType::Udt, 0, ColumnType::Udt),
649 (VarLenType::Text, 0, ColumnType::Text),
650 (VarLenType::Image, 0, ColumnType::Image),
651 (VarLenType::NText, 0, ColumnType::NText),
652 (VarLenType::SSVariant, 0, ColumnType::SSVariant),
653 ];
654
655 for (ty, len, expected) in cases {
656 let ti = TypeInfo::VarLenSized(VarLenContext::new(ty, len, None));
657 assert_eq!(ColumnType::from(&ti), expected, "{:?} len {}", ty, len);
658 }
659 }
660
661 #[test]
662 fn column_type_from_var_len_sized_precision_type_info() {
663 use crate::tds::codec::VarLenType;
664
665 let cases = [
666 (VarLenType::Guid, ColumnType::Guid),
667 (VarLenType::Intn, ColumnType::Intn),
668 (VarLenType::Bitn, ColumnType::Bitn),
669 (VarLenType::Decimaln, ColumnType::Decimaln),
670 (VarLenType::Numericn, ColumnType::Numericn),
671 (VarLenType::Floatn, ColumnType::Floatn),
672 (VarLenType::Money, ColumnType::Money),
673 (VarLenType::Datetimen, ColumnType::Datetimen),
674 (VarLenType::BigVarBin, ColumnType::BigVarBin),
675 (VarLenType::BigVarChar, ColumnType::BigVarChar),
676 (VarLenType::BigBinary, ColumnType::BigBinary),
677 (VarLenType::BigChar, ColumnType::BigChar),
678 (VarLenType::NVarchar, ColumnType::NVarchar),
679 (VarLenType::NChar, ColumnType::NChar),
680 (VarLenType::Xml, ColumnType::Xml),
681 (VarLenType::Udt, ColumnType::Udt),
682 (VarLenType::Text, ColumnType::Text),
683 (VarLenType::Image, ColumnType::Image),
684 (VarLenType::NText, ColumnType::NText),
685 (VarLenType::SSVariant, ColumnType::SSVariant),
686 ];
687
688 for (ty, expected) in cases {
689 let ti = TypeInfo::VarLenSizedPrecision {
690 ty,
691 size: 38,
692 precision: 38,
693 scale: 2,
694 };
695 assert_eq!(ColumnType::from(&ti), expected, "{:?}", ty);
696 }
697 }
698
699 #[test]
700 fn column_type_from_xml_and_udt_type_info() {
701 use crate::tds::codec::UdtInfo;
702 use crate::tds::xml::XmlSchema;
703 use std::sync::Arc as StdArc;
704
705 let ti = TypeInfo::Xml {
706 schema: None::<StdArc<XmlSchema>>,
707 size: 0,
708 };
709 assert_eq!(ColumnType::from(&ti), ColumnType::Xml);
710
711 let ti = TypeInfo::Udt(UdtInfo {
712 max_byte_size: 0xffff,
713 db_name: "db".to_string(),
714 schema_name: "dbo".to_string(),
715 type_name: "geometry".to_string(),
716 assembly_qualified_name: "asm".to_string(),
717 });
718 assert_eq!(ColumnType::from(&ti), ColumnType::Udt);
719 }
720
721 #[cfg(feature = "tds73")]
722 #[test]
723 fn column_type_from_var_len_sized_tds73_type_info() {
724 use crate::tds::codec::VarLenType;
725 use crate::VarLenContext;
726
727 let cases = [
728 (VarLenType::Daten, ColumnType::Daten),
729 (VarLenType::Timen, ColumnType::Timen),
730 (VarLenType::Datetime2, ColumnType::Datetime2),
731 (VarLenType::DatetimeOffsetn, ColumnType::DatetimeOffsetn),
732 ];
733
734 for (ty, expected) in cases {
735 let ti = TypeInfo::VarLenSized(VarLenContext::new(ty, 8, None));
736 assert_eq!(ColumnType::from(&ti), expected, "{:?}", ty);
737 }
738 }
739
740 #[cfg(feature = "tds73")]
741 #[test]
742 fn column_type_from_var_len_sized_precision_tds73_type_info() {
743 use crate::tds::codec::VarLenType;
744
745 let cases = [
746 (VarLenType::Daten, ColumnType::Daten),
747 (VarLenType::Timen, ColumnType::Timen),
748 (VarLenType::Datetime2, ColumnType::Datetime2),
749 (VarLenType::DatetimeOffsetn, ColumnType::DatetimeOffsetn),
750 ];
751
752 for (ty, expected) in cases {
753 let ti = TypeInfo::VarLenSizedPrecision {
754 ty,
755 size: 8,
756 precision: 0,
757 scale: 7,
758 };
759 assert_eq!(ColumnType::from(&ti), expected, "{:?}", ty);
760 }
761 }
762}