sql_middleware/sqlite/
query.rs

1use deadpool_sqlite::rusqlite;
2use rusqlite::types::Value;
3use rusqlite::{Statement, ToSql};
4
5use crate::middleware::{ResultSet, RowValues, SqlMiddlewareDbError};
6
7/// Extract a `RowValues` from a `SQLite` row
8pub fn sqlite_extract_value_sync(
9    row: &rusqlite::Row,
10    idx: usize,
11) -> Result<RowValues, SqlMiddlewareDbError> {
12    let val_ref_res = row.get_ref(idx);
13    match val_ref_res {
14        Err(e) => Err(SqlMiddlewareDbError::SqliteError(e)),
15        Ok(rusqlite::types::ValueRef::Null) => Ok(RowValues::Null),
16        Ok(rusqlite::types::ValueRef::Integer(i)) => Ok(RowValues::Int(i)),
17        Ok(rusqlite::types::ValueRef::Real(f)) => Ok(RowValues::Float(f)),
18        Ok(rusqlite::types::ValueRef::Text(bytes)) => {
19            // Fast path for ASCII (which most database text likely is)
20            if bytes.is_ascii() {
21                // ASCII is valid UTF-8, use lossy conversion for safety
22                let s = String::from_utf8_lossy(bytes).into_owned();
23                Ok(RowValues::Text(s))
24            } else {
25                // Fallback for non-ASCII
26                let s = String::from_utf8_lossy(bytes).into_owned();
27                Ok(RowValues::Text(s))
28            }
29        }
30        Ok(rusqlite::types::ValueRef::Blob(b)) => Ok(RowValues::Blob(b.to_vec())),
31    }
32}
33
34/// Build a result set from a `SQLite` query
35/// Only SELECT queries return rows affected. If a DML is sent, it does run it.
36/// If there's more than one query in the statement, idk which statement will be run.
37///
38/// # Errors
39/// Returns `SqlMiddlewareDbError::ExecutionError` if query execution or result processing fails.
40pub fn build_result_set(
41    stmt: &mut Statement,
42    params: &[Value],
43) -> Result<ResultSet, SqlMiddlewareDbError> {
44    let param_refs: Vec<&dyn ToSql> = params.iter().map(|v| v as &dyn ToSql).collect();
45    let column_names: Vec<String> = stmt
46        .column_names()
47        .iter()
48        .map(std::string::ToString::to_string)
49        .collect();
50
51    // Store column names once in the result set
52    let column_names_rc = std::sync::Arc::new(column_names);
53
54    let mut rows_iter = stmt.query(&param_refs[..])?;
55    // Create result set with default capacity
56    let mut result_set = ResultSet::with_capacity(10);
57    result_set.set_column_names(column_names_rc);
58
59    while let Some(row) = rows_iter.next()? {
60        let mut row_values = Vec::new();
61
62        let col_count = result_set
63            .get_column_names()
64            .ok_or_else(|| {
65                SqlMiddlewareDbError::ExecutionError("No column names available".to_string())
66            })?
67            .len();
68
69        for i in 0..col_count {
70            let value = sqlite_extract_value_sync(row, i)?;
71            row_values.push(value);
72        }
73
74        result_set.add_row_values(row_values);
75    }
76
77    Ok(result_set)
78}