include_sqlite_sql/
lib.rs

1#![cfg_attr(docsrs, doc = include_str!("../docs/index.md"))]
2
3pub use ::include_sql::{include_sql, index_of};
4
5/**
6Generates Rust code to use included SQL.
7
8This macro defines a trait with methods to access data and implements it for `rusqlite::Connection`.
9
10This macro recognizes and generates 3 variants of database access methods using the following selectors:
11* `?` - methods that process rows retrieved by `SELECT`,
12* `!` - methods that execute all other non-`SELECT` methods, and
13* `&` - methods that execute multiple SQL statements (as a batch), and
14* `->` - methods that execute `RETURNING` statements and provide access to returned data.
15
16For `SELECT` statements (`?`) like:
17
18```sql
19-- name: get_loaned_books?
20-- param: user_id: &str
21SELECT book_title FROM library WHERE loaned_to = :user_id
22/
23```
24
25Method with the following signature is generated:
26
27```rust , ignore
28fn get_loaned_books<F>(&self, user_id: &str, row_callback: F) -> -> rusqlite::Result<()>
29where F: FnMut(&rusqlite::Row) -> rusqlite::Result<()>;
30```
31
32For non-select statements (`!`) - INSERT, UPDATE, DELETE, etc. - like:
33
34```sql
35-- name: loan_books!
36-- param: book_ids: i32
37-- param: user_id: &str
38UPDATE library
39   SET loaned_to = :user_id
40     , loaned_on = current_timestamp
41 WHERE book_id IN (:book_ids)
42/
43```
44
45Method with the following signature is generated:
46
47```rust , ignore
48fn loan_books( &self, book_ids: &[i32], user_id: &str ) -> rusqlite::Result<usize>;
49```
50
51> **Note** that the `loan_books` signature also shows that parameter order follows `param` order in the SQL file.
52> However, if `param` are not specified, the method parameters will be ordered using netural order of the SQL statement parameters.
53
54For batches of statements (`&`) like:
55
56```sql
57-- name: create_tables &
58-- Note that SQL statemetns in a batch cannot have any parameters.
59BEGIN;
60CREATE TABLE foo(x INTEGER);
61CREATE TABLE bar(y TEXT);
62COMMIT;
63/
64```
65
66Method with the following signature is generated:
67
68```rust , ignore
69fn create_tables(&self) -> rusqlite::Result<()>;
70```
71
72For DELETE, INSERT, and UPDATE statements that return data via `RETURNING` clause (`->`) like:
73
74```sql
75-- name: add_book ->
76-- param: book_author: &str
77-- param: book_title: &str
78INSERT INTO library (book_author, book_title) VALUES (:book_author, :book_title)
79RETURNING book_id
80/
81```
82
83Method with the following signature is generated:
84
85```rust , ignore
86fn add_book<F,R>( &self, book_author: &str, book_title: &str, row_callback: F ) -> rusqlite::Result<R>
87where F: FnOnce(&rusqlite::Row) -> rusqlite::Result<R>;
88```
89*/
90#[macro_export]
91macro_rules! impl_sql {
92    ( $sql_name:ident = $( { $kind:tt $name:ident ($($variant:tt $param:ident $ptype:tt)*) $doc:literal $s:tt $( $text:tt )+ } ),+ ) => {
93        trait $sql_name {
94            $( $crate::decl_method!{ $kind $name $doc () () $($param $variant $ptype)* } )+
95        }
96        impl $sql_name for ::rusqlite::Connection {
97            $( $crate::impl_method!{ $kind $name () () ($($param $variant $ptype)*) => ($($variant $param)*) $($text)+ } )+
98        }
99    };
100}
101
102#[macro_export]
103#[doc(hidden)]
104macro_rules! decl_method {
105    ( ? $name:ident $doc:literal ($($gen_type:ident)*) ($($fn_params:tt)*) ) => {
106        #[doc=$doc]
107        fn $name<$($gen_type : ::rusqlite::ToSql ,)* F>(&self $($fn_params)* , row_cb: F) -> ::rusqlite::Result<()>
108        where F: FnMut(&::rusqlite::Row) -> ::rusqlite::Result<()>;
109    };
110    ( ! $name:ident $doc:literal ($($gen_type:ident)*) ($($fn_params:tt)*) ) => {
111        #[doc=$doc]
112        fn $name<$($gen_type : ::rusqlite::ToSql),*>(&self $($fn_params)*) -> ::rusqlite::Result<usize>;
113    };
114    ( & $name:ident $doc:literal ($($gen_type:ident)*) ($($fn_params:tt)*) ) => {
115        #[doc=$doc]
116        fn $name<$($gen_type : ::rusqlite::ToSql),*>(&self $($fn_params)*) -> ::rusqlite::Result<()>;
117    };
118    ( -> $name:ident $doc:literal ($($gen_type:ident)*) ($($fn_params:tt)*) ) => {
119        #[doc=$doc]
120        fn $name<$($gen_type : ::rusqlite::ToSql ,)* F, R>(&self $($fn_params)* , row_cb: F) -> ::rusqlite::Result<R>
121        where F: FnOnce(&::rusqlite::Row) -> ::rusqlite::Result<R>;
122    };
123    ( $kind:tt $name:ident $doc:literal ($($gen_type:ident)*) ($($fn_params:tt)*) $param:ident : _ $($tail:tt)* ) => {
124        $crate::decl_method!{
125            $kind
126            $name
127            $doc
128            ($($gen_type)*)
129            ($($fn_params)* , $param : impl ::rusqlite::ToSql)
130            $($tail)*
131        }
132    };
133    ( $kind:tt $name:ident $doc:literal ($($gen_type:ident)*) ($($fn_params:tt)*) $param:ident : ($ptype:ty) $($tail:tt)* ) => {
134        $crate::decl_method!{
135            $kind
136            $name
137            $doc
138            ($($gen_type)*)
139            ($($fn_params)* , $param : $ptype)
140            $($tail)*
141        }
142    };
143    ( $kind:tt $name:ident $doc:literal ($($gen_type:ident)*) ($($fn_params:tt)*) $param:ident # [$gtype:ident] $($tail:tt)* ) => {
144        $crate::decl_method!{
145            $kind
146            $name
147            $doc
148            ($($gen_type)* $gtype)
149            ($($fn_params)* , $param : & [ $gtype ] )
150            $($tail)*
151        }
152    };
153    ( $kind:tt $name:ident $doc:literal ($($gen_type:ident)*) ($($fn_params:tt)*) $param:ident # ($ptype:ty) $($tail:tt)* ) => {
154        $crate::decl_method!{
155            $kind
156            $name
157            $doc
158            ($($gen_type)*)
159            ($($fn_params)* , $param : & [ $ptype ] )
160            $($tail)*
161        }
162    };
163}
164
165#[macro_export]
166#[doc(hidden)]
167macro_rules! impl_method {
168    ( ? $name:ident () () () => () $text:literal ) => {
169        fn $name<F>(&self, mut row_cb: F) -> ::rusqlite::Result<()>
170        where F: FnMut(&::rusqlite::Row) -> ::rusqlite::Result<()>
171        {
172            let mut stmt = self.prepare( $text )?;
173            let mut rows = stmt.raw_query();
174            while let Some(row) = rows.next()? {
175                row_cb(row)?;
176            }
177            Ok(())
178        }
179    };
180    ( ? $name:ident () ($($fn_params:tt)+) () => ( $(: $param:ident)+ ) $($text:tt)+) => {
181        fn $name<F>(&self $($fn_params)+ , mut row_cb: F) -> ::rusqlite::Result<()>
182        where F: FnMut(&::rusqlite::Row) -> ::rusqlite::Result<()>
183        {
184            let mut stmt = self.prepare( $crate::sql_literal!( $($param)+ => $($text)+ ) )?;
185            $crate::bind_args!($($param)+ => stmt 1usize);
186            let mut rows = stmt.raw_query();
187            while let Some(row) = rows.next()? {
188                row_cb(row)?;
189            }
190            Ok(())
191        }
192    };
193    ( ? $name:ident ($($gen_type:ident)*) ($($fn_params:tt)+) () => ($($pv:tt $param:ident)+) $($text:tt)+) => {
194        fn $name<$($gen_type : ::rusqlite::ToSql ,)* F>(&self $($fn_params)+, mut row_cb: F) -> ::rusqlite::Result<()>
195        where F: FnMut(&::rusqlite::Row) -> ::rusqlite::Result<()>
196        {
197            let mut sql = ::std::string::String::with_capacity($crate::sql_len!($($text)+));
198            let mut args = ::std::vec::Vec::<&dyn ::rusqlite::ToSql>::with_capacity($crate::num_args!($($pv $param)+));
199            let mut i = 0;
200            $crate::dynamic_sql!(sql args i $($text)+);
201            let mut stmt = self.prepare(&sql)?;
202            let mut rows = stmt.query(args.as_slice())?;
203            while let Some(row) = rows.next()? {
204                row_cb(row)?;
205            }
206            Ok(())
207        }
208    };
209    ( ! $name:ident () () () => () $text:literal ) => {
210        fn $name(&self) -> ::rusqlite::Result<usize> {
211            let mut stmt = self.prepare( $text )?;
212            stmt.raw_execute()
213        }
214    };
215    ( ! $name:ident () ($($fn_params:tt)+) () => ( $(: $param:ident)+ ) $($text:tt)+) => {
216        fn $name(&self $($fn_params)+ ) -> ::rusqlite::Result<usize> {
217            let mut stmt = self.prepare( $crate::sql_literal!( $($param)+ => $($text)+ ) )?;
218            $crate::bind_args!($($param)+ => stmt 1usize);
219            stmt.raw_execute()
220        }
221    };
222    ( ! $name:ident ($($gen_type:ident)*) ($($fn_params:tt)+) () => ($($pv:tt $param:ident)+) $($text:tt)+) => {
223        fn $name<$($gen_type : ::rusqlite::ToSql),*>(&self $($fn_params)+ ) -> ::rusqlite::Result<usize> {
224            let mut sql = ::std::string::String::with_capacity($crate::sql_len!($($text)+));
225            let mut args = ::std::vec::Vec::<&dyn ::rusqlite::ToSql>::with_capacity($crate::num_args!($($pv $param)+));
226            let mut i = 0;
227            $crate::dynamic_sql!(sql args i $($text)+);
228            let mut stmt = self.prepare(&sql)?;
229            stmt.execute(args.as_slice())
230        }
231    };
232    ( & $name:ident () () () => () $text:literal ) => {
233        fn $name(&self) -> ::rusqlite::Result<()> {
234            self.execute_batch( $text )
235        }
236    };
237    ( -> $name:ident () () () => () $text:literal ) => {
238        fn $name<F,R>(&self, row_cb: F) -> ::rusqlite::Result<R>
239        where F: FnOnce(&::rusqlite::Row) -> ::rusqlite::Result<R>
240        {
241            let mut stmt = self.prepare( $text )?;
242            let mut rows = stmt.raw_query();
243            match rows.next()? {
244                Some(row) => row_cb(row),
245                _ => Err(::rusqlite::Error::QueryReturnedNoRows)
246            }
247        }
248    };
249    ( -> $name:ident () ($($fn_params:tt)+) () => ( $(: $param:ident)+ ) $($text:tt)+) => {
250        fn $name<F,R>(&self $($fn_params)+ , row_cb: F) -> ::rusqlite::Result<R>
251        where F: FnOnce(&::rusqlite::Row) -> ::rusqlite::Result<R>
252        {
253            let mut stmt = self.prepare( $crate::sql_literal!( $($param)+ => $($text)+ ) )?;
254            $crate::bind_args!($($param)+ => stmt 1usize);
255            let mut rows = stmt.raw_query();
256            match rows.next()? {
257                Some(row) => row_cb(row),
258                _ => Err(::rusqlite::Error::QueryReturnedNoRows)
259            }
260        }
261    };
262    ( -> $name:ident ($($gen_type:ident)*) ($($fn_params:tt)+) () => ($($pv:tt $param:ident)+) $($text:tt)+) => {
263        fn $name<$($gen_type : ::rusqlite::ToSql ,)* F,R>(&self $($fn_params)+, row_cb: F) -> ::rusqlite::Result<R>
264        where F: FnOnce(&::rusqlite::Row) -> ::rusqlite::Result<R>
265        {
266            let mut sql = ::std::string::String::with_capacity($crate::sql_len!($($text)+));
267            let mut args = ::std::vec::Vec::<&dyn ::rusqlite::ToSql>::with_capacity($crate::num_args!($($pv $param)+));
268            let mut i = 0;
269            $crate::dynamic_sql!(sql args i $($text)+);
270            let mut stmt = self.prepare(&sql)?;
271            let mut rows = stmt.query(args.as_slice())?;
272            match rows.next()? {
273                Some(row) => row_cb(row),
274                _ => Err(::rusqlite::Error::QueryReturnedNoRows)
275            }
276        }
277    };
278    ( $kind:tt $name:ident ($($gen_type:ident)*) ($($fn_params:tt)*) ($param:ident : _ $($tail:tt)*) => ($($pv:tt $param_name:ident)+) $($text:tt)+)  => {
279        $crate::impl_method!{
280            $kind
281            $name
282            ($($gen_type)*)
283            ($($fn_params)* , $param : impl ::rusqlite::ToSql)
284            ($($tail)*)
285            =>
286            ($($pv $param_name)+)
287            $($text)+
288        }
289    };
290    ( $kind:tt $name:ident ($($gen_type:ident)*) ($($fn_params:tt)*) ($param:ident : ($ptype:ty) $($tail:tt)*) => ($($pv:tt $param_name:ident)+) $($text:tt)+)  => {
291        $crate::impl_method!{
292            $kind
293            $name
294            ($($gen_type)*)
295            ($($fn_params)* , $param : $ptype)
296            ($($tail)*)
297            =>
298            ($($pv $param_name)+)
299            $($text)+
300        }
301    };
302    ( $kind:tt $name:ident ($($gen_type:ident)*) ($($fn_params:tt)*) ($param:ident # [$gtype:ident] $($tail:tt)*) => ($($pv:tt $param_name:ident)+) $($text:tt)+)  => {
303        $crate::impl_method!{
304            $kind
305            $name
306            ($($gen_type)* $gtype)
307            ($($fn_params)* , $param : & [ $gtype ])
308            ($($tail)*)
309            =>
310            ($($pv $param_name)+)
311            $($text)+
312        }
313    };
314    ( $kind:tt $name:ident ($($gen_type:ident)*) ($($fn_params:tt)*) ($param:ident # ($ptype:ty) $($tail:tt)*) => ($($pv:tt $param_name:ident)+) $($text:tt)+)  => {
315        $crate::impl_method!{
316            $kind
317            $name
318            ($($gen_type)*)
319            ($($fn_params)* , $param : & [ $ptype ])
320            ($($tail)*)
321            =>
322            ($($pv $param_name)+)
323            $($text)+
324        }
325    };
326}
327
328#[macro_export]
329#[doc(hidden)]
330macro_rules! sql_literal {
331    ($($name:ident)+ => $text:literal) => {
332        $text
333    };
334    ($($name:ident)+ => $text:literal : $param:ident) => {
335        ::std::concat!( $text, '?', $crate::index_of!($param in [ $( $name ),+ ] + 1) )
336    };
337    ($($name:ident)+ => $text:literal : $param:ident $($tail:tt)+) => {
338        ::std::concat!(
339            $text, '?', $crate::index_of!($param in [ $( $name ),+ ] + 1),
340            $crate::sql_literal!($($name)+ => $($tail)+)
341        )
342    };
343}
344
345#[macro_export]
346#[doc(hidden)]
347macro_rules! bind_args {
348    ($head:ident $($tail:ident)* => $stmt:ident $idx:expr) => {
349        $stmt.raw_bind_parameter($idx, $head)?;
350        $crate::bind_args!($($tail)* => $stmt $idx+1usize);
351    };
352    (=> $stmt:ident $idx:expr) => {};
353}
354
355#[macro_export]
356#[doc(hidden)]
357macro_rules! num_args {
358    () => { 0 };
359    (: $head:ident $($tail:tt)*) => { 1 + $crate::num_args!($($tail)*) };
360    (# $head:ident $($tail:tt)*) => { $head.len() + $crate::num_args!($($tail)*) };
361}
362
363#[macro_export]
364#[doc(hidden)]
365macro_rules! sql_len {
366    () => { 0 };
367    ($text:literal $($tail:tt)*) => { $text.len() + $crate::sql_len!($($tail)*) };
368    (: $head:ident $($tail:tt)*) => { 3 + $crate::sql_len!($($tail)*) };
369    (# $head:ident $($tail:tt)*) => { $head.len() * 5 + $crate::sql_len!($($tail)*) };
370}
371
372#[macro_export]
373#[doc(hidden)]
374macro_rules! dynamic_sql {
375    ($stmt:ident $args:ident $i:ident) => {};
376    ($stmt:ident $args:ident $i:ident $text:literal $($tail:tt)*) => {
377        $stmt.push_str($text);
378        $crate::dynamic_sql!($stmt $args $i $($tail)*);
379    };
380    ($stmt:ident $args:ident $i:ident : $param:ident $($tail:tt)*) => {
381        $i += 1;
382        $stmt.push_str(&::std::format!("?{}", $i));
383        $args.push(&$param);
384        $crate::dynamic_sql!($stmt $args $i $($tail)*);
385    };
386    ($stmt:ident $args:ident $i:ident # $param:ident $($tail:tt)*) => {
387        let mut iter = $param.into_iter();
388        if let Some(arg) = iter.next() {
389            $i += 1;
390            $stmt.push_str(&::std::format!("?{}", $i));
391            $args.push(arg);
392            while let Some(arg) = iter.next() {
393                $i += 1;
394                $stmt.push_str(&::std::format!(", ?{}", $i));
395                $args.push(arg);
396            }
397        } else {
398            $stmt.push_str("NULL");
399        }
400        $crate::dynamic_sql!($stmt $args $i $($tail)*);
401    };
402}