Integrate FromRow while maintaining type fallback for query!

This commit is contained in:
Ryan Leckey 2019-11-25 23:01:33 -08:00
parent ab32d0a5c4
commit 73ca673bf2
7 changed files with 119 additions and 78 deletions

View File

@ -1,4 +1,3 @@
use futures::TryStreamExt;
use sqlx::{FromRow, Pool, Postgres};
use std::env;
use tide::error::ResultExt;
@ -22,6 +21,8 @@ async fn main() -> anyhow::Result<()> {
app.at("/v1/user/first").get(get_first_user);
app.at("/v1/user/last").get(get_last_user);
app.serve(("localhost", 8080))?;
Ok(())
@ -47,19 +48,28 @@ async fn get_all_users(cx: Context<Pool<Postgres>>) -> EndpointResult {
let mut pool = cx.state();
let users: Vec<(i32, String)> = sqlx::query(r#"SELECT id, name FROM users"#)
.fetch(&mut pool) // -> Stream<Row>
.map_ok(FromRow::from_row)
.try_collect()
.fetch_all(&mut pool)
.await
.server_err()?;
Ok(response::json(users))
}
// TODO: #[derive(FromRow)]
struct UserRow {
id: i32,
}
impl FromRow<Postgres, (i32,)> for UserRow {
fn from_row(row: sqlx::Row<Postgres>) -> Self {
Self { id: row.get(0) }
}
}
async fn get_first_user(cx: Context<Pool<Postgres>>) -> EndpointResult {
let mut pool = cx.state();
let (user_id,) /* : (i32,) */ = sqlx::query!(
let row: UserRow = sqlx::query!(
r#"
SELECT id
FROM users
@ -71,6 +81,24 @@ LIMIT 1
.await
.server_err()?;
Ok(response::json(vec![row.id]))
}
async fn get_last_user(cx: Context<Pool<Postgres>>) -> EndpointResult {
let mut pool = cx.state();
let (user_id,) = sqlx::query!(
r#"
SELECT id
FROM users
ORDER BY created_at ASC
LIMIT 1
"#,
)
.fetch_one(&mut pool)
.await
.server_err()?;
Ok(response::json(vec![user_id]))
}

View File

@ -53,25 +53,25 @@ where
{
type Backend = DB;
fn execute<'c, 'q: 'c, A: 'c>(
fn execute<'c, 'q: 'c, I: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxFuture<'c, Result<u64, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
I: IntoQueryParameters<Self::Backend> + Send,
{
Box::pin(async move { self.live.execute(query, params.into_params()).await })
}
fn fetch<'c, 'q: 'c, T: 'c, A: 'c>(
fn fetch<'c, 'q: 'c, I: 'c, O: 'c, T: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxStream<'c, Result<T, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send + Unpin,
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O> + Send + Unpin,
{
Box::pin(async_stream::try_stream! {
let mut s = self.live.fetch(query, params.into_params());
@ -82,14 +82,14 @@ where
})
}
fn fetch_optional<'c, 'q: 'c, T: 'c, A: 'c>(
fn fetch_optional<'c, 'q: 'c, I: 'c, O: 'c, T: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxFuture<'c, Result<Option<T>, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend>,
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O>,
{
Box::pin(async move {
let row = self

View File

@ -5,52 +5,52 @@ use futures_util::TryStreamExt;
pub trait Executor: Send {
type Backend: Backend;
fn execute<'c, 'q: 'c, A: 'c>(
fn execute<'c, 'q: 'c, I: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxFuture<'c, Result<u64, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send;
I: IntoQueryParameters<Self::Backend> + Send;
fn fetch<'c, 'q: 'c, T: 'c, A: 'c>(
fn fetch<'c, 'q: 'c, I: 'c, O: 'c, T: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxStream<'c, Result<T, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send + Unpin;
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O> + Send + Unpin;
fn fetch_all<'c, 'q: 'c, T: 'c, A: 'c>(
fn fetch_all<'c, 'q: 'c, I: 'c, O: 'c, T: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxFuture<'c, Result<Vec<T>, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send + Unpin,
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O> + Send + Unpin,
{
Box::pin(self.fetch(query, params).try_collect())
}
fn fetch_optional<'c, 'q: 'c, T: 'c, A: 'c>(
fn fetch_optional<'c, 'q: 'c, I: 'c, O: 'c, T: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxFuture<'c, Result<Option<T>, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send;
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O> + Send;
fn fetch_one<'c, 'q: 'c, T: 'c, A: 'c>(
fn fetch_one<'c, 'q: 'c, I: 'c, O: 'c, T: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxFuture<'c, Result<T, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send,
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O> + Send,
{
let fut = self.fetch_optional(query, params);
Box::pin(async move { fut.await?.ok_or(Error::NotFound) })

View File

@ -244,25 +244,25 @@ where
{
type Backend = DB;
fn execute<'c, 'q: 'c, A: 'c>(
fn execute<'c, 'q: 'c, I: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxFuture<'c, Result<u64, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
I: IntoQueryParameters<Self::Backend> + Send,
{
Box::pin(async move { <&Pool<DB> as Executor>::execute(&mut &*self, query, params).await })
}
fn fetch<'c, 'q: 'c, T: 'c, A: 'c>(
fn fetch<'c, 'q: 'c, I: 'c, O: 'c, T: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxStream<'c, Result<T, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send + Unpin,
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O> + Send + Unpin,
{
Box::pin(async_stream::try_stream! {
let mut self_ = &*self;
@ -276,14 +276,14 @@ where
})
}
fn fetch_optional<'c, 'q: 'c, T: 'c, A: 'c>(
fn fetch_optional<'c, 'q: 'c, I: 'c, O: 'c, T: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxFuture<'c, Result<Option<T>, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send,
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O> + Send,
{
Box::pin(async move {
<&Pool<DB> as Executor>::fetch_optional(&mut &*self, query, params).await
@ -297,13 +297,13 @@ where
{
type Backend = DB;
fn execute<'c, 'q: 'c, A: 'c>(
fn execute<'c, 'q: 'c, I: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxFuture<'c, Result<u64, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
I: IntoQueryParameters<Self::Backend> + Send,
{
Box::pin(async move {
let mut live = self.0.acquire().await?;
@ -312,14 +312,14 @@ where
})
}
fn fetch<'c, 'q: 'c, T: 'c, A: 'c>(
fn fetch<'c, 'q: 'c, I: 'c, O: 'c, T: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxStream<'c, Result<T, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send + Unpin,
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O> + Send + Unpin,
{
Box::pin(async_stream::try_stream! {
let mut live = self.0.acquire().await?;
@ -331,14 +331,14 @@ where
})
}
fn fetch_optional<'c, 'q: 'c, T: 'c, A: 'c>(
fn fetch_optional<'c, 'q: 'c, I: 'c, O: 'c, T: 'c>(
&'c mut self,
query: &'q str,
params: A,
params: I,
) -> BoxFuture<'c, Result<Option<T>, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send,
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O> + Send,
{
Box::pin(async move {
Ok(self

View File

@ -6,7 +6,7 @@ use crate::{
use bitflags::_core::marker::PhantomData;
use futures_core::{future::BoxFuture, stream::BoxStream};
pub struct Query<'q, DB, I = <DB as Backend>::QueryParameters, O = Row<DB>>
pub struct Query<'q, DB, I = <DB as Backend>::QueryParameters, O = Row<DB>, T = O>
where
DB: Backend,
{
@ -19,6 +19,9 @@ where
#[doc(hidden)]
pub output: PhantomData<O>,
#[doc(hidden)]
pub target: PhantomData<T>,
#[doc(hidden)]
pub backend: PhantomData<DB>,
}
@ -28,20 +31,7 @@ where
DB: Backend,
DB::QueryParameters: 'q,
I: IntoQueryParameters<DB> + Send,
O: FromRow<DB> + Send + Unpin,
{
pub fn cast<U>(self) -> Query<'q, DB, I, U>
where
U: FromRow<DB> + Send + Unpin,
{
Query {
query: self.query,
input: self.input,
output: PhantomData,
backend: PhantomData,
}
}
#[inline]
pub fn execute<E>(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result<u64>>
where
@ -49,29 +39,37 @@ where
{
executor.execute(self.query, self.input)
}
}
pub fn fetch<E>(self, executor: &'q mut E) -> BoxStream<'q, crate::Result<O>>
impl<'q, DB, I: 'q, O: 'q, T: 'q> Query<'q, DB, I, O, T>
where
DB: Backend,
DB::QueryParameters: 'q,
I: IntoQueryParameters<DB> + Send,
T: FromRow<DB, O> + Send + Unpin,
{
pub fn fetch<E>(self, executor: &'q mut E) -> BoxStream<'q, crate::Result<T>>
where
E: Executor<Backend = DB>,
{
executor.fetch(self.query, self.input)
}
pub fn fetch_all<E>(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result<Vec<O>>>
pub fn fetch_all<E>(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result<Vec<T>>>
where
E: Executor<Backend = DB>,
{
executor.fetch_all(self.query, self.input)
}
pub fn fetch_optional<E>(self, executor: &'q mut E) -> BoxFuture<'q, Result<Option<O>, Error>>
pub fn fetch_optional<E>(self, executor: &'q mut E) -> BoxFuture<'q, Result<Option<T>, Error>>
where
E: Executor<Backend = DB>,
{
executor.fetch_optional(self.query, self.input)
}
pub fn fetch_one<E>(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result<O>>
pub fn fetch_one<E>(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result<T>>
where
E: Executor<Backend = DB>,
{
@ -102,7 +100,7 @@ where
/// Construct a full SQL query using raw SQL.
#[inline]
pub fn query<DB>(query: &str) -> Query<'_, DB, DB::QueryParameters, Row<DB>>
pub fn query<DB, T>(query: &str) -> Query<'_, DB, DB::QueryParameters, Row<DB>, T>
where
DB: Backend,
{
@ -111,5 +109,6 @@ where
input: DB::QueryParameters::new(),
output: PhantomData,
backend: PhantomData,
target: PhantomData,
}
}

View File

@ -33,13 +33,14 @@ where
}
}
pub trait FromRow<DB: Backend> {
pub trait FromRow<DB: Backend, O = Row<DB>> {
fn from_row(row: Row<DB>) -> Self;
}
#[allow(unused)]
macro_rules! impl_from_row {
($B:ident: $( ($idx:tt) -> $T:ident );+;) => {
// Row -> (T1, T2, ...)
impl<$($T,)+> crate::row::FromRow<$B> for ($($T,)+)
where
$($B: crate::types::HasSqlType<$T>,)+
@ -50,6 +51,18 @@ macro_rules! impl_from_row {
($(row.get($idx),)+)
}
}
// (T1, T2, ...) -> (T1, T2, ...)
impl<$($T,)+> crate::row::FromRow<$B, ($($T,)+)> for ($($T,)+)
where
$($B: crate::types::HasSqlType<$T>,)+
$($T: crate::decode::Decode<$B>,)+
{
#[inline]
fn from_row(row: crate::row::Row<$B>) -> Self {
($(row.get($idx),)+)
}
}
};
}

View File

@ -188,10 +188,11 @@ where
Ok(quote! {{
#params
sqlx::Query::<#backend_path, _, (#(#output_types),*,)> {
sqlx::Query::<#backend_path, _, (#(#output_types),*,), _> {
query: #query,
input: params,
output: ::core::marker::PhantomData,
target: ::core::marker::PhantomData,
backend: ::core::marker::PhantomData,
}
}}