drizzle_core/prepared/
owned.rs

1use crate::prelude::*;
2use crate::{
3    OwnedParam, ParamBind, SQL, SQLChunk, ToSQL,
4    prepared::{PreparedStatement, bind_parameters_internal},
5    traits::SQLParam,
6};
7use compact_str::CompactString;
8use core::fmt;
9use smallvec::SmallVec;
10
11/// An owned version of PreparedStatement with no lifetime dependencies
12#[derive(Debug, Clone)]
13pub struct OwnedPreparedStatement<V: SQLParam> {
14    /// Pre-rendered text segments
15    pub text_segments: Box<[CompactString]>,
16    /// Parameter placeholders (in order) - only placeholders, no values
17    pub params: Box<[OwnedParam<V>]>,
18}
19impl<V: SQLParam + core::fmt::Display + 'static> core::fmt::Display for OwnedPreparedStatement<V> {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        write!(f, "{}", self.to_sql())
22    }
23}
24
25impl<'a, V: SQLParam> From<PreparedStatement<'a, V>> for OwnedPreparedStatement<V> {
26    fn from(prepared: PreparedStatement<'a, V>) -> Self {
27        OwnedPreparedStatement {
28            text_segments: prepared.text_segments,
29            params: prepared.params.into_iter().map(|p| p.into()).collect(),
30        }
31    }
32}
33
34impl<V: SQLParam> OwnedPreparedStatement<V> {
35    /// Bind parameters and render final SQL string
36    pub fn bind<'a, T: SQLParam + Into<V>>(
37        &self,
38        param_binds: impl IntoIterator<Item = ParamBind<'a, T>>,
39    ) -> (String, impl Iterator<Item = V>) {
40        bind_parameters_internal(
41            &self.text_segments,
42            &self.params,
43            param_binds,
44            |p| p.placeholder.name,
45            |p| p.value.as_ref(), // OwnedParam can store values
46            |p| {
47                if let Some(name) = &p.placeholder.name {
48                    format!(":{}", name)
49                } else {
50                    "?".to_string()
51                }
52            },
53        )
54    }
55}
56
57impl<'a, V: SQLParam> ToSQL<'a, V> for OwnedPreparedStatement<V> {
58    fn to_sql(&self) -> SQL<'a, V> {
59        // Calculate exact capacity needed: text_segments.len() + params.len()
60        let capacity = self.text_segments.len() + self.params.len();
61        let mut chunks = SmallVec::with_capacity(capacity);
62
63        // Interleave text segments and params: text[0], param[0], text[1], param[1], ..., text[n]
64        // Use iterators to avoid bounds checking and minimize allocations
65        let mut param_iter = self.params.iter();
66
67        for text_segment in &self.text_segments {
68            chunks.push(SQLChunk::Raw(Cow::Owned(text_segment.to_string())));
69
70            // Add corresponding param if available
71            if let Some(param) = param_iter.next() {
72                chunks.push(SQLChunk::Param(param.clone().into()));
73            }
74        }
75
76        SQL { chunks }
77    }
78}