use std::error::Error;
use crate::middleware::{ConversionMode, ParamConverter, RowValues, SqlMiddlewareDbError};
use tokio_postgres::types::{IsNull, ToSql, Type, to_sql_checked};
use tokio_util::bytes;
pub struct Params<'a> {
references: Vec<&'a (dyn ToSql + Sync)>,
}
impl<'a> Params<'a> {
pub fn convert(params: &'a [RowValues]) -> Result<Params<'a>, SqlMiddlewareDbError> {
let references: Vec<&(dyn ToSql + Sync)> =
params.iter().map(|p| p as &(dyn ToSql + Sync)).collect();
Ok(Params { references })
}
pub fn convert_for_batch(
params: &'a [RowValues],
) -> Result<Vec<&'a (dyn ToSql + Sync + 'a)>, SqlMiddlewareDbError> {
let mut references = Vec::with_capacity(params.len());
for p in params {
references.push(p as &(dyn ToSql + Sync));
}
Ok(references)
}
#[must_use]
pub fn as_refs(&self) -> &[&(dyn ToSql + Sync)] {
&self.references
}
}
impl<'a> ParamConverter<'a> for Params<'a> {
type Converted = Params<'a>;
fn convert_sql_params(
params: &'a [RowValues],
_mode: ConversionMode,
) -> Result<Self::Converted, SqlMiddlewareDbError> {
Self::convert(params)
}
fn supports_mode(_mode: ConversionMode) -> bool {
true
}
}
impl ToSql for RowValues {
fn to_sql(
&self,
ty: &Type,
out: &mut bytes::BytesMut,
) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
match self {
RowValues::Int(i) => match *ty {
Type::INT8 => (*i).to_sql(ty, out),
Type::INT4 => {
let v = i32::try_from(*i).map_err(|_| {
SqlMiddlewareDbError::ExecutionError(format!(
"integer value {i} overflows Postgres INT4 parameter"
))
})?;
v.to_sql(ty, out)
}
Type::INT2 => {
let v = i16::try_from(*i).map_err(|_| {
SqlMiddlewareDbError::ExecutionError(format!(
"integer value {i} overflows Postgres INT2 parameter"
))
})?;
v.to_sql(ty, out)
}
_ => Err(Box::new(SqlMiddlewareDbError::ExecutionError(format!(
"unsupported integer parameter type: {ty:?}"
)))),
},
RowValues::Float(f) => (*f).to_sql(ty, out),
RowValues::Text(s) => s.to_sql(ty, out),
RowValues::Bool(b) => (*b).to_sql(ty, out),
RowValues::Timestamp(dt) => dt.to_sql(ty, out),
RowValues::Null => Ok(IsNull::Yes),
RowValues::JSON(jsval) => jsval.to_sql(ty, out),
RowValues::Blob(bytes) => bytes.to_sql(ty, out),
}
}
fn accepts(ty: &Type) -> bool {
match *ty {
Type::INT2 | Type::INT4 | Type::INT8 | Type::FLOAT4 | Type::FLOAT8 | Type::TEXT | Type::VARCHAR | Type::CHAR | Type::NAME | Type::BOOL | Type::TIMESTAMP | Type::TIMESTAMPTZ | Type::DATE | Type::JSON | Type::JSONB | Type::BYTEA => true, _ => false,
}
}
to_sql_checked!();
}