diff --git a/examples/sqlite/todos/src/main.rs b/examples/sqlite/todos/src/main.rs index e9aaebf7..31eb1f6a 100644 --- a/examples/sqlite/todos/src/main.rs +++ b/examples/sqlite/todos/src/main.rs @@ -1,5 +1,4 @@ use anyhow::Context; -use sqlx::sqlite::SqliteQueryAs; use sqlx::SqlitePool; use std::env; use structopt::StructOpt; @@ -48,6 +47,8 @@ async fn main(args: Args) -> anyhow::Result<()> { } async fn add_todo(pool: &SqlitePool, description: String) -> anyhow::Result { + let mut conn = pool.acquire().await?; + // Insert the TODO, then obtain the ID of this row sqlx::query!( r#" @@ -56,11 +57,11 @@ VALUES ( $1 ) "#, description ) - .execute(pool) + .execute(&mut conn) .await?; let rec: (i64,) = sqlx::query_as("SELECT last_insert_rowid()") - .fetch_one(pool) + .fetch_one(&mut conn) .await?; Ok(rec.0) diff --git a/sqlx-core/src/arguments.rs b/sqlx-core/src/arguments.rs index aeaac892..0be3a1d1 100644 --- a/sqlx-core/src/arguments.rs +++ b/sqlx-core/src/arguments.rs @@ -1,6 +1,6 @@ //! Types and traits for passing arguments to SQL queries. -use crate::database::Database; +use crate::database::{Database, HasArguments}; use crate::encode::Encode; /// A tuple of arguments to be sent to the database. @@ -16,3 +16,26 @@ pub trait Arguments<'q>: Send + Sized + Default { where T: 'q + Encode<'q, Self::Database>; } + +pub trait IntoArguments<'q, DB: HasArguments<'q>>: Sized + Send { + fn into_arguments(self) -> >::Arguments; +} + +// NOTE: required due to lack of lazy normalization +macro_rules! impl_into_arguments_for_arguments { + ($Arguments:path) => { + impl<'q> + crate::arguments::IntoArguments< + 'q, + <$Arguments as crate::arguments::Arguments<'q>>::Database, + > for $Arguments + { + fn into_arguments(self) -> $Arguments { + self + } + } + }; +} + +// TODO: Impl `IntoArguments` for &[&dyn Encode] +// TODO: Impl `IntoArguments` for (impl Encode, ...) x16 diff --git a/sqlx-core/src/database.rs b/sqlx-core/src/database.rs index 9ebe8e5e..ae13ae73 100644 --- a/sqlx-core/src/database.rs +++ b/sqlx-core/src/database.rs @@ -11,7 +11,8 @@ use crate::value::{Value, ValueRef}; /// This trait encapsulates a complete set of traits that implement a driver for a /// specific database (e.g., MySQL, PostgreSQL). pub trait Database: - Sized + 'static + + Sized + Send + Debug + for<'r> HasValueRef<'r, Database = Self> diff --git a/sqlx-core/src/executor.rs b/sqlx-core/src/executor.rs index 5c238a99..58f84681 100644 --- a/sqlx-core/src/executor.rs +++ b/sqlx-core/src/executor.rs @@ -28,7 +28,7 @@ pub trait Executor<'c>: Send + Debug + Sized { type Database: Database; /// Execute the query and return the total number of rows affected. - fn execute<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result> + fn execute<'q: 'c, E: 'c>(self, query: E) -> BoxFuture<'c, Result> where E: Execute<'q, Self::Database>, { @@ -38,7 +38,7 @@ pub trait Executor<'c>: Send + Debug + Sized { } /// Execute multiple queries and return the rows affected from each query, in a stream. - fn execute_many<'q: 'c, E>(self, query: E) -> BoxStream<'c, Result> + fn execute_many<'q: 'c, E: 'c>(self, query: E) -> BoxStream<'c, Result> where E: Execute<'q, Self::Database>, { @@ -53,7 +53,7 @@ pub trait Executor<'c>: Send + Debug + Sized { } /// Execute the query and return the generated results as a stream. - fn fetch<'q: 'c, E>( + fn fetch<'q: 'c, E: 'c>( self, query: E, ) -> BoxStream<'c, Result<::Row, Error>> @@ -72,7 +72,7 @@ pub trait Executor<'c>: Send + Debug + Sized { /// Execute multiple queries and return the generated results as a stream /// from each query, in a stream. - fn fetch_many<'q: 'c, E>( + fn fetch_many<'q: 'c, E: 'c>( self, query: E, ) -> BoxStream<'c, Result::Row>, Error>> @@ -80,7 +80,7 @@ pub trait Executor<'c>: Send + Debug + Sized { E: Execute<'q, Self::Database>; /// Execute the query and return all the generated results, collected into a [`Vec`]. - fn fetch_all<'q: 'c, E>( + fn fetch_all<'q: 'c, E: 'c>( self, query: E, ) -> BoxFuture<'c, Result::Row>, Error>> @@ -91,7 +91,7 @@ pub trait Executor<'c>: Send + Debug + Sized { } /// Execute the query and returns exactly one row. - fn fetch_one<'q: 'c, E>( + fn fetch_one<'q: 'c, E: 'c>( self, query: E, ) -> BoxFuture<'c, Result<::Row, Error>> @@ -107,7 +107,7 @@ pub trait Executor<'c>: Send + Debug + Sized { } /// Execute the query and returns at most one row. - fn fetch_optional<'q: 'c, E>( + fn fetch_optional<'q: 'c, E: 'c>( self, query: E, ) -> BoxFuture<'c, Result::Row>, Error>> @@ -120,7 +120,7 @@ pub trait Executor<'c>: Send + Debug + Sized { /// This is used by compile-time verification in the query macros to /// power their type inference. #[doc(hidden)] - fn describe<'q: 'c, E>( + fn describe<'q: 'c, E: 'c>( self, query: E, ) -> BoxFuture<'c, Result, Error>> @@ -135,7 +135,7 @@ pub trait Executor<'c>: Send + Debug + Sized { /// * [`&str`] /// * [`Query`] /// -pub trait Execute<'q, DB: Database>: Send { +pub trait Execute<'q, DB: Database>: Send + Sized { /// Returns the query string that will be executed. fn query(&self) -> &'q str; diff --git a/sqlx-core/src/lib.rs b/sqlx-core/src/lib.rs index d3238db9..81ea5aa8 100644 --- a/sqlx-core/src/lib.rs +++ b/sqlx-core/src/lib.rs @@ -18,7 +18,12 @@ extern crate bigdecimal_ as bigdecimal; #[macro_use] pub mod error; +#[macro_use] pub mod arguments; + +#[macro_use] +pub mod pool; + pub mod connection; pub mod database; pub mod decode; @@ -29,7 +34,6 @@ mod ext; pub mod from_row; mod io; mod net; -pub mod pool; pub mod query; pub mod query_as; pub mod query_scalar; diff --git a/sqlx-core/src/mysql/connection/executor.rs b/sqlx-core/src/mysql/connection/executor.rs index d5d19613..34ba7db2 100644 --- a/sqlx-core/src/mysql/connection/executor.rs +++ b/sqlx-core/src/mysql/connection/executor.rs @@ -198,7 +198,7 @@ impl MySqlConnection { impl<'c> Executor<'c> for &'c mut MySqlConnection { type Database = MySql; - fn fetch_many<'q: 'c, E>( + fn fetch_many<'q: 'c, E: 'c>( self, mut query: E, ) -> BoxStream<'c, Result, Error>> @@ -218,7 +218,10 @@ impl<'c> Executor<'c> for &'c mut MySqlConnection { }) } - fn fetch_optional<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result, Error>> + fn fetch_optional<'q: 'c, E: 'c>( + self, + query: E, + ) -> BoxFuture<'c, Result, Error>> where E: Execute<'q, Self::Database>, { @@ -236,7 +239,7 @@ impl<'c> Executor<'c> for &'c mut MySqlConnection { } #[doc(hidden)] - fn describe<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result, Error>> + fn describe<'q: 'c, E: 'c>(self, query: E) -> BoxFuture<'c, Result, Error>> where E: Execute<'q, Self::Database>, { diff --git a/sqlx-core/src/mysql/mod.rs b/sqlx-core/src/mysql/mod.rs index 75281d84..c9c6b640 100644 --- a/sqlx-core/src/mysql/mod.rs +++ b/sqlx-core/src/mysql/mod.rs @@ -23,3 +23,7 @@ pub use value::{MySqlValue, MySqlValueFormat, MySqlValueRef}; /// An alias for [`Pool`][crate::pool::Pool], specialized for MySQL. pub type MySqlPool = crate::pool::Pool; + +// NOTE: required due to the lack of lazy normalization +impl_into_arguments_for_arguments!(MySqlArguments); +impl_executor_for_pool_connection!(MySql, MySqlConnection, MySqlRow); diff --git a/sqlx-core/src/pool/executor.rs b/sqlx-core/src/pool/executor.rs new file mode 100644 index 00000000..a7b11940 --- /dev/null +++ b/sqlx-core/src/pool/executor.rs @@ -0,0 +1,104 @@ +use async_stream::try_stream; +use either::Either; +use futures_core::future::BoxFuture; +use futures_core::stream::BoxStream; +use futures_util::TryStreamExt; + +use crate::connection::Connect; +use crate::database::Database; +use crate::describe::Describe; +use crate::error::Error; +use crate::executor::{Execute, Executor}; +use crate::pool::Pool; + +impl<'p, C> Executor<'p> for &'p Pool +where + C: Connect, + for<'c> &'c mut C: Executor<'c, Database = C::Database>, +{ + type Database = C::Database; + + fn fetch_many<'q, E: 'p>( + self, + query: E, + ) -> BoxStream<'p, Result::Row>, Error>> + where + E: Execute<'q, Self::Database>, + { + Box::pin(try_stream! { + let mut conn = self.acquire().await?; + let mut s = conn.fetch_many(query); + + for v in s.try_next().await? { + yield v; + } + }) + } + + fn fetch_optional<'q, E: 'p>( + self, + query: E, + ) -> BoxFuture<'p, Result::Row>, Error>> + where + E: Execute<'q, Self::Database>, + { + Box::pin(async move { self.acquire().await?.fetch_optional(query).await }) + } + + #[doc(hidden)] + fn describe<'q, E: 'p>(self, query: E) -> BoxFuture<'p, Result, Error>> + where + E: Execute<'q, Self::Database>, + { + Box::pin(async move { self.acquire().await?.describe(query).await }) + } +} + +// NOTE: required due to lack of lazy normalization +macro_rules! impl_executor_for_pool_connection { + ($DB:ident, $C:ident, $R:ident) => { + impl<'c> crate::executor::Executor<'c> for &'c mut crate::pool::PoolConnection<$C> { + type Database = $DB; + + #[inline] + fn fetch_many<'q: 'c, E: 'c>( + self, + query: E, + ) -> futures_core::stream::BoxStream< + 'c, + Result, crate::error::Error>, + > + where + E: crate::executor::Execute<'q, $DB>, + { + (&mut **self).fetch_many(query) + } + + #[inline] + fn fetch_optional<'q: 'c, E: 'c>( + self, + query: E, + ) -> futures_core::future::BoxFuture<'c, Result, crate::error::Error>> + where + E: crate::executor::Execute<'q, $DB>, + { + (&mut **self).fetch_optional(query) + } + + #[doc(hidden)] + #[inline] + fn describe<'q: 'c, E: 'c>( + self, + query: E, + ) -> futures_core::future::BoxFuture< + 'c, + Result, crate::error::Error>, + > + where + E: crate::executor::Execute<'q, $DB>, + { + (&mut **self).describe(query) + } + } + }; +} diff --git a/sqlx-core/src/pool/mod.rs b/sqlx-core/src/pool/mod.rs index cb6b10a8..cae16de6 100644 --- a/sqlx-core/src/pool/mod.rs +++ b/sqlx-core/src/pool/mod.rs @@ -13,6 +13,9 @@ use crate::error::Error; use self::inner::SharedPool; use self::options::Options; +#[macro_use] +mod executor; + mod connection; mod inner; mod options; diff --git a/sqlx-core/src/postgres/connection/describe.rs b/sqlx-core/src/postgres/connection/describe.rs index 2ca47181..9b895dcc 100644 --- a/sqlx-core/src/postgres/connection/describe.rs +++ b/sqlx-core/src/postgres/connection/describe.rs @@ -1,4 +1,5 @@ use std::fmt::Write; +use std::mem; use std::sync::Arc; use futures_util::{stream, StreamExt, TryStreamExt}; @@ -12,10 +13,9 @@ use crate::postgres::message::{ParameterDescription, RowDescription}; use crate::postgres::row::PgColumn; use crate::postgres::type_info::{PgCustomType, PgType, PgTypeKind}; use crate::postgres::{PgArguments, PgConnection, PgTypeInfo, Postgres}; -use crate::query_as::query_as; +use crate::query_as::{query_as, query_as_with}; use crate::query_scalar::query_scalar; use futures_core::future::BoxFuture; -use std::mem; impl PgConnection { pub(super) async fn handle_row_description( @@ -242,8 +242,7 @@ ORDER BY attnum ORDER BY col.idx", ); - query_as::<_, (i32, Option)>(&query) - .__bind_all(args) + query_as_with::<_, (i32, Option), _>(&query, args) .fetch(self) .zip(stream::iter(columns.into_iter().enumerate())) .map(|(row, (field_idx, column))| -> Result, Error> { diff --git a/sqlx-core/src/postgres/connection/executor.rs b/sqlx-core/src/postgres/connection/executor.rs index 26816070..7930b054 100644 --- a/sqlx-core/src/postgres/connection/executor.rs +++ b/sqlx-core/src/postgres/connection/executor.rs @@ -242,7 +242,10 @@ SELECT oid FROM pg_catalog.pg_type WHERE typname ILIKE $1 impl<'c> Executor<'c> for &'c mut PgConnection { type Database = Postgres; - fn fetch_many<'q: 'c, E>(self, mut query: E) -> BoxStream<'c, Result, Error>> + fn fetch_many<'q: 'c, E: 'c>( + self, + mut query: E, + ) -> BoxStream<'c, Result, Error>> where E: Execute<'q, Self::Database>, { @@ -259,7 +262,10 @@ impl<'c> Executor<'c> for &'c mut PgConnection { }) } - fn fetch_optional<'q: 'c, E>(self, mut query: E) -> BoxFuture<'c, Result, Error>> + fn fetch_optional<'q: 'c, E: 'c>( + self, + mut query: E, + ) -> BoxFuture<'c, Result, Error>> where E: Execute<'q, Self::Database>, { @@ -281,7 +287,7 @@ impl<'c> Executor<'c> for &'c mut PgConnection { } #[doc(hidden)] - fn describe<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result, Error>> + fn describe<'q: 'c, E: 'c>(self, query: E) -> BoxFuture<'c, Result, Error>> where E: Execute<'q, Self::Database>, { diff --git a/sqlx-core/src/postgres/listener.rs b/sqlx-core/src/postgres/listener.rs index 602111da..42343dd8 100644 --- a/sqlx-core/src/postgres/listener.rs +++ b/sqlx-core/src/postgres/listener.rs @@ -195,14 +195,14 @@ impl PgListener { impl<'c> Executor<'c> for &'c mut PgListener { type Database = Postgres; - fn fetch_many<'q: 'c, E>(self, query: E) -> BoxStream<'c, Result, Error>> + fn fetch_many<'q: 'c, E: 'c>(self, query: E) -> BoxStream<'c, Result, Error>> where E: Execute<'q, Self::Database>, { self.connection().fetch_many(query) } - fn fetch_optional<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result, Error>> + fn fetch_optional<'q: 'c, E: 'c>(self, query: E) -> BoxFuture<'c, Result, Error>> where E: Execute<'q, Self::Database>, { @@ -210,7 +210,10 @@ impl<'c> Executor<'c> for &'c mut PgListener { } #[doc(hidden)] - fn describe<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result, Error>> + fn describe<'q: 'c, E: 'c>( + self, + query: E, + ) -> BoxFuture<'c, Result, Error>> where E: Execute<'q, Self::Database>, { diff --git a/sqlx-core/src/postgres/mod.rs b/sqlx-core/src/postgres/mod.rs index 85752fae..4ccde1d0 100644 --- a/sqlx-core/src/postgres/mod.rs +++ b/sqlx-core/src/postgres/mod.rs @@ -26,3 +26,7 @@ pub use value::{PgValue, PgValueFormat, PgValueRef}; /// An alias for [`Pool`][crate::pool::Pool], specialized for Postgres. pub type PgPool = crate::pool::Pool; + +// NOTE: required due to the lack of lazy normalization +impl_into_arguments_for_arguments!(PgArguments); +impl_executor_for_pool_connection!(Postgres, PgConnection, PgRow); diff --git a/sqlx-core/src/query.rs b/sqlx-core/src/query.rs index aebdcb46..d6d540f6 100644 --- a/sqlx-core/src/query.rs +++ b/sqlx-core/src/query.rs @@ -1,5 +1,4 @@ use std::marker::PhantomData; -use std::mem; use async_stream::try_stream; use either::Either; @@ -7,7 +6,7 @@ use futures_core::future::BoxFuture; use futures_core::stream::BoxStream; use futures_util::{future, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; -use crate::arguments::Arguments; +use crate::arguments::{Arguments, IntoArguments}; use crate::database::{Database, HasArguments}; use crate::encode::Encode; use crate::error::Error; @@ -15,10 +14,10 @@ use crate::executor::{Execute, Executor}; /// Raw SQL query with bind parameters. Returned by [`query`][crate::query::query]. #[must_use = "query must be executed to affect database"] -pub struct Query<'q, DB: Database> { - query: &'q str, - pub(crate) arguments: >::Arguments, - database: PhantomData, +pub struct Query<'q, DB: Database, A> { + pub(crate) query: &'q str, + pub(crate) arguments: Option, + pub(crate) database: PhantomData, } /// SQL query that will map its results to owned Rust types. @@ -30,14 +29,15 @@ pub struct Query<'q, DB: Database> { /// [Query::bind] is also omitted; stylistically we recommend placing your `.bind()` calls /// before `.try_map()`. #[must_use = "query must be executed to affect database"] -pub struct Map<'q, DB: Database, F> { - inner: Query<'q, DB>, +pub struct Map<'q, DB: Database, F, A> { + inner: Query<'q, DB, A>, mapper: F, } -impl<'q, DB> Execute<'q, DB> for Query<'q, DB> +impl<'q, DB, A> Execute<'q, DB> for Query<'q, DB, A> where DB: Database, + A: Send + IntoArguments<'q, DB>, { #[inline] fn query(&self) -> &'q str { @@ -46,14 +46,11 @@ where #[inline] fn take_arguments(&mut self) -> Option<>::Arguments> { - Some(mem::take(&mut self.arguments)) + self.arguments.take().map(IntoArguments::into_arguments) } } -impl<'q, DB> Query<'q, DB> -where - DB: Database, -{ +impl<'q, DB: Database> Query<'q, DB, >::Arguments> { /// Bind a value for use with this SQL query. /// /// If the number of times this is called does not match the number of bind parameters that @@ -63,10 +60,19 @@ where /// There is no validation that the value is of the type expected by the query. Most SQL /// flavors will perform type coercion (Postgres will return a database error). pub fn bind>(mut self, value: T) -> Self { - self.arguments.add(value); + if let Some(arguments) = &mut self.arguments { + arguments.add(value); + } + self } +} +impl<'q, DB, A: Send> Query<'q, DB, A> +where + DB: Database, + A: 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. @@ -74,7 +80,7 @@ where /// The [`query_as`](crate::query_as::query_as) method will construct a mapped query using /// a [`FromRow`](crate::row::FromRow) implementation. #[inline] - pub fn map(self, f: F) -> Map<'q, DB, impl Fn(DB::Row) -> Result> + pub fn map(self, f: F) -> Map<'q, DB, impl Fn(DB::Row) -> Result, A> where F: Fn(DB::Row) -> O, { @@ -86,7 +92,7 @@ where /// The [`query_as`](crate::query_as::query_as) method will construct a mapped query using /// a [`FromRow`](crate::row::FromRow) implementation. #[inline] - pub fn try_map(self, f: F) -> Map<'q, DB, F> + pub fn try_map(self, f: F) -> Map<'q, DB, F, A> where F: Fn(DB::Row) -> Result, { @@ -101,6 +107,7 @@ where pub async fn execute<'c, E>(self, executor: E) -> Result where 'q: 'c, + A: 'c, E: Executor<'c, Database = DB>, { executor.execute(self).await @@ -111,6 +118,7 @@ where pub async fn execute_many<'c, E>(self, executor: E) -> BoxStream<'c, Result> where 'q: 'c, + A: 'c, E: Executor<'c, Database = DB>, { executor.execute_many(self) @@ -121,6 +129,7 @@ where pub fn fetch<'c, E>(self, executor: E) -> BoxStream<'c, Result> where 'q: 'c, + A: 'c, E: Executor<'c, Database = DB>, { executor.fetch(self) @@ -135,6 +144,7 @@ where ) -> BoxStream<'c, Result, Error>> where 'q: 'c, + A: 'c, E: Executor<'c, Database = DB>, { executor.fetch_many(self) @@ -145,6 +155,7 @@ where pub async fn fetch_all<'c, E>(self, executor: E) -> Result, Error> where 'q: 'c, + A: 'c, E: Executor<'c, Database = DB>, { executor.fetch_all(self).await @@ -155,6 +166,7 @@ where pub async fn fetch_one<'c, E>(self, executor: E) -> Result where 'q: 'c, + A: 'c, E: Executor<'c, Database = DB>, { executor.fetch_one(self).await @@ -165,32 +177,35 @@ where pub async fn fetch_optional<'c, E>(self, executor: E) -> Result, Error> where 'q: 'c, + A: 'c, E: Executor<'c, Database = DB>, { executor.fetch_optional(self).await } } -impl<'q, DB, F: Send> Execute<'q, DB> for Map<'q, DB, F> +impl<'q, DB, F: Send, A: Send> Execute<'q, DB> for Map<'q, DB, F, A> where DB: Database, + A: IntoArguments<'q, DB>, { #[inline] fn query(&self) -> &'q str { - self.inner.query + self.inner.query() } #[inline] fn take_arguments(&mut self) -> Option<>::Arguments> { - Some(mem::take(&mut self.inner.arguments)) + self.inner.take_arguments() } } -impl<'q, DB, F, O> Map<'q, DB, F> +impl<'q, DB, F, O, A> Map<'q, DB, F, A> where DB: Database, F: Send + Sync + Fn(DB::Row) -> Result, O: Send + Unpin, + A: 'q + Send + IntoArguments<'q, DB>, { // FIXME: This is very close 1:1 with [`Executor::fetch`] // noinspection DuplicatedCode @@ -289,9 +304,9 @@ where } } -/// Construct a raw SQL query that can be chained to bind parameters and executed. +/// Make a SQL query. #[inline] -pub fn query(sql: &str) -> Query +pub fn query<'q, DB>(sql: &'q str) -> Query>::Arguments> where DB: Database, { @@ -301,3 +316,17 @@ where query: sql, } } + +/// Make a SQL query, with the given arguments. +#[inline] +pub fn query_with<'q, DB, A>(sql: &'q str, arguments: A) -> Query<'q, DB, A> +where + DB: Database, + A: IntoArguments<'q, DB>, +{ + Query { + database: PhantomData, + arguments: Some(arguments), + query: sql, + } +} diff --git a/sqlx-core/src/query_as.rs b/sqlx-core/src/query_as.rs index b57773e5..3f96ac36 100644 --- a/sqlx-core/src/query_as.rs +++ b/sqlx-core/src/query_as.rs @@ -5,25 +5,26 @@ use either::Either; use futures_core::stream::BoxStream; use futures_util::{StreamExt, TryStreamExt}; -use crate::arguments::Arguments; +use crate::arguments::IntoArguments; use crate::database::{Database, HasArguments}; use crate::encode::Encode; use crate::error::Error; use crate::executor::{Execute, Executor}; use crate::from_row::FromRow; -use crate::query::{query, Query}; +use crate::query::{query, query_with, Query}; /// Raw SQL query with bind parameters, mapped to a concrete type using [`FromRow`]. /// Returned from [`query_as`]. #[must_use = "query must be executed to affect database"] -pub struct QueryAs<'q, DB: Database, O> { - pub(crate) inner: Query<'q, DB>, - output: PhantomData, +pub struct QueryAs<'q, DB: Database, O, A> { + pub(crate) inner: Query<'q, DB, A>, + pub(crate) output: PhantomData, } -impl<'q, DB, O: Send> Execute<'q, DB> for QueryAs<'q, DB, O> +impl<'q, DB, O: Send, A: Send> Execute<'q, DB> for QueryAs<'q, DB, O, A> where DB: Database, + A: IntoArguments<'q, DB>, { #[inline] fn query(&self) -> &'q str { @@ -36,28 +37,24 @@ where } } -// FIXME: This is very close, nearly 1:1 with `Map` -// noinspection DuplicatedCode -impl<'q, DB, O> QueryAs<'q, DB, O> -where - DB: Database, - O: Send + Unpin + for<'r> FromRow<'r, DB::Row>, -{ +impl<'q, DB: Database, O> QueryAs<'q, DB, O, >::Arguments> { /// Bind a value for use with this SQL query. /// /// See [`Query::bind`](crate::query::Query::bind). - #[inline] pub fn bind>(mut self, value: T) -> Self { - self.inner.arguments.add(value); - self - } - - #[doc(hidden)] - pub fn __bind_all(mut self, arguments: >::Arguments) -> Self { - self.inner.arguments = arguments; + self.inner = self.inner.bind(value); self } +} +// FIXME: This is very close, nearly 1:1 with `Map` +// noinspection DuplicatedCode +impl<'q, DB, O, A> QueryAs<'q, DB, O, A> +where + DB: Database, + A: 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<'c, E>(self, executor: E) -> BoxStream<'c, Result> where @@ -65,6 +62,7 @@ where E: 'c + Executor<'c, Database = DB>, DB: 'c, O: 'c, + A: 'c, { self.fetch_many(executor) .try_filter_map(|step| async move { Ok(step.right()) }) @@ -79,6 +77,7 @@ where E: 'c + Executor<'c, Database = DB>, DB: 'c, O: 'c, + A: 'c, { Box::pin(try_stream! { let mut s = executor.fetch_many(self.inner); @@ -101,6 +100,7 @@ where 'q: 'c, E: 'c + Executor<'c, Database = DB>, DB: 'c, + A: 'c, O: 'c, { self.fetch(executor).try_collect().await @@ -113,6 +113,7 @@ where E: 'c + Executor<'c, Database = DB>, DB: 'c, O: 'c, + A: 'c, { self.fetch_optional(executor) .await @@ -126,6 +127,7 @@ where E: 'c + Executor<'c, Database = DB>, DB: 'c, O: 'c, + A: 'c, { let row = executor.fetch_optional(self.inner).await?; if let Some(row) = row { @@ -136,10 +138,10 @@ where } } -/// Construct a raw SQL query that is mapped to a concrete type +/// Make a SQL query that is mapped to a concrete type /// using [`FromRow`](crate::row::FromRow). #[inline] -pub fn query_as(sql: &str) -> QueryAs +pub fn query_as<'q, DB, O>(sql: &'q str) -> QueryAs<'q, DB, O, >::Arguments> where DB: Database, O: for<'r> FromRow<'r, DB::Row>, @@ -149,3 +151,18 @@ where output: PhantomData, } } + +/// Make a SQL query, with the given arguments, that is mapped to a concrete type +/// using [`FromRow`](crate::row::FromRow). +#[inline] +pub fn query_as_with<'q, DB, O, A>(sql: &'q str, arguments: A) -> QueryAs<'q, DB, O, A> +where + DB: Database, + A: IntoArguments<'q, DB>, + O: for<'r> FromRow<'r, DB::Row>, +{ + QueryAs { + inner: query_with(sql, arguments), + output: PhantomData, + } +} diff --git a/sqlx-core/src/query_scalar.rs b/sqlx-core/src/query_scalar.rs index fa8cab47..00056c93 100644 --- a/sqlx-core/src/query_scalar.rs +++ b/sqlx-core/src/query_scalar.rs @@ -2,24 +2,24 @@ use either::Either; use futures_core::stream::BoxStream; use futures_util::{StreamExt, TryFutureExt, TryStreamExt}; -use crate::arguments::Arguments; +use crate::arguments::IntoArguments; use crate::database::{Database, HasArguments}; use crate::encode::Encode; use crate::error::Error; use crate::executor::{Execute, Executor}; use crate::from_row::FromRow; -use crate::query_as::{query_as, QueryAs}; +use crate::query_as::{query_as, query_as_with, QueryAs}; /// Raw SQL query with bind parameters, mapped to a concrete type using [`FromRow`] on `(O,)`. /// Returned from [`query_scalar`]. #[must_use = "query must be executed to affect database"] -pub struct QueryScalar<'q, DB: Database, O> { - inner: QueryAs<'q, DB, (O,)>, +pub struct QueryScalar<'q, DB: Database, O, A> { + inner: QueryAs<'q, DB, (O,), A>, } -impl<'q, DB, O: Send> Execute<'q, DB> for QueryScalar<'q, DB, O> +impl<'q, DB: Database, O: Send, A: Send> Execute<'q, DB> for QueryScalar<'q, DB, O, A> where - DB: Database, + A: IntoArguments<'q, DB>, { #[inline] fn query(&self) -> &'q str { @@ -32,23 +32,25 @@ where } } -// FIXME: This is very close, nearly 1:1 with `Map` -// noinspection DuplicatedCode -impl<'q, DB, O> QueryScalar<'q, DB, O> -where - DB: Database, - O: Send + Unpin, - (O,): Send + Unpin + for<'r> FromRow<'r, DB::Row>, -{ +impl<'q, DB: Database, O> QueryScalar<'q, DB, O, >::Arguments> { /// Bind a value for use with this SQL query. /// /// See [`Query::bind`](crate::query::Query::bind). - #[inline] pub fn bind>(mut self, value: T) -> Self { - self.inner.inner.arguments.add(value); + self.inner = self.inner.bind(value); self } +} +// FIXME: This is very close, nearly 1:1 with `Map` +// noinspection DuplicatedCode +impl<'q, DB, O, A> QueryScalar<'q, DB, O, A> +where + DB: Database, + O: Send + Unpin, + A: 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<'c, E>(self, executor: E) -> BoxStream<'c, Result> @@ -56,6 +58,7 @@ where 'q: 'c, E: 'c + Executor<'c, Database = DB>, DB: 'c, + A: 'c, O: 'c, { self.inner.fetch(executor).map_ok(|it| it.0).boxed() @@ -70,6 +73,7 @@ where E: 'c + Executor<'c, Database = DB>, DB: 'c, O: 'c, + A: 'c, { self.inner .fetch_many(executor) @@ -85,6 +89,7 @@ where E: 'c + Executor<'c, Database = DB>, DB: 'c, (O,): 'c, + A: 'c, { self.inner .fetch(executor) @@ -101,6 +106,7 @@ where E: 'c + Executor<'c, Database = DB>, DB: 'c, O: 'c, + A: 'c, { self.inner.fetch_one(executor).map_ok(|it| it.0).await } @@ -113,15 +119,18 @@ where E: 'c + Executor<'c, Database = DB>, DB: 'c, O: 'c, + A: 'c, { Ok(self.inner.fetch_optional(executor).await?.map(|it| it.0)) } } -/// Construct a raw SQL query that is mapped to a concrete type -/// using [`FromRow`](crate::row::FromRow) on `(O,)`. +/// Make a SQL query that is mapped to a single concrete type +/// using [`FromRow`](crate::row::FromRow). #[inline] -pub fn query_scalar(sql: &str) -> QueryScalar +pub fn query_scalar<'q, DB, O>( + sql: &'q str, +) -> QueryScalar>::Arguments> where DB: Database, (O,): for<'r> FromRow<'r, DB::Row>, @@ -130,3 +139,17 @@ where inner: query_as(sql), } } + +/// Make a SQL query, with the given arguments, that is mapped to a single concrete type +/// using [`FromRow`](crate::row::FromRow). +#[inline] +pub fn query_scalar_with<'q, DB, O, A>(sql: &'q str, arguments: A) -> QueryScalar +where + DB: Database, + A: IntoArguments<'q, DB>, + (O,): for<'r> FromRow<'r, DB::Row>, +{ + QueryScalar { + inner: query_as_with(sql, arguments), + } +} diff --git a/sqlx-core/src/sqlite/connection/executor.rs b/sqlx-core/src/sqlite/connection/executor.rs index 1b19aceb..290bcfb6 100644 --- a/sqlx-core/src/sqlite/connection/executor.rs +++ b/sqlx-core/src/sqlite/connection/executor.rs @@ -74,7 +74,7 @@ fn emplace_row_metadata( impl<'c> Executor<'c> for &'c mut SqliteConnection { type Database = Sqlite; - fn fetch_many<'q: 'c, E>( + fn fetch_many<'q: 'c, E: 'c>( self, mut query: E, ) -> BoxStream<'c, Result, Error>> @@ -148,7 +148,10 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection { }) } - fn fetch_optional<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result, Error>> + fn fetch_optional<'q: 'c, E: 'c>( + self, + query: E, + ) -> BoxFuture<'c, Result, Error>> where E: Execute<'q, Self::Database>, { @@ -166,7 +169,7 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection { } #[doc(hidden)] - fn describe<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result, Error>> + fn describe<'q: 'c, E: 'c>(self, query: E) -> BoxFuture<'c, Result, Error>> where E: Execute<'q, Self::Database>, { diff --git a/sqlx-core/src/sqlite/mod.rs b/sqlx-core/src/sqlite/mod.rs index d91e3750..5108d90a 100644 --- a/sqlx-core/src/sqlite/mod.rs +++ b/sqlx-core/src/sqlite/mod.rs @@ -27,3 +27,7 @@ pub use value::{SqliteValue, SqliteValueRef}; /// An alias for [`Pool`][crate::pool::Pool], specialized for SQLite. pub type SqlitePool = crate::pool::Pool; + +// NOTE: required due to the lack of lazy normalization +impl_into_arguments_for_arguments!(SqliteArguments<'q>); +impl_executor_for_pool_connection!(Sqlite, SqliteConnection, SqliteRow); diff --git a/sqlx-macros/src/query/args.rs b/sqlx-macros/src/query/args.rs index 468ec315..5129259f 100644 --- a/sqlx-macros/src/query/args.rs +++ b/sqlx-macros/src/query/args.rs @@ -18,7 +18,7 @@ pub fn quote_args( if input.arg_names.is_empty() { return Ok(quote! { - let query_args = <#db_path as sqlx::Database>::Arguments::default(); + let query_args = <#db_path as sqlx::database::HasArguments>::Arguments::default(); }); } @@ -88,7 +88,7 @@ pub fn quote_args( // bind as a local expression, by-ref #(let #arg_name = &$#arg_name;)* - let mut query_args = <#db_path as sqlx::Database>::Arguments::default(); + let mut query_args = <#db_path as sqlx::database::HasArguments>::Arguments::default(); query_args.reserve( #args_count, 0 #(+ sqlx::encode::Encode::<#db_path>::size_hint(#arg_name))* diff --git a/sqlx-macros/src/query/output.rs b/sqlx-macros/src/query/output.rs index 77d3aac7..09c78cd9 100644 --- a/sqlx-macros/src/query/output.rs +++ b/sqlx-macros/src/query/output.rs @@ -119,7 +119,7 @@ pub fn quote_query_as( let sql = &input.src; quote! { - sqlx::query::<#db_path>(#sql).bind_all(#bind_args).try_map(|row: #row_path| { + sqlx::query_with::<#db_path>(#sql, #bind_args).try_map(|row: #row_path| { use sqlx::Row as _; use sqlx::result_ext::ResultExt as _; diff --git a/src/lib.rs b/src/lib.rs index 6a5f9aeb..c2d83d32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,17 @@ #![cfg_attr(docsrs, feature(doc_cfg))] -pub use sqlx_core::arguments; +pub use sqlx_core::arguments::{Arguments, IntoArguments}; pub use sqlx_core::connection::{Connect, Connection}; pub use sqlx_core::database::{self, Database}; -pub use sqlx_core::executor::{self, Execute, Executor}; +pub use sqlx_core::executor::{Execute, Executor}; pub use sqlx_core::from_row::FromRow; pub use sqlx_core::pool::{self, Pool}; -pub use sqlx_core::query::{self, query, Query}; -pub use sqlx_core::query_as::{query_as, QueryAs}; -pub use sqlx_core::query_scalar::{query_scalar, QueryScalar}; -pub use sqlx_core::row::{self, Row}; +pub use sqlx_core::query::{query, query_with}; +pub use sqlx_core::query_as::{query_as, query_as_with}; +pub use sqlx_core::query_scalar::{query_scalar, query_scalar_with}; +pub use sqlx_core::row::{ColumnIndex, Row}; // pub use sqlx_core::transaction::Transaction; -pub use sqlx_core::value; +pub use sqlx_core::value::{Value, ValueRef}; #[doc(hidden)] pub use sqlx_core::describe; @@ -69,11 +69,19 @@ pub mod decode { pub use sqlx_macros::Decode; } +pub mod query { + pub use sqlx_core::query::{query, query_with, Map, Query}; + pub use sqlx_core::query_as::{query_as, query_as_with, QueryAs}; + pub use sqlx_core::query_scalar::{query_scalar, query_scalar_with, QueryScalar}; +} + /// Convenience re-export of common traits. pub mod prelude { pub use super::Connect; pub use super::Connection; pub use super::Executor; pub use super::FromRow; + pub use super::IntoArguments; pub use super::Row; + pub use super::Type; }