drizzle_postgres/builder/
prepared.rs

1use std::borrow::Cow;
2
3use drizzle_core::{
4    OwnedParam, Param,
5    prepared::{
6        OwnedPreparedStatement as CoreOwnedPreparedStatement,
7        PreparedStatement as CorePreparedStatement,
8    },
9};
10
11use crate::{PostgresValue, values::OwnedPostgresValue};
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,ignore
30/// use drizzle_postgres::builder::QueryBuilder;
31/// use drizzle_macros::{PostgresTable, PostgresSchema};
32/// use drizzle_core::{ToSQL, SQL, expressions::conditions::eq};
33///
34/// #[PostgresTable(name = "users")]
35/// struct User {
36///     #[column(serial, primary)]
37///     id: i32,
38///     name: String,
39/// }
40///
41/// #[derive(PostgresSchema)]
42/// struct Schema {
43///     user: User,
44/// }
45///
46/// let builder = QueryBuilder::new::<Schema>();
47/// let Schema { user } = Schema::new();
48///
49/// // Build query with a placeholder
50/// let query = builder
51///     .select(user.name)
52///     .from(user)
53///     .r#where(eq(user.id, SQL::placeholder("id")));
54///
55/// // Convert to prepared statement
56/// let prepared = query.prepare();
57/// println!("SQL: {}", prepared);  // SELECT "users"."name" FROM "users" WHERE "users"."id" = :id
58/// ```
59///
60/// ## Lifetime Management
61///
62/// The prepared statement can be converted between borrowed and owned forms:
63///
64/// - `PreparedStatement<'a>` - Borrows data with lifetime 'a
65/// - `OwnedPreparedStatement` - Owns all data, no lifetime constraints
66///
67/// This allows for flexible usage patterns depending on whether you need to
68/// store the prepared statement long-term or use it immediately.
69#[derive(Debug, Clone)]
70pub struct PreparedStatement<'a> {
71    pub inner: CorePreparedStatement<'a, crate::PostgresValue<'a>>,
72}
73
74impl<'a> PreparedStatement<'a> {
75    /// Converts this borrowed prepared statement into an owned one.
76    ///
77    /// This method clones all the internal data to create an `OwnedPreparedStatement`
78    /// that doesn't have any lifetime constraints. This is useful when you need to
79    /// store the prepared statement beyond the lifetime of the original query builder.
80    ///
81    /// # Examples
82    ///
83    /// ```rust,ignore
84    /// # use drizzle_postgres::builder::PreparedStatement;
85    /// // Convert borrowed to owned for long-term storage
86    /// let owned = prepared_statement.into_owned();
87    ///
88    /// // Now `owned` can be stored without lifetime constraints
89    /// ```
90    pub fn into_owned(&self) -> OwnedPreparedStatement {
91        let owned_params = self.inner.params.iter().map(|p| OwnedParam {
92            placeholder: p.placeholder,
93            value: p
94                .value
95                .clone()
96                .map(|v| OwnedPostgresValue::from(v.into_owned())),
97        });
98
99        let inner = CoreOwnedPreparedStatement {
100            text_segments: self.inner.text_segments.clone(),
101            params: owned_params.collect::<Box<[_]>>(),
102            sql: self.inner.sql.clone(),
103        };
104
105        OwnedPreparedStatement { inner }
106    }
107}
108
109/// Owned PostgreSQL prepared statement wrapper.
110///
111/// This is the owned counterpart to [`PreparedStatement`] that doesn't have any lifetime
112/// constraints. All data is owned by this struct, making it suitable for long-term storage,
113/// caching, or passing across thread boundaries.
114///
115/// ## Use Cases
116///
117/// - **Caching**: Store prepared statements in a cache for reuse
118/// - **Multi-threading**: Pass prepared statements between threads (with tokio-postgres)
119/// - **Long-term storage**: Keep prepared statements in application state
120/// - **Query reuse**: Execute the same query with different parameters efficiently
121///
122/// ## Examples
123///
124/// ```rust,ignore
125/// use drizzle_postgres::builder::{QueryBuilder, PreparedStatement, OwnedPreparedStatement};
126/// use drizzle_macros::{PostgresTable, PostgresSchema};
127/// use drizzle_core::{ToSQL, SQL};
128///
129/// #[PostgresTable(name = "users")]
130/// struct User {
131///     #[column(serial, primary)]
132///     id: i32,
133///     name: String,
134/// }
135///
136/// #[derive(PostgresSchema)]
137/// struct Schema {
138///     user: User,
139/// }
140///
141/// let builder = QueryBuilder::new::<Schema>();
142/// let Schema { user } = Schema::new();
143///
144/// // Create a prepared statement and convert to owned
145/// let prepared = builder
146///     .select(user.name)
147///     .from(user)
148///     .prepare();
149///
150/// let owned: OwnedPreparedStatement = prepared.into_owned();
151///
152/// // Owned can be stored in a HashMap for reuse
153/// // let mut cache: HashMap<String, OwnedPreparedStatement> = HashMap::new();
154/// // cache.insert("select_user_name".to_string(), owned);
155/// ```
156///
157/// ## Conversion
158///
159/// You can convert between borrowed and owned forms:
160/// - `PreparedStatement::into_owned()` → `OwnedPreparedStatement`
161/// - `OwnedPreparedStatement` → `PreparedStatement` (via `From` trait)
162#[derive(Debug, Clone)]
163pub struct OwnedPreparedStatement {
164    pub inner: CoreOwnedPreparedStatement<crate::values::OwnedPostgresValue>,
165}
166
167impl<'a> From<PreparedStatement<'a>> for OwnedPreparedStatement {
168    fn from(value: PreparedStatement<'a>) -> Self {
169        let owned_params = value.inner.params.iter().map(|p| OwnedParam {
170            placeholder: p.placeholder,
171            value: p
172                .value
173                .clone()
174                .map(|v| OwnedPostgresValue::from(v.into_owned())),
175        });
176        let inner = CoreOwnedPreparedStatement {
177            text_segments: value.inner.text_segments,
178            params: owned_params.collect::<Box<[_]>>(),
179            sql: value.inner.sql,
180        };
181        Self { inner }
182    }
183}
184
185impl From<OwnedPreparedStatement> for PreparedStatement<'_> {
186    fn from(value: OwnedPreparedStatement) -> Self {
187        let postgresvalue = value.inner.params.iter().map(|v| {
188            Param::new(
189                v.placeholder,
190                v.value.clone().map(|v| Cow::Owned(PostgresValue::from(v))),
191            )
192        });
193        let inner = CorePreparedStatement {
194            text_segments: value.inner.text_segments,
195            params: postgresvalue.collect::<Box<[_]>>(),
196            sql: value.inner.sql,
197        };
198        PreparedStatement { inner }
199    }
200}
201
202impl OwnedPreparedStatement {}
203
204impl<'a> std::fmt::Display for PreparedStatement<'a> {
205    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206        write!(f, "{}", self.inner)
207    }
208}
209
210impl std::fmt::Display for OwnedPreparedStatement {
211    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
212        write!(f, "{}", self.inner)
213    }
214}
215
216#[cfg(test)]
217mod tests {
218    use super::*;
219    use drizzle_core::{SQL, prepared::prepare_render};
220
221    #[test]
222    fn test_prepare_render_basic() {
223        // Test the basic prepare_render functionality for PostgreSQL
224        let sql: SQL<'_, PostgresValue<'_>> = SQL::raw("SELECT * FROM users WHERE id = ")
225            .append(SQL::placeholder("user_id"))
226            .append(SQL::raw(" AND name = "))
227            .append(SQL::placeholder("user_name"));
228
229        let prepared = prepare_render(sql);
230
231        // Should have 3 text segments: before first param, between params, after last param
232        assert_eq!(prepared.text_segments.len(), 3);
233        assert_eq!(prepared.params.len(), 2);
234
235        // Verify text segments contain expected content
236        assert!(prepared.text_segments[0].contains("SELECT * FROM users WHERE id"));
237        assert!(prepared.text_segments[1].contains("AND name"));
238    }
239
240    #[test]
241    fn test_prepare_with_no_parameters() {
242        // Test preparing SQL with no parameters
243        let sql: SQL<'_, PostgresValue<'_>> = SQL::raw("SELECT COUNT(*) FROM users");
244        let prepared = prepare_render(sql);
245
246        assert_eq!(prepared.text_segments.len(), 1);
247        assert_eq!(prepared.params.len(), 0);
248        assert_eq!(prepared.text_segments[0], "SELECT COUNT(*) FROM users");
249    }
250
251    #[test]
252    fn test_prepared_statement_display() {
253        let sql: SQL<'_, PostgresValue<'_>> = SQL::raw("SELECT * FROM users")
254            .append(SQL::raw(" WHERE id = "))
255            .append(SQL::placeholder("id"));
256
257        let prepared = prepare_render(sql);
258        let display = format!("{}", prepared);
259
260        assert!(display.contains("SELECT * FROM users"));
261        assert!(display.contains("WHERE id"));
262    }
263
264    #[test]
265    fn test_owned_conversion_roundtrip() {
266        let sql: SQL<'_, PostgresValue<'_>> =
267            SQL::raw("SELECT name FROM users WHERE id = ").append(SQL::placeholder("id"));
268
269        let prepared = prepare_render(sql);
270        let core_prepared = PreparedStatement { inner: prepared };
271
272        // Convert to owned
273        let owned = core_prepared.into_owned();
274
275        // Convert back to borrowed
276        let borrowed: PreparedStatement<'_> = owned.into();
277
278        // Verify structure is preserved
279        assert_eq!(borrowed.inner.text_segments.len(), 2);
280        assert_eq!(borrowed.inner.params.len(), 1);
281    }
282}