use crate::prelude::*;
use crate::{
OwnedParam, ParamBind, SQL, SQLChunk, ToSQL,
prepared::{PreparedStatement, bind_values_internal},
traits::SQLParam,
};
use compact_str::CompactString;
use core::fmt;
use hashbrown::HashMap;
use smallvec::SmallVec;
#[derive(Debug, Clone)]
pub struct OwnedPreparedStatement<V: SQLParam> {
pub text_segments: Box<[CompactString]>,
pub params: Box<[OwnedParam<V>]>,
pub sql: CompactString,
}
impl<V: SQLParam> core::fmt::Display for OwnedPreparedStatement<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.sql())
}
}
impl<'a, V: SQLParam> From<PreparedStatement<'a, V>> for OwnedPreparedStatement<V> {
fn from(prepared: PreparedStatement<'a, V>) -> Self {
Self {
text_segments: prepared.text_segments,
params: prepared
.params
.into_iter()
.map(core::convert::Into::into)
.collect(),
sql: prepared.sql,
}
}
}
impl<V: SQLParam> OwnedPreparedStatement<V> {
#[must_use]
pub fn external_param_count(&self) -> usize {
let mut named = HashMap::<&str, ()>::new();
let mut positional = 0usize;
for param in &self.params {
if param.value.is_some() {
continue;
}
match param.placeholder.name {
Some(name) if !name.is_empty() => {
named.entry(name).or_insert(());
}
_ => positional += 1,
}
}
named.len() + positional
}
pub fn bind<'a, T: SQLParam + Into<V>>(
&self,
param_binds: impl IntoIterator<Item = ParamBind<'a, T>>,
) -> crate::error::Result<(&str, impl Iterator<Item = V>)> {
let bound_params = bind_values_internal(
&self.params,
param_binds,
|p| p.placeholder.name,
|p| p.value.as_ref(), )?;
Ok((self.sql.as_str(), bound_params.into_iter()))
}
#[must_use]
pub fn sql(&self) -> &str {
self.sql.as_str()
}
}
impl<'a, V: SQLParam> ToSQL<'a, V> for OwnedPreparedStatement<V> {
fn to_sql(&self) -> SQL<'a, V> {
let capacity = self.text_segments.len() + self.params.len();
let mut chunks = SmallVec::with_capacity(capacity);
let mut param_iter = self.params.iter();
for text_segment in &self.text_segments {
chunks.push(SQLChunk::Raw(Cow::Owned(text_segment.to_string())));
if let Some(param) = param_iter.next() {
chunks.push(SQLChunk::Param(param.clone().into()));
}
}
SQL { chunks }
}
}