nanosql/
query.rs

1//! Strongly-typed queries.
2//!
3//! Consult the official [SQLite documentation](https://www.sqlite.org/lang.html) on the
4//! supported queries for an in-depth explanation of the precise SQL understood by SQLite.
5
6use core::fmt::{self, Display, Formatter};
7use crate::param::Param;
8use crate::row::ResultSet;
9
10
11/// Describes the input (parameter) and output (relation/row/tuple)
12/// types of a query, as well as its actual SQL source text.
13///
14/// Consult the official [SQLite documentation](https://www.sqlite.org/lang.html) on the
15/// supported queries for an in-depth explanation of the precise SQL understood by SQLite.
16pub trait Query {
17    /// The parameter type of the query. This must be either of the following:
18    ///
19    /// * a scalar (integer, floating-point number, string, blob, or null/unit);
20    /// * an ordered tuple (or tuple struct) of scalars;
21    /// * a struct with named fields of scalar type;
22    /// * a map with string-like keys and scalar values;
23    /// * or a newtype or anything that implements [`Param`] like any of the items above.
24    ///
25    /// The lifetime parameter allows the implementor to use a type containing
26    /// references, so as to avoid allocations when binding strings and blobs.
27    type Input<'p>: Param;
28
29    /// The result type returned by the query. This must be either of the following:
30    ///
31    /// * a scalar (integer, floating-point number, string, blob, or null/unit);
32    /// * an ordered tuple (or tuple struct) of scalars;
33    /// * a struct with named fields of scalar type;
34    /// * a map with string-like keys and scalar values;
35    /// * a sequence of any of the items above;
36    /// * or a newtype or any other type that deserializes as such (via [`ResultSet`]).
37    type Output: ResultSet;
38
39    /// Provides the SQL source text of the query.
40    fn format_sql(&self, formatter: &mut Formatter<'_>) -> fmt::Result;
41
42    /// Returns a formatter object that displays the SQL text for this query.
43    fn display_sql(&self) -> SqlDisplay<&Self> {
44        SqlDisplay::new(self)
45    }
46}
47
48impl<Q> Query for &Q
49where
50    Q: ?Sized + Query
51{
52    type Input<'p> = Q::Input<'p>;
53    type Output = Q::Output;
54
55    fn format_sql(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
56        Q::format_sql(&**self, formatter)
57    }
58}
59
60impl<Q> Query for &mut Q
61where
62    Q: ?Sized + Query
63{
64    type Input<'p> = Q::Input<'p>;
65    type Output = Q::Output;
66
67    fn format_sql(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
68        Q::format_sql(&**self, formatter)
69    }
70}
71
72impl<Q> Query for Box<Q>
73where
74    Q: ?Sized + Query
75{
76    type Input<'p> = Q::Input<'p>;
77    type Output = Q::Output;
78
79    fn format_sql(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
80        Q::format_sql(&**self, formatter)
81    }
82}
83
84/// Formats the SQL source of a query (an adapter between `Query` and `Display`)
85#[derive(Clone, Copy, Debug)]
86pub struct SqlDisplay<Q: ?Sized> {
87    /// The query of which the SQL text is to be displayed.
88    pub query: Q,
89}
90
91impl<Q> SqlDisplay<Q> {
92    /// Creates a new `SqlDisplay`, wrapping a specific query instance.
93    pub const fn new(query: Q) -> Self {
94        SqlDisplay { query }
95    }
96
97    /// Returns ownership of the wrapped query back to the caller.
98    pub fn into_inner(self) -> Q {
99        self.query
100    }
101}
102
103impl<Q: Query + ?Sized> Display for SqlDisplay<Q> {
104    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
105        self.query.format_sql(formatter)
106    }
107}
108
109/// Creates a new `struct` and implements [`Query`] for it using a function-like syntax.
110/// The invocation looks like the following:
111///
112/// ```ignore
113/// define_query!{
114///     QueryName<'lt>: InputType<'lt> => OutputType { "SQL (impl Display)" }
115/// }
116/// ```
117///
118/// The query name may be preceded by a visibility specifier (e.g. `pub`) to control the scope,
119/// just like normal Rust UDT declarations. Likewise, it may also be preceded by `#[attributes]`
120/// such as `#[derive(Clone, Copy, Default)]` or documentation comments (which expand to such
121/// an attribute). These will all be forwarded to the definition of the query type itsef.
122///
123/// The macro brings the lifetime `'lt` into scope when binding the input type, so
124/// you can use it for defining the input type as a reference or reference-like type.
125/// The SQL expression may borrow immutably from `self` and may use the `?` operator
126/// to return an error when building the SQL query string.
127///
128/// You can declare multiple queries in the same invocation by repeating the above pattern.
129///
130/// Example:
131///
132/// ```rust
133/// # use nanosql::{Result, Connection, ConnectionExt, Param, ResultRecord, Table};
134/// #[derive(Clone, Copy, Debug, Param)]
135/// #[nanosql(param_prefix = '@')]
136/// struct YoungEmployeesByNameParams<'n> {
137///     name: &'n str,
138///     max_age: usize,
139/// }
140///
141/// #[derive(Clone, Default, Debug, Param, ResultRecord, Table)]
142/// struct Employee {
143///     id: u64,
144///     name: String,
145///     age: usize,
146///     boss_id: u64,
147/// }
148///
149/// nanosql::define_query! {
150///     // A simple query that only uses built-in types.
151///     pub PetNameById<'p>: i64 => Option<String> {
152///         "SELECT name FROM pet WHERE id = ?"
153///     }
154///
155///     // A more involved query that uses the domain types defined above.
156///     pub(crate) YoungEmployeesByName<'p>: YoungEmployeesByNameParams<'p> => Vec<Employee> {
157///         r#"
158///         SELECT id, name, age, boss_id
159///         FROM employee
160///         WHERE name LIKE @name AND age <= @max_age
161///         "#
162///     }
163/// }
164///
165/// fn main() -> Result<()> {
166///     let mut conn = Connection::connect_in_memory()?;
167/// #
168/// #   conn.create_table::<Employee>()?;
169/// #   conn.insert_batch([
170/// #       Employee {
171/// #           id: 1,
172/// #           name: "Alice".into(),
173/// #           age: 18,
174/// #           boss_id: 0,
175/// #       },
176/// #       Employee {
177/// #           id: 1,
178/// #           name: "Joe".into(),
179/// #           age: 19,
180/// #           boss_id: 0,
181/// #       },
182/// #       Employee {
183/// #           id: 1,
184/// #           name: "Joe".into(),
185/// #           age: 20,
186/// #           boss_id: 0,
187/// #       },
188/// #       Employee {
189/// #           id: 1,
190/// #           name: "Joe".into(),
191/// #           age: 22,
192/// #           boss_id: 0,
193/// #       },
194/// #   ])?;
195///
196///     // Compile the query
197///     let mut stmt = conn.compile(YoungEmployeesByName)?;
198///
199///     // Get all employees named Joe under 21
200///     // (details of creating and populating the table have been omitted)
201///     let employees: Vec<Employee> = stmt.invoke(YoungEmployeesByNameParams {
202///         name: "Joe",
203///         max_age: 21,
204///     })?;
205///
206///     // suppose there are 2 of them
207///     assert_eq!(employees.len(), 2);
208///
209///     Ok(())
210/// }
211/// ```
212#[macro_export]
213macro_rules! define_query {
214    ($(
215        $(#[$($attrs:tt)*])*
216        $vis:vis $tyname:ident<$lt:lifetime>: $input_ty:ty => $output_ty:ty { $sql:expr }
217    )*) => {$(
218        $(#[$($attrs)*])*
219        $vis struct $tyname;
220
221        impl $crate::Query for $tyname {
222            type Input<$lt> = $input_ty;
223            type Output = $output_ty;
224
225            fn format_sql(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
226                ::core::fmt::Display::fmt(&$sql, formatter)
227            }
228        }
229    )*}
230}