Skip to main content

sqlx_odbc/
row.rs

1use crate::{Odbc, OdbcColumn, OdbcValue};
2
3/// Minimal ODBC row container used by the SQLx-core skeleton.
4#[derive(Debug, Clone, Default)]
5pub struct OdbcRow {
6    columns: Vec<OdbcColumn>,
7    values: Vec<OdbcValue>,
8}
9
10impl OdbcRow {
11    /// Creates a row from column metadata and values.
12    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}