diff --git a/sqlx-core/src/execute.rs b/sqlx-core/src/execute.rs new file mode 100644 index 00000000..2b77778c --- /dev/null +++ b/sqlx-core/src/execute.rs @@ -0,0 +1,103 @@ +use crate::{Arguments, Database}; + +/// A type that may be executed against a SQL executor. +pub trait Execute<'q, 'a, Db: Database>: Send + Sync { + /// Returns the SQL to be executed. + fn sql(&self) -> &str; + + /// Returns the arguments for bind variables in the SQL. + /// + /// A value of `None` for arguments is different from an empty list of + /// arguments. The latter instructs SQLx to prepare the SQL command + /// (with no arguments) and then execute it. The former + /// will result in a simple and unprepared SQL command. + /// + fn arguments(&self) -> Option<&'_ Arguments<'a, Db>> { + None + } + + /// Returns `true` if the SQL statement should be cached for re-use. + fn persistent(&self) -> bool { + true + } +} + +impl<'q, Db: Database> Execute<'q, '_, Db> for &'q str { + fn sql(&self) -> &str { + self + } +} + +impl Execute<'_, '_, Db> for String { + fn sql(&self) -> &str { + self + } +} + +impl<'q, 'a, Db: Database, E: Execute<'q, 'a, Db>> Execute<'q, 'a, Db> for &'_ E { + fn sql(&self) -> &str { + (*self).sql() + } + + fn arguments(&self) -> Option<&'_ Arguments<'a, Db>> { + (*self).arguments() + } +} + +impl<'q, 'a, Db: Database> Execute<'q, 'a, Db> for (&'q str, Arguments<'a, Db>) { + fn sql(&self) -> &str { + self.0 + } + + fn arguments(&self) -> Option<&'_ Arguments<'a, Db>> { + Some(&self.1) + } +} + +impl<'q, 'a, Db: Database> Execute<'q, 'a, Db> for (&'q String, Arguments<'a, Db>) { + fn sql(&self) -> &str { + self.0 + } + + fn arguments(&self) -> Option<&'_ Arguments<'a, Db>> { + Some(&self.1) + } +} +impl<'a, Db: Database> Execute<'_, 'a, Db> for (String, Arguments<'a, Db>) { + fn sql(&self) -> &str { + &self.0 + } + + fn arguments(&self) -> Option<&'_ Arguments<'a, Db>> { + Some(&self.1) + } +} + +impl<'q, 'a, Db: Database> Execute<'q, 'a, Db> for (&'q str, &'a Arguments<'a, Db>) { + fn sql(&self) -> &str { + self.0 + } + + fn arguments(&self) -> Option<&'_ Arguments<'a, Db>> { + Some(&self.1) + } +} + +impl<'q, 'a, Db: Database> Execute<'q, 'a, Db> for (&'q String, &'a Arguments<'a, Db>) { + fn sql(&self) -> &str { + self.0 + } + + fn arguments(&self) -> Option<&'_ Arguments<'a, Db>> { + Some(&self.1) + } +} +impl<'a, Db: Database> Execute<'_, 'a, Db> for (String, &'a Arguments<'a, Db>) { + fn sql(&self) -> &str { + &self.0 + } + + fn arguments(&self) -> Option<&'_ Arguments<'a, Db>> { + Some(&self.1) + } +} diff --git a/sqlx-core/src/executor.rs b/sqlx-core/src/executor.rs index f0de10d6..705a091f 100644 --- a/sqlx-core/src/executor.rs +++ b/sqlx-core/src/executor.rs @@ -1,7 +1,7 @@ #[cfg(feature = "async")] use futures_util::future::{self, BoxFuture, FutureExt, TryFutureExt}; -use crate::{Database, Runtime}; +use crate::{Arguments, Database, Execute, Runtime}; /// Describes a type that can execute SQL queries on a self-provided database connection. /// @@ -18,46 +18,54 @@ pub trait Executor { /// Execute the SQL query and return information about the result, including /// the number of rows affected, if any. #[cfg(feature = "async")] - fn execute<'x, 'e, 'q>( + fn execute<'x, 'e, 'q, 'a, E>( &'e mut self, - sql: &'q str, + query: E, ) -> BoxFuture<'x, crate::Result<::QueryResult>> where Rt: crate::Async, + E: 'x + Execute<'q, 'a, Self::Database>, 'e: 'x, - 'q: 'x; + 'q: 'x, + 'a: 'x; #[cfg(feature = "async")] - fn fetch_all<'x, 'e, 'q>( + fn fetch_all<'x, 'e, 'q, 'a, E>( &'e mut self, - sql: &'q str, + query: E, ) -> BoxFuture<'x, crate::Result::Row>>> where Rt: crate::Async, + E: 'x + Execute<'q, 'a, Self::Database>, 'e: 'x, - 'q: 'x; + 'q: 'x, + 'a: 'x; #[cfg(feature = "async")] - fn fetch_optional<'x, 'e, 'q>( + fn fetch_optional<'x, 'e, 'q, 'a, E>( &'e mut self, - sql: &'q str, + query: E, ) -> BoxFuture<'x, crate::Result::Row>>> where Rt: crate::Async, + E: 'x + Execute<'q, 'a, Self::Database>, 'e: 'x, - 'q: 'x; + 'q: 'x, + 'a: 'x; #[cfg(feature = "async")] - fn fetch_one<'x, 'e, 'q>( + fn fetch_one<'x, 'e, 'q, 'a, E>( &'e mut self, - sql: &'q str, + query: E, ) -> BoxFuture<'x, crate::Result<::Row>> where Rt: crate::Async, + E: 'x + Execute<'q, 'a, Self::Database>, 'e: 'x, 'q: 'x, + 'a: 'x, { - self.fetch_optional(sql) + self.fetch_optional(query) .and_then(|maybe_row| match maybe_row { Some(row) => future::ok(row), None => future::err(crate::Error::RowNotFound), @@ -65,3 +73,52 @@ pub trait Executor { .boxed() } } + +impl> Executor for &'_ mut X { + type Database = X::Database; + + #[cfg(feature = "async")] + fn execute<'x, 'e, 'q, 'a, E>( + &'e mut self, + query: E, + ) -> BoxFuture<'x, crate::Result<::QueryResult>> + where + Rt: crate::Async, + E: 'x + Execute<'q, 'a, Self::Database>, + 'e: 'x, + 'q: 'x, + 'a: 'x, + { + (**self).execute(query) + } + + #[cfg(feature = "async")] + fn fetch_all<'x, 'e, 'q, 'a, E>( + &'e mut self, + query: E, + ) -> BoxFuture<'x, crate::Result::Row>>> + where + Rt: crate::Async, + E: 'x + Execute<'q, 'a, Self::Database>, + 'e: 'x, + 'q: 'x, + 'a: 'x, + { + (**self).fetch_all(query) + } + + #[cfg(feature = "async")] + fn fetch_optional<'x, 'e, 'q, 'a, E>( + &'e mut self, + query: E, + ) -> BoxFuture<'x, crate::Result::Row>>> + where + Rt: crate::Async, + E: 'x + Execute<'q, 'a, Self::Database>, + 'e: 'x, + 'q: 'x, + 'a: 'x, + { + (**self).fetch_optional(query) + } +} diff --git a/sqlx-core/src/lib.rs b/sqlx-core/src/lib.rs index f42e030d..5b6ac9f7 100644 --- a/sqlx-core/src/lib.rs +++ b/sqlx-core/src/lib.rs @@ -28,6 +28,7 @@ pub mod database; pub mod decode; pub mod encode; mod error; +mod execute; mod executor; mod options; mod query_result; @@ -61,6 +62,7 @@ pub use database::Database; pub use decode::Decode; pub use encode::Encode; pub use error::{DatabaseError, Error, Result}; +pub use execute::Execute; pub use executor::Executor; pub use options::ConnectOptions; pub use query_result::QueryResult;