diff --git a/sqlx-core/src/query.rs b/sqlx-core/src/query.rs index 60f509c342..8e30695ab3 100644 --- a/sqlx-core/src/query.rs +++ b/sqlx-core/src/query.rs @@ -1,8 +1,9 @@ use std::marker::PhantomData; use either::Either; +use futures_core::future::BoxFuture; use futures_core::stream::BoxStream; -use futures_util::{future, StreamExt, TryFutureExt, TryStreamExt}; +use futures_util::{FutureExt, StreamExt, TryStreamExt}; use crate::arguments::{Arguments, IntoArguments}; use crate::database::{Database, HasStatementCache}; @@ -12,6 +13,145 @@ use crate::executor::{Execute, Executor}; use crate::statement::Statement; use crate::types::Type; +pub trait Fetch<'q, DB: Database>: Sized { + type Output: Send + Unpin; + + /// Execute the query and return the generated results as a stream. + fn fetch<'e, 'c: 'e, E>(self, executor: E) -> BoxStream<'e, Result> + where + 'q: 'e, + E: 'e + Executor<'c, Database = DB>, + DB: 'e, + Self::Output: 'e + Send, + { + // FIXME: this should have used `executor.fetch()` but that's a breaking change + // because this technically allows multiple statements in one query string. + #[allow(deprecated)] + self.fetch_many(executor) + .try_filter_map(|step| async move { + Ok(match step { + Either::Left(_) => None, + Either::Right(o) => Some(o), + }) + }) + .boxed() + } + + /// Execute multiple queries and return the generated results as a stream + /// from each query, in a stream. + #[deprecated = "Only the SQLite driver supports multiple statements in one prepared statement and that behavior is deprecated. Use `sqlx::raw_sql()` instead."] + fn fetch_many<'e, 'c: 'e, E>( + self, + executor: E, + ) -> BoxStream<'e, Result, Error>> + where + 'q: 'e, + E: 'e + Executor<'c, Database = DB>, + DB: 'e, + Self::Output: 'e; + + /// Execute the query and return all the resulting rows collected into a [`Vec`]. + /// + /// ### Note: beware result set size. + /// This will attempt to collect the full result set of the query into memory. + /// + /// To avoid exhausting available memory, ensure the result set has a known upper bound, + /// e.g. using `LIMIT`. + fn fetch_all<'e, 'c: 'e, E>( + self, + executor: E, + ) -> BoxFuture<'e, Result, Error>> + where + 'q: 'e, + E: 'e + Executor<'c, Database = DB>, + DB: 'e, + Self::Output: 'e + Send + Unpin, + { + self.fetch(executor).try_collect().boxed() + } + + /// Execute the query, returning the first row or [`Error::RowNotFound`] otherwise. + /// + /// ### Note: for best performance, ensure the query returns at most one row. + /// Depending on the driver implementation, if your query can return more than one row, + /// it may lead to wasted CPU time and bandwidth on the database server. + /// + /// Even when the driver implementation takes this into account, ensuring the query returns at most one row + /// can result in a more optimal query plan. + /// + /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good. + /// + /// Otherwise, you might want to add `LIMIT 1` to your query. + fn fetch_one<'e, 'c: 'e, E>(self, executor: E) -> BoxFuture<'e, Result> + where + 'q: 'e, + E: 'e + Executor<'c, Database = DB>, + DB: 'e, + Self::Output: 'e + Send + Unpin, + { + self.fetch_optional(executor) + .map(|row| row.and_then(|row| row.ok_or(Error::RowNotFound))) + .boxed() + } + + /// Execute the query, returning the first row or `None` otherwise. + /// + /// ### Note: for best performance, ensure the query returns at most one row. + /// Depending on the driver implementation, if your query can return more than one row, + /// it may lead to wasted CPU time and bandwidth on the database server. + /// + /// Even when the driver implementation takes this into account, ensuring the query returns at most one row + /// can result in a more optimal query plan. + /// + /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good. + /// + /// Otherwise, you might want to add `LIMIT 1` to your query. + fn fetch_optional<'e, 'c: 'e, E>( + self, + executor: E, + ) -> BoxFuture<'e, Result, Error>> + where + 'q: 'e, + E: 'e + Executor<'c, Database = DB>, + DB: 'e, + Self::Output: 'e + Send + Unpin; + + /// Map each row in the result to another type. + /// + /// See [`try_map`](Query::try_map) for a fallible version of this method. + /// + /// The [`query_as`](super::query_as::query_as) method will construct a mapped query using + /// a [`FromRow`](super::from_row::FromRow) implementation. + #[inline] + fn map( + self, + mut f: F, + ) -> Map<'q, DB, Self, impl FnMut(DB::Row) -> Result + Send> + where + F: FnMut(DB::Row) -> O + Send, + O: Unpin, + { + self.try_map(move |row| Ok(f(row))) + } + + /// Map each row in the result to another type. + /// + /// The [`query_as`](super::query_as::query_as) method will construct a mapped query using + /// a [`FromRow`](super::from_row::FromRow) implementation. + #[inline] + fn try_map(self, f: F) -> Map<'q, DB, Self, F> + where + F: FnMut(DB::Row) -> Result + Send, + O: Unpin, + { + Map { + inner: self, + mapper: f, + inner_statement: PhantomData, + } + } +} + /// A single SQL query as a prepared statement. Returned by [`query()`]. #[must_use = "query must be executed to affect database"] pub struct Query<'q, DB: Database, A> { @@ -33,9 +173,10 @@ pub struct Query<'q, DB: Database, A> { /// before `.try_map()`. This is also to prevent adding superfluous binds to the result of /// `query!()` et al. #[must_use = "query must be executed to affect database"] -pub struct Map<'q, DB: Database, F, A> { - inner: Query<'q, DB, A>, +pub struct Map<'q, DB: Database, Inner, F> { + inner: Inner, mapper: F, + inner_statement: PhantomData<&'q DB::Statement<'q>>, } impl<'q, DB, A> Execute<'q, DB> for Query<'q, DB, A> @@ -145,40 +286,6 @@ where DB: Database, A: 'q + IntoArguments<'q, DB>, { - /// Map each row in the result to another type. - /// - /// See [`try_map`](Query::try_map) for a fallible version of this method. - /// - /// The [`query_as`](super::query_as::query_as) method will construct a mapped query using - /// a [`FromRow`](super::from_row::FromRow) implementation. - #[inline] - pub fn map( - self, - mut f: F, - ) -> Map<'q, DB, impl FnMut(DB::Row) -> Result + Send, A> - where - F: FnMut(DB::Row) -> O + Send, - O: Unpin, - { - self.try_map(move |row| Ok(f(row))) - } - - /// Map each row in the result to another type. - /// - /// The [`query_as`](super::query_as::query_as) method will construct a mapped query using - /// a [`FromRow`](super::from_row::FromRow) implementation. - #[inline] - pub fn try_map(self, f: F) -> Map<'q, DB, F, A> - where - F: FnMut(DB::Row) -> Result + Send, - O: Unpin, - { - Map { - inner: self, - mapper: f, - } - } - /// Execute the query and return the total number of rows affected. #[inline] pub async fn execute<'e, 'c: 'e, E>(self, executor: E) -> Result @@ -204,103 +311,41 @@ where { executor.execute_many(self) } +} - /// Execute the query and return the generated results as a stream. - #[inline] - pub fn fetch<'e, 'c: 'e, E>(self, executor: E) -> BoxStream<'e, Result> - where - 'q: 'e, - A: 'e, - E: Executor<'c, Database = DB>, - { - executor.fetch(self) - } +impl<'q, DB: Database, A: 'q + IntoArguments<'q, DB> + Send> Fetch<'q, DB> for Query<'q, DB, A> { + type Output = DB::Row; - /// Execute multiple queries and return the generated results as a stream. - /// - /// For each query in the stream, any generated rows are returned first, - /// then the `QueryResult` with the number of rows affected. - #[inline] - #[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."] - // TODO: we'll probably still want a way to get the `DB::QueryResult` at the end of a `fetch()` stream. - pub fn fetch_many<'e, 'c: 'e, E>( + fn fetch_many<'e, 'c: 'e, E>( self, executor: E, - ) -> BoxStream<'e, Result, Error>> + ) -> BoxStream<'e, Result, Error>> where 'q: 'e, - A: 'e, - E: Executor<'c, Database = DB>, + E: 'e + Executor<'c, Database = DB>, + DB: 'e, + Self::Output: 'e, { executor.fetch_many(self) } - /// Execute the query and return all the resulting rows collected into a [`Vec`]. - /// - /// ### Note: beware result set size. - /// This will attempt to collect the full result set of the query into memory. - /// - /// To avoid exhausting available memory, ensure the result set has a known upper bound, - /// e.g. using `LIMIT`. - #[inline] - pub async fn fetch_all<'e, 'c: 'e, E>(self, executor: E) -> Result, Error> - where - 'q: 'e, - A: 'e, - E: Executor<'c, Database = DB>, - { - executor.fetch_all(self).await - } - - /// Execute the query, returning the first row or [`Error::RowNotFound`] otherwise. - /// - /// ### Note: for best performance, ensure the query returns at most one row. - /// Depending on the driver implementation, if your query can return more than one row, - /// it may lead to wasted CPU time and bandwidth on the database server. - /// - /// Even when the driver implementation takes this into account, ensuring the query returns at most one row - /// can result in a more optimal query plan. - /// - /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good. - /// - /// Otherwise, you might want to add `LIMIT 1` to your query. - #[inline] - pub async fn fetch_one<'e, 'c: 'e, E>(self, executor: E) -> Result - where - 'q: 'e, - A: 'e, - E: Executor<'c, Database = DB>, - { - executor.fetch_one(self).await - } - - /// Execute the query, returning the first row or `None` otherwise. - /// - /// ### Note: for best performance, ensure the query returns at most one row. - /// Depending on the driver implementation, if your query can return more than one row, - /// it may lead to wasted CPU time and bandwidth on the database server. - /// - /// Even when the driver implementation takes this into account, ensuring the query returns at most one row - /// can result in a more optimal query plan. - /// - /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good. - /// - /// Otherwise, you might want to add `LIMIT 1` to your query. - #[inline] - pub async fn fetch_optional<'e, 'c: 'e, E>(self, executor: E) -> Result, Error> + fn fetch_optional<'e, 'c: 'e, E>( + self, + executor: E, + ) -> BoxFuture<'e, Result, Error>> where 'q: 'e, - A: 'e, - E: Executor<'c, Database = DB>, + E: 'e + Executor<'c, Database = DB>, + DB: 'e, + Self::Output: 'e + Send + Unpin, { - executor.fetch_optional(self).await + executor.fetch_optional(self) } } -impl<'q, DB, F: Send, A: Send> Execute<'q, DB> for Map<'q, DB, F, A> +impl<'q, DB, Inner: Execute<'q, DB>, F: Send> Execute<'q, DB> for Map<'q, DB, Inner, F> where DB: Database, - A: IntoArguments<'q, DB>, { #[inline] fn sql(&self) -> &'q str { @@ -319,93 +364,32 @@ where #[inline] fn persistent(&self) -> bool { - self.inner.arguments.is_some() + self.inner.persistent() } } -impl<'q, DB, F, O, A> Map<'q, DB, F, A> +impl<'q, DB, Inner: Fetch<'q, DB>, F, O> Fetch<'q, DB> for Map<'q, DB, Inner, F> where DB: Database, - F: FnMut(DB::Row) -> Result + Send, + F: FnMut(Inner::Output) -> Result + Send + 'q, O: Send + Unpin, - A: 'q + Send + IntoArguments<'q, DB>, + Inner: Send + 'q, { - /// Map each row in the result to another type. - /// - /// See [`try_map`](Map::try_map) for a fallible version of this method. - /// - /// The [`query_as`](super::query_as::query_as) method will construct a mapped query using - /// a [`FromRow`](super::from_row::FromRow) implementation. - #[inline] - pub fn map( - self, - mut g: G, - ) -> Map<'q, DB, impl FnMut(DB::Row) -> Result + Send, A> - where - G: FnMut(O) -> P + Send, - P: Unpin, - { - self.try_map(move |data| Ok(g(data))) - } + type Output = O; - /// Map each row in the result to another type. - /// - /// The [`query_as`](super::query_as::query_as) method will construct a mapped query using - /// a [`FromRow`](super::from_row::FromRow) implementation. - #[inline] - pub fn try_map( - self, - mut g: G, - ) -> Map<'q, DB, impl FnMut(DB::Row) -> Result + Send, A> - where - G: FnMut(O) -> Result + Send, - P: Unpin, - { - let mut f = self.mapper; - Map { - inner: self.inner, - mapper: move |row| f(row).and_then(&mut g), - } - } - - /// Execute the query and return the generated results as a stream. - pub fn fetch<'e, 'c: 'e, E>(self, executor: E) -> BoxStream<'e, Result> - where - 'q: 'e, - E: 'e + Executor<'c, Database = DB>, - DB: 'e, - F: 'e, - O: 'e, - { - // FIXME: this should have used `executor.fetch()` but that's a breaking change - // because this technically allows multiple statements in one query string. - #[allow(deprecated)] - self.fetch_many(executor) - .try_filter_map(|step| async move { - Ok(match step { - Either::Left(_) => None, - Either::Right(o) => Some(o), - }) - }) - .boxed() - } - - /// Execute multiple queries and return the generated results as a stream - /// from each query, in a stream. - #[deprecated = "Only the SQLite driver supports multiple statements in one prepared statement and that behavior is deprecated. Use `sqlx::raw_sql()` instead."] - pub fn fetch_many<'e, 'c: 'e, E>( + fn fetch_many<'e, 'c: 'e, E>( mut self, executor: E, - ) -> BoxStream<'e, Result, Error>> + ) -> BoxStream<'e, Result, Error>> where 'q: 'e, E: 'e + Executor<'c, Database = DB>, DB: 'e, - F: 'e, - O: 'e, + Self::Output: 'e, { Box::pin(try_stream! { - let mut s = executor.fetch_many(self.inner); + #[allow(deprecated)] + let mut s = self.inner.fetch_many(executor); while let Some(v) = s.try_next().await? { r#yield!(match v { @@ -420,79 +404,26 @@ where }) } - /// Execute the query and return all the resulting rows collected into a [`Vec`]. - /// - /// ### Note: beware result set size. - /// This will attempt to collect the full result set of the query into memory. - /// - /// To avoid exhausting available memory, ensure the result set has a known upper bound, - /// e.g. using `LIMIT`. - pub async fn fetch_all<'e, 'c: 'e, E>(self, executor: E) -> Result, Error> - where - 'q: 'e, - E: 'e + Executor<'c, Database = DB>, - DB: 'e, - F: 'e, - O: 'e, - { - self.fetch(executor).try_collect().await - } - - /// Execute the query, returning the first row or [`Error::RowNotFound`] otherwise. - /// - /// ### Note: for best performance, ensure the query returns at most one row. - /// Depending on the driver implementation, if your query can return more than one row, - /// it may lead to wasted CPU time and bandwidth on the database server. - /// - /// Even when the driver implementation takes this into account, ensuring the query returns at most one row - /// can result in a more optimal query plan. - /// - /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good. - /// - /// Otherwise, you might want to add `LIMIT 1` to your query. - pub async fn fetch_one<'e, 'c: 'e, E>(self, executor: E) -> Result - where - 'q: 'e, - E: 'e + Executor<'c, Database = DB>, - DB: 'e, - F: 'e, - O: 'e, - { - self.fetch_optional(executor) - .and_then(|row| match row { - Some(row) => future::ok(row), - None => future::err(Error::RowNotFound), - }) - .await - } - - /// Execute the query, returning the first row or `None` otherwise. - /// - /// ### Note: for best performance, ensure the query returns at most one row. - /// Depending on the driver implementation, if your query can return more than one row, - /// it may lead to wasted CPU time and bandwidth on the database server. - /// - /// Even when the driver implementation takes this into account, ensuring the query returns at most one row - /// can result in a more optimal query plan. - /// - /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good. - /// - /// Otherwise, you might want to add `LIMIT 1` to your query. - pub async fn fetch_optional<'e, 'c: 'e, E>(mut self, executor: E) -> Result, Error> + fn fetch_optional<'e, 'c: 'e, E>( + mut self, + executor: E, + ) -> BoxFuture<'e, Result, Error>> where 'q: 'e, E: 'e + Executor<'c, Database = DB>, DB: 'e, - F: 'e, - O: 'e, + Self::Output: 'e + Send + Unpin, { - let row = executor.fetch_optional(self.inner).await?; + async move { + let row = self.inner.fetch_optional(executor).await?; - if let Some(row) = row { - (self.mapper)(row).map(Some) - } else { - Ok(None) + if let Some(row) = row { + (self.mapper)(row).map(Some) + } else { + Ok(None) + } } + .boxed() } } diff --git a/sqlx-core/src/query_as.rs b/sqlx-core/src/query_as.rs index fbc7fab55b..a9f890c758 100644 --- a/sqlx-core/src/query_as.rs +++ b/sqlx-core/src/query_as.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use either::Either; use futures_core::stream::BoxStream; -use futures_util::{StreamExt, TryStreamExt}; +use futures_util::{FutureExt, StreamExt}; use crate::arguments::IntoArguments; use crate::database::{Database, HasStatementCache}; @@ -10,7 +10,7 @@ use crate::encode::Encode; use crate::error::{BoxDynError, Error}; use crate::executor::{Execute, Executor}; use crate::from_row::FromRow; -use crate::query::{query, query_statement, query_statement_with, query_with_result, Query}; +use crate::query::{query, query_statement, query_statement_with, query_with_result, Fetch, Query}; use crate::types::Type; /// A single SQL query as a prepared statement, mapping results using [`FromRow`]. @@ -77,44 +77,23 @@ where } } -// FIXME: This is very close, nearly 1:1 with `Map` -// noinspection DuplicatedCode -impl<'q, DB, O, A> QueryAs<'q, DB, O, A> +impl<'q, DB, O, A> Fetch<'q, DB> for QueryAs<'q, DB, O, A> where DB: Database, A: 'q + IntoArguments<'q, DB>, O: Send + Unpin + for<'r> FromRow<'r, DB::Row>, { - /// Execute the query and return the generated results as a stream. - pub fn fetch<'e, 'c: 'e, E>(self, executor: E) -> BoxStream<'e, Result> - where - 'q: 'e, - E: 'e + Executor<'c, Database = DB>, - DB: 'e, - O: 'e, - A: 'e, - { - // FIXME: this should have used `executor.fetch()` but that's a breaking change - // because this technically allows multiple statements in one query string. - #[allow(deprecated)] - self.fetch_many(executor) - .try_filter_map(|step| async move { Ok(step.right()) }) - .boxed() - } + type Output = O; - /// Execute multiple queries and return the generated results as a stream - /// from each query, in a stream. - #[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."] - pub fn fetch_many<'e, 'c: 'e, E>( + fn fetch_many<'e, 'c: 'e, E>( self, executor: E, - ) -> BoxStream<'e, Result, Error>> + ) -> BoxStream<'e, Result, Error>> where 'q: 'e, E: 'e + Executor<'c, Database = DB>, DB: 'e, - O: 'e, - A: 'e, + Self::Output: 'e, { executor .fetch_many(self.inner) @@ -126,76 +105,25 @@ where .boxed() } - /// Execute the query and return all the resulting rows collected into a [`Vec`]. - /// - /// ### Note: beware result set size. - /// This will attempt to collect the full result set of the query into memory. - /// - /// To avoid exhausting available memory, ensure the result set has a known upper bound, - /// e.g. using `LIMIT`. - #[inline] - pub async fn fetch_all<'e, 'c: 'e, E>(self, executor: E) -> Result, Error> - where - 'q: 'e, - E: 'e + Executor<'c, Database = DB>, - DB: 'e, - O: 'e, - A: 'e, - { - self.fetch(executor).try_collect().await - } - - /// Execute the query, returning the first row or [`Error::RowNotFound`] otherwise. - /// - /// ### Note: for best performance, ensure the query returns at most one row. - /// Depending on the driver implementation, if your query can return more than one row, - /// it may lead to wasted CPU time and bandwidth on the database server. - /// - /// Even when the driver implementation takes this into account, ensuring the query returns at most one row - /// can result in a more optimal query plan. - /// - /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good. - /// - /// Otherwise, you might want to add `LIMIT 1` to your query. - pub async fn fetch_one<'e, 'c: 'e, E>(self, executor: E) -> Result - where - 'q: 'e, - E: 'e + Executor<'c, Database = DB>, - DB: 'e, - O: 'e, - A: 'e, - { - self.fetch_optional(executor) - .await - .and_then(|row| row.ok_or(Error::RowNotFound)) - } - - /// Execute the query, returning the first row or `None` otherwise. - /// - /// ### Note: for best performance, ensure the query returns at most one row. - /// Depending on the driver implementation, if your query can return more than one row, - /// it may lead to wasted CPU time and bandwidth on the database server. - /// - /// Even when the driver implementation takes this into account, ensuring the query returns at most one row - /// can result in a more optimal query plan. - /// - /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good. - /// - /// Otherwise, you might want to add `LIMIT 1` to your query. - pub async fn fetch_optional<'e, 'c: 'e, E>(self, executor: E) -> Result, Error> + fn fetch_optional<'e, 'c: 'e, E>( + self, + executor: E, + ) -> futures_core::future::BoxFuture<'e, Result, Error>> where 'q: 'e, E: 'e + Executor<'c, Database = DB>, DB: 'e, - O: 'e, - A: 'e, + Self::Output: 'e + Send + Unpin, { - let row = executor.fetch_optional(self.inner).await?; - if let Some(row) = row { - O::from_row(&row).map(Some) - } else { - Ok(None) + async { + let row = executor.fetch_optional(self.inner).await?; + if let Some(row) = row { + O::from_row(&row).map(Some) + } else { + Ok(None) + } } + .boxed() } } diff --git a/sqlx-core/src/query_scalar.rs b/sqlx-core/src/query_scalar.rs index c131adcca3..87ac1618f3 100644 --- a/sqlx-core/src/query_scalar.rs +++ b/sqlx-core/src/query_scalar.rs @@ -1,6 +1,6 @@ use either::Either; use futures_core::stream::BoxStream; -use futures_util::{StreamExt, TryFutureExt, TryStreamExt}; +use futures_util::{FutureExt, StreamExt, TryStreamExt}; use crate::arguments::IntoArguments; use crate::database::{Database, HasStatementCache}; @@ -8,6 +8,7 @@ use crate::encode::Encode; use crate::error::{BoxDynError, Error}; use crate::executor::{Execute, Executor}; use crate::from_row::FromRow; +use crate::query::Fetch; use crate::query_as::{ query_as, query_as_with_result, query_statement_as, query_statement_as_with, QueryAs, }; @@ -74,42 +75,24 @@ where } } -// FIXME: This is very close, nearly 1:1 with `Map` -// noinspection DuplicatedCode -impl<'q, DB, O, A> QueryScalar<'q, DB, O, A> +impl<'q, DB, O, A> Fetch<'q, DB> for QueryScalar<'q, DB, O, A> where DB: Database, O: Send + Unpin, A: 'q + IntoArguments<'q, DB>, (O,): Send + Unpin + for<'r> FromRow<'r, DB::Row>, { - /// Execute the query and return the generated results as a stream. - #[inline] - pub fn fetch<'e, 'c: 'e, E>(self, executor: E) -> BoxStream<'e, Result> - where - 'q: 'e, - E: 'e + Executor<'c, Database = DB>, - DB: 'e, - A: 'e, - O: 'e, - { - self.inner.fetch(executor).map_ok(|it| it.0).boxed() - } + type Output = O; - /// Execute multiple queries and return the generated results as a stream - /// from each query, in a stream. - #[inline] - #[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."] - pub fn fetch_many<'e, 'c: 'e, E>( + fn fetch_many<'e, 'c: 'e, E>( self, executor: E, - ) -> BoxStream<'e, Result, Error>> + ) -> BoxStream<'e, Result, Error>> where 'q: 'e, E: 'e + Executor<'c, Database = DB>, DB: 'e, - A: 'e, - O: 'e, + Self::Output: 'e, { #[allow(deprecated)] self.inner @@ -118,75 +101,17 @@ where .boxed() } - /// Execute the query and return all the resulting rows collected into a [`Vec`]. - /// - /// ### Note: beware result set size. - /// This will attempt to collect the full result set of the query into memory. - /// - /// To avoid exhausting available memory, ensure the result set has a known upper bound, - /// e.g. using `LIMIT`. - #[inline] - pub async fn fetch_all<'e, 'c: 'e, E>(self, executor: E) -> Result, Error> - where - 'q: 'e, - E: 'e + Executor<'c, Database = DB>, - DB: 'e, - (O,): 'e, - A: 'e, - { - self.inner - .fetch(executor) - .map_ok(|it| it.0) - .try_collect() - .await - } - - /// Execute the query, returning the first row or [`Error::RowNotFound`] otherwise. - /// - /// ### Note: for best performance, ensure the query returns at most one row. - /// Depending on the driver implementation, if your query can return more than one row, - /// it may lead to wasted CPU time and bandwidth on the database server. - /// - /// Even when the driver implementation takes this into account, ensuring the query returns at most one row - /// can result in a more optimal query plan. - /// - /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good. - /// - /// Otherwise, you might want to add `LIMIT 1` to your query. - #[inline] - pub async fn fetch_one<'e, 'c: 'e, E>(self, executor: E) -> Result - where - 'q: 'e, - E: 'e + Executor<'c, Database = DB>, - DB: 'e, - O: 'e, - A: 'e, - { - self.inner.fetch_one(executor).map_ok(|it| it.0).await - } - - /// Execute the query, returning the first row or `None` otherwise. - /// - /// ### Note: for best performance, ensure the query returns at most one row. - /// Depending on the driver implementation, if your query can return more than one row, - /// it may lead to wasted CPU time and bandwidth on the database server. - /// - /// Even when the driver implementation takes this into account, ensuring the query returns at most one row - /// can result in a more optimal query plan. - /// - /// If your query has a `WHERE` clause filtering a unique column by a single value, you're good. - /// - /// Otherwise, you might want to add `LIMIT 1` to your query. - #[inline] - pub async fn fetch_optional<'e, 'c: 'e, E>(self, executor: E) -> Result, Error> + fn fetch_optional<'e, 'c: 'e, E>( + self, + executor: E, + ) -> futures_core::future::BoxFuture<'e, Result, Error>> where 'q: 'e, E: 'e + Executor<'c, Database = DB>, DB: 'e, - O: 'e, - A: 'e, + Self::Output: 'e + Send + Unpin, { - Ok(self.inner.fetch_optional(executor).await?.map(|it| it.0)) + async { Ok(self.inner.fetch_optional(executor).await?.map(|it| it.0)) }.boxed() } } diff --git a/sqlx-mysql/src/migrate.rs b/sqlx-mysql/src/migrate.rs index 79b55ace3c..f5fcb194e1 100644 --- a/sqlx-mysql/src/migrate.rs +++ b/sqlx-mysql/src/migrate.rs @@ -8,7 +8,7 @@ pub(crate) use sqlx_core::migrate::*; use crate::connection::{ConnectOptions, Connection}; use crate::error::Error; use crate::executor::Executor; -use crate::query::query; +use crate::query::{query, Fetch}; use crate::query_as::query_as; use crate::query_scalar::query_scalar; use crate::{MySql, MySqlConnectOptions, MySqlConnection}; diff --git a/sqlx-mysql/src/testing/mod.rs b/sqlx-mysql/src/testing/mod.rs index 2a9216d1b8..e7faf50581 100644 --- a/sqlx-mysql/src/testing/mod.rs +++ b/sqlx-mysql/src/testing/mod.rs @@ -13,7 +13,7 @@ use crate::connection::Connection; use crate::error::Error; use crate::executor::Executor; use crate::pool::{Pool, PoolOptions}; -use crate::query::query; +use crate::query::{query, Fetch}; use crate::query_builder::QueryBuilder; use crate::query_scalar::query_scalar; use crate::{MySql, MySqlConnectOptions, MySqlConnection}; diff --git a/sqlx-postgres/src/advisory_lock.rs b/sqlx-postgres/src/advisory_lock.rs index d1aef176fb..0f19998cb3 100644 --- a/sqlx-postgres/src/advisory_lock.rs +++ b/sqlx-postgres/src/advisory_lock.rs @@ -4,6 +4,7 @@ use crate::PgConnection; use hkdf::Hkdf; use once_cell::sync::OnceCell; use sha2::Sha256; +use sqlx_core::query::Fetch; use std::ops::{Deref, DerefMut}; /// A mutex-like type utilizing [Postgres advisory locks]. diff --git a/sqlx-postgres/src/connection/describe.rs b/sqlx-postgres/src/connection/describe.rs index 9a46a202d5..894f8d8bf6 100644 --- a/sqlx-postgres/src/connection/describe.rs +++ b/sqlx-postgres/src/connection/describe.rs @@ -2,6 +2,7 @@ use crate::error::Error; use crate::ext::ustr::UStr; use crate::io::StatementId; use crate::message::{ParameterDescription, RowDescription}; +use crate::query::Fetch; use crate::query_as::query_as; use crate::query_scalar::query_scalar; use crate::statement::PgStatementMetadata; diff --git a/sqlx-postgres/src/migrate.rs b/sqlx-postgres/src/migrate.rs index da3080581e..abf7f31064 100644 --- a/sqlx-postgres/src/migrate.rs +++ b/sqlx-postgres/src/migrate.rs @@ -11,7 +11,7 @@ pub(crate) use sqlx_core::migrate::{Migrate, MigrateDatabase}; use crate::connection::{ConnectOptions, Connection}; use crate::error::Error; use crate::executor::Executor; -use crate::query::query; +use crate::query::{query, Fetch}; use crate::query_as::query_as; use crate::query_scalar::query_scalar; use crate::{PgConnectOptions, PgConnection, Postgres}; diff --git a/sqlx-postgres/src/testing/mod.rs b/sqlx-postgres/src/testing/mod.rs index fb36ab4136..2ccf165346 100644 --- a/sqlx-postgres/src/testing/mod.rs +++ b/sqlx-postgres/src/testing/mod.rs @@ -13,7 +13,7 @@ use crate::connection::Connection; use crate::error::Error; use crate::executor::Executor; use crate::pool::{Pool, PoolOptions}; -use crate::query::query; +use crate::query::{query, Fetch}; use crate::query_scalar::query_scalar; use crate::{PgConnectOptions, PgConnection, Postgres}; diff --git a/sqlx-sqlite/src/migrate.rs b/sqlx-sqlite/src/migrate.rs index b9ce22dccd..245f1b03cb 100644 --- a/sqlx-sqlite/src/migrate.rs +++ b/sqlx-sqlite/src/migrate.rs @@ -5,7 +5,7 @@ use crate::fs; use crate::migrate::MigrateError; use crate::migrate::{AppliedMigration, Migration}; use crate::migrate::{Migrate, MigrateDatabase}; -use crate::query::query; +use crate::query::{query, Fetch}; use crate::query_as::query_as; use crate::{Sqlite, SqliteConnectOptions, SqliteConnection, SqliteJournalMode}; use futures_core::future::BoxFuture; diff --git a/src/lib.rs b/src/lib.rs index d675fa11c3..63c5a0dd2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ pub use sqlx_core::from_row::FromRow; pub use sqlx_core::pool::{self, Pool}; #[doc(hidden)] pub use sqlx_core::query::query_with_result as __query_with_result; -pub use sqlx_core::query::{query, query_with}; +pub use sqlx_core::query::{query, query_with, Fetch}; pub use sqlx_core::query_as::{query_as, query_as_with}; pub use sqlx_core::query_builder::{self, QueryBuilder}; #[doc(hidden)] diff --git a/tests/any/any.rs b/tests/any/any.rs index 2c59ca5339..d9c69704b1 100644 --- a/tests/any/any.rs +++ b/tests/any/any.rs @@ -1,5 +1,5 @@ use sqlx::any::AnyRow; -use sqlx::{Any, Connection, Executor, Row}; +use sqlx::{Any, Connection, Executor, Fetch, Row}; use sqlx_test::new; #[sqlx_macros::test] diff --git a/tests/any/pool.rs b/tests/any/pool.rs index 3130b4f1c6..30284912c2 100644 --- a/tests/any/pool.rs +++ b/tests/any/pool.rs @@ -1,5 +1,5 @@ use sqlx::any::{AnyConnectOptions, AnyPoolOptions}; -use sqlx::Executor; +use sqlx::{Executor, Fetch}; use std::sync::{ atomic::{AtomicI32, AtomicUsize, Ordering}, Arc, Mutex, diff --git a/tests/postgres/derives.rs b/tests/postgres/derives.rs index dada74fe4d..a1cbfe3703 100644 --- a/tests/postgres/derives.rs +++ b/tests/postgres/derives.rs @@ -1,6 +1,6 @@ use futures::TryStreamExt; use sqlx::postgres::types::PgRange; -use sqlx::{Connection, Executor, FromRow, Postgres}; +use sqlx::{Connection, Executor, Fetch, FromRow, Postgres}; use sqlx_postgres::PgHasArrayType; use sqlx_test::{new, test_type}; use std::fmt::Debug; diff --git a/tests/postgres/postgres.rs b/tests/postgres/postgres.rs index 7edb5a7a8c..f717ca366a 100644 --- a/tests/postgres/postgres.rs +++ b/tests/postgres/postgres.rs @@ -5,7 +5,7 @@ use sqlx::postgres::{ PgAdvisoryLock, PgConnectOptions, PgConnection, PgDatabaseError, PgErrorPosition, PgListener, PgPoolOptions, PgRow, PgSeverity, Postgres, }; -use sqlx::{Column, Connection, Executor, Row, Statement, TypeInfo}; +use sqlx::{Column, Connection, Executor, Fetch, Row, Statement, TypeInfo}; use sqlx_core::{bytes::Bytes, error::BoxDynError}; use sqlx_test::{new, pool, setup_if_needed}; use std::env; diff --git a/tests/postgres/query_builder.rs b/tests/postgres/query_builder.rs index 08ed7d11a3..3762b54936 100644 --- a/tests/postgres/query_builder.rs +++ b/tests/postgres/query_builder.rs @@ -2,7 +2,7 @@ use sqlx::postgres::Postgres; use sqlx::query_builder::QueryBuilder; use sqlx::Executor; use sqlx::Type; -use sqlx::{Either, Execute}; +use sqlx::{Either, Execute, Fetch}; use sqlx_test::new; #[test]