sqlx_core/query_as.rs
1use std::marker::PhantomData;
2
3use either::Either;
4use futures_core::stream::BoxStream;
5use futures_util::{StreamExt, TryStreamExt};
6
7use crate::arguments::IntoArguments;
8use crate::database::{Database, HasStatementCache};
9use crate::encode::Encode;
10use crate::error::{BoxDynError, Error};
11use crate::executor::{Execute, Executor};
12use crate::from_row::FromRow;
13use crate::query::{query, query_statement, query_statement_with, query_with_result, Query};
14use crate::types::Type;
15
16/// A single SQL query as a prepared statement, mapping results using [`FromRow`].
17/// Returned by [`query_as()`].
18#[must_use = "query must be executed to affect database"]
19pub struct QueryAs<'q, DB: Database, O, A> {
20 pub(crate) inner: Query<'q, DB, A>,
21 pub(crate) output: PhantomData<O>,
22}
23
24impl<'q, DB, O: Send, A: Send> Execute<'q, DB> for QueryAs<'q, DB, O, A>
25where
26 DB: Database,
27 A: 'q + IntoArguments<'q, DB>,
28{
29 #[inline]
30 fn sql(&self) -> &'q str {
31 self.inner.sql()
32 }
33
34 #[inline]
35 fn statement(&self) -> Option<&DB::Statement<'q>> {
36 self.inner.statement()
37 }
38
39 #[inline]
40 fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments<'q>>, BoxDynError> {
41 self.inner.take_arguments()
42 }
43
44 #[inline]
45 fn persistent(&self) -> bool {
46 self.inner.persistent()
47 }
48}
49
50impl<'q, DB: Database, O> QueryAs<'q, DB, O, <DB as Database>::Arguments<'q>> {
51 /// Bind a value for use with this SQL query.
52 ///
53 /// See [`Query::bind`](Query::bind).
54 pub fn bind<T: 'q + Encode<'q, DB> + Type<DB>>(mut self, value: T) -> Self {
55 self.inner = self.inner.bind(value);
56 self
57 }
58}
59
60impl<'q, DB, O, A> QueryAs<'q, DB, O, A>
61where
62 DB: Database + HasStatementCache,
63{
64 /// If `true`, the statement will get prepared once and cached to the
65 /// connection's statement cache.
66 ///
67 /// If queried once with the flag set to `true`, all subsequent queries
68 /// matching the one with the flag will use the cached statement until the
69 /// cache is cleared.
70 ///
71 /// If `false`, the prepared statement will be closed after execution.
72 ///
73 /// Default: `true`.
74 pub fn persistent(mut self, value: bool) -> Self {
75 self.inner = self.inner.persistent(value);
76 self
77 }
78}
79
80// FIXME: This is very close, nearly 1:1 with `Map`
81// noinspection DuplicatedCode
82impl<'q, DB, O, A> QueryAs<'q, DB, O, A>
83where
84 DB: Database,
85 A: 'q + IntoArguments<'q, DB>,
86 O: Send + Unpin + for<'r> FromRow<'r, DB::Row>,
87{
88 /// Execute the query and return the generated results as a stream.
89 pub fn fetch<'e, 'c: 'e, E>(self, executor: E) -> BoxStream<'e, Result<O, Error>>
90 where
91 'q: 'e,
92 E: 'e + Executor<'c, Database = DB>,
93 DB: 'e,
94 O: 'e,
95 A: 'e,
96 {
97 // FIXME: this should have used `executor.fetch()` but that's a breaking change
98 // because this technically allows multiple statements in one query string.
99 #[allow(deprecated)]
100 self.fetch_many(executor)
101 .try_filter_map(|step| async move { Ok(step.right()) })
102 .boxed()
103 }
104
105 /// Execute multiple queries and return the generated results as a stream
106 /// from each query, in a stream.
107 #[deprecated = "Only the SQLite driver supports multiple statements in one prepared statement and that behavior is deprecated. Use `sqlx::raw_sql()` instead. See https://github.com/launchbadge/sqlx/issues/3108 for discussion."]
108 pub fn fetch_many<'e, 'c: 'e, E>(
109 self,
110 executor: E,
111 ) -> BoxStream<'e, Result<Either<DB::QueryResult, O>, Error>>
112 where
113 'q: 'e,
114 E: 'e + Executor<'c, Database = DB>,
115 DB: 'e,
116 O: 'e,
117 A: 'e,
118 {
119 executor
120 .fetch_many(self.inner)
121 .map(|v| match v {
122 Ok(Either::Right(row)) => O::from_row(&row).map(Either::Right),
123 Ok(Either::Left(v)) => Ok(Either::Left(v)),
124 Err(e) => Err(e),
125 })
126 .boxed()
127 }
128
129 /// Execute the query and return all the resulting rows collected into a [`Vec`].
130 ///
131 /// ### Note: beware result set size.
132 /// This will attempt to collect the full result set of the query into memory.
133 ///
134 /// To avoid exhausting available memory, ensure the result set has a known upper bound,
135 /// e.g. using `LIMIT`.
136 #[inline]
137 pub async fn fetch_all<'e, 'c: 'e, E>(self, executor: E) -> Result<Vec<O>, Error>
138 where
139 'q: 'e,
140 E: 'e + Executor<'c, Database = DB>,
141 DB: 'e,
142 O: 'e,
143 A: 'e,
144 {
145 self.fetch(executor).try_collect().await
146 }
147
148 /// Execute the query, returning the first row or [`Error::RowNotFound`] otherwise.
149 ///
150 /// ### Note: for best performance, ensure the query returns at most one row.
151 /// Depending on the driver implementation, if your query can return more than one row,
152 /// it may lead to wasted CPU time and bandwidth on the database server.
153 ///
154 /// Even when the driver implementation takes this into account, ensuring the query returns at most one row
155 /// can result in a more optimal query plan.
156 ///
157 /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good.
158 ///
159 /// Otherwise, you might want to add `LIMIT 1` to your query.
160 pub async fn fetch_one<'e, 'c: 'e, E>(self, executor: E) -> Result<O, Error>
161 where
162 'q: 'e,
163 E: 'e + Executor<'c, Database = DB>,
164 DB: 'e,
165 O: 'e,
166 A: 'e,
167 {
168 self.fetch_optional(executor)
169 .await
170 .and_then(|row| row.ok_or(Error::RowNotFound))
171 }
172
173 /// Execute the query, returning the first row or `None` otherwise.
174 ///
175 /// ### Note: for best performance, ensure the query returns at most one row.
176 /// Depending on the driver implementation, if your query can return more than one row,
177 /// it may lead to wasted CPU time and bandwidth on the database server.
178 ///
179 /// Even when the driver implementation takes this into account, ensuring the query returns at most one row
180 /// can result in a more optimal query plan.
181 ///
182 /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good.
183 ///
184 /// Otherwise, you might want to add `LIMIT 1` to your query.
185 pub async fn fetch_optional<'e, 'c: 'e, E>(self, executor: E) -> Result<Option<O>, Error>
186 where
187 'q: 'e,
188 E: 'e + Executor<'c, Database = DB>,
189 DB: 'e,
190 O: 'e,
191 A: 'e,
192 {
193 let row = executor.fetch_optional(self.inner).await?;
194 if let Some(row) = row {
195 O::from_row(&row).map(Some)
196 } else {
197 Ok(None)
198 }
199 }
200}
201
202/// Execute a single SQL query as a prepared statement (transparently cached).
203/// Maps rows to Rust types using [`FromRow`].
204///
205/// For details about prepared statements and allowed SQL syntax, see [`query()`][crate::query::query].
206///
207/// ### Example: Map Rows using Tuples
208/// [`FromRow`] is implemented for tuples of up to 16 elements<sup>1</sup>.
209/// Using a tuple of N elements will extract the first N columns from each row using [`Decode`][crate::decode::Decode].
210/// Any extra columns are ignored.
211///
212/// See [`sqlx::types`][crate::types] for the types that can be used.
213///
214/// The `FromRow` implementation will check [`Type::compatible()`] for each column to ensure a compatible type mapping
215/// is used. If an incompatible mapping is detected, an error is returned.
216/// To statically assert compatible types at compile time, see the `query!()` family of macros.
217///
218/// **NOTE**: `SELECT *` is not recommended with this approach because the ordering of returned columns may be different
219/// than expected, especially when using joins.
220///
221/// ```rust,no_run
222/// # async fn example1() -> sqlx::Result<()> {
223/// use sqlx::Connection;
224/// use sqlx::PgConnection;
225///
226/// // This example can be applied to any database as it only uses standard types and syntax.
227/// let mut conn: PgConnection = PgConnection::connect("<Database URL>").await?;
228///
229/// sqlx::raw_sql(
230/// "CREATE TABLE users(id INTEGER PRIMARY KEY, username TEXT UNIQUE, created_at TIMESTAMPTZ DEFAULT (now()))"
231/// )
232/// .execute(&mut conn)
233/// .await?;
234///
235/// sqlx::query("INSERT INTO users(id, username) VALUES (1, 'alice'), (2, 'bob');")
236/// .execute(&mut conn)
237/// .await?;
238///
239/// // Get the first row of the result (note the `LIMIT 1` for efficiency)
240/// // This assumes the `time` feature of SQLx is enabled.
241/// let oldest_user: (i32, String, time::OffsetDateTime) = sqlx::query_as(
242/// "SELECT id, username, created_at FROM users ORDER BY created_at LIMIT 1"
243/// )
244/// .fetch_one(&mut conn)
245/// .await?;
246///
247/// assert_eq!(oldest_user.0, 1);
248/// assert_eq!(oldest_user.1, "alice");
249///
250/// // Get at most one row
251/// let maybe_charlie: Option<(i32, String, time::OffsetDateTime)> = sqlx::query_as(
252/// "SELECT id, username, created_at FROM users WHERE username = 'charlie'"
253/// )
254/// .fetch_optional(&mut conn)
255/// .await?;
256///
257/// assert_eq!(maybe_charlie, None);
258///
259/// // Get all rows in result (Beware of the size of the result set! Consider using `LIMIT`)
260/// let users: Vec<(i32, String, time::OffsetDateTime)> = sqlx::query_as(
261/// "SELECT id, username, created_at FROM users ORDER BY id"
262/// )
263/// .fetch_all(&mut conn)
264/// .await?;
265///
266/// println!("{users:?}");
267/// # Ok(())
268/// # }
269/// ```
270///
271/// <sup>1</sup>: It's impossible in Rust to implement a trait for tuples of arbitrary size.
272/// For larger result sets, either use an explicit struct (see below) or use [`query()`][crate::query::query]
273/// instead and extract columns dynamically.
274///
275/// ### Example: Map Rows using `#[derive(FromRow)]`
276/// Using `#[derive(FromRow)]`, we can create a Rust struct to represent our row type
277/// so we can look up fields by name instead of tuple index.
278///
279/// When querying this way, columns will be matched up to the corresponding fields by name, so `SELECT *` is safe to use.
280/// However, you will still want to be aware of duplicate column names in your query when using joins.
281///
282/// The derived `FromRow` implementation will check [`Type::compatible()`] for each column to ensure a compatible type
283/// mapping is used. If an incompatible mapping is detected, an error is returned.
284/// To statically assert compatible types at compile time, see the `query!()` family of macros.
285///
286/// An error will also be returned if an expected column is missing from the result set.
287///
288/// `#[derive(FromRow)]` supports several control attributes which can be used to change how column names and types
289/// are mapped. See [`FromRow`] for details.
290///
291/// Using our previous table definition, we can convert our queries like so:
292/// ```rust,no_run
293/// # async fn example2() -> sqlx::Result<()> {
294/// use sqlx::Connection;
295/// use sqlx::PgConnection;
296///
297/// use time::OffsetDateTime;
298///
299/// #[derive(sqlx::FromRow, Debug, PartialEq, Eq)]
300/// struct User {
301/// id: i64,
302/// username: String,
303/// // Note: the derive won't compile if the `time` feature of SQLx is not enabled.
304/// created_at: OffsetDateTime,
305/// }
306///
307/// let mut conn: PgConnection = PgConnection::connect("<Database URL>").await?;
308///
309/// // Get the first row of the result (note the `LIMIT 1` for efficiency)
310/// let oldest_user: User = sqlx::query_as(
311/// "SELECT id, username, created_at FROM users ORDER BY created_at LIMIT 1"
312/// )
313/// .fetch_one(&mut conn)
314/// .await?;
315///
316/// assert_eq!(oldest_user.id, 1);
317/// assert_eq!(oldest_user.username, "alice");
318///
319/// // Get at most one row
320/// let maybe_charlie: Option<User> = sqlx::query_as(
321/// "SELECT id, username, created_at FROM users WHERE username = 'charlie'"
322/// )
323/// .fetch_optional(&mut conn)
324/// .await?;
325///
326/// assert_eq!(maybe_charlie, None);
327///
328/// // Get all rows in result (Beware of the size of the result set! Consider using `LIMIT`)
329/// let users: Vec<User> = sqlx::query_as(
330/// "SELECT id, username, created_at FROM users ORDER BY id"
331/// )
332/// .fetch_all(&mut conn)
333/// .await?;
334///
335/// assert_eq!(users[1].id, 2);
336/// assert_eq!(users[1].username, "bob");
337/// # Ok(())
338/// # }
339///
340/// ```
341#[inline]
342pub fn query_as<'q, DB, O>(sql: &'q str) -> QueryAs<'q, DB, O, <DB as Database>::Arguments<'q>>
343where
344 DB: Database,
345 O: for<'r> FromRow<'r, DB::Row>,
346{
347 QueryAs {
348 inner: query(sql),
349 output: PhantomData,
350 }
351}
352
353/// Execute a single SQL query, with the given arguments as a prepared statement (transparently cached).
354/// Maps rows to Rust types using [`FromRow`].
355///
356/// For details about prepared statements and allowed SQL syntax, see [`query()`][crate::query::query].
357///
358/// For details about type mapping from [`FromRow`], see [`query_as()`].
359#[inline]
360pub fn query_as_with<'q, DB, O, A>(sql: &'q str, arguments: A) -> QueryAs<'q, DB, O, A>
361where
362 DB: Database,
363 A: IntoArguments<'q, DB>,
364 O: for<'r> FromRow<'r, DB::Row>,
365{
366 query_as_with_result(sql, Ok(arguments))
367}
368
369/// Same as [`query_as_with`] but takes arguments as a Result
370#[inline]
371pub fn query_as_with_result<'q, DB, O, A>(
372 sql: &'q str,
373 arguments: Result<A, BoxDynError>,
374) -> QueryAs<'q, DB, O, A>
375where
376 DB: Database,
377 A: IntoArguments<'q, DB>,
378 O: for<'r> FromRow<'r, DB::Row>,
379{
380 QueryAs {
381 inner: query_with_result(sql, arguments),
382 output: PhantomData,
383 }
384}
385
386// Make a SQL query from a statement, that is mapped to a concrete type.
387pub fn query_statement_as<'q, DB, O>(
388 statement: &'q DB::Statement<'q>,
389) -> QueryAs<'q, DB, O, <DB as Database>::Arguments<'_>>
390where
391 DB: Database,
392 O: for<'r> FromRow<'r, DB::Row>,
393{
394 QueryAs {
395 inner: query_statement(statement),
396 output: PhantomData,
397 }
398}
399
400// Make a SQL query from a statement, with the given arguments, that is mapped to a concrete type.
401pub fn query_statement_as_with<'q, DB, O, A>(
402 statement: &'q DB::Statement<'q>,
403 arguments: A,
404) -> QueryAs<'q, DB, O, A>
405where
406 DB: Database,
407 A: IntoArguments<'q, DB>,
408 O: for<'r> FromRow<'r, DB::Row>,
409{
410 QueryAs {
411 inner: query_statement_with(statement, arguments),
412 output: PhantomData,
413 }
414}