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}