Wrap the query in a top-level SqlQuery to facilitate type inference.

This commit is contained in:
Ryan Leckey 2019-08-20 18:06:00 -07:00
parent 154cb51bf6
commit 6a18d0effd
11 changed files with 111 additions and 74 deletions

View file

@ -2,10 +2,7 @@
use failure::Fallible;
use futures::{future, TryStreamExt};
use sqlx::{
pg::{Pg, PgQuery},
Connection, Query,
};
use sqlx::{pg::Pg, Connection};
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
@ -51,10 +48,10 @@ async fn main() -> Fallible<()> {
}
async fn ensure_schema(conn: &mut Connection<Pg>) -> Fallible<()> {
sqlx::query::<PgQuery>("BEGIN").execute(conn).await?;
sqlx::query("BEGIN").execute(conn).await?;
// language=sql
sqlx::query::<PgQuery>(
sqlx::query(
r#"
CREATE TABLE IF NOT EXISTS tasks (
id BIGSERIAL PRIMARY KEY,
@ -67,14 +64,14 @@ CREATE TABLE IF NOT EXISTS tasks (
.execute(conn)
.await?;
sqlx::query::<PgQuery>("COMMIT").execute(conn).await?;
sqlx::query("COMMIT").execute(conn).await?;
Ok(())
}
async fn print_all_tasks(conn: &mut Connection<Pg>) -> Fallible<()> {
// language=sql
sqlx::query::<PgQuery>(
sqlx::query(
r#"
SELECT id, text
FROM tasks
@ -95,7 +92,7 @@ WHERE done_at IS NULL
async fn add_task(conn: &mut Connection<Pg>, text: &str) -> Fallible<()> {
// language=sql
sqlx::query::<PgQuery>(
sqlx::query(
r#"
INSERT INTO tasks ( text )
VALUES ( $1 )
@ -110,7 +107,7 @@ VALUES ( $1 )
async fn mark_task_as_done(conn: &mut Connection<Pg>, id: i64) -> Fallible<()> {
// language=sql
sqlx::query::<PgQuery>(
sqlx::query(
r#"
UPDATE tasks
SET done_at = now()

View file

@ -1,4 +1,14 @@
use crate::{connection::RawConnection, row::Row};
pub(crate) use self::internal::BackendAssocRawQuery;
use crate::{connection::RawConnection, query::RawQuery, row::Row};
mod internal {
pub trait BackendAssocRawQuery<'q, DB>
where
DB: super::Backend,
{
type RawQuery: super::RawQuery<'q, Backend = DB>;
}
}
/// A database backend.
///
@ -6,7 +16,7 @@ use crate::{connection::RawConnection, row::Row};
/// e.g., implementing `ToSql for Uuid` differently for MySQL and Postgres) and
/// to query capabilities within a database backend (e.g., with a specific
/// `Connection` can we `bind` a `i64`?).
pub trait Backend: Sized {
pub trait Backend: Sized + for<'q> BackendAssocRawQuery<'q, Self> {
type RawConnection: RawConnection<Backend = Self>;
type Row: Row<Backend = Self>;
}

View file

@ -1,4 +1,4 @@
use crate::{backend::Backend, executor::Executor, query::Query, row::FromRow};
use crate::{backend::Backend, executor::Executor, query::RawQuery, row::FromRow};
use crossbeam_queue::SegQueue;
use crossbeam_utils::atomic::AtomicCell;
use futures::{
@ -30,21 +30,21 @@ pub trait RawConnection: Send {
fn execute<'c, 'q, Q: 'q>(&'c mut self, query: Q) -> BoxFuture<'c, io::Result<u64>>
where
Q: Query<'q, Backend = Self::Backend>;
Q: RawQuery<'q, Backend = Self::Backend>;
fn fetch<'c, 'q, Q: 'q>(
&'c mut self,
query: Q,
) -> BoxStream<'c, io::Result<<Self::Backend as Backend>::Row>>
where
Q: Query<'q, Backend = Self::Backend>;
Q: RawQuery<'q, Backend = Self::Backend>;
fn fetch_optional<'c, 'q, Q: 'q>(
&'c mut self,
query: Q,
) -> BoxFuture<'c, io::Result<Option<<Self::Backend as Backend>::Row>>>
where
Q: Query<'q, Backend = Self::Backend>;
Q: RawQuery<'q, Backend = Self::Backend>;
}
pub struct Connection<DB>(Arc<SharedConnection<DB>>)
@ -92,7 +92,7 @@ where
fn execute<'c, 'q, Q: 'q + 'c>(&'c self, query: Q) -> BoxFuture<'c, io::Result<u64>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
{
Box::pin(async move {
let mut conn = self.get().await;
@ -102,7 +102,7 @@ where
fn fetch<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result<T>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend> + Send + Unpin,
{
Box::pin(async_stream::try_stream! {
@ -120,7 +120,7 @@ where
query: Q,
) -> BoxFuture<'c, io::Result<Option<T>>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend>,
{
Box::pin(async move {

View file

@ -1,4 +1,4 @@
use crate::{backend::Backend, row::FromRow, Query};
use crate::{backend::Backend, query::RawQuery, row::FromRow};
use futures::{future::BoxFuture, stream::BoxStream};
use std::io;
@ -7,11 +7,11 @@ pub trait Executor: Send {
fn execute<'c, 'q, Q: 'q + 'c>(&'c self, query: Q) -> BoxFuture<'c, io::Result<u64>>
where
Q: Query<'q, Backend = Self::Backend>;
Q: RawQuery<'q, Backend = Self::Backend>;
fn fetch<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result<T>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend> + Send + Unpin;
fn fetch_optional<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>(
@ -19,7 +19,7 @@ pub trait Executor: Send {
query: Q,
) -> BoxFuture<'c, io::Result<Option<T>>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend>;
}
@ -32,14 +32,14 @@ where
#[inline]
fn execute<'c, 'q, Q: 'q + 'c>(&'c self, query: Q) -> BoxFuture<'c, io::Result<u64>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
{
(*self).execute(query)
}
fn fetch<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result<T>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend> + Send + Unpin,
{
(*self).fetch(query)
@ -50,7 +50,7 @@ where
query: Q,
) -> BoxFuture<'c, io::Result<Option<T>>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend>,
{
(*self).fetch_optional(query)

View file

@ -38,7 +38,7 @@ mod query;
pub use self::{
connection::Connection,
pool::Pool,
query::{query, Query},
query::{query, SqlQuery},
};
// TODO: Remove after Mariadb transitions to URIs

View file

@ -1,7 +1,11 @@
use crate::backend::Backend;
use crate::backend::{Backend, BackendAssocRawQuery};
pub struct Pg;
impl<'q> BackendAssocRawQuery<'q, Pg> for Pg {
type RawQuery = super::PgRawQuery<'q>;
}
impl Backend for Pg {
type RawConnection = super::PgRawConnection;
type Row = super::PgRow;

View file

@ -1,8 +1,8 @@
use super::{
protocol::{Authentication, Encode, Message, PasswordMessage, StartupMessage, Terminate},
Pg, PgQuery, PgRow,
Pg, PgRawQuery, PgRow,
};
use crate::{connection::RawConnection, query::Query, row::FromRow};
use crate::{connection::RawConnection, query::RawQuery, row::FromRow};
use bytes::{BufMut, BytesMut};
use futures::{
future::BoxFuture,
@ -169,7 +169,7 @@ impl RawConnection for PgRawConnection {
fn execute<'c, 'q, Q: 'q>(&'c mut self, query: Q) -> BoxFuture<'c, io::Result<u64>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
{
query.finish(self);
@ -178,7 +178,7 @@ impl RawConnection for PgRawConnection {
fn fetch<'c, 'q, Q: 'q>(&'c mut self, query: Q) -> BoxStream<'c, io::Result<PgRow>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
{
query.finish(self);
@ -190,7 +190,7 @@ impl RawConnection for PgRawConnection {
query: Q,
) -> BoxFuture<'c, io::Result<Option<PgRow>>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
{
query.finish(self);

View file

@ -6,4 +6,4 @@ mod query;
mod row;
pub mod types;
pub use self::{backend::Pg, connection::PgRawConnection, query::PgQuery, row::PgRow};
pub use self::{backend::Pg, connection::PgRawConnection, query::PgRawQuery, row::PgRow};

View file

@ -3,7 +3,7 @@ use super::{
Pg, PgRawConnection, PgRow,
};
use crate::{
query::Query,
query::RawQuery,
row::FromRow,
serialize::{IsNull, ToSql},
types::HasSqlType,
@ -16,7 +16,7 @@ use futures::{
};
use std::io;
pub struct PgQuery<'q> {
pub struct PgRawQuery<'q> {
limit: i32,
query: &'q str,
// OIDs of the bind parameters
@ -25,7 +25,7 @@ pub struct PgQuery<'q> {
buf: Vec<u8>,
}
impl<'q> Query<'q> for PgQuery<'q> {
impl<'q> RawQuery<'q> for PgRawQuery<'q> {
type Backend = Pg;
fn new(query: &'q str) -> Self {

View file

@ -1,5 +1,5 @@
use crate::{
backend::Backend, connection::RawConnection, executor::Executor, query::Query, row::FromRow,
backend::Backend, connection::RawConnection, executor::Executor, query::RawQuery, row::FromRow,
Connection,
};
use crossbeam_queue::{ArrayQueue, SegQueue};
@ -136,7 +136,7 @@ where
fn execute<'c, 'q, Q: 'q + 'c>(&'c self, query: Q) -> BoxFuture<'c, io::Result<u64>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
{
Box::pin(async move {
let live = self.0.acquire().await?;
@ -148,7 +148,7 @@ where
fn fetch<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result<T>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend> + Send + Unpin,
{
Box::pin(async_stream::try_stream! {
@ -167,7 +167,7 @@ where
query: Q,
) -> BoxFuture<'c, io::Result<Option<T>>>
where
Q: Query<'q, Backend = Self::Backend>,
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend>,
{
Box::pin(async move {

View file

@ -1,5 +1,5 @@
use crate::{
backend::Backend,
backend::{Backend, BackendAssocRawQuery},
executor::Executor,
pool::Pool,
row::FromRow,
@ -9,65 +9,91 @@ use crate::{
use futures::{future::BoxFuture, stream::BoxStream};
use std::io;
pub trait Query<'q>: Sized + Send + Sync {
pub trait RawQuery<'q>: Sized + Send + Sync {
type Backend: Backend;
fn new(query: &'q str) -> Self;
#[inline]
fn bind<T>(self, value: T) -> Self
where
Self::Backend: HasSqlType<<T as AsSqlType<Self::Backend>>::SqlType>,
T: AsSqlType<Self::Backend>
+ ToSql<<T as AsSqlType<Self::Backend>>::SqlType, Self::Backend>,
{
self.bind_as::<T::SqlType, T>(value)
}
fn bind_as<ST, T>(self, value: T) -> Self
where
Self::Backend: HasSqlType<ST>,
T: ToSql<ST, Self::Backend>;
fn finish(self, conn: &mut <Self::Backend as Backend>::RawConnection);
}
pub struct SqlQuery<'q, E>
where
E: Executor,
{
inner:
<<E as Executor>::Backend as BackendAssocRawQuery<'q, <E as Executor>::Backend>>::RawQuery,
}
impl<'q, E> SqlQuery<'q, E>
where
E: Executor,
{
#[inline]
fn execute<'c, C>(self, executor: &'c C) -> BoxFuture<'c, io::Result<u64>>
where
Self: 'c + 'q,
C: Executor<Backend = Self::Backend>,
{
executor.execute(self)
pub fn new(query: &'q str) -> Self {
Self {
inner: <<E as Executor>::Backend as BackendAssocRawQuery<
'q,
<E as Executor>::Backend,
>>::RawQuery::new(query),
}
}
#[inline]
fn fetch<'c, A: 'c, T: 'c, C>(self, executor: &'c C) -> BoxStream<'c, io::Result<T>>
pub fn bind<T>(self, value: T) -> Self
where
Self: 'c + 'q,
C: Executor<Backend = Self::Backend>,
T: FromRow<A, Self::Backend> + Send + Unpin,
E::Backend: HasSqlType<<T as AsSqlType<E::Backend>>::SqlType>,
T: AsSqlType<E::Backend> + ToSql<<T as AsSqlType<E::Backend>>::SqlType, E::Backend>,
{
executor.fetch(self)
self.bind_as::<T::SqlType, T>(value)
}
#[inline]
fn fetch_optional<'c, A: 'c, T: 'c, C>(
pub fn bind_as<ST, T>(mut self, value: T) -> Self
where
E::Backend: HasSqlType<ST>,
T: ToSql<ST, E::Backend>,
{
self.inner = self.inner.bind_as::<ST, T>(value);
self
}
// TODO: These methods should go on a [Execute] trait (so more execut-able things can be defined)
#[inline]
pub fn execute(self, executor: &'q E) -> BoxFuture<'q, io::Result<u64>> {
executor.execute(self.inner)
}
#[inline]
pub fn fetch<A: 'q, T: 'q>(self, executor: &'q E) -> BoxStream<'q, io::Result<T>>
where
T: FromRow<A, E::Backend> + Send + Unpin,
{
executor.fetch(self.inner)
}
#[inline]
pub fn fetch_optional<A: 'q, T: 'q>(
self,
executor: &'c C,
) -> BoxFuture<'c, io::Result<Option<T>>>
executor: &'q E,
) -> BoxFuture<'q, io::Result<Option<T>>>
where
Self: 'c + 'q,
C: Executor<Backend = Self::Backend>,
T: FromRow<A, Self::Backend>,
T: FromRow<A, E::Backend>,
{
executor.fetch_optional(self)
executor.fetch_optional(self.inner)
}
}
#[inline]
pub fn query<'q, Q>(query: &'q str) -> Q
pub fn query<'q, E>(query: &'q str) -> SqlQuery<'q, E>
where
Q: Query<'q>,
E: Executor,
{
Q::new(query)
SqlQuery::new(query)
}