feat(core): add query_as, FromRow

This commit is contained in:
Ryan Leckey 2021-02-23 02:28:09 -08:00
parent 0708b3a873
commit 1107241170
No known key found for this signature in database
GPG Key ID: F8AA68C235AB08C9
5 changed files with 279 additions and 1 deletions

204
sqlx-core/src/from_row.rs Normal file
View File

@ -0,0 +1,204 @@
use crate::{Row, TypeDecodeOwned};
/// A type that can be built from a row returned by the database.
pub trait FromRow<R: Row>: Sized {
/// Build a value of `Self` from the given [`Row`].
fn from_row(row: &R) -> crate::Result<Self>;
}
macro_rules! impl_from_row_for_tuple {
($( ($idx:tt) -> $T:ident );+;) => {
impl<R: Row, $($T,)+> FromRow<R> for ($($T,)+)
where
$($T: TypeDecodeOwned<R::Database>,)+
{
fn from_row(row: &R) -> crate::Result<Self> {
Ok(($(row.try_get($idx)?,)+))
}
}
};
}
impl_from_row_for_tuple!(
(0) -> T1;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
(5) -> T6;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
(5) -> T6;
(6) -> T7;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
(5) -> T6;
(6) -> T7;
(7) -> T8;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
(5) -> T6;
(6) -> T7;
(7) -> T8;
(8) -> T9;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
(5) -> T6;
(6) -> T7;
(7) -> T8;
(8) -> T9;
(9) -> T10;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
(5) -> T6;
(6) -> T7;
(7) -> T8;
(8) -> T9;
(9) -> T10;
(10) -> T11;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
(5) -> T6;
(6) -> T7;
(7) -> T8;
(8) -> T9;
(9) -> T10;
(10) -> T11;
(11) -> T12;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
(5) -> T6;
(6) -> T7;
(7) -> T8;
(8) -> T9;
(9) -> T10;
(10) -> T11;
(11) -> T12;
(12) -> T13;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
(5) -> T6;
(6) -> T7;
(7) -> T8;
(8) -> T9;
(9) -> T10;
(10) -> T11;
(11) -> T12;
(12) -> T13;
(13) -> T14;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
(5) -> T6;
(6) -> T7;
(7) -> T8;
(8) -> T9;
(9) -> T10;
(10) -> T11;
(11) -> T12;
(12) -> T13;
(13) -> T14;
(14) -> T15;
);
impl_from_row_for_tuple!(
(0) -> T1;
(1) -> T2;
(2) -> T3;
(3) -> T4;
(4) -> T5;
(5) -> T6;
(6) -> T7;
(7) -> T8;
(8) -> T9;
(9) -> T10;
(10) -> T11;
(11) -> T12;
(12) -> T13;
(13) -> T14;
(14) -> T15;
(15) -> T16;
);

View File

@ -32,6 +32,7 @@ mod error;
mod execute;
mod executor;
mod isolation_level;
mod from_row;
mod options;
mod query_result;
pub mod row;
@ -63,13 +64,14 @@ pub use connection::Connection;
pub use database::Database;
pub use decode::Decode;
pub use encode::Encode;
pub use from_row::FromRow;
pub use error::{DatabaseError, Error, Result};
pub use execute::Execute;
pub use executor::Executor;
pub use isolation_level::IsolationLevel;
pub use options::ConnectOptions;
pub use query_result::QueryResult;
pub use r#type::{Type, TypeDecode, TypeEncode};
pub use r#type::{Type, TypeDecode, TypeEncode, TypeDecodeOwned};
pub use row::{ColumnIndex, Row};
#[cfg(feature = "actix")]
pub use runtime::Actix;

View File

@ -74,3 +74,8 @@ impl<Db: Database, T: Type<Db> + Encode<Db>> TypeEncode<Db> for T {
pub trait TypeDecode<'r, Db: Database>: Type<Db> + Decode<'r, Db> {}
impl<'r, T: Type<Db> + Decode<'r, Db>, Db: Database> TypeDecode<'r, Db> for T {}
#[allow(clippy::module_name_repetitions)]
pub trait TypeDecodeOwned<Db: Database>: for<'r> TypeDecode<'r, Db> {}
impl<T, Db: Database> TypeDecodeOwned<Db> for T where T: for<'r> TypeDecode<'r, Db> {}

View File

@ -47,6 +47,7 @@
pub mod blocking;
mod query;
mod query_as;
mod runtime;
#[cfg(feature = "mysql")]
@ -58,6 +59,7 @@ pub mod mysql;
#[cfg(feature = "blocking")]
pub use blocking::Blocking;
pub use query::{query, Query};
pub use query_as::{query_as, QueryAs};
pub use runtime::DefaultRuntime;
#[cfg(feature = "actix")]
pub use sqlx_core::Actix;

65
sqlx/src/query_as.rs Normal file
View File

@ -0,0 +1,65 @@
use std::borrow::Cow;
use std::marker::PhantomData;
use sqlx_core::{Execute, Executor, FromRow, TypeEncode};
use crate::{query, Arguments, Database, DefaultRuntime, Query, Runtime};
pub struct QueryAs<'q, 'a, O, Db: Database, Rt: Runtime = DefaultRuntime> {
inner: Query<'q, 'a, Db, Rt>,
output: PhantomData<O>,
}
impl<'q, 'a, Db: Database, Rt: Runtime, O: Send + Sync> Execute<'q, 'a, Db>
for QueryAs<'q, 'a, O, Db, Rt>
{
fn sql(&self) -> &str {
self.inner.sql()
}
fn arguments(&self) -> Option<&Arguments<'a, Db>> {
self.inner.arguments()
}
}
impl<'q, 'a, Db: Database, Rt: Runtime, O> QueryAs<'q, 'a, O, Db, Rt> {
pub fn bind<T: 'a + TypeEncode<Db>>(&mut self, value: &'a T) -> &mut Self {
self.inner.bind(value);
self
}
}
#[cfg(feature = "async")]
impl<'q, 'a, O, Db, Rt> QueryAs<'q, 'a, O, Db, Rt>
where
Db: Database,
Rt: crate::Async,
O: Send + Sync + FromRow<Db::Row>,
{
pub async fn fetch_optional<X>(&self, mut executor: X) -> crate::Result<Option<O>>
where
X: Send + Executor<Rt, Database = Db>,
{
executor.fetch_optional(&self.inner).await?.as_ref().map(O::from_row).transpose()
}
pub async fn fetch_one<X>(&self, mut executor: X) -> crate::Result<O>
where
X: Send + Executor<Rt, Database = Db>,
{
O::from_row(&executor.fetch_one(&self.inner).await?)
}
pub async fn fetch_all<X>(&self, mut executor: X) -> crate::Result<Vec<O>>
where
X: Send + Executor<Rt, Database = Db>,
{
executor.fetch_all(self).await?.iter().map(O::from_row).collect()
}
}
pub fn query_as<'q, 'a, O, Db: Database, Rt: Runtime>(
sql: impl Into<Cow<'q, str>>,
) -> QueryAs<'q, 'a, O, Db, Rt> {
QueryAs::<'q, 'a, O, Db, Rt> { inner: query(sql), output: PhantomData }
}