drizzle_core/prepared/
owned.rs

1use crate::prelude::*;
2use crate::{
3    OwnedParam, ParamBind, SQL, SQLChunk, ToSQL,
4    prepared::{PreparedStatement, bind_values_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    /// Fully rendered SQL with placeholders for this dialect
19    pub sql: CompactString,
20}
21impl<V: SQLParam> core::fmt::Display for OwnedPreparedStatement<V> {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        write!(f, "{}", self.sql())
24    }
25}
26
27impl<'a, V: SQLParam> From<PreparedStatement<'a, V>> for OwnedPreparedStatement<V> {
28    fn from(prepared: PreparedStatement<'a, V>) -> Self {
29        OwnedPreparedStatement {
30            text_segments: prepared.text_segments,
31            params: prepared.params.into_iter().map(|p| p.into()).collect(),
32            sql: prepared.sql,
33        }
34    }
35}
36
37impl<V: SQLParam> OwnedPreparedStatement<V> {
38    /// Bind parameters and return SQL with dialect-appropriate placeholders.
39    /// Uses `$1, $2, ...` for PostgreSQL, `?` for SQLite/MySQL.
40    pub fn bind<'a, T: SQLParam + Into<V>>(
41        &self,
42        param_binds: impl IntoIterator<Item = ParamBind<'a, T>>,
43    ) -> (&str, impl Iterator<Item = V>) {
44        let bound_params = bind_values_internal(
45            &self.params,
46            param_binds,
47            |p| p.placeholder.name,
48            |p| p.value.as_ref(), // OwnedParam can store values
49        );
50
51        (self.sql.as_str(), bound_params)
52    }
53
54    /// Returns the fully rendered SQL with placeholders.
55    pub fn sql(&self) -> &str {
56        self.sql.as_str()
57    }
58}
59
60impl<'a, V: SQLParam> ToSQL<'a, V> for OwnedPreparedStatement<V> {
61    fn to_sql(&self) -> SQL<'a, V> {
62        // Calculate exact capacity needed: text_segments.len() + params.len()
63        let capacity = self.text_segments.len() + self.params.len();
64        let mut chunks = SmallVec::with_capacity(capacity);
65
66        // Interleave text segments and params: text[0], param[0], text[1], param[1], ..., text[n]
67        // Use iterators to avoid bounds checking and minimize allocations
68        let mut param_iter = self.params.iter();
69
70        for text_segment in &self.text_segments {
71            chunks.push(SQLChunk::Raw(Cow::Owned(text_segment.to_string())));
72
73            // Add corresponding param if available
74            if let Some(param) = param_iter.next() {
75                chunks.push(SQLChunk::Param(param.clone().into()));
76            }
77        }
78
79        SQL { chunks }
80    }
81}