Skip to main content

drizzle_sqlite/builder/
prepared.rs

1//! `SQLite` statements rendered once, with their parameter slots.
2
3use crate::prelude::*;
4
5use drizzle_core::{
6    OwnedParam, Param,
7    prepared::{
8        OwnedPreparedStatement as CoreOwnedPreparedStatement,
9        PreparedStatement as CorePreparedStatement,
10    },
11};
12
13use crate::values::{OwnedSQLiteValue, SQLiteValue};
14
15/// A rendered `SQLite` statement split into SQL text and parameter slots.
16///
17/// Placeholders (see `drizzle_core::Placeholder`) stay as empty slots that
18/// are filled when the statement runs; other values are already bound. This
19/// type borrows its values for `'a`; use [`into_owned`](Self::into_owned)
20/// or [`OwnedPreparedStatement`] to keep it longer. Its `Display` output is
21/// the SQL text.
22#[derive(Debug, Clone)]
23pub struct PreparedStatement<'a> {
24    pub(crate) inner: CorePreparedStatement<'a, SQLiteValue<'a>>,
25}
26
27impl PreparedStatement<'_> {
28    /// Clones this statement into an [`OwnedPreparedStatement`] with no
29    /// borrowed data.
30    #[must_use]
31    pub fn into_owned(&self) -> OwnedPreparedStatement {
32        let owned_params = self.inner.params.iter().map(|p| OwnedParam {
33            placeholder: p.placeholder,
34            value: p
35                .value
36                .clone()
37                .map(|v| OwnedSQLiteValue::from(v.into_owned())),
38        });
39
40        let inner = CoreOwnedPreparedStatement {
41            text_segments: self.inner.text_segments.clone(),
42            params: owned_params.collect::<Box<[_]>>(),
43            sql: self.inner.sql.clone(),
44        };
45
46        OwnedPreparedStatement { inner }
47    }
48}
49
50/// A [`PreparedStatement`] that owns all of its values.
51///
52/// It has no lifetime, so it can be cached or stored in application state.
53/// Convert with [`PreparedStatement::into_owned`] or
54/// `From<PreparedStatement>`, and back with `From<OwnedPreparedStatement>`.
55#[derive(Debug, Clone)]
56pub struct OwnedPreparedStatement {
57    pub(crate) inner: CoreOwnedPreparedStatement<crate::values::OwnedSQLiteValue>,
58}
59impl<'a> From<PreparedStatement<'a>> for OwnedPreparedStatement {
60    fn from(value: PreparedStatement<'a>) -> Self {
61        let owned_params = value.inner.params.iter().map(|p| OwnedParam {
62            placeholder: p.placeholder,
63            value: p
64                .value
65                .clone()
66                .map(|v| OwnedSQLiteValue::from(v.into_owned())),
67        });
68        let inner = CoreOwnedPreparedStatement {
69            text_segments: value.inner.text_segments,
70            params: owned_params.collect::<Box<[_]>>(),
71            sql: value.inner.sql,
72        };
73        Self { inner }
74    }
75}
76
77impl From<OwnedPreparedStatement> for PreparedStatement<'_> {
78    fn from(value: OwnedPreparedStatement) -> Self {
79        let sqlitevalue = value.inner.params.iter().map(|v| {
80            Param::new(
81                v.placeholder,
82                v.value.clone().map(|v| Cow::Owned(SQLiteValue::from(v))),
83            )
84        });
85        let inner = CorePreparedStatement {
86            text_segments: value.inner.text_segments,
87            params: sqlitevalue.collect::<Box<[_]>>(),
88            sql: value.inner.sql,
89        };
90        PreparedStatement { inner }
91    }
92}
93
94impl OwnedPreparedStatement {}
95
96impl core::fmt::Display for PreparedStatement<'_> {
97    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
98        write!(f, "{}", self.inner)
99    }
100}
101
102impl core::fmt::Display for OwnedPreparedStatement {
103    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104        write!(f, "{}", self.inner)
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111    use crate::values::SQLiteValue;
112    use drizzle_core::{SQL, ToSQL, prepared::prepare_render};
113
114    #[test]
115    fn test_prepare_render_basic() {
116        // Test the basic prepare_render functionality for SQLite
117        let sql: SQL<'_, SQLiteValue<'_>> = SQL::raw("SELECT * FROM users WHERE id = ")
118            .append(drizzle_core::Placeholder::named("user_id").to_sql())
119            .append(SQL::raw(" AND name = "))
120            .append(drizzle_core::Placeholder::named("user_name").to_sql());
121
122        let prepared = prepare_render(&sql);
123
124        // Should have 3 text segments: before first param, between params, after last param
125        assert_eq!(prepared.text_segments.len(), 3);
126        assert_eq!(prepared.params.len(), 2);
127
128        // Verify text segments contain expected content
129        assert!(prepared.text_segments[0].contains("SELECT * FROM users WHERE id"));
130        assert!(prepared.text_segments[1].contains("AND name"));
131    }
132
133    #[test]
134    fn test_prepare_with_no_parameters() {
135        // Test preparing SQL with no parameters
136        let sql: SQL<'_, SQLiteValue<'_>> = SQL::raw("SELECT COUNT(*) FROM users");
137        let prepared = prepare_render(&sql);
138
139        assert_eq!(prepared.text_segments.len(), 1);
140        assert_eq!(prepared.params.len(), 0);
141        assert_eq!(prepared.text_segments[0], "SELECT COUNT(*) FROM users");
142    }
143
144    #[test]
145    fn test_prepared_statement_display() {
146        let sql: SQL<'_, SQLiteValue<'_>> = SQL::raw("SELECT * FROM users")
147            .append(SQL::raw(" WHERE id = "))
148            .append(drizzle_core::Placeholder::named("id").to_sql());
149
150        let prepared = prepare_render(&sql);
151        let display = format!("{}", prepared);
152
153        assert!(display.contains("SELECT * FROM users"));
154        assert!(display.contains("WHERE id"));
155    }
156
157    #[test]
158    fn test_owned_conversion_roundtrip() {
159        let sql: SQL<'_, SQLiteValue<'_>> = SQL::raw("SELECT name FROM users WHERE id = ")
160            .append(drizzle_core::Placeholder::named("id").to_sql());
161
162        let prepared = prepare_render(&sql);
163        let core_prepared = PreparedStatement { inner: prepared };
164
165        // Convert to owned
166        let owned = core_prepared.into_owned();
167
168        // Convert back to borrowed
169        let borrowed: PreparedStatement<'_> = owned.into();
170
171        // Verify structure is preserved
172        assert_eq!(borrowed.inner.text_segments.len(), 2);
173        assert_eq!(borrowed.inner.params.len(), 1);
174    }
175}