mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-27 19:23:01 +00:00
feat: introduce IntoArguments, query_with, query_as_with, and query_scalar_with
This commit is contained in:
parent
88532ffc28
commit
9d2a0141cb
@ -1,5 +1,4 @@
|
||||
use anyhow::Context;
|
||||
use sqlx::sqlite::SqliteQueryAs;
|
||||
use sqlx::SqlitePool;
|
||||
use std::env;
|
||||
use structopt::StructOpt;
|
||||
@ -48,6 +47,8 @@ async fn main(args: Args) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
async fn add_todo(pool: &SqlitePool, description: String) -> anyhow::Result<i64> {
|
||||
let mut conn = pool.acquire().await?;
|
||||
|
||||
// Insert the TODO, then obtain the ID of this row
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@ -56,11 +57,11 @@ VALUES ( $1 )
|
||||
"#,
|
||||
description
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
let rec: (i64,) = sqlx::query_as("SELECT last_insert_rowid()")
|
||||
.fetch_one(pool)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
Ok(rec.0)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//! Types and traits for passing arguments to SQL queries.
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::database::{Database, HasArguments};
|
||||
use crate::encode::Encode;
|
||||
|
||||
/// A tuple of arguments to be sent to the database.
|
||||
@ -16,3 +16,26 @@ pub trait Arguments<'q>: Send + Sized + Default {
|
||||
where
|
||||
T: 'q + Encode<'q, Self::Database>;
|
||||
}
|
||||
|
||||
pub trait IntoArguments<'q, DB: HasArguments<'q>>: Sized + Send {
|
||||
fn into_arguments(self) -> <DB as HasArguments<'q>>::Arguments;
|
||||
}
|
||||
|
||||
// NOTE: required due to lack of lazy normalization
|
||||
macro_rules! impl_into_arguments_for_arguments {
|
||||
($Arguments:path) => {
|
||||
impl<'q>
|
||||
crate::arguments::IntoArguments<
|
||||
'q,
|
||||
<$Arguments as crate::arguments::Arguments<'q>>::Database,
|
||||
> for $Arguments
|
||||
{
|
||||
fn into_arguments(self) -> $Arguments {
|
||||
self
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Impl `IntoArguments` for &[&dyn Encode]
|
||||
// TODO: Impl `IntoArguments` for (impl Encode, ...) x16
|
||||
|
||||
@ -11,7 +11,8 @@ use crate::value::{Value, ValueRef};
|
||||
/// This trait encapsulates a complete set of traits that implement a driver for a
|
||||
/// specific database (e.g., MySQL, PostgreSQL).
|
||||
pub trait Database:
|
||||
Sized
|
||||
'static
|
||||
+ Sized
|
||||
+ Send
|
||||
+ Debug
|
||||
+ for<'r> HasValueRef<'r, Database = Self>
|
||||
|
||||
@ -28,7 +28,7 @@ pub trait Executor<'c>: Send + Debug + Sized {
|
||||
type Database: Database;
|
||||
|
||||
/// Execute the query and return the total number of rows affected.
|
||||
fn execute<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result<u64, Error>>
|
||||
fn execute<'q: 'c, E: 'c>(self, query: E) -> BoxFuture<'c, Result<u64, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
@ -38,7 +38,7 @@ pub trait Executor<'c>: Send + Debug + Sized {
|
||||
}
|
||||
|
||||
/// Execute multiple queries and return the rows affected from each query, in a stream.
|
||||
fn execute_many<'q: 'c, E>(self, query: E) -> BoxStream<'c, Result<u64, Error>>
|
||||
fn execute_many<'q: 'c, E: 'c>(self, query: E) -> BoxStream<'c, Result<u64, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
@ -53,7 +53,7 @@ pub trait Executor<'c>: Send + Debug + Sized {
|
||||
}
|
||||
|
||||
/// Execute the query and return the generated results as a stream.
|
||||
fn fetch<'q: 'c, E>(
|
||||
fn fetch<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxStream<'c, Result<<Self::Database as Database>::Row, Error>>
|
||||
@ -72,7 +72,7 @@ pub trait Executor<'c>: Send + Debug + Sized {
|
||||
|
||||
/// Execute multiple queries and return the generated results as a stream
|
||||
/// from each query, in a stream.
|
||||
fn fetch_many<'q: 'c, E>(
|
||||
fn fetch_many<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxStream<'c, Result<Either<u64, <Self::Database as Database>::Row>, Error>>
|
||||
@ -80,7 +80,7 @@ pub trait Executor<'c>: Send + Debug + Sized {
|
||||
E: Execute<'q, Self::Database>;
|
||||
|
||||
/// Execute the query and return all the generated results, collected into a [`Vec`].
|
||||
fn fetch_all<'q: 'c, E>(
|
||||
fn fetch_all<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'c, Result<Vec<<Self::Database as Database>::Row>, Error>>
|
||||
@ -91,7 +91,7 @@ pub trait Executor<'c>: Send + Debug + Sized {
|
||||
}
|
||||
|
||||
/// Execute the query and returns exactly one row.
|
||||
fn fetch_one<'q: 'c, E>(
|
||||
fn fetch_one<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'c, Result<<Self::Database as Database>::Row, Error>>
|
||||
@ -107,7 +107,7 @@ pub trait Executor<'c>: Send + Debug + Sized {
|
||||
}
|
||||
|
||||
/// Execute the query and returns at most one row.
|
||||
fn fetch_optional<'q: 'c, E>(
|
||||
fn fetch_optional<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'c, Result<Option<<Self::Database as Database>::Row>, Error>>
|
||||
@ -120,7 +120,7 @@ pub trait Executor<'c>: Send + Debug + Sized {
|
||||
/// This is used by compile-time verification in the query macros to
|
||||
/// power their type inference.
|
||||
#[doc(hidden)]
|
||||
fn describe<'q: 'c, E>(
|
||||
fn describe<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'c, Result<Describe<Self::Database>, Error>>
|
||||
@ -135,7 +135,7 @@ pub trait Executor<'c>: Send + Debug + Sized {
|
||||
/// * [`&str`]
|
||||
/// * [`Query`]
|
||||
///
|
||||
pub trait Execute<'q, DB: Database>: Send {
|
||||
pub trait Execute<'q, DB: Database>: Send + Sized {
|
||||
/// Returns the query string that will be executed.
|
||||
fn query(&self) -> &'q str;
|
||||
|
||||
|
||||
@ -18,7 +18,12 @@ extern crate bigdecimal_ as bigdecimal;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
|
||||
#[macro_use]
|
||||
pub mod arguments;
|
||||
|
||||
#[macro_use]
|
||||
pub mod pool;
|
||||
|
||||
pub mod connection;
|
||||
pub mod database;
|
||||
pub mod decode;
|
||||
@ -29,7 +34,6 @@ mod ext;
|
||||
pub mod from_row;
|
||||
mod io;
|
||||
mod net;
|
||||
pub mod pool;
|
||||
pub mod query;
|
||||
pub mod query_as;
|
||||
pub mod query_scalar;
|
||||
|
||||
@ -198,7 +198,7 @@ impl MySqlConnection {
|
||||
impl<'c> Executor<'c> for &'c mut MySqlConnection {
|
||||
type Database = MySql;
|
||||
|
||||
fn fetch_many<'q: 'c, E>(
|
||||
fn fetch_many<'q: 'c, E: 'c>(
|
||||
self,
|
||||
mut query: E,
|
||||
) -> BoxStream<'c, Result<Either<u64, MySqlRow>, Error>>
|
||||
@ -218,7 +218,10 @@ impl<'c> Executor<'c> for &'c mut MySqlConnection {
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_optional<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result<Option<MySqlRow>, Error>>
|
||||
fn fetch_optional<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'c, Result<Option<MySqlRow>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
@ -236,7 +239,7 @@ impl<'c> Executor<'c> for &'c mut MySqlConnection {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn describe<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result<Describe<MySql>, Error>>
|
||||
fn describe<'q: 'c, E: 'c>(self, query: E) -> BoxFuture<'c, Result<Describe<MySql>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
|
||||
@ -23,3 +23,7 @@ pub use value::{MySqlValue, MySqlValueFormat, MySqlValueRef};
|
||||
|
||||
/// An alias for [`Pool`][crate::pool::Pool], specialized for MySQL.
|
||||
pub type MySqlPool = crate::pool::Pool<MySqlConnection>;
|
||||
|
||||
// NOTE: required due to the lack of lazy normalization
|
||||
impl_into_arguments_for_arguments!(MySqlArguments);
|
||||
impl_executor_for_pool_connection!(MySql, MySqlConnection, MySqlRow);
|
||||
|
||||
104
sqlx-core/src/pool/executor.rs
Normal file
104
sqlx-core/src/pool/executor.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use async_stream::try_stream;
|
||||
use either::Either;
|
||||
use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::TryStreamExt;
|
||||
|
||||
use crate::connection::Connect;
|
||||
use crate::database::Database;
|
||||
use crate::describe::Describe;
|
||||
use crate::error::Error;
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::pool::Pool;
|
||||
|
||||
impl<'p, C> Executor<'p> for &'p Pool<C>
|
||||
where
|
||||
C: Connect,
|
||||
for<'c> &'c mut C: Executor<'c, Database = C::Database>,
|
||||
{
|
||||
type Database = C::Database;
|
||||
|
||||
fn fetch_many<'q, E: 'p>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxStream<'p, Result<Either<u64, <Self::Database as Database>::Row>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
Box::pin(try_stream! {
|
||||
let mut conn = self.acquire().await?;
|
||||
let mut s = conn.fetch_many(query);
|
||||
|
||||
for v in s.try_next().await? {
|
||||
yield v;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_optional<'q, E: 'p>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'p, Result<Option<<Self::Database as Database>::Row>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
Box::pin(async move { self.acquire().await?.fetch_optional(query).await })
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn describe<'q, E: 'p>(self, query: E) -> BoxFuture<'p, Result<Describe<Self::Database>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
Box::pin(async move { self.acquire().await?.describe(query).await })
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: required due to lack of lazy normalization
|
||||
macro_rules! impl_executor_for_pool_connection {
|
||||
($DB:ident, $C:ident, $R:ident) => {
|
||||
impl<'c> crate::executor::Executor<'c> for &'c mut crate::pool::PoolConnection<$C> {
|
||||
type Database = $DB;
|
||||
|
||||
#[inline]
|
||||
fn fetch_many<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> futures_core::stream::BoxStream<
|
||||
'c,
|
||||
Result<either::Either<u64, $R>, crate::error::Error>,
|
||||
>
|
||||
where
|
||||
E: crate::executor::Execute<'q, $DB>,
|
||||
{
|
||||
(&mut **self).fetch_many(query)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fetch_optional<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> futures_core::future::BoxFuture<'c, Result<Option<$R>, crate::error::Error>>
|
||||
where
|
||||
E: crate::executor::Execute<'q, $DB>,
|
||||
{
|
||||
(&mut **self).fetch_optional(query)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn describe<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> futures_core::future::BoxFuture<
|
||||
'c,
|
||||
Result<crate::describe::Describe<$DB>, crate::error::Error>,
|
||||
>
|
||||
where
|
||||
E: crate::executor::Execute<'q, $DB>,
|
||||
{
|
||||
(&mut **self).describe(query)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -13,6 +13,9 @@ use crate::error::Error;
|
||||
use self::inner::SharedPool;
|
||||
use self::options::Options;
|
||||
|
||||
#[macro_use]
|
||||
mod executor;
|
||||
|
||||
mod connection;
|
||||
mod inner;
|
||||
mod options;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use std::fmt::Write;
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures_util::{stream, StreamExt, TryStreamExt};
|
||||
@ -12,10 +13,9 @@ use crate::postgres::message::{ParameterDescription, RowDescription};
|
||||
use crate::postgres::row::PgColumn;
|
||||
use crate::postgres::type_info::{PgCustomType, PgType, PgTypeKind};
|
||||
use crate::postgres::{PgArguments, PgConnection, PgTypeInfo, Postgres};
|
||||
use crate::query_as::query_as;
|
||||
use crate::query_as::{query_as, query_as_with};
|
||||
use crate::query_scalar::query_scalar;
|
||||
use futures_core::future::BoxFuture;
|
||||
use std::mem;
|
||||
|
||||
impl PgConnection {
|
||||
pub(super) async fn handle_row_description(
|
||||
@ -242,8 +242,7 @@ ORDER BY attnum
|
||||
ORDER BY col.idx",
|
||||
);
|
||||
|
||||
query_as::<_, (i32, Option<bool>)>(&query)
|
||||
.__bind_all(args)
|
||||
query_as_with::<_, (i32, Option<bool>), _>(&query, args)
|
||||
.fetch(self)
|
||||
.zip(stream::iter(columns.into_iter().enumerate()))
|
||||
.map(|(row, (field_idx, column))| -> Result<Column<_>, Error> {
|
||||
|
||||
@ -242,7 +242,10 @@ SELECT oid FROM pg_catalog.pg_type WHERE typname ILIKE $1
|
||||
impl<'c> Executor<'c> for &'c mut PgConnection {
|
||||
type Database = Postgres;
|
||||
|
||||
fn fetch_many<'q: 'c, E>(self, mut query: E) -> BoxStream<'c, Result<Either<u64, PgRow>, Error>>
|
||||
fn fetch_many<'q: 'c, E: 'c>(
|
||||
self,
|
||||
mut query: E,
|
||||
) -> BoxStream<'c, Result<Either<u64, PgRow>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
@ -259,7 +262,10 @@ impl<'c> Executor<'c> for &'c mut PgConnection {
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_optional<'q: 'c, E>(self, mut query: E) -> BoxFuture<'c, Result<Option<PgRow>, Error>>
|
||||
fn fetch_optional<'q: 'c, E: 'c>(
|
||||
self,
|
||||
mut query: E,
|
||||
) -> BoxFuture<'c, Result<Option<PgRow>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
@ -281,7 +287,7 @@ impl<'c> Executor<'c> for &'c mut PgConnection {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn describe<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result<Describe<Postgres>, Error>>
|
||||
fn describe<'q: 'c, E: 'c>(self, query: E) -> BoxFuture<'c, Result<Describe<Postgres>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
|
||||
@ -195,14 +195,14 @@ impl PgListener {
|
||||
impl<'c> Executor<'c> for &'c mut PgListener {
|
||||
type Database = Postgres;
|
||||
|
||||
fn fetch_many<'q: 'c, E>(self, query: E) -> BoxStream<'c, Result<Either<u64, PgRow>, Error>>
|
||||
fn fetch_many<'q: 'c, E: 'c>(self, query: E) -> BoxStream<'c, Result<Either<u64, PgRow>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
self.connection().fetch_many(query)
|
||||
}
|
||||
|
||||
fn fetch_optional<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result<Option<PgRow>, Error>>
|
||||
fn fetch_optional<'q: 'c, E: 'c>(self, query: E) -> BoxFuture<'c, Result<Option<PgRow>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
@ -210,7 +210,10 @@ impl<'c> Executor<'c> for &'c mut PgListener {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn describe<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result<Describe<Self::Database>, Error>>
|
||||
fn describe<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'c, Result<Describe<Self::Database>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
|
||||
@ -26,3 +26,7 @@ pub use value::{PgValue, PgValueFormat, PgValueRef};
|
||||
|
||||
/// An alias for [`Pool`][crate::pool::Pool], specialized for Postgres.
|
||||
pub type PgPool = crate::pool::Pool<PgConnection>;
|
||||
|
||||
// NOTE: required due to the lack of lazy normalization
|
||||
impl_into_arguments_for_arguments!(PgArguments);
|
||||
impl_executor_for_pool_connection!(Postgres, PgConnection, PgRow);
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
|
||||
use async_stream::try_stream;
|
||||
use either::Either;
|
||||
@ -7,7 +6,7 @@ use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::{future, FutureExt, StreamExt, TryFutureExt, TryStreamExt};
|
||||
|
||||
use crate::arguments::Arguments;
|
||||
use crate::arguments::{Arguments, IntoArguments};
|
||||
use crate::database::{Database, HasArguments};
|
||||
use crate::encode::Encode;
|
||||
use crate::error::Error;
|
||||
@ -15,10 +14,10 @@ use crate::executor::{Execute, Executor};
|
||||
|
||||
/// Raw SQL query with bind parameters. Returned by [`query`][crate::query::query].
|
||||
#[must_use = "query must be executed to affect database"]
|
||||
pub struct Query<'q, DB: Database> {
|
||||
query: &'q str,
|
||||
pub(crate) arguments: <DB as HasArguments<'q>>::Arguments,
|
||||
database: PhantomData<DB>,
|
||||
pub struct Query<'q, DB: Database, A> {
|
||||
pub(crate) query: &'q str,
|
||||
pub(crate) arguments: Option<A>,
|
||||
pub(crate) database: PhantomData<DB>,
|
||||
}
|
||||
|
||||
/// SQL query that will map its results to owned Rust types.
|
||||
@ -30,14 +29,15 @@ pub struct Query<'q, DB: Database> {
|
||||
/// [Query::bind] is also omitted; stylistically we recommend placing your `.bind()` calls
|
||||
/// before `.try_map()`.
|
||||
#[must_use = "query must be executed to affect database"]
|
||||
pub struct Map<'q, DB: Database, F> {
|
||||
inner: Query<'q, DB>,
|
||||
pub struct Map<'q, DB: Database, F, A> {
|
||||
inner: Query<'q, DB, A>,
|
||||
mapper: F,
|
||||
}
|
||||
|
||||
impl<'q, DB> Execute<'q, DB> for Query<'q, DB>
|
||||
impl<'q, DB, A> Execute<'q, DB> for Query<'q, DB, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: Send + IntoArguments<'q, DB>,
|
||||
{
|
||||
#[inline]
|
||||
fn query(&self) -> &'q str {
|
||||
@ -46,14 +46,11 @@ where
|
||||
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as HasArguments<'q>>::Arguments> {
|
||||
Some(mem::take(&mut self.arguments))
|
||||
self.arguments.take().map(IntoArguments::into_arguments)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB> Query<'q, DB>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
impl<'q, DB: Database> Query<'q, DB, <DB as HasArguments<'q>>::Arguments> {
|
||||
/// Bind a value for use with this SQL query.
|
||||
///
|
||||
/// If the number of times this is called does not match the number of bind parameters that
|
||||
@ -63,10 +60,19 @@ where
|
||||
/// There is no validation that the value is of the type expected by the query. Most SQL
|
||||
/// flavors will perform type coercion (Postgres will return a database error).
|
||||
pub fn bind<T: 'q + Encode<'q, DB>>(mut self, value: T) -> Self {
|
||||
self.arguments.add(value);
|
||||
if let Some(arguments) = &mut self.arguments {
|
||||
arguments.add(value);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB, A: Send> Query<'q, DB, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
{
|
||||
/// Map each row in the result to another type.
|
||||
///
|
||||
/// See [`try_map`](Query::try_map) for a fallible version of this method.
|
||||
@ -74,7 +80,7 @@ where
|
||||
/// The [`query_as`](crate::query_as::query_as) method will construct a mapped query using
|
||||
/// a [`FromRow`](crate::row::FromRow) implementation.
|
||||
#[inline]
|
||||
pub fn map<F, O>(self, f: F) -> Map<'q, DB, impl Fn(DB::Row) -> Result<O, Error>>
|
||||
pub fn map<F, O>(self, f: F) -> Map<'q, DB, impl Fn(DB::Row) -> Result<O, Error>, A>
|
||||
where
|
||||
F: Fn(DB::Row) -> O,
|
||||
{
|
||||
@ -86,7 +92,7 @@ where
|
||||
/// The [`query_as`](crate::query_as::query_as) method will construct a mapped query using
|
||||
/// a [`FromRow`](crate::row::FromRow) implementation.
|
||||
#[inline]
|
||||
pub fn try_map<F, O>(self, f: F) -> Map<'q, DB, F>
|
||||
pub fn try_map<F, O>(self, f: F) -> Map<'q, DB, F, A>
|
||||
where
|
||||
F: Fn(DB::Row) -> Result<O, Error>,
|
||||
{
|
||||
@ -101,6 +107,7 @@ where
|
||||
pub async fn execute<'c, E>(self, executor: E) -> Result<u64, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
A: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.execute(self).await
|
||||
@ -111,6 +118,7 @@ where
|
||||
pub async fn execute_many<'c, E>(self, executor: E) -> BoxStream<'c, Result<u64, Error>>
|
||||
where
|
||||
'q: 'c,
|
||||
A: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.execute_many(self)
|
||||
@ -121,6 +129,7 @@ where
|
||||
pub fn fetch<'c, E>(self, executor: E) -> BoxStream<'c, Result<DB::Row, Error>>
|
||||
where
|
||||
'q: 'c,
|
||||
A: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.fetch(self)
|
||||
@ -135,6 +144,7 @@ where
|
||||
) -> BoxStream<'c, Result<Either<u64, DB::Row>, Error>>
|
||||
where
|
||||
'q: 'c,
|
||||
A: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.fetch_many(self)
|
||||
@ -145,6 +155,7 @@ where
|
||||
pub async fn fetch_all<'c, E>(self, executor: E) -> Result<Vec<DB::Row>, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
A: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.fetch_all(self).await
|
||||
@ -155,6 +166,7 @@ where
|
||||
pub async fn fetch_one<'c, E>(self, executor: E) -> Result<DB::Row, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
A: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.fetch_one(self).await
|
||||
@ -165,32 +177,35 @@ where
|
||||
pub async fn fetch_optional<'c, E>(self, executor: E) -> Result<Option<DB::Row>, Error>
|
||||
where
|
||||
'q: 'c,
|
||||
A: 'c,
|
||||
E: Executor<'c, Database = DB>,
|
||||
{
|
||||
executor.fetch_optional(self).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB, F: Send> Execute<'q, DB> for Map<'q, DB, F>
|
||||
impl<'q, DB, F: Send, A: Send> Execute<'q, DB> for Map<'q, DB, F, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
{
|
||||
#[inline]
|
||||
fn query(&self) -> &'q str {
|
||||
self.inner.query
|
||||
self.inner.query()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn take_arguments(&mut self) -> Option<<DB as HasArguments<'q>>::Arguments> {
|
||||
Some(mem::take(&mut self.inner.arguments))
|
||||
self.inner.take_arguments()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB, F, O> Map<'q, DB, F>
|
||||
impl<'q, DB, F, O, A> Map<'q, DB, F, A>
|
||||
where
|
||||
DB: Database,
|
||||
F: Send + Sync + Fn(DB::Row) -> Result<O, Error>,
|
||||
O: Send + Unpin,
|
||||
A: 'q + Send + IntoArguments<'q, DB>,
|
||||
{
|
||||
// FIXME: This is very close 1:1 with [`Executor::fetch`]
|
||||
// noinspection DuplicatedCode
|
||||
@ -289,9 +304,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a raw SQL query that can be chained to bind parameters and executed.
|
||||
/// Make a SQL query.
|
||||
#[inline]
|
||||
pub fn query<DB>(sql: &str) -> Query<DB>
|
||||
pub fn query<'q, DB>(sql: &'q str) -> Query<DB, <DB as HasArguments<'q>>::Arguments>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
@ -301,3 +316,17 @@ where
|
||||
query: sql,
|
||||
}
|
||||
}
|
||||
|
||||
/// Make a SQL query, with the given arguments.
|
||||
#[inline]
|
||||
pub fn query_with<'q, DB, A>(sql: &'q str, arguments: A) -> Query<'q, DB, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
{
|
||||
Query {
|
||||
database: PhantomData,
|
||||
arguments: Some(arguments),
|
||||
query: sql,
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,25 +5,26 @@ use either::Either;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::{StreamExt, TryStreamExt};
|
||||
|
||||
use crate::arguments::Arguments;
|
||||
use crate::arguments::IntoArguments;
|
||||
use crate::database::{Database, HasArguments};
|
||||
use crate::encode::Encode;
|
||||
use crate::error::Error;
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::from_row::FromRow;
|
||||
use crate::query::{query, Query};
|
||||
use crate::query::{query, query_with, Query};
|
||||
|
||||
/// Raw SQL query with bind parameters, mapped to a concrete type using [`FromRow`].
|
||||
/// Returned from [`query_as`].
|
||||
#[must_use = "query must be executed to affect database"]
|
||||
pub struct QueryAs<'q, DB: Database, O> {
|
||||
pub(crate) inner: Query<'q, DB>,
|
||||
output: PhantomData<O>,
|
||||
pub struct QueryAs<'q, DB: Database, O, A> {
|
||||
pub(crate) inner: Query<'q, DB, A>,
|
||||
pub(crate) output: PhantomData<O>,
|
||||
}
|
||||
|
||||
impl<'q, DB, O: Send> Execute<'q, DB> for QueryAs<'q, DB, O>
|
||||
impl<'q, DB, O: Send, A: Send> Execute<'q, DB> for QueryAs<'q, DB, O, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
{
|
||||
#[inline]
|
||||
fn query(&self) -> &'q str {
|
||||
@ -36,28 +37,24 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This is very close, nearly 1:1 with `Map`
|
||||
// noinspection DuplicatedCode
|
||||
impl<'q, DB, O> QueryAs<'q, DB, O>
|
||||
where
|
||||
DB: Database,
|
||||
O: Send + Unpin + for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
impl<'q, DB: Database, O> QueryAs<'q, DB, O, <DB as HasArguments<'q>>::Arguments> {
|
||||
/// Bind a value for use with this SQL query.
|
||||
///
|
||||
/// See [`Query::bind`](crate::query::Query::bind).
|
||||
#[inline]
|
||||
pub fn bind<T: 'q + Encode<'q, DB>>(mut self, value: T) -> Self {
|
||||
self.inner.arguments.add(value);
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn __bind_all(mut self, arguments: <DB as HasArguments<'q>>::Arguments) -> Self {
|
||||
self.inner.arguments = arguments;
|
||||
self.inner = self.inner.bind(value);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This is very close, nearly 1:1 with `Map`
|
||||
// noinspection DuplicatedCode
|
||||
impl<'q, DB, O, A> QueryAs<'q, DB, O, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
O: Send + Unpin + for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
/// Execute the query and return the generated results as a stream.
|
||||
pub fn fetch<'c, E>(self, executor: E) -> BoxStream<'c, Result<O, Error>>
|
||||
where
|
||||
@ -65,6 +62,7 @@ where
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
A: 'c,
|
||||
{
|
||||
self.fetch_many(executor)
|
||||
.try_filter_map(|step| async move { Ok(step.right()) })
|
||||
@ -79,6 +77,7 @@ where
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
A: 'c,
|
||||
{
|
||||
Box::pin(try_stream! {
|
||||
let mut s = executor.fetch_many(self.inner);
|
||||
@ -101,6 +100,7 @@ where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
A: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
self.fetch(executor).try_collect().await
|
||||
@ -113,6 +113,7 @@ where
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
A: 'c,
|
||||
{
|
||||
self.fetch_optional(executor)
|
||||
.await
|
||||
@ -126,6 +127,7 @@ where
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
A: 'c,
|
||||
{
|
||||
let row = executor.fetch_optional(self.inner).await?;
|
||||
if let Some(row) = row {
|
||||
@ -136,10 +138,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a raw SQL query that is mapped to a concrete type
|
||||
/// Make a SQL query that is mapped to a concrete type
|
||||
/// using [`FromRow`](crate::row::FromRow).
|
||||
#[inline]
|
||||
pub fn query_as<DB, O>(sql: &str) -> QueryAs<DB, O>
|
||||
pub fn query_as<'q, DB, O>(sql: &'q str) -> QueryAs<'q, DB, O, <DB as HasArguments<'q>>::Arguments>
|
||||
where
|
||||
DB: Database,
|
||||
O: for<'r> FromRow<'r, DB::Row>,
|
||||
@ -149,3 +151,18 @@ where
|
||||
output: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Make a SQL query, with the given arguments, that is mapped to a concrete type
|
||||
/// using [`FromRow`](crate::row::FromRow).
|
||||
#[inline]
|
||||
pub fn query_as_with<'q, DB, O, A>(sql: &'q str, arguments: A) -> QueryAs<'q, DB, O, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
O: for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
QueryAs {
|
||||
inner: query_with(sql, arguments),
|
||||
output: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,24 +2,24 @@ use either::Either;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::{StreamExt, TryFutureExt, TryStreamExt};
|
||||
|
||||
use crate::arguments::Arguments;
|
||||
use crate::arguments::IntoArguments;
|
||||
use crate::database::{Database, HasArguments};
|
||||
use crate::encode::Encode;
|
||||
use crate::error::Error;
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::from_row::FromRow;
|
||||
use crate::query_as::{query_as, QueryAs};
|
||||
use crate::query_as::{query_as, query_as_with, QueryAs};
|
||||
|
||||
/// Raw SQL query with bind parameters, mapped to a concrete type using [`FromRow`] on `(O,)`.
|
||||
/// Returned from [`query_scalar`].
|
||||
#[must_use = "query must be executed to affect database"]
|
||||
pub struct QueryScalar<'q, DB: Database, O> {
|
||||
inner: QueryAs<'q, DB, (O,)>,
|
||||
pub struct QueryScalar<'q, DB: Database, O, A> {
|
||||
inner: QueryAs<'q, DB, (O,), A>,
|
||||
}
|
||||
|
||||
impl<'q, DB, O: Send> Execute<'q, DB> for QueryScalar<'q, DB, O>
|
||||
impl<'q, DB: Database, O: Send, A: Send> Execute<'q, DB> for QueryScalar<'q, DB, O, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
{
|
||||
#[inline]
|
||||
fn query(&self) -> &'q str {
|
||||
@ -32,23 +32,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This is very close, nearly 1:1 with `Map`
|
||||
// noinspection DuplicatedCode
|
||||
impl<'q, DB, O> QueryScalar<'q, DB, O>
|
||||
where
|
||||
DB: Database,
|
||||
O: Send + Unpin,
|
||||
(O,): Send + Unpin + for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
impl<'q, DB: Database, O> QueryScalar<'q, DB, O, <DB as HasArguments<'q>>::Arguments> {
|
||||
/// Bind a value for use with this SQL query.
|
||||
///
|
||||
/// See [`Query::bind`](crate::query::Query::bind).
|
||||
#[inline]
|
||||
pub fn bind<T: 'q + Encode<'q, DB>>(mut self, value: T) -> Self {
|
||||
self.inner.inner.arguments.add(value);
|
||||
self.inner = self.inner.bind(value);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This is very close, nearly 1:1 with `Map`
|
||||
// noinspection DuplicatedCode
|
||||
impl<'q, DB, O, A> QueryScalar<'q, DB, O, A>
|
||||
where
|
||||
DB: Database,
|
||||
O: Send + Unpin,
|
||||
A: IntoArguments<'q, DB>,
|
||||
(O,): Send + Unpin + for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
/// Execute the query and return the generated results as a stream.
|
||||
#[inline]
|
||||
pub fn fetch<'c, E>(self, executor: E) -> BoxStream<'c, Result<O, Error>>
|
||||
@ -56,6 +58,7 @@ where
|
||||
'q: 'c,
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
A: 'c,
|
||||
O: 'c,
|
||||
{
|
||||
self.inner.fetch(executor).map_ok(|it| it.0).boxed()
|
||||
@ -70,6 +73,7 @@ where
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
A: 'c,
|
||||
{
|
||||
self.inner
|
||||
.fetch_many(executor)
|
||||
@ -85,6 +89,7 @@ where
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
(O,): 'c,
|
||||
A: 'c,
|
||||
{
|
||||
self.inner
|
||||
.fetch(executor)
|
||||
@ -101,6 +106,7 @@ where
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
A: 'c,
|
||||
{
|
||||
self.inner.fetch_one(executor).map_ok(|it| it.0).await
|
||||
}
|
||||
@ -113,15 +119,18 @@ where
|
||||
E: 'c + Executor<'c, Database = DB>,
|
||||
DB: 'c,
|
||||
O: 'c,
|
||||
A: 'c,
|
||||
{
|
||||
Ok(self.inner.fetch_optional(executor).await?.map(|it| it.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a raw SQL query that is mapped to a concrete type
|
||||
/// using [`FromRow`](crate::row::FromRow) on `(O,)`.
|
||||
/// Make a SQL query that is mapped to a single concrete type
|
||||
/// using [`FromRow`](crate::row::FromRow).
|
||||
#[inline]
|
||||
pub fn query_scalar<DB, O>(sql: &str) -> QueryScalar<DB, O>
|
||||
pub fn query_scalar<'q, DB, O>(
|
||||
sql: &'q str,
|
||||
) -> QueryScalar<DB, O, <DB as HasArguments<'q>>::Arguments>
|
||||
where
|
||||
DB: Database,
|
||||
(O,): for<'r> FromRow<'r, DB::Row>,
|
||||
@ -130,3 +139,17 @@ where
|
||||
inner: query_as(sql),
|
||||
}
|
||||
}
|
||||
|
||||
/// Make a SQL query, with the given arguments, that is mapped to a single concrete type
|
||||
/// using [`FromRow`](crate::row::FromRow).
|
||||
#[inline]
|
||||
pub fn query_scalar_with<'q, DB, O, A>(sql: &'q str, arguments: A) -> QueryScalar<DB, O, A>
|
||||
where
|
||||
DB: Database,
|
||||
A: IntoArguments<'q, DB>,
|
||||
(O,): for<'r> FromRow<'r, DB::Row>,
|
||||
{
|
||||
QueryScalar {
|
||||
inner: query_as_with(sql, arguments),
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ fn emplace_row_metadata(
|
||||
impl<'c> Executor<'c> for &'c mut SqliteConnection {
|
||||
type Database = Sqlite;
|
||||
|
||||
fn fetch_many<'q: 'c, E>(
|
||||
fn fetch_many<'q: 'c, E: 'c>(
|
||||
self,
|
||||
mut query: E,
|
||||
) -> BoxStream<'c, Result<Either<u64, SqliteRow>, Error>>
|
||||
@ -148,7 +148,10 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_optional<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result<Option<SqliteRow>, Error>>
|
||||
fn fetch_optional<'q: 'c, E: 'c>(
|
||||
self,
|
||||
query: E,
|
||||
) -> BoxFuture<'c, Result<Option<SqliteRow>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
@ -166,7 +169,7 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn describe<'q: 'c, E>(self, query: E) -> BoxFuture<'c, Result<Describe<Sqlite>, Error>>
|
||||
fn describe<'q: 'c, E: 'c>(self, query: E) -> BoxFuture<'c, Result<Describe<Sqlite>, Error>>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
|
||||
@ -27,3 +27,7 @@ pub use value::{SqliteValue, SqliteValueRef};
|
||||
|
||||
/// An alias for [`Pool`][crate::pool::Pool], specialized for SQLite.
|
||||
pub type SqlitePool = crate::pool::Pool<SqliteConnection>;
|
||||
|
||||
// NOTE: required due to the lack of lazy normalization
|
||||
impl_into_arguments_for_arguments!(SqliteArguments<'q>);
|
||||
impl_executor_for_pool_connection!(Sqlite, SqliteConnection, SqliteRow);
|
||||
|
||||
@ -18,7 +18,7 @@ pub fn quote_args<DB: DatabaseExt>(
|
||||
|
||||
if input.arg_names.is_empty() {
|
||||
return Ok(quote! {
|
||||
let query_args = <#db_path as sqlx::Database>::Arguments::default();
|
||||
let query_args = <#db_path as sqlx::database::HasArguments>::Arguments::default();
|
||||
});
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ pub fn quote_args<DB: DatabaseExt>(
|
||||
|
||||
// bind as a local expression, by-ref
|
||||
#(let #arg_name = &$#arg_name;)*
|
||||
let mut query_args = <#db_path as sqlx::Database>::Arguments::default();
|
||||
let mut query_args = <#db_path as sqlx::database::HasArguments>::Arguments::default();
|
||||
query_args.reserve(
|
||||
#args_count,
|
||||
0 #(+ sqlx::encode::Encode::<#db_path>::size_hint(#arg_name))*
|
||||
|
||||
@ -119,7 +119,7 @@ pub fn quote_query_as<DB: DatabaseExt>(
|
||||
let sql = &input.src;
|
||||
|
||||
quote! {
|
||||
sqlx::query::<#db_path>(#sql).bind_all(#bind_args).try_map(|row: #row_path| {
|
||||
sqlx::query_with::<#db_path>(#sql, #bind_args).try_map(|row: #row_path| {
|
||||
use sqlx::Row as _;
|
||||
use sqlx::result_ext::ResultExt as _;
|
||||
|
||||
|
||||
22
src/lib.rs
22
src/lib.rs
@ -1,17 +1,17 @@
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
pub use sqlx_core::arguments;
|
||||
pub use sqlx_core::arguments::{Arguments, IntoArguments};
|
||||
pub use sqlx_core::connection::{Connect, Connection};
|
||||
pub use sqlx_core::database::{self, Database};
|
||||
pub use sqlx_core::executor::{self, Execute, Executor};
|
||||
pub use sqlx_core::executor::{Execute, Executor};
|
||||
pub use sqlx_core::from_row::FromRow;
|
||||
pub use sqlx_core::pool::{self, Pool};
|
||||
pub use sqlx_core::query::{self, query, Query};
|
||||
pub use sqlx_core::query_as::{query_as, QueryAs};
|
||||
pub use sqlx_core::query_scalar::{query_scalar, QueryScalar};
|
||||
pub use sqlx_core::row::{self, Row};
|
||||
pub use sqlx_core::query::{query, query_with};
|
||||
pub use sqlx_core::query_as::{query_as, query_as_with};
|
||||
pub use sqlx_core::query_scalar::{query_scalar, query_scalar_with};
|
||||
pub use sqlx_core::row::{ColumnIndex, Row};
|
||||
// pub use sqlx_core::transaction::Transaction;
|
||||
pub use sqlx_core::value;
|
||||
pub use sqlx_core::value::{Value, ValueRef};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use sqlx_core::describe;
|
||||
@ -69,11 +69,19 @@ pub mod decode {
|
||||
pub use sqlx_macros::Decode;
|
||||
}
|
||||
|
||||
pub mod query {
|
||||
pub use sqlx_core::query::{query, query_with, Map, Query};
|
||||
pub use sqlx_core::query_as::{query_as, query_as_with, QueryAs};
|
||||
pub use sqlx_core::query_scalar::{query_scalar, query_scalar_with, QueryScalar};
|
||||
}
|
||||
|
||||
/// Convenience re-export of common traits.
|
||||
pub mod prelude {
|
||||
pub use super::Connect;
|
||||
pub use super::Connection;
|
||||
pub use super::Executor;
|
||||
pub use super::FromRow;
|
||||
pub use super::IntoArguments;
|
||||
pub use super::Row;
|
||||
pub use super::Type;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user