From 73ca673bf2fdf1eefc64300430a5b686857f4788 Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Mon, 25 Nov 2019 23:01:33 -0800 Subject: [PATCH] Integrate FromRow while maintaining type fallback for query! --- examples/tide/src/main.rs | 38 +++++++++++++++++++++++++++----- sqlx-core/src/connection.rs | 22 +++++++++---------- sqlx-core/src/executor.rs | 38 ++++++++++++++++---------------- sqlx-core/src/pool.rs | 44 ++++++++++++++++++------------------- sqlx-core/src/query.rs | 37 +++++++++++++++---------------- sqlx-core/src/row.rs | 15 ++++++++++++- sqlx-macros/src/lib.rs | 3 ++- 7 files changed, 119 insertions(+), 78 deletions(-) diff --git a/examples/tide/src/main.rs b/examples/tide/src/main.rs index b71760ae..fa3684d1 100644 --- a/examples/tide/src/main.rs +++ b/examples/tide/src/main.rs @@ -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>) -> EndpointResult { let mut pool = cx.state(); let users: Vec<(i32, String)> = sqlx::query(r#"SELECT id, name FROM users"#) - .fetch(&mut pool) // -> Stream - .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 for UserRow { + fn from_row(row: sqlx::Row) -> Self { + Self { id: row.get(0) } + } +} + async fn get_first_user(cx: Context>) -> 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>) -> 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])) } diff --git a/sqlx-core/src/connection.rs b/sqlx-core/src/connection.rs index 9fbcf1f7..76afb10c 100644 --- a/sqlx-core/src/connection.rs +++ b/sqlx-core/src/connection.rs @@ -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> where - A: IntoQueryParameters + Send, + I: IntoQueryParameters + 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> where - A: IntoQueryParameters + Send, - T: FromRow + Send + Unpin, + I: IntoQueryParameters + Send, + T: FromRow + 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, Error>> where - A: IntoQueryParameters + Send, - T: FromRow, + I: IntoQueryParameters + Send, + T: FromRow, { Box::pin(async move { let row = self diff --git a/sqlx-core/src/executor.rs b/sqlx-core/src/executor.rs index b07204cb..b6d940c0 100644 --- a/sqlx-core/src/executor.rs +++ b/sqlx-core/src/executor.rs @@ -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> where - A: IntoQueryParameters + Send; + I: IntoQueryParameters + 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> where - A: IntoQueryParameters + Send, - T: FromRow + Send + Unpin; + I: IntoQueryParameters + Send, + T: FromRow + 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, Error>> where - A: IntoQueryParameters + Send, - T: FromRow + Send + Unpin, + I: IntoQueryParameters + Send, + T: FromRow + 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, Error>> where - A: IntoQueryParameters + Send, - T: FromRow + Send; + I: IntoQueryParameters + Send, + T: FromRow + 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> where - A: IntoQueryParameters + Send, - T: FromRow + Send, + I: IntoQueryParameters + Send, + T: FromRow + Send, { let fut = self.fetch_optional(query, params); Box::pin(async move { fut.await?.ok_or(Error::NotFound) }) diff --git a/sqlx-core/src/pool.rs b/sqlx-core/src/pool.rs index 0e004665..97d5440c 100644 --- a/sqlx-core/src/pool.rs +++ b/sqlx-core/src/pool.rs @@ -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> where - A: IntoQueryParameters + Send, + I: IntoQueryParameters + Send, { Box::pin(async move { <&Pool 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> where - A: IntoQueryParameters + Send, - T: FromRow + Send + Unpin, + I: IntoQueryParameters + Send, + T: FromRow + 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, Error>> where - A: IntoQueryParameters + Send, - T: FromRow + Send, + I: IntoQueryParameters + Send, + T: FromRow + Send, { Box::pin(async move { <&Pool 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> where - A: IntoQueryParameters + Send, + I: IntoQueryParameters + 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> where - A: IntoQueryParameters + Send, - T: FromRow + Send + Unpin, + I: IntoQueryParameters + Send, + T: FromRow + 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, Error>> where - A: IntoQueryParameters + Send, - T: FromRow + Send, + I: IntoQueryParameters + Send, + T: FromRow + Send, { Box::pin(async move { Ok(self diff --git a/sqlx-core/src/query.rs b/sqlx-core/src/query.rs index bc19cf2b..dbee99a0 100644 --- a/sqlx-core/src/query.rs +++ b/sqlx-core/src/query.rs @@ -6,7 +6,7 @@ use crate::{ use bitflags::_core::marker::PhantomData; use futures_core::{future::BoxFuture, stream::BoxStream}; -pub struct Query<'q, DB, I = ::QueryParameters, O = Row> +pub struct Query<'q, DB, I = ::QueryParameters, O = Row, T = O> where DB: Backend, { @@ -19,6 +19,9 @@ where #[doc(hidden)] pub output: PhantomData, + #[doc(hidden)] + pub target: PhantomData, + #[doc(hidden)] pub backend: PhantomData, } @@ -28,20 +31,7 @@ where DB: Backend, DB::QueryParameters: 'q, I: IntoQueryParameters + Send, - O: FromRow + Send + Unpin, { - pub fn cast(self) -> Query<'q, DB, I, U> - where - U: FromRow + Send + Unpin, - { - Query { - query: self.query, - input: self.input, - output: PhantomData, - backend: PhantomData, - } - } - #[inline] pub fn execute(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result> where @@ -49,29 +39,37 @@ where { executor.execute(self.query, self.input) } +} - pub fn fetch(self, executor: &'q mut E) -> BoxStream<'q, crate::Result> +impl<'q, DB, I: 'q, O: 'q, T: 'q> Query<'q, DB, I, O, T> +where + DB: Backend, + DB::QueryParameters: 'q, + I: IntoQueryParameters + Send, + T: FromRow + Send + Unpin, +{ + pub fn fetch(self, executor: &'q mut E) -> BoxStream<'q, crate::Result> where E: Executor, { executor.fetch(self.query, self.input) } - pub fn fetch_all(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result>> + pub fn fetch_all(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result>> where E: Executor, { executor.fetch_all(self.query, self.input) } - pub fn fetch_optional(self, executor: &'q mut E) -> BoxFuture<'q, Result, Error>> + pub fn fetch_optional(self, executor: &'q mut E) -> BoxFuture<'q, Result, Error>> where E: Executor, { executor.fetch_optional(self.query, self.input) } - pub fn fetch_one(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result> + pub fn fetch_one(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result> where E: Executor, { @@ -102,7 +100,7 @@ where /// Construct a full SQL query using raw SQL. #[inline] -pub fn query(query: &str) -> Query<'_, DB, DB::QueryParameters, Row> +pub fn query(query: &str) -> Query<'_, DB, DB::QueryParameters, Row, T> where DB: Backend, { @@ -111,5 +109,6 @@ where input: DB::QueryParameters::new(), output: PhantomData, backend: PhantomData, + target: PhantomData, } } diff --git a/sqlx-core/src/row.rs b/sqlx-core/src/row.rs index acd249ae..a6226ff2 100644 --- a/sqlx-core/src/row.rs +++ b/sqlx-core/src/row.rs @@ -33,13 +33,14 @@ where } } -pub trait FromRow { +pub trait FromRow> { fn from_row(row: Row) -> 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),)+) + } + } }; } diff --git a/sqlx-macros/src/lib.rs b/sqlx-macros/src/lib.rs index b8343200..986a9123 100644 --- a/sqlx-macros/src/lib.rs +++ b/sqlx-macros/src/lib.rs @@ -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, } }}