sql_middleware/postgres/
params.rs

1use std::error::Error;
2
3use crate::middleware::{ConversionMode, ParamConverter, RowValues, SqlMiddlewareDbError};
4use tokio_postgres::types::{IsNull, ToSql, Type, to_sql_checked};
5use tokio_util::bytes;
6
7/// Container for Postgres parameters with lifetime tracking
8pub struct Params<'a> {
9    references: Vec<&'a (dyn ToSql + Sync)>,
10}
11
12impl<'a> Params<'a> {
13    /// Convert from a slice of `RowValues` to Postgres parameters
14    ///
15    /// # Errors
16    /// Currently never returns an error but maintains Result for API consistency.
17    pub fn convert(params: &'a [RowValues]) -> Result<Params<'a>, SqlMiddlewareDbError> {
18        let references: Vec<&(dyn ToSql + Sync)> =
19            params.iter().map(|p| p as &(dyn ToSql + Sync)).collect();
20
21        Ok(Params { references })
22    }
23
24    /// Convert a slice of `RowValues` for batch operations
25    ///
26    /// # Errors
27    /// Currently never returns an error but maintains Result for API consistency.
28    pub fn convert_for_batch(
29        params: &'a [RowValues],
30    ) -> Result<Vec<&'a (dyn ToSql + Sync + 'a)>, SqlMiddlewareDbError> {
31        // Pre-allocate capacity for better performance
32        let mut references = Vec::with_capacity(params.len());
33
34        // Avoid collect() and just push directly
35        for p in params {
36            references.push(p as &(dyn ToSql + Sync));
37        }
38
39        Ok(references)
40    }
41
42    /// Get a reference to the underlying parameter array
43    #[must_use]
44    pub fn as_refs(&self) -> &[&(dyn ToSql + Sync)] {
45        &self.references
46    }
47}
48
49impl<'a> ParamConverter<'a> for Params<'a> {
50    type Converted = Params<'a>;
51
52    fn convert_sql_params(
53        params: &'a [RowValues],
54        _mode: ConversionMode,
55    ) -> Result<Self::Converted, SqlMiddlewareDbError> {
56        // Simply delegate to your existing conversion:
57        Self::convert(params)
58    }
59
60    // PostgresParams supports both query and execution modes
61    fn supports_mode(_mode: ConversionMode) -> bool {
62        true
63    }
64}
65
66impl ToSql for RowValues {
67    fn to_sql(
68        &self,
69        ty: &Type,
70        out: &mut bytes::BytesMut,
71    ) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
72        match self {
73            RowValues::Int(i) => match *ty {
74                Type::INT8 => (*i).to_sql(ty, out),
75                Type::INT4 => {
76                    let v = i32::try_from(*i).map_err(|_| {
77                        SqlMiddlewareDbError::ExecutionError(format!(
78                            "integer value {i} overflows Postgres INT4 parameter"
79                        ))
80                    })?;
81                    v.to_sql(ty, out)
82                }
83                Type::INT2 => {
84                    let v = i16::try_from(*i).map_err(|_| {
85                        SqlMiddlewareDbError::ExecutionError(format!(
86                            "integer value {i} overflows Postgres INT2 parameter"
87                        ))
88                    })?;
89                    v.to_sql(ty, out)
90                }
91                _ => Err(Box::new(SqlMiddlewareDbError::ExecutionError(format!(
92                    "unsupported integer parameter type: {ty:?}"
93                )))),
94            },
95            RowValues::Float(f) => (*f).to_sql(ty, out),
96            RowValues::Text(s) => s.to_sql(ty, out),
97            RowValues::Bool(b) => (*b).to_sql(ty, out),
98            RowValues::Timestamp(dt) => dt.to_sql(ty, out),
99            RowValues::Null => Ok(IsNull::Yes),
100            RowValues::JSON(jsval) => jsval.to_sql(ty, out),
101            RowValues::Blob(bytes) => bytes.to_sql(ty, out),
102        }
103    }
104
105    fn accepts(ty: &Type) -> bool {
106        // Only accept types we can properly handle
107        match *ty {
108            // All supported types
109            Type::INT2 | Type::INT4 | Type::INT8 |                    // Integer types
110            Type::FLOAT4 | Type::FLOAT8 |                             // Floating point types
111            Type::TEXT | Type::VARCHAR | Type::CHAR | Type::NAME |    // Text types
112            Type::BOOL |                                              // Boolean type
113            Type::TIMESTAMP | Type::TIMESTAMPTZ | Type::DATE |        // Date/time types
114            Type::JSON | Type::JSONB |                                // JSON types
115            Type::BYTEA => true,                                      // Binary data
116            // For any other type, we don't accept
117            _ => false,
118        }
119    }
120
121    to_sql_checked!();
122}