1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Runtime support for compile-time checked queries.
//!
//! The `query!()` macro generates a `CheckedQuery<T>` that holds the SQL,
//! encoded params, and a mapper function. The user calls `.fetch_all()`,
//! `.fetch_one()`, `.fetch_opt()`, or `.execute()` to run it.
//!
//! All terminator methods accept `&impl Executor`, so they work with
//! `Client`, `Transaction`, and `PooledClient`.
use crate::encode::SqlParam;
use crate::error::TypedError;
use crate::executor::Executor;
use crate::row::Row;
/// A compile-time checked query ready for execution.
/// Generated by the `query!()` macro.
#[must_use = "CheckedQuery does nothing until a terminator like .fetch_all() is awaited"]
pub struct CheckedQuery<'a, T> {
#[doc(hidden)]
pub sql: &'a str,
#[doc(hidden)]
pub params: Vec<&'a dyn SqlParam>,
#[doc(hidden)]
pub _marker: std::marker::PhantomData<T>,
#[doc(hidden)]
pub mapper: fn(&Row) -> Result<T, TypedError>,
}
impl<'a, T> std::fmt::Debug for CheckedQuery<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CheckedQuery")
.field("sql", &self.sql)
.field("param_count", &self.params.len())
.finish()
}
}
impl<'a, T> CheckedQuery<'a, T> {
/// Execute and return all rows.
///
/// # Errors
///
/// Returns whatever the executor's `query` returns (wire error, broken
/// connection, param encoding failure). Additionally, if any row's
/// mapper fails to decode a column, that error is propagated.
pub async fn fetch_all(self, db: &impl Executor) -> Result<Vec<T>, TypedError> {
let rows = db.query(self.sql, &self.params).await?;
rows.iter().map(self.mapper).collect()
}
/// Execute and return exactly one row.
///
/// # Errors
///
/// Same as [`fetch_all`](Self::fetch_all), plus `TypedError::NotExactlyOne`
/// when the result set has zero or more than one row.
pub async fn fetch_one(self, db: &impl Executor) -> Result<T, TypedError> {
let row = db.query_one(self.sql, &self.params).await?;
(self.mapper)(&row)
}
/// Execute and return an optional row.
///
/// # Errors
///
/// Same as [`fetch_all`](Self::fetch_all), plus `TypedError::NotExactlyOne`
/// when the result set has more than one row. Zero rows returns `Ok(None)`.
pub async fn fetch_opt(self, db: &impl Executor) -> Result<Option<T>, TypedError> {
let row = db.query_opt(self.sql, &self.params).await?;
match row {
Some(r) => Ok(Some((self.mapper)(&r)?)),
None => Ok(None),
}
}
/// Execute a DML statement (INSERT/UPDATE/DELETE without RETURNING) and
/// return the affected row count. The mapper is ignored.
///
/// # Errors
///
/// Same as the executor's `execute`: wire error, broken connection, or
/// param encoding failure.
pub async fn execute(self, db: &impl Executor) -> Result<u64, TypedError> {
db.execute(self.sql, &self.params).await
}
}
/// An unchecked query (no compile-time validation).
/// Generated by `query_unchecked!()`.
#[must_use = "UncheckedQuery does nothing until a terminator like .fetch_all() is awaited"]
pub struct UncheckedQuery<'a> {
#[doc(hidden)]
pub sql: &'a str,
#[doc(hidden)]
pub params: Vec<&'a dyn SqlParam>,
}
impl<'a> std::fmt::Debug for UncheckedQuery<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UncheckedQuery")
.field("sql", &self.sql)
.field("param_count", &self.params.len())
.finish()
}
}
impl<'a> UncheckedQuery<'a> {
/// Execute and return raw rows.
///
/// # Errors
///
/// Returns whatever the executor's `query` returns: wire error, broken
/// connection, or param encoding failure.
pub async fn fetch_all(self, db: &impl Executor) -> Result<Vec<Row>, TypedError> {
db.query(self.sql, &self.params).await
}
/// Execute and return exactly one row.
///
/// # Errors
///
/// Same as [`fetch_all`](Self::fetch_all), plus `TypedError::NotExactlyOne`
/// for zero or multi-row result sets.
pub async fn fetch_one(self, db: &impl Executor) -> Result<Row, TypedError> {
db.query_one(self.sql, &self.params).await
}
/// Execute and return an optional row.
///
/// # Errors
///
/// Same as [`fetch_all`](Self::fetch_all), plus `TypedError::NotExactlyOne`
/// when the result set has more than one row. Zero rows returns `Ok(None)`.
pub async fn fetch_opt(self, db: &impl Executor) -> Result<Option<Row>, TypedError> {
db.query_opt(self.sql, &self.params).await
}
/// Execute a statement (INSERT/UPDATE/DELETE), return affected row count.
///
/// # Errors
///
/// Same as the executor's `execute`.
pub async fn execute(self, db: &impl Executor) -> Result<u64, TypedError> {
db.execute(self.sql, &self.params).await
}
}