sqlx_mssql_odbc_core/
row.rs1use crate::{Mssql, MssqlColumn, MssqlValue};
2use std::sync::Arc;
3
4#[derive(Debug, Clone, Default)]
6pub struct MssqlRow {
7 columns: Arc<[MssqlColumn]>,
8 values: Vec<MssqlValue>,
9}
10
11impl MssqlRow {
12 pub fn new(columns: Vec<MssqlColumn>, values: Vec<MssqlValue>) -> Self {
14 Self::new_shared(columns.into(), values)
15 }
16
17 pub(crate) fn new_shared(columns: Arc<[MssqlColumn]>, values: Vec<MssqlValue>) -> Self {
18 Self { columns, values }
19 }
20}
21
22impl sqlx_core::row::Row for MssqlRow {
23 type Database = Mssql;
24
25 fn columns(&self) -> &[MssqlColumn] {
26 self.columns.as_ref()
27 }
28
29 fn try_get_raw<I>(
30 &self,
31 index: I,
32 ) -> Result<<Self::Database as sqlx_core::database::Database>::ValueRef<'_>, sqlx_core::Error>
33 where
34 I: sqlx_core::column::ColumnIndex<Self>,
35 {
36 let index = index.index(self)?;
37 let value = self
38 .values
39 .get(index)
40 .ok_or(sqlx_core::Error::ColumnIndexOutOfBounds {
41 index,
42 len: self.values.len(),
43 })?;
44
45 Ok(sqlx_core::value::Value::as_ref(value))
46 }
47}
48
49impl sqlx_core::column::ColumnIndex<MssqlRow> for usize {
50 fn index(&self, row: &MssqlRow) -> Result<usize, sqlx_core::Error> {
51 if *self >= row.columns.len() {
52 return Err(sqlx_core::Error::ColumnIndexOutOfBounds {
53 index: *self,
54 len: row.columns.len(),
55 });
56 }
57
58 Ok(*self)
59 }
60}
61
62impl sqlx_core::column::ColumnIndex<MssqlRow> for &str {
63 fn index(&self, row: &MssqlRow) -> Result<usize, sqlx_core::Error> {
64 if let Some(index) = row
65 .columns
66 .iter()
67 .position(|column| sqlx_core::column::Column::name(column) == *self)
68 {
69 return Ok(index);
70 }
71
72 row.columns
73 .iter()
74 .position(|column| sqlx_core::column::Column::name(column).eq_ignore_ascii_case(self))
75 .ok_or_else(|| sqlx_core::Error::ColumnNotFound((*self).to_owned()))
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::{MssqlTypeInfo, MssqlValueKind};
83
84 fn create_test_row() -> MssqlRow {
85 MssqlRow::new(
86 vec![
87 MssqlColumn::new(
88 0,
89 "lowercase_col",
90 MssqlTypeInfo::new(odbc_api::DataType::Integer),
91 None,
92 ),
93 MssqlColumn::new(
94 1,
95 "UPPERCASE_COL",
96 MssqlTypeInfo::new(odbc_api::DataType::Varchar { length: None }),
97 None,
98 ),
99 MssqlColumn::new(
100 2,
101 "MixedCase_Col",
102 MssqlTypeInfo::new(odbc_api::DataType::Double),
103 None,
104 ),
105 ],
106 vec![
107 MssqlValue::new(MssqlValueKind::Integer(42)),
108 MssqlValue::new(MssqlValueKind::Text("test".to_owned())),
109 MssqlValue::new(MssqlValueKind::Double(std::f64::consts::PI)),
110 ],
111 )
112 }
113
114 #[test]
115 fn exact_column_match_works() {
116 let row = create_test_row();
117
118 assert_eq!(
119 sqlx_core::column::ColumnIndex::<MssqlRow>::index(&"lowercase_col", &row).unwrap(),
120 0
121 );
122 assert_eq!(
123 sqlx_core::column::ColumnIndex::<MssqlRow>::index(&"UPPERCASE_COL", &row).unwrap(),
124 1
125 );
126 assert_eq!(
127 sqlx_core::column::ColumnIndex::<MssqlRow>::index(&"MixedCase_Col", &row).unwrap(),
128 2
129 );
130 }
131
132 #[test]
133 fn case_insensitive_column_match_works() {
134 let row = create_test_row();
135
136 assert_eq!(
137 sqlx_core::column::ColumnIndex::<MssqlRow>::index(&"LOWERCASE_COL", &row).unwrap(),
138 0
139 );
140 assert_eq!(
141 sqlx_core::column::ColumnIndex::<MssqlRow>::index(&"uppercase_col", &row).unwrap(),
142 1
143 );
144 assert_eq!(
145 sqlx_core::column::ColumnIndex::<MssqlRow>::index(&"mixedcase_col", &row).unwrap(),
146 2
147 );
148 }
149
150 #[test]
151 fn missing_column_reports_name() {
152 let row = create_test_row();
153 let error = sqlx_core::column::ColumnIndex::<MssqlRow>::index(&"missing", &row).unwrap_err();
154
155 assert!(matches!(error, sqlx_core::Error::ColumnNotFound(name) if name == "missing"));
156 }
157
158 #[test]
159 fn try_get_raw_uses_case_insensitive_column_lookup() {
160 use sqlx_core::row::Row;
161
162 let row = create_test_row();
163 let value = row.try_get_raw("mixedcase_col").unwrap();
164
165 assert_eq!(value.as_f64(), Some(std::f64::consts::PI));
166 }
167
168 #[test]
169 fn columns_returns_metadata_in_order() {
170 use sqlx_core::column::Column;
171 use sqlx_core::row::Row;
172
173 let row = create_test_row();
174 let columns = row.columns();
175
176 assert_eq!(columns.len(), 3);
177 assert_eq!(columns[0].ordinal(), 0);
178 assert_eq!(columns[0].name(), "lowercase_col");
179 assert_eq!(columns[1].ordinal(), 1);
180 assert_eq!(columns[1].name(), "UPPERCASE_COL");
181 assert_eq!(columns[2].ordinal(), 2);
182 assert_eq!(columns[2].name(), "MixedCase_Col");
183 }
184}