Skip to main content

drizzle_sqlite/builder/
prepared.rs

1use crate::prelude::*;
2
3use drizzle_core::{
4    OwnedParam, Param,
5    prepared::{
6        OwnedPreparedStatement as CoreOwnedPreparedStatement,
7        PreparedStatement as CorePreparedStatement,
8    },
9};
10
11use crate::values::{OwnedSQLiteValue, SQLiteValue};
12
13/// SQLite-specific prepared statement wrapper.
14///
15/// A prepared statement represents a compiled SQL query with placeholder parameters
16/// that can be executed multiple times with different parameter values. This wrapper
17/// provides SQLite-specific functionality while maintaining compatibility with the
18/// core Drizzle prepared statement infrastructure.
19///
20/// ## Features
21///
22/// - **Parameter Binding**: Safely bind values to SQL placeholders
23/// - **Reusable Execution**: Execute the same query multiple times efficiently  
24/// - **Memory Management**: Automatic handling of borrowed/owned lifetimes
25/// - **Type Safety**: Compile-time verification of parameter types
26///
27/// ## Basic Usage
28///
29/// ```rust
30/// # mod drizzle {
31/// #     pub mod core { pub use drizzle_core::*; }
32/// #     pub mod error { pub use drizzle_core::error::*; }
33/// #     pub mod types { pub use drizzle_types::*; }
34/// #     pub mod migrations { pub use drizzle_migrations::*; }
35/// #     pub use drizzle_types::Dialect;
36/// #     pub use drizzle_types as ddl;
37/// #     pub mod sqlite {
38/// #         pub use drizzle_sqlite::*;
39/// #         #[cfg(feature = "rusqlite")]
40/// #         pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
41/// #         #[cfg(feature = "libsql")]
42/// #         pub mod libsql { pub use ::libsql::{Row, Value}; }
43/// #         #[cfg(feature = "turso")]
44/// #         pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
45/// #         pub mod prelude {
46/// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
47/// #             pub use drizzle_sqlite::{*, attrs::*};
48/// #             pub use drizzle_core::*;
49/// #         }
50/// #     }
51/// # }
52/// # use drizzle::sqlite::prelude::*;
53/// # use drizzle::sqlite::builder::QueryBuilder;
54/// # use drizzle::core::expr::eq;
55/// #
56/// # #[SQLiteTable(name = "users")]
57/// # struct User {
58/// #     #[column(primary)]
59/// #     id: i32,
60/// #     name: String,
61/// # }
62/// #
63/// # #[derive(SQLiteSchema)]
64/// # struct Schema {
65/// #     user: User,
66/// # }
67/// #
68/// # let builder = QueryBuilder::new::<Schema>();
69/// # let Schema { user } = Schema::new();
70/// // Build query that will become a prepared statement
71/// let query = builder
72///     .select(user.name)
73///     .from(user)
74///     .r#where(eq(user.id, Placeholder::anonymous()));
75///
76/// // Convert to SQL
77/// let sql = query.to_sql();
78/// println!("SQL: {}", sql.sql());
79/// ```
80///
81/// ## Lifetime Management
82///
83/// The prepared statement can be converted between borrowed and owned forms:
84///
85/// - `PreparedStatement<'a>` - Borrows data with lifetime 'a
86/// - `OwnedPreparedStatement` - Owns all data, no lifetime constraints
87///
88/// This allows for flexible usage patterns depending on whether you need to
89/// store the prepared statement long-term or use it immediately.
90#[derive(Debug, Clone)]
91pub struct PreparedStatement<'a> {
92    pub(crate) inner: CorePreparedStatement<'a, SQLiteValue<'a>>,
93}
94
95impl PreparedStatement<'_> {
96    /// Converts this borrowed prepared statement into an owned one.
97    ///
98    /// This method clones all the internal data to create an `OwnedPreparedStatement`
99    /// that doesn't have any lifetime constraints. This is useful when you need to
100    /// store the prepared statement beyond the lifetime of the original query builder.
101    ///
102    /// # Examples
103    ///
104    /// ```rust
105    /// # mod drizzle {
106    /// #     pub mod core { pub use drizzle_core::*; }
107    /// #     pub mod error { pub use drizzle_core::error::*; }
108    /// #     pub mod types { pub use drizzle_types::*; }
109    /// #     pub mod migrations { pub use drizzle_migrations::*; }
110    /// #     pub use drizzle_types::Dialect;
111    /// #     pub use drizzle_types as ddl;
112    /// #     pub mod sqlite {
113    /// #         pub use drizzle_sqlite::*;
114    /// #         #[cfg(feature = "rusqlite")]
115    /// #         pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
116    /// #         #[cfg(feature = "libsql")]
117    /// #         pub mod libsql { pub use ::libsql::{Row, Value}; }
118    /// #         #[cfg(feature = "turso")]
119    /// #         pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
120    /// #         pub mod prelude {
121    /// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
122    /// #             pub use drizzle_sqlite::{*, attrs::*};
123    /// #             pub use drizzle_core::*;
124    /// #         }
125    /// #     }
126    /// # }
127    /// # fn example(prepared: drizzle::sqlite::builder::prepared::PreparedStatement<'_>) {
128    /// // Convert borrowed to owned for long-term storage
129    /// let owned = prepared.into_owned();
130    ///
131    /// // Now `owned` can be stored without lifetime constraints
132    /// # }
133    /// ```
134    #[must_use]
135    pub fn into_owned(&self) -> OwnedPreparedStatement {
136        let owned_params = self.inner.params.iter().map(|p| OwnedParam {
137            placeholder: p.placeholder,
138            value: p
139                .value
140                .clone()
141                .map(|v| OwnedSQLiteValue::from(v.into_owned())),
142        });
143
144        let inner = CoreOwnedPreparedStatement {
145            text_segments: self.inner.text_segments.clone(),
146            params: owned_params.collect::<Box<[_]>>(),
147            sql: self.inner.sql.clone(),
148        };
149
150        OwnedPreparedStatement { inner }
151    }
152}
153
154/// Owned `SQLite` prepared statement wrapper.
155///
156/// This is the owned counterpart to [`PreparedStatement`] that doesn't have any lifetime
157/// constraints. All data is owned by this struct, making it suitable for long-term storage,
158/// caching, or passing across thread boundaries.
159///
160/// ## Use Cases
161///
162/// - **Caching**: Store prepared statements in a cache for reuse
163/// - **Multi-threading**: Pass prepared statements between threads
164/// - **Long-term storage**: Keep prepared statements in application state
165/// - **Serialization**: Convert to/from persistent storage (when serialization is implemented)
166///
167/// ## Examples
168///
169/// ```rust
170/// # mod drizzle {
171/// #     pub mod core { pub use drizzle_core::*; }
172/// #     pub mod error { pub use drizzle_core::error::*; }
173/// #     pub mod types { pub use drizzle_types::*; }
174/// #     pub mod migrations { pub use drizzle_migrations::*; }
175/// #     pub use drizzle_types::Dialect;
176/// #     pub use drizzle_types as ddl;
177/// #     pub mod sqlite {
178/// #         pub use drizzle_sqlite::*;
179/// #         #[cfg(feature = "rusqlite")]
180/// #         pub mod rusqlite { pub use ::rusqlite::{Error, Result, Row, types}; }
181/// #         #[cfg(feature = "libsql")]
182/// #         pub mod libsql { pub use ::libsql::{Row, Value}; }
183/// #         #[cfg(feature = "turso")]
184/// #         pub mod turso { pub use ::turso::{Error, IntoValue, Result, Row, Value}; }
185/// #         pub mod prelude {
186/// #             pub use drizzle_macros::{SQLiteTable, SQLiteSchema};
187/// #             pub use drizzle_sqlite::{*, attrs::*};
188/// #             pub use drizzle_core::*;
189/// #         }
190/// #     }
191/// # }
192/// use drizzle::sqlite::prelude::*;
193/// use drizzle::sqlite::builder::QueryBuilder;
194///
195/// #[SQLiteTable(name = "users")]
196/// struct User {
197///     #[column(primary)]
198///     id: i32,
199///     name: String,
200/// }
201///
202/// #[derive(SQLiteSchema)]
203/// struct Schema {
204///     user: User,
205/// }
206///
207/// let builder = QueryBuilder::new::<Schema>();
208/// let Schema { user } = Schema::new();
209///
210/// // Create a query and convert to SQL
211/// let query = builder.select(user.name).from(user);
212/// let sql = query.to_sql();
213///
214/// // In practice, the driver creates a PreparedStatement from the SQL
215/// // let prepared: PreparedStatement = driver.prepare(sql)?;
216/// // let owned: OwnedPreparedStatement = prepared.into_owned();
217/// ```
218///
219/// ## Conversion
220///
221/// You can convert between borrowed and owned forms:
222/// - `PreparedStatement::into_owned()` → `OwnedPreparedStatement`
223/// - `OwnedPreparedStatement` → `PreparedStatement` (via `From` trait)
224#[derive(Debug, Clone)]
225pub struct OwnedPreparedStatement {
226    pub(crate) inner: CoreOwnedPreparedStatement<crate::values::OwnedSQLiteValue>,
227}
228impl<'a> From<PreparedStatement<'a>> for OwnedPreparedStatement {
229    fn from(value: PreparedStatement<'a>) -> Self {
230        let owned_params = value.inner.params.iter().map(|p| OwnedParam {
231            placeholder: p.placeholder,
232            value: p
233                .value
234                .clone()
235                .map(|v| OwnedSQLiteValue::from(v.into_owned())),
236        });
237        let inner = CoreOwnedPreparedStatement {
238            text_segments: value.inner.text_segments,
239            params: owned_params.collect::<Box<[_]>>(),
240            sql: value.inner.sql,
241        };
242        Self { inner }
243    }
244}
245
246impl From<OwnedPreparedStatement> for PreparedStatement<'_> {
247    fn from(value: OwnedPreparedStatement) -> Self {
248        let sqlitevalue = value.inner.params.iter().map(|v| {
249            Param::new(
250                v.placeholder,
251                v.value.clone().map(|v| Cow::Owned(SQLiteValue::from(v))),
252            )
253        });
254        let inner = CorePreparedStatement {
255            text_segments: value.inner.text_segments,
256            params: sqlitevalue.collect::<Box<[_]>>(),
257            sql: value.inner.sql,
258        };
259        PreparedStatement { inner }
260    }
261}
262
263impl OwnedPreparedStatement {}
264
265impl core::fmt::Display for PreparedStatement<'_> {
266    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
267        write!(f, "{}", self.inner)
268    }
269}
270
271impl core::fmt::Display for OwnedPreparedStatement {
272    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
273        write!(f, "{}", self.inner)
274    }
275}
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280    use crate::values::SQLiteValue;
281    use drizzle_core::{SQL, ToSQL, prepared::prepare_render};
282
283    #[test]
284    fn test_prepare_render_basic() {
285        // Test the basic prepare_render functionality for SQLite
286        let sql: SQL<'_, SQLiteValue<'_>> = SQL::raw("SELECT * FROM users WHERE id = ")
287            .append(drizzle_core::Placeholder::named("user_id").to_sql())
288            .append(SQL::raw(" AND name = "))
289            .append(drizzle_core::Placeholder::named("user_name").to_sql());
290
291        let prepared = prepare_render(&sql);
292
293        // Should have 3 text segments: before first param, between params, after last param
294        assert_eq!(prepared.text_segments.len(), 3);
295        assert_eq!(prepared.params.len(), 2);
296
297        // Verify text segments contain expected content
298        assert!(prepared.text_segments[0].contains("SELECT * FROM users WHERE id"));
299        assert!(prepared.text_segments[1].contains("AND name"));
300    }
301
302    #[test]
303    fn test_prepare_with_no_parameters() {
304        // Test preparing SQL with no parameters
305        let sql: SQL<'_, SQLiteValue<'_>> = SQL::raw("SELECT COUNT(*) FROM users");
306        let prepared = prepare_render(&sql);
307
308        assert_eq!(prepared.text_segments.len(), 1);
309        assert_eq!(prepared.params.len(), 0);
310        assert_eq!(prepared.text_segments[0], "SELECT COUNT(*) FROM users");
311    }
312
313    #[test]
314    fn test_prepared_statement_display() {
315        let sql: SQL<'_, SQLiteValue<'_>> = SQL::raw("SELECT * FROM users")
316            .append(SQL::raw(" WHERE id = "))
317            .append(drizzle_core::Placeholder::named("id").to_sql());
318
319        let prepared = prepare_render(&sql);
320        let display = format!("{}", prepared);
321
322        assert!(display.contains("SELECT * FROM users"));
323        assert!(display.contains("WHERE id"));
324    }
325
326    #[test]
327    fn test_owned_conversion_roundtrip() {
328        let sql: SQL<'_, SQLiteValue<'_>> = SQL::raw("SELECT name FROM users WHERE id = ")
329            .append(drizzle_core::Placeholder::named("id").to_sql());
330
331        let prepared = prepare_render(&sql);
332        let core_prepared = PreparedStatement { inner: prepared };
333
334        // Convert to owned
335        let owned = core_prepared.into_owned();
336
337        // Convert back to borrowed
338        let borrowed: PreparedStatement<'_> = owned.into();
339
340        // Verify structure is preserved
341        assert_eq!(borrowed.inner.text_segments.len(), 2);
342        assert_eq!(borrowed.inner.params.len(), 1);
343    }
344}