Skip to main content

chain_builder/
fetch.rs

1//! Typed fetch / execution helpers for v2 (`feature = "sqlx_*"`).
2//!
3//! These are thin async delegations on top of the sqlx query objects already
4//! produced by [`to_sqlx_query`](QueryBuilder::to_sqlx_query) and
5//! [`to_sqlx_query_as`](QueryBuilder::to_sqlx_query_as) in
6//! [`crate::sqlx_bind`]. They let any [`QueryBuilder<D>`] whose
7//! `D: SqlxDialect` run against a sqlx [`Executor`](sqlx::Executor) and decode
8//! rows into a `T: FromRow`, or pull a single scalar column.
9//!
10//! [`count`](QueryBuilder::count) mirrors 1.x `ChainBuilder::count`: it wraps the
11//! built SQL in `SELECT COUNT(*) FROM (<sql>) AS __cb_count`, binds the same
12//! arguments, and fetches a single `i64`.
13
14use crate::builder::QueryBuilder;
15use crate::sqlx_bind::SqlxDialect;
16
17impl<D: SqlxDialect> QueryBuilder<D> {
18    /// Fetch all rows, decoding each into `T`.
19    ///
20    /// Delegates to `self.to_sqlx_query_as::<T>().fetch_all(executor)`.
21    pub async fn fetch_all<'e, T, E>(&self, executor: E) -> Result<Vec<T>, sqlx::Error>
22    where
23        T: for<'r> sqlx::FromRow<'r, <D::Database as sqlx::Database>::Row> + Send + Unpin,
24        E: sqlx::Executor<'e, Database = D::Database>,
25    {
26        self.to_sqlx_query_as::<T>().fetch_all(executor).await
27    }
28
29    /// Fetch exactly one row, decoding it into `T`.
30    ///
31    /// Errors with [`sqlx::Error::RowNotFound`] if no row is returned.
32    pub async fn fetch_one<'e, T, E>(&self, executor: E) -> Result<T, sqlx::Error>
33    where
34        T: for<'r> sqlx::FromRow<'r, <D::Database as sqlx::Database>::Row> + Send + Unpin,
35        E: sqlx::Executor<'e, Database = D::Database>,
36    {
37        self.to_sqlx_query_as::<T>().fetch_one(executor).await
38    }
39
40    /// Fetch at most one row, decoding it into `T`.
41    pub async fn fetch_optional<'e, T, E>(&self, executor: E) -> Result<Option<T>, sqlx::Error>
42    where
43        T: for<'r> sqlx::FromRow<'r, <D::Database as sqlx::Database>::Row> + Send + Unpin,
44        E: sqlx::Executor<'e, Database = D::Database>,
45    {
46        self.to_sqlx_query_as::<T>().fetch_optional(executor).await
47    }
48
49    /// Execute the query (INSERT / UPDATE / DELETE / DDL), returning the
50    /// database's `QueryResult` (affected rows, last insert id, …).
51    ///
52    /// Delegates to `self.to_sqlx_query().execute(executor)`.
53    pub async fn execute<'e, E>(
54        &self,
55        executor: E,
56    ) -> Result<<D::Database as sqlx::Database>::QueryResult, sqlx::Error>
57    where
58        E: sqlx::Executor<'e, Database = D::Database>,
59    {
60        self.to_sqlx_query().execute(executor).await
61    }
62
63    /// Count the rows the query would return.
64    ///
65    /// Wraps the built SQL as `SELECT COUNT(*) FROM (<sql>) AS __cb_count`, binds
66    /// the same arguments, and fetches a single `i64`. Mirrors 1.x
67    /// `ChainBuilder::count`.
68    pub async fn count<'e, E>(&self, executor: E) -> Result<i64, sqlx::Error>
69    where
70        E: sqlx::Executor<'e, Database = D::Database>,
71        // `query_scalar_with` decodes `(i64,)` from the row, which needs `i64` to
72        // be decodable for this database and a positional column index. These hold
73        // for every sqlx built-in backend but are not implied by `SqlxDialect`.
74        i64: for<'r> sqlx::Decode<'r, D::Database> + sqlx::Type<D::Database>,
75        usize: sqlx::ColumnIndex<<D::Database as sqlx::Database>::Row>,
76    {
77        let (sql, binds) = self.to_sql();
78        let wrapped = format!("SELECT COUNT(*) FROM ({sql}) AS __cb_count");
79        let args = D::bind_arguments(&binds);
80        sqlx::query_scalar_with::<D::Database, i64, _>(sqlx::AssertSqlSafe(wrapped), args)
81            .fetch_one(executor)
82            .await
83    }
84
85    /// Fetch the first column of the first row, decoded into `T`.
86    ///
87    /// Errors with [`sqlx::Error::RowNotFound`] if no row is returned. Useful for
88    /// `pluck`/aggregate-style queries.
89    pub async fn fetch_scalar<'e, T, E>(&self, executor: E) -> Result<T, sqlx::Error>
90    where
91        T: for<'r> sqlx::Decode<'r, D::Database> + sqlx::Type<D::Database> + Send + Unpin,
92        E: sqlx::Executor<'e, Database = D::Database>,
93        // `query_scalar_with` decodes `(T,)`, which requires a positional column
94        // index on the backend's row type.
95        usize: sqlx::ColumnIndex<<D::Database as sqlx::Database>::Row>,
96    {
97        let (sql, binds) = self.to_sql();
98        let args = D::bind_arguments(&binds);
99        sqlx::query_scalar_with::<D::Database, T, _>(sqlx::AssertSqlSafe(sql), args)
100            .fetch_one(executor)
101            .await
102    }
103
104    /// Fetch the first column of the first row (if any), decoded into `T`.
105    pub async fn fetch_optional_scalar<'e, T, E>(
106        &self,
107        executor: E,
108    ) -> Result<Option<T>, sqlx::Error>
109    where
110        T: for<'r> sqlx::Decode<'r, D::Database> + sqlx::Type<D::Database> + Send + Unpin,
111        E: sqlx::Executor<'e, Database = D::Database>,
112        usize: sqlx::ColumnIndex<<D::Database as sqlx::Database>::Row>,
113    {
114        let (sql, binds) = self.to_sql();
115        let args = D::bind_arguments(&binds);
116        sqlx::query_scalar_with::<D::Database, T, _>(sqlx::AssertSqlSafe(sql), args)
117            .fetch_optional(executor)
118            .await
119    }
120}