1use crate::{Odbc, OdbcColumn, OdbcValue};
2use std::sync::Arc;
3
4#[derive(Debug, Clone, Default)]
6pub struct OdbcRow {
7 columns: Arc<[OdbcColumn]>,
8 values: Vec<OdbcValue>,
9}
10
11impl OdbcRow {
12 pub fn new(columns: Vec<OdbcColumn>, values: Vec<OdbcValue>) -> Self {
14 Self::new_shared(columns.into(), values)
15 }
16
17 pub(crate) fn new_shared(columns: Arc<[OdbcColumn]>, values: Vec<OdbcValue>) -> Self {
18 Self { columns, values }
19 }
20}
21
22impl sqlx_core::row::Row for OdbcRow {
23 type Database = Odbc;
24
25 fn columns(&self) -> &[OdbcColumn] {
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<OdbcRow> for usize {
50 fn index(&self, row: &OdbcRow) -> 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<OdbcRow> for &str {
63 fn index(&self, row: &OdbcRow) -> 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::{OdbcTypeInfo, OdbcValueKind};
83
84 fn create_test_row() -> OdbcRow {
85 OdbcRow::new(
86 vec![
87 OdbcColumn::new(
88 0,
89 "lowercase_col",
90 OdbcTypeInfo::new(odbc_api::DataType::Integer),
91 ),
92 OdbcColumn::new(
93 1,
94 "UPPERCASE_COL",
95 OdbcTypeInfo::new(odbc_api::DataType::Varchar { length: None }),
96 ),
97 OdbcColumn::new(
98 2,
99 "MixedCase_Col",
100 OdbcTypeInfo::new(odbc_api::DataType::Double),
101 ),
102 ],
103 vec![
104 OdbcValue::new(OdbcValueKind::Integer(42)),
105 OdbcValue::new(OdbcValueKind::Text("test".to_owned())),
106 OdbcValue::new(OdbcValueKind::Double(std::f64::consts::PI)),
107 ],
108 )
109 }
110
111 #[test]
112 fn exact_column_match_works() {
113 let row = create_test_row();
114
115 assert_eq!(
116 sqlx_core::column::ColumnIndex::<OdbcRow>::index(&"lowercase_col", &row).unwrap(),
117 0
118 );
119 assert_eq!(
120 sqlx_core::column::ColumnIndex::<OdbcRow>::index(&"UPPERCASE_COL", &row).unwrap(),
121 1
122 );
123 assert_eq!(
124 sqlx_core::column::ColumnIndex::<OdbcRow>::index(&"MixedCase_Col", &row).unwrap(),
125 2
126 );
127 }
128
129 #[test]
130 fn case_insensitive_column_match_works() {
131 let row = create_test_row();
132
133 assert_eq!(
134 sqlx_core::column::ColumnIndex::<OdbcRow>::index(&"LOWERCASE_COL", &row).unwrap(),
135 0
136 );
137 assert_eq!(
138 sqlx_core::column::ColumnIndex::<OdbcRow>::index(&"uppercase_col", &row).unwrap(),
139 1
140 );
141 assert_eq!(
142 sqlx_core::column::ColumnIndex::<OdbcRow>::index(&"mixedcase_col", &row).unwrap(),
143 2
144 );
145 }
146
147 #[test]
148 fn missing_column_reports_name() {
149 let row = create_test_row();
150 let error = sqlx_core::column::ColumnIndex::<OdbcRow>::index(&"missing", &row).unwrap_err();
151
152 assert!(matches!(error, sqlx_core::Error::ColumnNotFound(name) if name == "missing"));
153 }
154}