Add sqlx::Row

- Rename the existing trait to an internal RawRow trait
 - Row is used for selecting a dyn and deferring pulling its values out till later
 - FromSqlRow -> FromRow
This commit is contained in:
Ryan Leckey 2019-11-22 18:33:30 +00:00
parent 1b92fe733b
commit b14d3da2ee
13 changed files with 84 additions and 60 deletions

View File

@ -1,4 +1,4 @@
use sqlx::{Pool, Postgres};
use sqlx::{Row, Pool, Postgres};
use std::env;
use tide::error::ResultExt;
use tide::http::StatusCode;
@ -19,6 +19,8 @@ async fn main() -> anyhow::Result<()> {
app.at("/v1/user").get(get_all_users).post(create_user);
app.at("/v1/user/latest").get(get_newest_user);
app.serve(("localhost", 8080))?;
Ok(())
@ -51,6 +53,25 @@ async fn get_all_users(cx: Context<Pool<Postgres>>) -> EndpointResult {
Ok(response::json(users))
}
async fn get_newest_user(cx: Context<Pool<Postgres>>) -> EndpointResult {
let mut pool = cx.state();
// This is an example of getting back a dyn row that we ...
let row: Row<Postgres> = sqlx::query(
r#"
SELECT id
FROM users
ORDER BY created_at DESC
LIMIT 1
"#)
.fetch_one(&mut pool)
.await
.server_err()?;
// ... can later ask its values by index
Ok(response::json(vec![row.get::<i32>(0)]))
}
#[derive(serde::Deserialize)]
struct CreateUserRequest {
name: String,

View File

@ -1,5 +1,5 @@
use crate::describe::Describe;
use crate::{query::QueryParameters, row::Row, types::HasTypeMetadata};
use crate::{query::QueryParameters, row::RawRow, types::HasTypeMetadata};
use async_trait::async_trait;
use futures_core::stream::BoxStream;
@ -17,7 +17,7 @@ pub trait Backend: HasTypeMetadata + Send + Sync + Sized {
type QueryParameters: QueryParameters<Backend = Self>;
/// The concrete `Row` implementation for this backend.
type Row: Row<Backend = Self>;
type Row: RawRow<Backend = Self>;
/// The identifier for tables; in Postgres this is an `oid` while
/// in MariaDB/MySQL this is the qualified name of the table.

View File

@ -1,4 +1,4 @@
use crate::{query::IntoQueryParameters, Backend, Executor, FromSqlRow};
use crate::{query::IntoQueryParameters, Backend, Executor, FromRow};
use futures_core::{future::BoxFuture, stream::BoxStream};
use std::marker::PhantomData;
@ -16,7 +16,7 @@ impl<DB, P, O> CompiledSql<P, O, DB>
where
DB: Backend,
P: IntoQueryParameters<DB> + Send,
O: FromSqlRow<DB> + Send + Unpin,
O: FromRow<DB> + Send + Unpin,
{
#[inline]
pub fn execute<'e, E: 'e>(self, executor: &'e mut E) -> BoxFuture<'e, crate::Result<u64>>

View File

@ -5,7 +5,7 @@ use crate::{
executor::Executor,
pool::{Live, SharedPool},
query::IntoQueryParameters,
row::FromSqlRow,
row::FromRow,
};
use futures_core::{future::BoxFuture, stream::BoxStream};
use futures_util::stream::StreamExt;
@ -70,7 +70,7 @@ where
) -> BoxStream<'c, Result<T, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromSqlRow<Self::Backend> + Send + Unpin,
T: FromRow<Self::Backend> + Send + Unpin,
{
Box::pin(async_stream::try_stream! {
let mut s = self.live.fetch(query, params.into_params());
@ -88,7 +88,7 @@ where
) -> BoxFuture<'c, Result<Option<T>, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromSqlRow<Self::Backend>,
T: FromRow<Self::Backend>,
{
Box::pin(async move {
let row = self

View File

@ -1,4 +1,4 @@
use crate::{backend::Backend, error::Error, query::IntoQueryParameters, row::FromSqlRow};
use crate::{backend::Backend, error::Error, query::IntoQueryParameters, row::FromRow};
use futures_core::{future::BoxFuture, stream::BoxStream};
use futures_util::TryStreamExt;
@ -20,7 +20,7 @@ pub trait Executor: Send {
) -> BoxStream<'c, Result<T, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromSqlRow<Self::Backend> + Send + Unpin;
T: FromRow<Self::Backend> + Send + Unpin;
fn fetch_all<'c, 'q: 'c, T: 'c, A: 'c>(
&'c mut self,
@ -29,7 +29,7 @@ pub trait Executor: Send {
) -> BoxFuture<'c, Result<Vec<T>, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromSqlRow<Self::Backend> + Send + Unpin,
T: FromRow<Self::Backend> + Send + Unpin,
{
Box::pin(self.fetch(query, params).try_collect())
}
@ -41,7 +41,7 @@ pub trait Executor: Send {
) -> BoxFuture<'c, Result<Option<T>, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromSqlRow<Self::Backend> + Send;
T: FromRow<Self::Backend> + Send;
fn fetch_one<'c, 'q: 'c, T: 'c, A: 'c>(
&'c mut self,
@ -50,7 +50,7 @@ pub trait Executor: Send {
) -> BoxFuture<'c, Result<T, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromSqlRow<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send,
{
let fut = self.fetch_optional(query, params);
Box::pin(async move { fut.await?.ok_or(Error::NotFound) })

View File

@ -42,7 +42,7 @@ pub use self::{
error::{Error, Result},
executor::Executor,
pool::Pool,
row::{FromSqlRow, Row},
row::{FromRow, Row},
sql::{query, SqlQuery},
types::HasSqlType,
};

View File

@ -84,5 +84,5 @@ impl Backend for MariaDb {
}
}
impl_from_sql_row_tuples_for_backend!(MariaDb);
impl_from_row_for_backend!(MariaDb);
impl_into_query_parameters_for_backend!(MariaDb);

View File

@ -1,18 +1,11 @@
use crate::{
mariadb::{protocol::ResultRow, MariaDb},
row::Row,
};
use crate::mariadb::{protocol::ResultRow, MariaDb};
use crate::row::RawRow;
pub struct MariaDbRow(pub(super) ResultRow);
impl Row for MariaDbRow {
impl RawRow for MariaDbRow {
type Backend = MariaDb;
#[inline]
fn is_empty(&self) -> bool {
self.0.values.is_empty()
}
#[inline]
fn len(&self) -> usize {
self.0.values.len()

View File

@ -1,6 +1,6 @@
use crate::{
backend::Backend, connection::Connection, error::Error, executor::Executor,
query::IntoQueryParameters, row::FromSqlRow,
query::IntoQueryParameters, row::FromRow,
};
use futures_channel::oneshot;
use futures_core::{future::BoxFuture, stream::BoxStream};
@ -258,7 +258,7 @@ where
) -> BoxStream<'c, Result<T, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromSqlRow<Self::Backend> + Send + Unpin,
T: FromRow<Self::Backend> + Send + Unpin,
{
Box::pin(async_stream::try_stream! {
let mut self_ = &*self;
@ -279,7 +279,7 @@ where
) -> BoxFuture<'c, Result<Option<T>, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromSqlRow<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send,
{
Box::pin(async move {
<&Pool<DB> as Executor>::fetch_optional(&mut &*self, query, params).await
@ -315,7 +315,7 @@ where
) -> BoxStream<'c, Result<T, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromSqlRow<Self::Backend> + Send + Unpin,
T: FromRow<Self::Backend> + Send + Unpin,
{
Box::pin(async_stream::try_stream! {
let mut live = self.0.acquire().await?;
@ -334,7 +334,7 @@ where
) -> BoxFuture<'c, Result<Option<T>, Error>>
where
A: IntoQueryParameters<Self::Backend> + Send,
T: FromSqlRow<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send,
{
Box::pin(async move {
let mut live = self.0.acquire().await?;

View File

@ -146,5 +146,5 @@ impl Backend for Postgres {
}
}
impl_from_sql_row_tuples_for_backend!(Postgres);
impl_from_row_for_backend!(Postgres);
impl_into_query_parameters_for_backend!(Postgres);

View File

@ -1,17 +1,12 @@
use super::{protocol::DataRow, Postgres};
use crate::row::Row;
use crate::row::RawRow;
#[derive(Debug)]
pub struct PostgresRow(pub(crate) DataRow);
impl Row for PostgresRow {
impl RawRow for PostgresRow {
type Backend = Postgres;
#[inline]
fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
fn len(&self) -> usize {
self.0.len()

View File

@ -1,58 +1,73 @@
use crate::{backend::Backend, decode::Decode, types::HasSqlType};
pub trait Row: Send {
pub trait RawRow: Send {
type Backend: Backend;
fn is_empty(&self) -> bool;
fn len(&self) -> usize;
fn get_raw(&self, index: usize) -> Option<&[u8]>;
}
#[inline]
fn get<T>(&self, index: usize) -> T
pub struct Row<DB>(pub(crate) DB::Row)
where
DB: Backend;
impl<DB> Row<DB>
where
DB: Backend,
{
pub fn get<T>(&self, index: usize) -> T
where
Self::Backend: HasSqlType<T>,
T: Decode<Self::Backend>,
DB: HasSqlType<T>,
T: Decode<DB>,
{
T::decode(self.get_raw(index))
T::decode(self.0.get_raw(index))
}
}
pub trait FromSqlRow<DB: Backend> {
fn from_row<R: Row<Backend = DB>>(row: R) -> Self;
pub trait FromRow<DB: Backend> {
fn from_row(row: DB::Row) -> Self;
}
impl<T, DB> FromSqlRow<DB> for T
impl<T, DB> FromRow<DB> for T
where
DB: Backend + HasSqlType<T>,
T: Decode<DB>,
{
#[inline]
fn from_row<R: Row<Backend = DB>>(row: R) -> Self {
row.get::<T>(0)
fn from_row(row: DB::Row) -> Self {
T::decode(row.get_raw(0))
}
}
#[allow(unused)]
macro_rules! impl_from_sql_row_tuple {
($B:ident: $( ($idx:tt) -> $T:ident );+;) => {
impl<$($T,)+> crate::row::FromSqlRow<$B> for ($($T,)+)
impl<$($T,)+> crate::row::FromRow<$B> for ($($T,)+)
where
$($B: crate::types::HasSqlType<$T>,)+
$($T: crate::decode::Decode<$B>,)+
{
#[inline]
fn from_row<R: crate::row::Row<Backend = $B>>(row: R) -> Self {
($(row.get($idx),)+)
fn from_row(row: <$B as crate::Backend>::Row) -> Self {
use crate::row::RawRow;
($($T::decode(row.get_raw($idx)),)+)
}
}
};
}
#[allow(unused)]
macro_rules! impl_from_sql_row_tuples_for_backend {
macro_rules! impl_from_row_for_backend {
($B:ident) => {
impl crate::row::FromRow<$B> for crate::row::Row<$B> where $B: crate::Backend {
#[inline]
fn from_row(row: <$B as crate::Backend>::Row) -> Self {
Self(row)
}
}
impl_from_sql_row_tuple!($B:
(0) -> T1;
);

View File

@ -1,6 +1,6 @@
use crate::{
backend::Backend, encode::Encode, error::Error, executor::Executor, query::QueryParameters,
row::FromSqlRow, types::HasSqlType,
row::FromRow, types::HasSqlType,
};
use futures_core::{future::BoxFuture, stream::BoxStream};
@ -43,7 +43,7 @@ where
pub fn fetch<E, T: 'q>(self, executor: &'q mut E) -> BoxStream<'q, Result<T, Error>>
where
E: Executor<Backend = DB>,
T: FromSqlRow<DB> + Send + Unpin,
T: FromRow<DB> + Send + Unpin,
DB::QueryParameters: 'q,
{
executor.fetch(self.query, self.params)
@ -52,7 +52,7 @@ where
pub fn fetch_all<E, T: 'q>(self, executor: &'q mut E) -> BoxFuture<'q, Result<Vec<T>, Error>>
where
E: Executor<Backend = DB>,
T: FromSqlRow<DB> + Send + Unpin,
T: FromRow<DB> + Send + Unpin,
DB::QueryParameters: 'q,
{
executor.fetch_all(self.query, self.params)
@ -64,7 +64,7 @@ where
) -> BoxFuture<'q, Result<Option<T>, Error>>
where
E: Executor<Backend = DB>,
T: FromSqlRow<DB> + Send,
T: FromRow<DB> + Send,
DB::QueryParameters: 'q,
{
executor.fetch_optional(self.query, self.params)
@ -73,7 +73,7 @@ where
pub fn fetch_one<E, T: 'q>(self, executor: &'q mut E) -> BoxFuture<'q, Result<T, Error>>
where
E: Executor<Backend = DB>,
T: FromSqlRow<DB> + Send + Unpin,
T: FromRow<DB> + Send + Unpin,
DB::QueryParameters: 'q,
{
executor.fetch_one(self.query, self.params)