Skip to main content

drizzle_postgres/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::{OwnedPostgresValue, PostgresValue};
12
13/// PostgreSQL-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 PostgreSQL-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 using `$1`, `$2`, etc.
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 postgres {
38/// #         pub mod values { pub use drizzle_postgres::values::*; }
39/// #         pub mod traits { pub use drizzle_postgres::traits::*; }
40/// #         pub mod common { pub use drizzle_postgres::common::*; }
41/// #         pub mod attrs { pub use drizzle_postgres::attrs::*; }
42/// #         pub mod builder { pub use drizzle_postgres::builder::*; }
43/// #         pub mod helpers { pub use drizzle_postgres::helpers::*; }
44/// #         pub mod expr { pub use drizzle_postgres::expr::*; }
45/// #         pub mod types { pub use drizzle_postgres::types::*; }
46/// #         pub struct Row;
47/// #         impl Row {
48/// #             pub fn get<'a, I, T>(&'a self, _: I) -> T { unimplemented!() }
49/// #             pub fn try_get<'a, I, T>(&'a self, _: I) -> Result<T, Box<dyn std::error::Error + Sync + Send>> { unimplemented!() }
50/// #         }
51/// #         pub mod prelude {
52/// #             pub use drizzle_macros::{PostgresTable, PostgresSchema, PostgresIndex};
53/// #             pub use drizzle_postgres::attrs::*;
54/// #             pub use drizzle_postgres::common::PostgresSchemaType;
55/// #             pub use drizzle_postgres::traits::{PostgresColumn, PostgresTable};
56/// #             pub use drizzle_postgres::values::{PostgresInsertValue, PostgresUpdateValue, PostgresValue};
57/// #             pub use drizzle_core::*;
58/// #         }
59/// #     }
60/// # }
61/// # use drizzle::postgres::prelude::*;
62/// # use drizzle::postgres::builder::QueryBuilder;
63/// # use drizzle::core::expr::eq;
64/// #
65/// # #[PostgresTable(name = "users")]
66/// # struct User {
67/// #     #[column(serial, primary)]
68/// #     id: i32,
69/// #     name: String,
70/// # }
71/// #
72/// # #[derive(PostgresSchema)]
73/// # struct Schema {
74/// #     user: User,
75/// # }
76/// #
77/// # let builder = QueryBuilder::new::<Schema>();
78/// # let Schema { user } = Schema::new();
79/// // Build query with a placeholder
80/// let query = builder
81///     .select(user.name)
82///     .from(user)
83///     .r#where(eq(user.id, Placeholder::anonymous()));
84///
85/// // Convert to SQL
86/// let sql = query.to_sql();
87/// println!("SQL: {}", sql.sql());
88/// ```
89///
90/// ## Lifetime Management
91///
92/// The prepared statement can be converted between borrowed and owned forms:
93///
94/// - `PreparedStatement<'a>` - Borrows data with lifetime 'a
95/// - `OwnedPreparedStatement` - Owns all data, no lifetime constraints
96///
97/// This allows for flexible usage patterns depending on whether you need to
98/// store the prepared statement long-term or use it immediately.
99#[derive(Debug, Clone)]
100pub struct PreparedStatement<'a> {
101    pub(crate) inner: CorePreparedStatement<'a, PostgresValue<'a>>,
102}
103
104impl PreparedStatement<'_> {
105    /// Converts this borrowed prepared statement into an owned one.
106    ///
107    /// This method clones all the internal data to create an `OwnedPreparedStatement`
108    /// that doesn't have any lifetime constraints. This is useful when you need to
109    /// store the prepared statement beyond the lifetime of the original query builder.
110    ///
111    /// # Examples
112    ///
113    /// ```rust
114    /// # mod drizzle {
115    /// #     pub mod core { pub use drizzle_core::*; }
116    /// #     pub mod error { pub use drizzle_core::error::*; }
117    /// #     pub mod types { pub use drizzle_types::*; }
118    /// #     pub mod migrations { pub use drizzle_migrations::*; }
119    /// #     pub use drizzle_types::Dialect;
120    /// #     pub use drizzle_types as ddl;
121    /// #     pub mod postgres {
122    /// #         pub mod values { pub use drizzle_postgres::values::*; }
123    /// #         pub mod traits { pub use drizzle_postgres::traits::*; }
124    /// #         pub mod common { pub use drizzle_postgres::common::*; }
125    /// #         pub mod attrs { pub use drizzle_postgres::attrs::*; }
126    /// #         pub mod builder { pub use drizzle_postgres::builder::*; }
127    /// #         pub mod helpers { pub use drizzle_postgres::helpers::*; }
128    /// #         pub mod expr { pub use drizzle_postgres::expr::*; }
129    /// #         pub mod types { pub use drizzle_postgres::types::*; }
130    /// #         pub struct Row;
131    /// #         impl Row {
132    /// #             pub fn get<'a, I, T>(&'a self, _: I) -> T { unimplemented!() }
133    /// #             pub fn try_get<'a, I, T>(&'a self, _: I) -> Result<T, Box<dyn std::error::Error + Sync + Send>> { unimplemented!() }
134    /// #         }
135    /// #         pub mod prelude {
136    /// #             pub use drizzle_macros::{PostgresTable, PostgresSchema, PostgresIndex};
137    /// #             pub use drizzle_postgres::attrs::*;
138    /// #             pub use drizzle_postgres::common::PostgresSchemaType;
139    /// #             pub use drizzle_postgres::traits::{PostgresColumn, PostgresTable};
140    /// #             pub use drizzle_postgres::values::{PostgresInsertValue, PostgresUpdateValue, PostgresValue};
141    /// #             pub use drizzle_core::*;
142    /// #         }
143    /// #     }
144    /// # }
145    /// # fn example(prepared: drizzle::postgres::builder::prepared::PreparedStatement<'_>) {
146    /// // Convert borrowed to owned for long-term storage
147    /// let owned = prepared.into_owned();
148    ///
149    /// // Now `owned` can be stored without lifetime constraints
150    /// # }
151    /// ```
152    #[must_use]
153    pub fn into_owned(&self) -> OwnedPreparedStatement {
154        let owned_params = self.inner.params.iter().map(|p| OwnedParam {
155            placeholder: p.placeholder,
156            value: p
157                .value
158                .clone()
159                .map(|v| OwnedPostgresValue::from(v.into_owned())),
160        });
161
162        let inner = CoreOwnedPreparedStatement {
163            text_segments: self.inner.text_segments.clone(),
164            params: owned_params.collect::<Box<[_]>>(),
165            sql: self.inner.sql.clone(),
166        };
167
168        OwnedPreparedStatement { inner }
169    }
170}
171
172/// Owned `PostgreSQL` prepared statement wrapper.
173///
174/// This is the owned counterpart to [`PreparedStatement`] that doesn't have any lifetime
175/// constraints. All data is owned by this struct, making it suitable for long-term storage,
176/// caching, or passing across thread boundaries.
177///
178/// ## Use Cases
179///
180/// - **Caching**: Store prepared statements in a cache for reuse
181/// - **Multi-threading**: Pass prepared statements between threads (with tokio-postgres)
182/// - **Long-term storage**: Keep prepared statements in application state
183/// - **Query reuse**: Execute the same query with different parameters efficiently
184///
185/// ## Examples
186///
187/// ```rust
188/// # mod drizzle {
189/// #     pub mod core { pub use drizzle_core::*; }
190/// #     pub mod error { pub use drizzle_core::error::*; }
191/// #     pub mod types { pub use drizzle_types::*; }
192/// #     pub mod migrations { pub use drizzle_migrations::*; }
193/// #     pub use drizzle_types::Dialect;
194/// #     pub use drizzle_types as ddl;
195/// #     pub mod postgres {
196/// #         pub mod values { pub use drizzle_postgres::values::*; }
197/// #         pub mod traits { pub use drizzle_postgres::traits::*; }
198/// #         pub mod common { pub use drizzle_postgres::common::*; }
199/// #         pub mod attrs { pub use drizzle_postgres::attrs::*; }
200/// #         pub mod builder { pub use drizzle_postgres::builder::*; }
201/// #         pub mod helpers { pub use drizzle_postgres::helpers::*; }
202/// #         pub mod expr { pub use drizzle_postgres::expr::*; }
203/// #         pub mod types { pub use drizzle_postgres::types::*; }
204/// #         pub struct Row;
205/// #         impl Row {
206/// #             pub fn get<'a, I, T>(&'a self, _: I) -> T { unimplemented!() }
207/// #             pub fn try_get<'a, I, T>(&'a self, _: I) -> Result<T, Box<dyn std::error::Error + Sync + Send>> { unimplemented!() }
208/// #         }
209/// #         pub mod prelude {
210/// #             pub use drizzle_macros::{PostgresTable, PostgresSchema, PostgresIndex};
211/// #             pub use drizzle_postgres::attrs::*;
212/// #             pub use drizzle_postgres::common::PostgresSchemaType;
213/// #             pub use drizzle_postgres::traits::{PostgresColumn, PostgresTable};
214/// #             pub use drizzle_postgres::values::{PostgresInsertValue, PostgresUpdateValue, PostgresValue};
215/// #             pub use drizzle_core::*;
216/// #         }
217/// #     }
218/// # }
219/// # use drizzle::postgres::prelude::*;
220/// # use drizzle::postgres::builder::QueryBuilder;
221/// #
222/// # #[PostgresTable(name = "users")]
223/// # struct User {
224/// #     #[column(serial, primary)]
225/// #     id: i32,
226/// #     name: String,
227/// # }
228/// #
229/// # #[derive(PostgresSchema)]
230/// # struct Schema {
231/// #     user: User,
232/// # }
233/// #
234/// # let builder = QueryBuilder::new::<Schema>();
235/// # let Schema { user } = Schema::new();
236/// // Create a query and convert to SQL
237/// let query = builder.select(user.name).from(user);
238/// let sql = query.to_sql();
239///
240/// // In practice, the driver creates a PreparedStatement from the SQL
241/// // let prepared = driver.prepare(sql)?;
242/// // let owned: OwnedPreparedStatement = prepared.into_owned();
243/// // Owned can be stored in a HashMap for reuse
244/// ```
245///
246/// ## Conversion
247///
248/// You can convert between borrowed and owned forms:
249/// - `PreparedStatement::into_owned()` → `OwnedPreparedStatement`
250/// - `OwnedPreparedStatement` → `PreparedStatement` (via `From` trait)
251#[derive(Debug, Clone)]
252pub struct OwnedPreparedStatement {
253    pub(crate) inner: CoreOwnedPreparedStatement<crate::values::OwnedPostgresValue>,
254}
255
256impl<'a> From<PreparedStatement<'a>> for OwnedPreparedStatement {
257    fn from(value: PreparedStatement<'a>) -> Self {
258        let owned_params = value.inner.params.iter().map(|p| OwnedParam {
259            placeholder: p.placeholder,
260            value: p
261                .value
262                .clone()
263                .map(|v| OwnedPostgresValue::from(v.into_owned())),
264        });
265        let inner = CoreOwnedPreparedStatement {
266            text_segments: value.inner.text_segments,
267            params: owned_params.collect::<Box<[_]>>(),
268            sql: value.inner.sql,
269        };
270        Self { inner }
271    }
272}
273
274impl From<OwnedPreparedStatement> for PreparedStatement<'_> {
275    fn from(value: OwnedPreparedStatement) -> Self {
276        let postgresvalue = value.inner.params.iter().map(|v| {
277            Param::new(
278                v.placeholder,
279                v.value.clone().map(|v| Cow::Owned(PostgresValue::from(v))),
280            )
281        });
282        let inner = CorePreparedStatement {
283            text_segments: value.inner.text_segments,
284            params: postgresvalue.collect::<Box<[_]>>(),
285            sql: value.inner.sql,
286        };
287        PreparedStatement { inner }
288    }
289}
290
291impl OwnedPreparedStatement {}
292
293impl core::fmt::Display for PreparedStatement<'_> {
294    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
295        write!(f, "{}", self.inner)
296    }
297}
298
299impl core::fmt::Display for OwnedPreparedStatement {
300    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
301        write!(f, "{}", self.inner)
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308    use drizzle_core::{SQL, ToSQL, prepared::prepare_render};
309
310    #[test]
311    fn test_prepare_render_basic() {
312        // Test the basic prepare_render functionality for PostgreSQL
313        let sql: SQL<'_, PostgresValue<'_>> = SQL::raw("SELECT * FROM users WHERE id = ")
314            .append(drizzle_core::Placeholder::named("user_id").to_sql())
315            .append(SQL::raw(" AND name = "))
316            .append(drizzle_core::Placeholder::named("user_name").to_sql());
317
318        let prepared = prepare_render(&sql);
319
320        // Should have 3 text segments: before first param, between params, after last param
321        assert_eq!(prepared.text_segments.len(), 3);
322        assert_eq!(prepared.params.len(), 2);
323
324        // Verify text segments contain expected content
325        assert!(prepared.text_segments[0].contains("SELECT * FROM users WHERE id"));
326        assert!(prepared.text_segments[1].contains("AND name"));
327    }
328
329    #[test]
330    fn test_prepare_with_no_parameters() {
331        // Test preparing SQL with no parameters
332        let sql: SQL<'_, PostgresValue<'_>> = SQL::raw("SELECT COUNT(*) FROM users");
333        let prepared = prepare_render(&sql);
334
335        assert_eq!(prepared.text_segments.len(), 1);
336        assert_eq!(prepared.params.len(), 0);
337        assert_eq!(prepared.text_segments[0], "SELECT COUNT(*) FROM users");
338    }
339
340    #[test]
341    fn test_prepared_statement_display() {
342        let sql: SQL<'_, PostgresValue<'_>> = SQL::raw("SELECT * FROM users")
343            .append(SQL::raw(" WHERE id = "))
344            .append(drizzle_core::Placeholder::named("id").to_sql());
345
346        let prepared = prepare_render(&sql);
347        let display = format!("{}", prepared);
348
349        assert!(display.contains("SELECT * FROM users"));
350        assert!(display.contains("WHERE id"));
351    }
352
353    #[test]
354    fn test_owned_conversion_roundtrip() {
355        let sql: SQL<'_, PostgresValue<'_>> = SQL::raw("SELECT name FROM users WHERE id = ")
356            .append(drizzle_core::Placeholder::named("id").to_sql());
357
358        let prepared = prepare_render(&sql);
359        let core_prepared = PreparedStatement { inner: prepared };
360
361        // Convert to owned
362        let owned = core_prepared.into_owned();
363
364        // Convert back to borrowed
365        let borrowed: PreparedStatement<'_> = owned.into();
366
367        // Verify structure is preserved
368        assert_eq!(borrowed.inner.text_segments.len(), 2);
369        assert_eq!(borrowed.inner.params.len(), 1);
370    }
371}