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