Skip to main content

sqlx_cloudflare_core/
row.rs

1use std::sync::Arc;
2
3use sqlx_core::error::Error;
4
5use crate::{TypeInfo, Value};
6
7/// The rows of one result, from its column names and its rows of values.
8///
9/// Each column is typed by its first non-NULL value, since neither backend
10/// reports declared types; `column` builds a driver's column from its name,
11/// ordinal and that type, and every row shares the one column list. `row`
12/// builds a driver's row from the columns and its values.
13///
14/// # Errors
15///
16/// When a row does not have one value per column.
17pub fn rows<C, R>(
18    names: Vec<String>,
19    rows: Vec<Vec<Value>>,
20    column: impl Fn(String, usize, TypeInfo) -> C,
21    row: impl Fn(Arc<[C]>, Box<[Value]>) -> R,
22) -> Result<Vec<R>, Error> {
23    let columns: Arc<[C]> = names
24        .into_iter()
25        .enumerate()
26        .map(|(ordinal, name)| {
27            let type_info = rows
28                .iter()
29                .filter_map(|row| row.get(ordinal))
30                .map(Value::type_info)
31                .find(|type_info| *type_info != TypeInfo::Null)
32                .unwrap_or(TypeInfo::Null);
33
34            column(name, ordinal, type_info)
35        })
36        .collect();
37
38    rows.into_iter()
39        .map(|values| {
40            if values.len() != columns.len() {
41                return Err(Error::Protocol(format!(
42                    "a row of {} values came back for {} columns",
43                    values.len(),
44                    columns.len()
45                )));
46            }
47
48            Ok(row(Arc::clone(&columns), values.into_boxed_slice()))
49        })
50        .collect()
51}
52
53#[cfg(test)]
54mod tests {
55    use super::rows;
56    use crate::{TypeInfo, Value};
57
58    fn types(names: &[&str], values: Vec<Vec<Value>>) -> Vec<TypeInfo> {
59        let names = names.iter().map(|name| (*name).to_owned()).collect();
60        let rows = rows(
61            names,
62            values,
63            |_, _, type_info| type_info,
64            |columns, _| columns,
65        )
66        .unwrap();
67        rows[0].to_vec()
68    }
69
70    #[test]
71    fn a_column_is_typed_by_its_first_non_null_value() {
72        let types = types(
73            &["id", "name", "gone"],
74            vec![
75                vec![Value::Integer(1), Value::Null, Value::Null],
76                vec![Value::Integer(2), Value::Text("b".into()), Value::Null],
77            ],
78        );
79
80        assert_eq!(types, [TypeInfo::Integer, TypeInfo::Text, TypeInfo::Null]);
81    }
82
83    #[test]
84    fn a_short_row_is_an_error() {
85        assert!(rows(
86            vec!["a".into(), "b".into()],
87            vec![vec![Value::Null]],
88            |_, _, _| (),
89            |_, _| ()
90        )
91        .is_err());
92    }
93}