query refactors

This commit is contained in:
Austin Bonander 2019-12-04 01:22:17 -08:00
parent acca40c88e
commit 193e79569a
16 changed files with 172 additions and 162 deletions

View file

@ -41,36 +41,33 @@ where
{ {
type Backend = DB; type Backend = DB;
fn execute<'e, 'q: 'e, I: 'e>( fn execute<'e, 'q: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: <DB as Backend>::QueryParameters,
) -> BoxFuture<'e, crate::Result<u64>> ) -> BoxFuture<'e, crate::Result<u64>> {
where
I: IntoQueryParameters<Self::Backend> + Send,
{
self.live.execute(query, params) self.live.execute(query, params)
} }
fn fetch<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: <DB as Backend>::QueryParameters,
) -> BoxStream<'e, crate::Result<T>> ) -> BoxStream<'e, crate::Result<T>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send + Unpin, T: FromRow<Self::Backend> + Send + Unpin,
{ {
self.live.fetch(query, params) self.live.fetch(query, params)
} }
fn fetch_optional<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch_optional<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: <DB as Backend>::QueryParameters,
) -> BoxFuture<'e, crate::Result<Option<T>>> ) -> BoxFuture<'e, crate::Result<Option<T>>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send, T: FromRow<Self::Backend> + Send,
{ {
self.live.fetch_optional(query, params) self.live.fetch_optional(query, params)

View file

@ -1,6 +1,8 @@
//! Types and traits related to serializing values for the database. //! Types and traits related to serializing values for the database.
use crate::{backend::Backend, types::HasSqlType}; use crate::{backend::Backend, types::HasSqlType};
use std::mem;
/// Annotates the result of [Encode] to differentiate between an empty value and a null value. /// Annotates the result of [Encode] to differentiate between an empty value and a null value.
pub enum IsNull { pub enum IsNull {
/// The value was null (and no data was written to the buffer). /// The value was null (and no data was written to the buffer).
@ -26,6 +28,11 @@ pub trait Encode<DB: Backend> {
/// The return value indicates if this value should be represented as `NULL`. /// The return value indicates if this value should be represented as `NULL`.
/// If this is the case, implementations **must not** write anything to `out`. /// If this is the case, implementations **must not** write anything to `out`.
fn encode(&self, buf: &mut Vec<u8>) -> IsNull; fn encode(&self, buf: &mut Vec<u8>) -> IsNull;
/// Calculate the number of bytes this type will use when encoded.
fn size_hint(&self) -> usize {
mem::size_of_val(self)
}
} }
/// [Encode] is implemented for `Option<T>` where `T` implements `Encode`. An `Option<T>` /// [Encode] is implemented for `Option<T>` where `T` implements `Encode`. An `Option<T>`
@ -43,6 +50,10 @@ where
IsNull::Yes IsNull::Yes
} }
} }
fn size_hint(&self) -> usize {
if self.is_some() { mem::size_of::<T>() } else { 0 }
}
} }
impl<T: ?Sized, DB> Encode<DB> for &'_ T impl<T: ?Sized, DB> Encode<DB> for &'_ T
@ -54,4 +65,8 @@ where
fn encode(&self, buf: &mut Vec<u8>) -> IsNull { fn encode(&self, buf: &mut Vec<u8>) -> IsNull {
(*self).encode(buf) (*self).encode(buf)
} }
fn size_hint(&self) -> usize {
(*self).size_hint()
}
} }

View file

@ -16,57 +16,51 @@ pub trait Executor: Send {
Box::pin( Box::pin(
self.execute( self.execute(
"SELECT 1", "SELECT 1",
<Self::Backend as Backend>::QueryParameters::new(), Default::default(),
) )
.map_ok(|_| ()), .map_ok(|_| ()),
) )
} }
fn execute<'e, 'q: 'e, I: 'e>( fn execute<'e, 'q: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: <Self::Backend as Backend>::QueryParameters,
) -> BoxFuture<'e, crate::Result<u64>> ) -> BoxFuture<'e, crate::Result<u64>>;
where
I: IntoQueryParameters<Self::Backend> + Send;
fn fetch<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: <Self::Backend as Backend>::QueryParameters,
) -> BoxStream<'e, crate::Result<T>> ) -> BoxStream<'e, crate::Result<T>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send + Unpin; T: FromRow<Self::Backend> + Send + Unpin;
fn fetch_all<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch_all<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: <Self::Backend as Backend>::QueryParameters,
) -> BoxFuture<'e, crate::Result<Vec<T>>> ) -> BoxFuture<'e, crate::Result<Vec<T>>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send + Unpin, T: FromRow<Self::Backend> + Send + Unpin,
{ {
Box::pin(self.fetch(query, params).try_collect()) Box::pin(self.fetch(query, params).try_collect())
} }
fn fetch_optional<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch_optional<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: <Self::Backend as Backend>::QueryParameters,
) -> BoxFuture<'e, crate::Result<Option<T>>> ) -> BoxFuture<'e, crate::Result<Option<T>>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send; T: FromRow<Self::Backend> + Send;
fn fetch_one<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch_one<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: <Self::Backend as Backend>::QueryParameters,
) -> BoxFuture<'e, crate::Result<T>> ) -> BoxFuture<'e, crate::Result<T>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send, T: FromRow<Self::Backend> + Send,
{ {
let fut = self.fetch_optional(query, params); let fut = self.fetch_optional(query, params);

View file

@ -248,7 +248,7 @@ impl MariaDb {
self.write(ComStmtExecute { self.write(ComStmtExecute {
statement_id, statement_id,
params: &params.params, params: &params.params,
null: &params.null, null: &params.null_bitmap,
flags: StmtExecFlag::NO_CURSOR, flags: StmtExecFlag::NO_CURSOR,
param_types: &params.param_types, param_types: &params.param_types,
}); });

View file

@ -19,14 +19,11 @@ use futures_core::{future::BoxFuture, stream::BoxStream};
impl Executor for MariaDb { impl Executor for MariaDb {
type Backend = Self; type Backend = Self;
fn execute<'e, 'q: 'e, I: 'e>( fn execute<'e, 'q: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: MariaDbQueryParameters,
) -> BoxFuture<'e, crate::Result<u64>> ) -> BoxFuture<'e, crate::Result<u64>> {
where
I: IntoQueryParameters<Self::Backend> + Send,
{
let params = params.into_params(); let params = params.into_params();
Box::pin(async move { Box::pin(async move {
@ -67,13 +64,12 @@ impl Executor for MariaDb {
}) })
} }
fn fetch<'e, 'q: 'e, I: 'e, O: 'e, T: 'e>( fn fetch<'e, 'q: 'e, O: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: MariaDbQueryParameters,
) -> BoxStream<'e, crate::Result<T>> ) -> BoxStream<'e, crate::Result<T>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O> + Send + Unpin, T: FromRow<Self::Backend, O> + Send + Unpin,
{ {
let params = params.into_params(); let params = params.into_params();
@ -108,13 +104,12 @@ impl Executor for MariaDb {
}) })
} }
fn fetch_optional<'e, 'q: 'e, I: 'e, O: 'e, T: 'e>( fn fetch_optional<'e, 'q: 'e, O: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: MariaDbQueryParameters,
) -> BoxFuture<'e, crate::Result<Option<T>>> ) -> BoxFuture<'e, crate::Result<Option<T>>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend, O> + Send, T: FromRow<Self::Backend, O> + Send,
{ {
let params = params.into_params(); let params = params.into_params();

View file

@ -6,21 +6,25 @@ use crate::{
types::HasSqlType, types::HasSqlType,
}; };
#[derive(Default)]
pub struct MariaDbQueryParameters { pub struct MariaDbQueryParameters {
pub(crate) param_types: Vec<MariaDbTypeMetadata>, pub(crate) param_types: Vec<MariaDbTypeMetadata>,
pub(crate) params: Vec<u8>, pub(crate) params: Vec<u8>,
pub(crate) null: Vec<u8>, pub(crate) null_bitmap: Vec<u8>,
} }
impl QueryParameters for MariaDbQueryParameters { impl QueryParameters for MariaDbQueryParameters {
type Backend = MariaDb; type Backend = MariaDb;
fn new() -> Self { fn reserve(&mut self, binds: usize, bytes: usize) {
Self { self.param_types.reserve(binds);
param_types: Vec::with_capacity(4), self.params.reserve(bytes);
params: Vec::with_capacity(32),
null: vec![0], // ensure we have enough bytes in the bitmap to hold at least `binds` extra bits
} // the second `& 7` gives us 0 spare bits when param_types.len() is a multiple of 8
let spare_bits = (8 - (self.param_types.len()) & 7) & 7;
// ensure that if there are no spare bits left, `binds = 1` reserves another byte
self.null_bitmap.reserve( (binds + 7 - spare_bits) / 8);
} }
fn bind<T>(&mut self, value: T) fn bind<T>(&mut self, value: T)
@ -33,9 +37,10 @@ impl QueryParameters for MariaDbQueryParameters {
let index = self.param_types.len(); let index = self.param_types.len();
self.param_types.push(metadata); self.param_types.push(metadata);
self.null_bitmap.resize(index / 8, 0);
if let IsNull::Yes = value.encode(&mut self.params) { if let IsNull::Yes = value.encode(&mut self.params) {
self.null[index / 8] = self.null[index / 8] & (1 << index % 8); self.null_bitmap[index / 8] &= (1 << index % 8);
} }
} }
} }

View file

@ -1,11 +1,9 @@
use crate::{backend::Backend, encode::Encode, types::HasSqlType}; use crate::{backend::Backend, encode::Encode, types::HasSqlType};
pub trait QueryParameters: Send { pub trait QueryParameters: Default + Send {
type Backend: Backend; type Backend: Backend;
fn new() -> Self fn reserve(&mut self, binds: usize, bytes: usize);
where
Self: Sized;
fn bind<T>(&mut self, value: T) fn bind<T>(&mut self, value: T)
where where
@ -29,8 +27,12 @@ macro_rules! impl_into_query_parameters {
$($T: crate::encode::Encode<$B>,)+ $($T: crate::encode::Encode<$B>,)+
{ {
fn into_params(self) -> <$B as crate::backend::Backend>::QueryParameters { fn into_params(self) -> <$B as crate::backend::Backend>::QueryParameters {
let mut params = <<$B as crate::backend::Backend>::QueryParameters let mut params = <$B as crate::backend::Backend>::QueryParameters::default();
as crate::params::QueryParameters>::new();
let binds = 0 $(+ { $idx; 1 } )+;
let bytes = 0 $(+ crate::encode::Encode::size_hint(&self.$idx))+;
params.reserve(binds, bytes);
$(crate::params::QueryParameters::bind(&mut params, self.$idx);)+ $(crate::params::QueryParameters::bind(&mut params, self.$idx);)+
@ -57,8 +59,7 @@ macro_rules! impl_into_query_parameters_for_backend {
{ {
#[inline] #[inline]
fn into_params(self) -> <$B as crate::backend::Backend>::QueryParameters { fn into_params(self) -> <$B as crate::backend::Backend>::QueryParameters {
<<$B as crate::backend::Backend>::QueryParameters Default::default()
as crate::params::QueryParameters>::new()
} }
} }

View file

@ -11,24 +11,20 @@ where
{ {
type Backend = DB; type Backend = DB;
fn execute<'e, 'q: 'e, I: 'e>( fn execute<'e, 'q: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: DB::QueryParameters,
) -> BoxFuture<'e, crate::Result<u64>> ) -> BoxFuture<'e, crate::Result<u64>> {
where
I: IntoQueryParameters<Self::Backend> + Send,
{
Box::pin(async move { <&Pool<DB> as Executor>::execute(&mut &*self, query, params).await }) Box::pin(async move { <&Pool<DB> as Executor>::execute(&mut &*self, query, params).await })
} }
fn fetch<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: DB::QueryParameters,
) -> BoxStream<'e, crate::Result<T>> ) -> BoxStream<'e, crate::Result<T>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send + Unpin, T: FromRow<Self::Backend> + Send + Unpin,
{ {
Box::pin(async_stream::try_stream! { Box::pin(async_stream::try_stream! {
@ -41,13 +37,12 @@ where
}) })
} }
fn fetch_optional<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch_optional<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: DB::QueryParameters,
) -> BoxFuture<'e, crate::Result<Option<T>>> ) -> BoxFuture<'e, crate::Result<Option<T>>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send, T: FromRow<Self::Backend> + Send,
{ {
Box::pin(async move { Box::pin(async move {
@ -69,24 +64,20 @@ where
{ {
type Backend = DB; type Backend = DB;
fn execute<'e, 'q: 'e, I: 'e>( fn execute<'e, 'q: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: DB::QueryParameters,
) -> BoxFuture<'e, crate::Result<u64>> ) -> BoxFuture<'e, crate::Result<u64>> {
where
I: IntoQueryParameters<Self::Backend> + Send,
{
Box::pin(async move { self.0.acquire().await?.execute(query, params).await }) Box::pin(async move { self.0.acquire().await?.execute(query, params).await })
} }
fn fetch<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: DB::QueryParameters,
) -> BoxStream<'e, crate::Result<T>> ) -> BoxStream<'e, crate::Result<T>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send + Unpin, T: FromRow<Self::Backend> + Send + Unpin,
{ {
Box::pin(async_stream::try_stream! { Box::pin(async_stream::try_stream! {
@ -99,13 +90,12 @@ where
}) })
} }
fn fetch_optional<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch_optional<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: DB::QueryParameters,
) -> BoxFuture<'e, crate::Result<Option<T>>> ) -> BoxFuture<'e, crate::Result<Option<T>>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send, T: FromRow<Self::Backend> + Send,
{ {
Box::pin(async move { self.0.acquire().await?.fetch_optional(query, params).await }) Box::pin(async move { self.0.acquire().await?.fetch_optional(query, params).await })

View file

@ -8,21 +8,17 @@ use crate::{
url::Url, url::Url,
}; };
use futures_core::{future::BoxFuture, stream::BoxStream}; use futures_core::{future::BoxFuture, stream::BoxStream};
use crate::postgres::query::PostgresQueryParameters;
impl Executor for Postgres { impl Executor for Postgres {
type Backend = Self; type Backend = Self;
fn execute<'e, 'q: 'e, I: 'e>( fn execute<'e, 'q: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: PostgresQueryParameters,
) -> BoxFuture<'e, crate::Result<u64>> ) -> BoxFuture<'e, crate::Result<u64>> {
where
I: IntoQueryParameters<Self::Backend> + Send,
{
Box::pin(async move { Box::pin(async move {
let params = params.into_params();
self.parse("", query, &params); self.parse("", query, &params);
self.bind("", "", &params); self.bind("", "", &params);
self.execute("", 1); self.execute("", 1);
@ -40,17 +36,14 @@ impl Executor for Postgres {
}) })
} }
fn fetch<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: PostgresQueryParameters,
) -> BoxStream<'e, crate::Result<T>> ) -> BoxStream<'e, crate::Result<T>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send + Unpin, T: FromRow<Self::Backend> + Send + Unpin,
{ {
let params = params.into_params();
self.parse("", query, &params); self.parse("", query, &params);
self.bind("", "", &params); self.bind("", "", &params);
self.execute("", 0); self.execute("", 0);
@ -66,18 +59,15 @@ impl Executor for Postgres {
}) })
} }
fn fetch_optional<'e, 'q: 'e, I: 'e, T: 'e>( fn fetch_optional<'e, 'q: 'e, T: 'e>(
&'e mut self, &'e mut self,
query: &'q str, query: &'q str,
params: I, params: PostgresQueryParameters,
) -> BoxFuture<'e, crate::Result<Option<T>>> ) -> BoxFuture<'e, crate::Result<Option<T>>>
where where
I: IntoQueryParameters<Self::Backend> + Send,
T: FromRow<Self::Backend> + Send, T: FromRow<Self::Backend> + Send,
{ {
Box::pin(async move { Box::pin(async move {
let params = params.into_params();
self.parse("", query, &params); self.parse("", query, &params);
self.bind("", "", &params); self.bind("", "", &params);
self.execute("", 2); self.execute("", 2);
@ -104,7 +94,7 @@ impl Executor for Postgres {
query: &'q str, query: &'q str,
) -> BoxFuture<'e, crate::Result<Describe<Self::Backend>>> { ) -> BoxFuture<'e, crate::Result<Describe<Self::Backend>>> {
Box::pin(async move { Box::pin(async move {
self.parse("", query, &QueryParameters::new()); self.parse("", query, &Default::default());
self.describe(""); self.describe("");
self.sync().await?; self.sync().await?;

View file

@ -7,6 +7,7 @@ use crate::{
}; };
use byteorder::{BigEndian, ByteOrder, NetworkEndian}; use byteorder::{BigEndian, ByteOrder, NetworkEndian};
#[derive(Default)]
pub struct PostgresQueryParameters { pub struct PostgresQueryParameters {
// OIDs of the bind parameters // OIDs of the bind parameters
pub(super) types: Vec<u32>, pub(super) types: Vec<u32>,
@ -17,13 +18,9 @@ pub struct PostgresQueryParameters {
impl QueryParameters for PostgresQueryParameters { impl QueryParameters for PostgresQueryParameters {
type Backend = Postgres; type Backend = Postgres;
fn new() -> Self { fn reserve(&mut self, binds: usize, bytes: usize) {
Self { self.types.reserve(binds);
// Estimates for average number of bind parameters were self.buf.reserve(bytes);
// chosen from sampling from internal projects
types: Vec::with_capacity(4),
buf: Vec::with_capacity(32),
}
} }
fn bind<T>(&mut self, value: T) fn bind<T>(&mut self, value: T)

View file

@ -31,6 +31,10 @@ impl Encode<Postgres> for Vec<u8> {
fn encode(&self, buf: &mut Vec<u8>) -> IsNull { fn encode(&self, buf: &mut Vec<u8>) -> IsNull {
<[u8] as Encode<Postgres>>::encode(self, buf) <[u8] as Encode<Postgres>>::encode(self, buf)
} }
fn size_hint(&self) -> usize {
self.len()
}
} }
impl Decode<Postgres> for Vec<u8> { impl Decode<Postgres> for Vec<u8> {

View file

@ -38,6 +38,10 @@ impl Encode<Postgres> for String {
fn encode(&self, buf: &mut Vec<u8>) -> IsNull { fn encode(&self, buf: &mut Vec<u8>) -> IsNull {
<str as Encode<Postgres>>::encode(self.as_str(), buf) <str as Encode<Postgres>>::encode(self.as_str(), buf)
} }
fn size_hint(&self) -> usize {
self.len()
}
} }
impl Decode<Postgres> for String { impl Decode<Postgres> for String {

View file

@ -1,84 +1,68 @@
use crate::{ use crate::{backend::Backend, encode::Encode, error::Error, executor::Executor, params::{IntoQueryParameters, QueryParameters}, row::FromRow, types::HasSqlType, Row, Decode};
backend::Backend,
encode::Encode,
error::Error,
executor::Executor,
params::{IntoQueryParameters, QueryParameters},
row::FromRow,
types::HasSqlType,
Row,
};
use bitflags::_core::marker::PhantomData; use bitflags::_core::marker::PhantomData;
use futures_core::{future::BoxFuture, stream::BoxStream}; use futures_core::{future::BoxFuture, stream::BoxStream};
pub struct Query<'q, DB, I = <DB as Backend>::QueryParameters, O = <DB as Backend>::Row> pub struct Query<'q, DB, P = <DB as Backend>::QueryParameters, R = <DB as Backend>::Row>
where where
DB: Backend, DB: Backend,
{ {
#[doc(hidden)] query: &'q str,
pub query: &'q str, params: P,
record: PhantomData<R>,
#[doc(hidden)] backend: PhantomData<DB>,
pub input: I,
#[doc(hidden)]
pub output: PhantomData<O>,
#[doc(hidden)]
pub backend: PhantomData<DB>,
} }
impl<'q, DB, I: 'q, O: 'q> Query<'q, DB, I, O> impl<'q, DB, P: 'q, R: 'q> Query<'q, DB, P, R>
where where
DB: Backend, DB: Backend,
DB::QueryParameters: 'q, DB::QueryParameters: 'q,
I: IntoQueryParameters<DB> + Send, P: IntoQueryParameters<DB> + Send,
O: FromRow<DB> + Send + Unpin, R: FromRow<DB> + Send + Unpin,
{ {
#[inline] #[inline]
pub fn execute<E>(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result<u64>> pub fn execute<E>(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result<u64>>
where where
E: Executor<Backend = DB>, E: Executor<Backend = DB>,
{ {
executor.execute(self.query, self.input) executor.execute(self.query, self.params.into_params())
} }
pub fn fetch<E>(self, executor: &'q mut E) -> BoxStream<'q, crate::Result<O>> pub fn fetch<E>(self, executor: &'q mut E) -> BoxStream<'q, crate::Result<R>>
where where
E: Executor<Backend = DB>, E: Executor<Backend = DB>,
{ {
executor.fetch(self.query, self.input) executor.fetch(self.query, self.params.into_params())
} }
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<R>>>
where where
E: Executor<Backend = DB>, E: Executor<Backend = DB>,
{ {
executor.fetch_all(self.query, self.input) executor.fetch_all(self.query, self.params.into_params())
} }
pub fn fetch_optional<E>(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result<Option<O>>> pub fn fetch_optional<E>(self, executor: &'q mut E) -> BoxFuture<'q, crate::Result<Option<R>>>
where where
E: Executor<Backend = DB>, E: Executor<Backend = DB>,
{ {
executor.fetch_optional(self.query, self.input) executor.fetch_optional(self.query, self.params.into_params())
} }
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<R>>
where where
E: Executor<Backend = DB>, E: Executor<Backend = DB>,
{ {
executor.fetch_one(self.query, self.input) executor.fetch_one(self.query, self.params.into_params())
} }
} }
impl<DB> Query<'_, DB> impl<'q, DB> Query<'q, DB>
where where
DB: Backend, DB: Backend,
{ {
/// Bind a value for use with this SQL query. /// Bind a value for use with this SQL query.
/// ///
/// # Safety /// # Logic Safety
/// ///
/// This function should be used with care, as SQLx cannot validate /// This function should be used with care, as SQLx cannot validate
/// that the value is of the right type nor can it validate that you have /// that the value is of the right type nor can it validate that you have
@ -88,17 +72,47 @@ where
DB: HasSqlType<T>, DB: HasSqlType<T>,
T: Encode<DB>, T: Encode<DB>,
{ {
self.input.bind(value); self.params.bind(value);
self self
} }
}
impl<'q, DB, I, O> Query<'q, DB, I, O> where DB: Backend { /// Bind all query parameters at once.
pub fn with_output_type<O_>(self) -> Query<'q, DB, I, O_> { ///
/// If any parameters were previously bound with `.bind()` they are discarded.
///
/// # Logic Safety
///
/// This function should be used with care, as SQLx cannot validate
/// that the value is of the right type nor can it validate that you have
/// passed the correct number of parameters.
pub fn bind_all<I>(self, values: I) -> Query<'q, DB, I> where I: IntoQueryParameters<DB> {
Query { Query {
query: self.query, query: self.query,
input: self.input, params: values,
output: PhantomData, record: PhantomData,
backend: PhantomData
}
}
}
//noinspection RsSelfConvention
impl<'q, DB, I, R> Query<'q, DB, I, R> where DB: Backend {
/// Change the expected output type of the query to a single scalar value.
pub fn as_scalar<R_>(self) -> Query<'q, DB, I, R_> where R_: Decode<DB> {
Query {
query: self.query,
params: self.params,
record: PhantomData,
backend: PhantomData,
}
}
/// Change the expected output of the query to a new type implementing `FromRow`.
pub fn as_record<R_>(self) -> Query<'q, DB, I, R_> where R_: FromRow<DB> {
Query {
query: self.query,
params: self.params,
record: PhantomData,
backend: PhantomData, backend: PhantomData,
} }
} }
@ -112,8 +126,8 @@ where
{ {
Query { Query {
query, query,
input: DB::QueryParameters::new(), params: Default::default(),
output: PhantomData, record: PhantomData,
backend: PhantomData, backend: PhantomData,
} }
} }

View file

@ -39,6 +39,13 @@ macro_rules! impl_from_row {
}; };
} }
/// Scalar conversions for rows
impl<T, DB> FromRow<DB> for T where DB: Backend + HasSqlType<T>, T: Decode<DB> {
fn from_row(row: <DB as Backend>::Row) -> Self {
row.get(0)
}
}
#[allow(unused)] #[allow(unused)]
macro_rules! impl_from_row_for_backend { macro_rules! impl_from_row_for_backend {
($B:ident, $row:ident) => { ($B:ident, $row:ident) => {

View file

@ -132,12 +132,9 @@ pub async fn process_sql<DB: BackendExt>(
#params #params
sqlx::Query::<#backend_path, _, (#record_type)> { sqlx::query::<#backend_path>(#query)
query: #query, .bind_all(params)
input: params, .as_record::<#record_type>()
output: ::core::marker::PhantomData,
backend: ::core::marker::PhantomData,
}
}}) }})
} }

View file

@ -6,12 +6,12 @@ async fn postgres_query() -> sqlx::Result<()> {
sqlx::Connection::<sqlx::Postgres>::open(&dotenv::var("DATABASE_URL").unwrap()).await?; sqlx::Connection::<sqlx::Postgres>::open(&dotenv::var("DATABASE_URL").unwrap()).await?;
let uuid: Uuid = "256ba9c8-0048-11ea-b0f0-8f04859d047e".parse().unwrap(); let uuid: Uuid = "256ba9c8-0048-11ea-b0f0-8f04859d047e".parse().unwrap();
let account = sqlx::query!("SELECT * from accounts where id != $1", uuid) let account_id = sqlx::query!("SELECT id from accounts where id != $1", uuid)
.as_scalar::<Uuid>()
.fetch_one(&mut conn) .fetch_one(&mut conn)
.await?; .await?;
println!("account ID: {:?}", account.id); println!("account ID: {:?}", account_id);
println!("account name: {}", account.name);
Ok(()) Ok(())
} }