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