sqlx_askama_template/
lib.rs

1#![doc = include_str!("../README.md")]
2use futures_core::stream::BoxStream;
3use sqlx::{
4    Arguments, Database, Either, Error, Execute, Executor, FromRow, IntoArguments,
5    database::HasStatementCache,
6    query,
7    query::{Map, Query, QueryAs},
8    query_as, query_as_with, query_with,
9};
10pub use sqlx_askama_template_macro::*;
11use std::cell::RefCell;
12/// Internal executor for SQL templates
13pub struct SqlTemplateExecute<'q, DB: Database> {
14    /// Reference to SQL query string
15    pub(crate) sql: &'q String,
16    /// SQL parameters
17    pub(crate) arguments: Option<DB::Arguments<'q>>,
18    /// Persistent flag
19    pub(crate) persistent: bool,
20}
21impl<'q, DB: Database> SqlTemplateExecute<'q, DB> {
22    /// Creates a new SQL template executor
23    pub fn new(sql: &'q String, arguments: Option<DB::Arguments<'q>>) -> Self {
24        SqlTemplateExecute {
25            sql,
26            arguments,
27            persistent: true,
28        }
29    }
30    /// If `true`, the statement will get prepared once and cached to the
31    /// connection's statement cache.
32    ///
33    /// If queried once with the flag set to `true`, all subsequent queries
34    /// matching the one with the flag will use the cached statement until the
35    /// cache is cleared.
36    ///
37    /// If `false`, the prepared statement will be closed after execution.
38    ///
39    /// Default: `true`.
40    pub fn set_persistent(mut self, persistent: bool) -> Self {
41        self.persistent = persistent;
42        self
43    }
44}
45impl<'q, DB: Database + HasStatementCache> SqlTemplateExecute<'q, DB>
46where
47    DB::Arguments<'q>: IntoArguments<'q, DB>,
48{
49    /// to sqlx::QueryAs
50    /// Converts the SQL template to a `QueryAs` object, which can be executed to fetch rows
51    #[inline]
52    pub fn to_query_as<O>(self) -> QueryAs<'q, DB, O, DB::Arguments<'q>>
53    where
54        O: Send + Unpin + for<'r> FromRow<'r, DB::Row>,
55    {
56        let q = match self.arguments {
57            Some(args) => query_as_with(self.sql, args),
58            None => query_as(self.sql),
59        };
60        q.persistent(self.persistent)
61    }
62    /// to sqlx::Query
63    /// Converts the SQL template to a `Query` object, which can be executed to fetch rows
64    #[inline]
65    pub fn to_query(self) -> Query<'q, DB, DB::Arguments<'q>> {
66        let q = match self.arguments {
67            Some(args) => {
68                //   let wrap = ArgWrapper(args);
69                query_with(self.sql, args)
70            }
71            None => query(self.sql),
72        };
73        q.persistent(self.persistent)
74    }
75    /// call sqlx::Query::map
76    /// Map each row in the result to another type.
77    #[inline]
78    pub fn map<F, O>(
79        self,
80        f: F,
81    ) -> Map<'q, DB, impl FnMut(DB::Row) -> Result<O, Error> + Send, DB::Arguments<'q>>
82    where
83        F: FnMut(DB::Row) -> O + Send,
84        O: Unpin,
85        DB::Arguments<'q>: IntoArguments<'q, DB>,
86    {
87        self.to_query().map(f)
88    }
89
90    /// call sqlx::Query::try_map
91    /// Map each row in the result to another type, returning an error if the mapping fails.
92    #[inline]
93    pub fn try_map<F, O>(self, f: F) -> Map<'q, DB, F, DB::Arguments<'q>>
94    where
95        F: FnMut(DB::Row) -> Result<O, Error> + Send,
96        O: Unpin,
97        DB::Arguments<'q>: IntoArguments<'q, DB>,
98    {
99        self.to_query().try_map(f)
100    }
101
102    /// call sqlx::Query::execute
103    /// Execute the query and return the number of rows affected.
104    #[inline]
105    pub async fn execute<'e, 'c: 'e, E>(self, executor: E) -> Result<DB::QueryResult, Error>
106    where
107        'q: 'e,
108        E: Executor<'c, Database = DB>,
109    {
110        self.to_query().execute(executor).await
111    }
112    /// call    sqlx::Query::execute_many
113    /// Execute multiple queries and return the rows affected from each query, in a stream.
114    #[inline]
115    pub async fn execute_many<'e, 'c: 'e, E>(
116        self,
117        executor: E,
118    ) -> BoxStream<'e, Result<DB::QueryResult, Error>>
119    where
120        'q: 'e,
121        E: Executor<'c, Database = DB>,
122    {
123        #[allow(deprecated)]
124        self.to_query().execute_many(executor).await
125    }
126    /// call sqlx::Query::fetch
127    /// Execute the query and return the generated results as a stream.
128    #[inline]
129    pub fn fetch<'e, 'c: 'e, E>(self, executor: E) -> BoxStream<'e, Result<DB::Row, Error>>
130    where
131        'q: 'e,
132        E: Executor<'c, Database = DB>,
133    {
134        self.to_query().fetch(executor)
135    }
136    /// call sqlx::Query::fetch_many
137    /// Execute multiple queries and return the generated results as a stream.
138    ///
139    /// For each query in the stream, any generated rows are returned first,
140    /// then the `QueryResult` with the number of rows affected.
141    #[inline]
142    #[allow(clippy::type_complexity)]
143    pub fn fetch_many<'e, 'c: 'e, E>(
144        self,
145        executor: E,
146    ) -> BoxStream<'e, Result<Either<DB::QueryResult, DB::Row>, Error>>
147    where
148        'q: 'e,
149        E: Executor<'c, Database = DB>,
150    {
151        #[allow(deprecated)]
152        self.to_query().fetch_many(executor)
153    }
154    /// call sqlx::Query::fetch_all
155    /// Execute the query and return all the resulting rows collected into a [`Vec`].
156    ///
157    /// ### Note: beware result set size.
158    /// This will attempt to collect the full result set of the query into memory.
159    ///
160    /// To avoid exhausting available memory, ensure the result set has a known upper bound,
161    /// e.g. using `LIMIT`.
162    #[inline]
163    pub async fn fetch_all<'e, 'c: 'e, E>(self, executor: E) -> Result<Vec<DB::Row>, Error>
164    where
165        'q: 'e,
166        E: Executor<'c, Database = DB>,
167    {
168        self.to_query().fetch_all(executor).await
169    }
170    /// call sqlx::Query::fetch_one
171    /// Execute the query, returning the first row or [`Error::RowNotFound`] otherwise.
172    ///
173    /// ### Note: for best performance, ensure the query returns at most one row.
174    /// Depending on the driver implementation, if your query can return more than one row,
175    /// it may lead to wasted CPU time and bandwidth on the database server.
176    ///
177    /// Even when the driver implementation takes this into account, ensuring the query returns at most one row
178    /// can result in a more optimal query plan.
179    ///
180    /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good.
181    ///
182    /// Otherwise, you might want to add `LIMIT 1` to your query.
183    #[inline]
184    pub async fn fetch_one<'e, 'c: 'e, E>(self, executor: E) -> Result<DB::Row, Error>
185    where
186        'q: 'e,
187        E: Executor<'c, Database = DB>,
188    {
189        self.to_query().fetch_one(executor).await
190    }
191    /// call sqlx::Query::fetch_optional
192    /// Execute the query, returning the first row or `None` otherwise.
193    ///
194    /// ### Note: for best performance, ensure the query returns at most one row.
195    /// Depending on the driver implementation, if your query can return more than one row,
196    /// it may lead to wasted CPU time and bandwidth on the database server.
197    ///
198    /// Even when the driver implementation takes this into account, ensuring the query returns at most one row
199    /// can result in a more optimal query plan.
200    ///
201    /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good.
202    ///
203    /// Otherwise, you might want to add `LIMIT 1` to your query.
204    #[inline]
205    pub async fn fetch_optional<'e, 'c: 'e, E>(self, executor: E) -> Result<Option<DB::Row>, Error>
206    where
207        'q: 'e,
208        E: Executor<'c, Database = DB>,
209    {
210        self.to_query().fetch_optional(executor).await
211    }
212
213    // QueryAs functions wrapp
214
215    /// call sqlx::QueryAs::fetch
216    /// Execute the query and return the generated results as a stream.
217    pub fn fetch_as<'e, 'c: 'e, O, E>(self, executor: E) -> BoxStream<'e, Result<O, Error>>
218    where
219        'q: 'e,
220        E: 'e + Executor<'c, Database = DB>,
221        DB: 'e,
222        O: Send + Unpin + for<'r> FromRow<'r, DB::Row> + 'e,
223    {
224        self.to_query_as().fetch(executor)
225    }
226    /// call sqlx::QueryAs::fetch_many
227    /// Execute multiple queries and return the generated results as a stream
228    /// from each query, in a stream.
229    pub fn fetch_many_as<'e, 'c: 'e, O, E>(
230        self,
231        executor: E,
232    ) -> BoxStream<'e, Result<Either<DB::QueryResult, O>, Error>>
233    where
234        'q: 'e,
235        E: 'e + Executor<'c, Database = DB>,
236        DB: 'e,
237        O: Send + Unpin + for<'r> FromRow<'r, DB::Row> + 'e,
238    {
239        #[allow(deprecated)]
240        self.to_query_as().fetch_many(executor)
241    }
242    /// call sqlx::QueryAs::fetch_all
243    /// Execute the query and return all the resulting rows collected into a [`Vec`].
244    ///
245    /// ### Note: beware result set size.
246    /// This will attempt to collect the full result set of the query into memory.
247    ///
248    /// To avoid exhausting available memory, ensure the result set has a known upper bound,
249    /// e.g. using `LIMIT`.
250    #[inline]
251    pub async fn fetch_all_as<'e, 'c: 'e, O, E>(self, executor: E) -> Result<Vec<O>, Error>
252    where
253        'q: 'e,
254        E: 'e + Executor<'c, Database = DB>,
255        DB: 'e,
256        O: Send + Unpin + for<'r> FromRow<'r, DB::Row> + 'e,
257    {
258        self.to_query_as().fetch_all(executor).await
259    }
260    /// call sqlx::QueryAs::fetch_one
261    /// Execute the query, returning the first row or [`Error::RowNotFound`] otherwise.
262    ///
263    /// ### Note: for best performance, ensure the query returns at most one row.
264    /// Depending on the driver implementation, if your query can return more than one row,
265    /// it may lead to wasted CPU time and bandwidth on the database server.
266    ///
267    /// Even when the driver implementation takes this into account, ensuring the query returns at most one row
268    /// can result in a more optimal query plan.
269    ///
270    /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good.
271    ///
272    /// Otherwise, you might want to add `LIMIT 1` to your query.
273    pub async fn fetch_one_as<'e, 'c: 'e, O, E>(self, executor: E) -> Result<O, Error>
274    where
275        'q: 'e,
276        E: 'e + Executor<'c, Database = DB>,
277        DB: 'e,
278        O: Send + Unpin + for<'r> FromRow<'r, DB::Row> + 'e,
279    {
280        self.to_query_as().fetch_one(executor).await
281    }
282    /// call sqlx::QueryAs::fetch_optional
283    /// Execute the query, returning the first row or `None` otherwise.
284    ///
285    /// ### Note: for best performance, ensure the query returns at most one row.
286    /// Depending on the driver implementation, if your query can return more than one row,
287    /// it may lead to wasted CPU time and bandwidth on the database server.
288    ///
289    /// Even when the driver implementation takes this into account, ensuring the query returns at most one row
290    /// can result in a more optimal query plan.
291    ///
292    /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good.
293    ///
294    /// Otherwise, you might want to add `LIMIT 1` to your query.
295    pub async fn fetch_optional_as<'e, 'c: 'e, O, E>(self, executor: E) -> Result<Option<O>, Error>
296    where
297        'q: 'e,
298        E: 'e + Executor<'c, Database = DB>,
299        DB: 'e,
300        O: Send + Unpin + for<'r> FromRow<'r, DB::Row> + 'e,
301    {
302        self.to_query_as().fetch_optional(executor).await
303    }
304}
305
306impl<'q, DB: Database> Execute<'q, DB> for SqlTemplateExecute<'q, DB> {
307    /// Returns the SQL query string
308    #[inline]
309    fn sql(&self) -> &'q str {
310        self.sql
311    }
312
313    /// Gets prepared statement (not supported in this implementation)
314    #[inline]
315    fn statement(&self) -> Option<&DB::Statement<'q>> {
316        None
317    }
318
319    /// Takes ownership of the bound arguments
320    #[inline]
321    fn take_arguments(&mut self) -> Result<Option<DB::Arguments<'q>>, sqlx::error::BoxDynError> {
322        Ok(self.arguments.take())
323    }
324
325    /// Checks if query is persistent
326    #[inline]
327    fn persistent(&self) -> bool {
328        self.persistent
329    }
330}
331
332/// SQL template argument processor
333///
334/// Handles parameter encoding and binding for SQL templates
335pub struct TemplateArg<'q, DB: Database> {
336    /// Stores any encoding errors
337    error: RefCell<Option<sqlx::error::BoxDynError>>,
338    /// Stores SQL parameters
339    arguments: RefCell<Option<DB::Arguments<'q>>>,
340}
341
342impl<DB: Database> Default for TemplateArg<'_, DB> {
343    /// Creates default TemplateArg
344    fn default() -> Self {
345        TemplateArg {
346            error: RefCell::new(None),
347            arguments: RefCell::new(None),
348        }
349    }
350}
351
352impl<'q, DB: Database> TemplateArg<'q, DB> {
353    /// Encodes a single parameter and returns its placeholder
354    ///
355    /// # Arguments
356    /// * `t` - Value to encode
357    ///
358    /// # Returns
359    /// Parameter placeholder string (e.g. "$1" or "?")
360    pub fn encode<T>(&self, t: T) -> String
361    where
362        T: sqlx::Encode<'q, DB> + sqlx::Type<DB> + 'q,
363    {
364        let mut err = self.error.borrow_mut();
365        let mut arguments = self.arguments.borrow_mut().take().unwrap_or_default();
366
367        if let Err(e) = arguments.add(t) {
368            if err.is_none() {
369                *err = Some(e);
370            }
371        }
372
373        let mut placeholder = String::new();
374        if let Err(e) = arguments.format_placeholder(&mut placeholder) {
375            if err.is_none() {
376                *err = Some(Box::new(e));
377            }
378        }
379
380        *self.arguments.borrow_mut() = Some(arguments);
381        placeholder
382    }
383
384    /// Encodes a parameter list and returns placeholder sequence
385    ///
386    /// # Arguments
387    /// * `args` - Iterator of values to encode
388    ///
389    /// # Returns
390    /// Parameter placeholder sequence (e.g. "($1,$2,$3)")
391    pub fn encode_list<T>(&self, args: impl Iterator<Item = T>) -> String
392    where
393        T: sqlx::Encode<'q, DB> + sqlx::Type<DB> + 'q,
394    {
395        let mut err = self.error.borrow_mut();
396        let mut arguments = self.arguments.borrow_mut().take().unwrap_or_default();
397        let mut placeholder = String::new();
398        placeholder.push('(');
399
400        for arg in args {
401            if let Err(e) = arguments.add(arg) {
402                if err.is_none() {
403                    *err = Some(e);
404                }
405            }
406
407            if let Err(e) = arguments.format_placeholder(&mut placeholder) {
408                if err.is_none() {
409                    *err = Some(Box::new(e));
410                }
411            }
412            placeholder.push(',');
413        }
414
415        if placeholder.ends_with(",") {
416            placeholder.pop();
417        }
418        placeholder.push(')');
419
420        *self.arguments.borrow_mut() = Some(arguments);
421        placeholder
422    }
423
424    /// Takes any encoding error that occurred
425    pub fn get_err(&self) -> Option<sqlx::error::BoxDynError> {
426        self.error.borrow_mut().take()
427    }
428
429    /// Takes ownership of the encoded arguments
430    pub fn get_arguments(&self) -> Option<DB::Arguments<'q>> {
431        self.arguments.borrow_mut().take()
432    }
433}
434
435/// SQL template trait
436///
437/// Defines basic operations for rendering SQL from templates
438pub trait SqlTemplate<'q, DB>: Sized
439where
440    DB: Database,
441{
442    /// Renders SQL template and returns query string with parameters
443    fn render_sql(self) -> Result<(String, Option<DB::Arguments<'q>>), askama::Error>;
444
445    /// Renders SQL template and returns executable query result
446    fn render_execute_able(
447        self,
448        sql_buffer: &'q mut String,
449    ) -> Result<SqlTemplateExecute<'q, DB>, askama::Error> {
450        let (sql, arguments) = self.render_sql()?;
451        *sql_buffer = sql;
452        Ok(SqlTemplateExecute {
453            sql: sql_buffer,
454
455            arguments,
456
457            persistent: true,
458        })
459    }
460}