sql_middleware/sqlite/
params.rs

1use deadpool_sqlite::rusqlite;
2use deadpool_sqlite::rusqlite::ParamsFromIter;
3use rusqlite::types::Value;
4use std::fmt::Write;
5
6use crate::middleware::{ConversionMode, ParamConverter, RowValues, SqlMiddlewareDbError};
7
8// Thread-local buffer for efficient timestamp formatting
9thread_local! {
10    static TIMESTAMP_BUF: std::cell::RefCell<String> = std::cell::RefCell::new(String::with_capacity(32));
11}
12
13/// Convert a single `RowValue` to a rusqlite `Value`
14pub fn row_value_to_sqlite_value(value: &RowValues, for_execute: bool) -> rusqlite::types::Value {
15    match value {
16        RowValues::Int(i) => rusqlite::types::Value::Integer(*i),
17        RowValues::Float(f) => rusqlite::types::Value::Real(*f),
18        RowValues::Text(s) => {
19            if for_execute {
20                // For execute, we can move the owned String directly
21                rusqlite::types::Value::Text(s.clone())
22            } else {
23                // For queries, we need to clone
24                rusqlite::types::Value::Text(s.clone())
25            }
26        }
27        RowValues::Bool(b) => rusqlite::types::Value::Integer(i64::from(*b)),
28        // Format timestamps once for better performance
29        RowValues::Timestamp(dt) => {
30            // Use a thread_local buffer for timestamp formatting to avoid allocation
31            TIMESTAMP_BUF.with(|buf| {
32                let mut borrow = buf.borrow_mut();
33                borrow.clear();
34                // Format directly into the string buffer
35                write!(borrow, "{}", dt.format("%F %T%.f")).unwrap();
36                rusqlite::types::Value::Text(borrow.clone())
37            })
38        }
39        RowValues::Null => rusqlite::types::Value::Null,
40        RowValues::JSON(jval) => {
41            // Only serialize once to avoid multiple allocations
42            let json_str = jval.to_string();
43            rusqlite::types::Value::Text(json_str)
44        }
45        RowValues::Blob(bytes) => {
46            if for_execute {
47                // For execute, we can directly use the bytes
48                rusqlite::types::Value::Blob(bytes.clone())
49            } else {
50                // For queries, we need to clone
51                rusqlite::types::Value::Blob(bytes.clone())
52            }
53        }
54    }
55}
56
57/// Bind middleware params to `SQLite` types.
58pub fn convert_params(params: &[RowValues]) -> Vec<rusqlite::types::Value> {
59    let mut vec_values = Vec::with_capacity(params.len());
60    for p in params {
61        vec_values.push(row_value_to_sqlite_value(p, false));
62    }
63    vec_values
64}
65
66/// Convert parameters for execution operations
67pub fn convert_params_for_execute<I>(iter: I) -> ParamsFromIter<std::vec::IntoIter<Value>>
68where
69    I: IntoIterator<Item = RowValues>,
70{
71    // Convert directly to avoid unnecessary collection
72    let mut values = Vec::new();
73    for p in iter {
74        values.push(row_value_to_sqlite_value(&p, true));
75    }
76    // Note: clippy suggests removing .into_iter(), but removing it causes
77    // a type error. This is because params_from_iter needs IntoIter<Value>,
78    // not Vec<Value>, despite the type parameter suggesting otherwise.
79    #[allow(clippy::useless_conversion)]
80    rusqlite::params_from_iter(values.into_iter())
81}
82
83/// Wrapper for `SQLite` parameters for queries.
84pub struct SqliteParamsQuery(pub Vec<rusqlite::types::Value>);
85
86/// Wrapper for `SQLite` parameters for execution.
87pub struct SqliteParamsExecute(
88    pub rusqlite::ParamsFromIter<std::vec::IntoIter<rusqlite::types::Value>>,
89);
90
91impl ParamConverter<'_> for SqliteParamsQuery {
92    type Converted = Self;
93
94    fn convert_sql_params(
95        params: &[RowValues],
96        mode: ConversionMode,
97    ) -> Result<Self::Converted, SqlMiddlewareDbError> {
98        match mode {
99            // For a query, use the conversion that returns a Vec<Value>
100            ConversionMode::Query => Ok(SqliteParamsQuery(convert_params(params))),
101            // Or, if you really want to support execution mode with this type, you might decide how to handle it:
102            ConversionMode::Execute => {
103                // For example, you could also call the "query" conversion here or return an error.
104                Ok(SqliteParamsQuery(convert_params(params)))
105            }
106        }
107    }
108
109    fn supports_mode(mode: ConversionMode) -> bool {
110        // This converter is primarily for query operations
111        mode == ConversionMode::Query
112    }
113}
114
115impl ParamConverter<'_> for SqliteParamsExecute {
116    type Converted = Self;
117
118    fn convert_sql_params(
119        params: &[RowValues],
120        mode: ConversionMode,
121    ) -> Result<Self::Converted, SqlMiddlewareDbError> {
122        match mode {
123            ConversionMode::Execute => Ok(SqliteParamsExecute(convert_params_for_execute(
124                params.to_vec(),
125            ))),
126            // For queries you might not support the "execute" wrapper:
127            ConversionMode::Query => Err(SqlMiddlewareDbError::ParameterError(
128                "SqliteParamsExecute can only be used with Execute mode".into(),
129            )),
130        }
131    }
132
133    fn supports_mode(mode: ConversionMode) -> bool {
134        // This converter is only for execution operations
135        mode == ConversionMode::Execute
136    }
137}