mirror of
https://github.com/launchbadge/sqlx
synced 2024-11-10 22:44:17 +00:00
add Cursor and rewrite Executor/Query over it
* this breaks a lot internally as-is * mysql needs a restructure
This commit is contained in:
parent
bb17ebfbbd
commit
ea1a4fb042
17 changed files with 407 additions and 763 deletions
|
@ -1,3 +1,5 @@
|
|||
use crate::database::Database;
|
||||
use crate::describe::Describe;
|
||||
use crate::executor::Executor;
|
||||
use crate::url::Url;
|
||||
use futures_core::future::BoxFuture;
|
||||
|
@ -8,13 +10,29 @@ use std::convert::TryInto;
|
|||
///
|
||||
/// Prefer running queries from [Pool] unless there is a specific need for a single, continuous
|
||||
/// connection.
|
||||
pub trait Connection: Executor + Send + 'static {
|
||||
pub trait Connection
|
||||
where
|
||||
Self: Send + 'static,
|
||||
{
|
||||
type Database: Database;
|
||||
|
||||
/// Close this database connection.
|
||||
fn close(self) -> BoxFuture<'static, crate::Result<()>>;
|
||||
|
||||
/// Verifies a connection to the database is still alive.
|
||||
fn ping(&mut self) -> BoxFuture<crate::Result<()>> {
|
||||
Box::pin(self.execute("SELECT 1", Default::default()).map_ok(|_| ()))
|
||||
fn ping(&mut self) -> BoxFuture<crate::Result<()>>
|
||||
where
|
||||
for<'a> &'a mut Self: Executor<'a>,
|
||||
{
|
||||
Box::pin((&mut *self).execute("SELECT 1").map_ok(|_| ()))
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn describe<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>> {
|
||||
todo!("make this a required function");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
36
sqlx-core/src/cursor.rs
Normal file
36
sqlx-core/src/cursor.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::future::Future;
|
||||
|
||||
use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::BoxStream;
|
||||
|
||||
use crate::database::{Database, HasRow};
|
||||
|
||||
/// Represents a result set, which is generated by executing a query against the database.
|
||||
///
|
||||
/// A `Cursor` can be created by either [`Executor::execute`](trait.Execute.html) or
|
||||
/// [`Query::fetch`](struct.Query.html).
|
||||
///
|
||||
/// Initially the `Cursor` is positioned before the first row. The `next` method moves the cursor
|
||||
/// to the next row, and because it returns `None` when there are no more rows, it can be used
|
||||
/// in a `while` loop to iterate through all returned rows.
|
||||
pub trait Cursor<'a>
|
||||
where
|
||||
Self: Send,
|
||||
// `.await`-ing a cursor will return the affected rows from the query
|
||||
Self: Future<Output = crate::Result<u64>>,
|
||||
{
|
||||
type Database: Database;
|
||||
|
||||
/// Fetch the first row in the result. Returns `None` if no row is present.
|
||||
///
|
||||
/// Returns `Error::MoreThanOneRow` if more than one row is in the result.
|
||||
fn first(self) -> BoxFuture<'a, crate::Result<Option<<Self::Database as HasRow>::Row>>>;
|
||||
|
||||
/// Fetch the next row in the result. Returns `None` if there are no more rows.
|
||||
fn next(&mut self) -> BoxFuture<crate::Result<Option<<Self::Database as HasRow>::Row>>>;
|
||||
|
||||
/// Map the `Row`s in this result to a different type, returning a [`Stream`] of the results.
|
||||
fn map<T, F>(self, f: F) -> BoxStream<'a, crate::Result<T>>
|
||||
where
|
||||
F: Fn(<Self::Database as HasRow>::Row) -> T;
|
||||
}
|
|
@ -2,6 +2,7 @@ use std::fmt::Display;
|
|||
|
||||
use crate::arguments::Arguments;
|
||||
use crate::connection::Connection;
|
||||
use crate::cursor::Cursor;
|
||||
use crate::row::Row;
|
||||
use crate::types::TypeInfo;
|
||||
|
||||
|
@ -9,19 +10,38 @@ use crate::types::TypeInfo;
|
|||
///
|
||||
/// This trait encapsulates a complete driver implementation to a specific
|
||||
/// database (e.g., MySQL, Postgres).
|
||||
pub trait Database: 'static {
|
||||
pub trait Database
|
||||
where
|
||||
Self: Sized + 'static,
|
||||
Self: HasRow<Database = Self>,
|
||||
Self: for<'a> HasRawValue<'a>,
|
||||
Self: for<'a> HasCursor<'a, Database = Self>,
|
||||
{
|
||||
/// The concrete `Connection` implementation for this database.
|
||||
type Connection: Connection<Database = Self>;
|
||||
|
||||
/// The concrete `Arguments` implementation for this database.
|
||||
type Arguments: Arguments<Database = Self>;
|
||||
|
||||
/// The concrete `Row` implementation for this database.
|
||||
type Row: Row<Database = Self>;
|
||||
|
||||
/// The concrete `TypeInfo` implementation for this database.
|
||||
type TypeInfo: TypeInfo;
|
||||
|
||||
/// The Rust type of table identifiers for this database.
|
||||
type TableId: Display + Clone;
|
||||
}
|
||||
|
||||
pub trait HasRawValue<'a> {
|
||||
type RawValue;
|
||||
}
|
||||
|
||||
pub trait HasCursor<'a> {
|
||||
type Database: Database;
|
||||
|
||||
type Cursor: Cursor<'a, Database = Self::Database>;
|
||||
}
|
||||
|
||||
pub trait HasRow {
|
||||
type Database: Database;
|
||||
|
||||
type Row: Row<Database = Self::Database>;
|
||||
}
|
||||
|
|
|
@ -1,72 +1,57 @@
|
|||
use crate::database::Database;
|
||||
use crate::database::{Database, HasCursor};
|
||||
use crate::describe::Describe;
|
||||
use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::TryStreamExt;
|
||||
|
||||
/// Encapsulates query execution on the database.
|
||||
/// A type that contains or can provide a database connection to use for executing queries
|
||||
/// against the database.
|
||||
///
|
||||
/// Implemented primarily by [crate::Pool].
|
||||
pub trait Executor {
|
||||
type Database: Database + ?Sized;
|
||||
|
||||
/// Send a raw SQL command to the database.
|
||||
/// No guarantees are provided that successive queries run on the same physical database
|
||||
/// connection. A [`Connection`](trait.Connection.html) is an `Executor` that guarantees that successive
|
||||
/// queries are run on the same physical database connection.
|
||||
///
|
||||
/// This is intended for queries that cannot or should not be prepared (ex. `BEGIN`).
|
||||
///
|
||||
/// Does not support fetching results.
|
||||
fn send<'e, 'q: 'e>(&'e mut self, command: &'q str) -> BoxFuture<'e, crate::Result<()>>;
|
||||
/// Implementations are provided for [`&Pool`](struct.Pool.html),
|
||||
/// [`&mut PoolConnection`](struct.PoolConnection.html),
|
||||
/// and [`&mut Connection`](trait.Connection.html).
|
||||
pub trait Executor<'a>
|
||||
where
|
||||
Self: Send,
|
||||
{
|
||||
/// The specific database that this type is implemented for.
|
||||
type Database: Database;
|
||||
|
||||
/// Execute the query, returning the number of rows affected.
|
||||
fn execute<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <Self::Database as Database>::Arguments,
|
||||
) -> BoxFuture<'e, crate::Result<u64>>;
|
||||
|
||||
/// Executes the query and returns a [Stream] of [Row].
|
||||
fn fetch<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <Self::Database as Database>::Arguments,
|
||||
) -> BoxStream<'e, crate::Result<<Self::Database as Database>::Row>>;
|
||||
|
||||
/// Executes the query and returns up to resulting record.
|
||||
///
|
||||
/// * [crate::Error::FoundMoreThanOne] will be returned if the query produced more than 1 row.
|
||||
fn fetch_optional<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <Self::Database as Database>::Arguments,
|
||||
) -> BoxFuture<'e, crate::Result<Option<<Self::Database as Database>::Row>>> {
|
||||
let mut s = self.fetch(query, args);
|
||||
Box::pin(async move {
|
||||
match s.try_next().await? {
|
||||
Some(val) => {
|
||||
if s.try_next().await?.is_some() {
|
||||
Err(crate::Error::FoundMoreThanOne)
|
||||
} else {
|
||||
Ok(Some(val))
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Execute the query and return at most one resulting record.
|
||||
fn fetch_one<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <Self::Database as Database>::Arguments,
|
||||
) -> BoxFuture<'e, crate::Result<<Self::Database as Database>::Row>> {
|
||||
let mut s = self.fetch(query, args);
|
||||
Box::pin(async move { s.try_next().await?.ok_or(crate::Error::NotFound) })
|
||||
}
|
||||
/// Executes a query that may or may not return a result set.
|
||||
fn execute<'b, E>(self, query: E) -> <Self::Database as HasCursor<'a>>::Cursor
|
||||
where
|
||||
E: Execute<'b, Self::Database>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn describe<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>>;
|
||||
fn execute_by_ref<'b, E>(&mut self, query: E) -> <Self::Database as HasCursor<'_>>::Cursor
|
||||
where
|
||||
E: Execute<'b, Self::Database>;
|
||||
}
|
||||
|
||||
/// A type that may be executed against a database connection.
|
||||
pub trait Execute<'a, DB>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
/// Returns the query to be executed and the arguments to bind against the query, if any.
|
||||
///
|
||||
/// Returning `None` for `Arguments` indicates to use a "simple" query protocol and to not
|
||||
/// prepare the query. Returning `Some(Default::default())` is an empty arguments object that
|
||||
/// will be prepared (and cached) before execution.
|
||||
#[doc(hidden)]
|
||||
fn into_parts(self) -> (&'a str, Option<DB::Arguments>);
|
||||
}
|
||||
|
||||
impl<'a, DB> Execute<'a, DB> for &'a str
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
#[inline]
|
||||
fn into_parts(self) -> (&'a str, Option<DB::Arguments>) {
|
||||
(self, None)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,10 @@ mod io;
|
|||
mod cache;
|
||||
|
||||
mod connection;
|
||||
mod cursor;
|
||||
mod database;
|
||||
mod executor;
|
||||
mod query;
|
||||
mod query_as;
|
||||
mod transaction;
|
||||
mod url;
|
||||
|
||||
|
@ -51,14 +51,11 @@ pub use database::Database;
|
|||
pub use error::{Error, Result};
|
||||
|
||||
pub use connection::{Connect, Connection};
|
||||
pub use cursor::Cursor;
|
||||
pub use executor::Executor;
|
||||
pub use query::{query, Query};
|
||||
pub use query_as::{query_as, QueryAs};
|
||||
pub use transaction::Transaction;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use query_as::query_as_mapped;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use pool::Pool;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::Database;
|
||||
use crate::database::{Database, HasCursor, HasRawValue, HasRow};
|
||||
|
||||
/// **MySQL** database driver.
|
||||
pub struct MySql;
|
||||
|
@ -8,9 +8,23 @@ impl Database for MySql {
|
|||
|
||||
type Arguments = super::MySqlArguments;
|
||||
|
||||
type Row = super::MySqlRow;
|
||||
|
||||
type TypeInfo = super::MySqlTypeInfo;
|
||||
|
||||
type TableId = Box<str>;
|
||||
}
|
||||
|
||||
impl HasRow for MySql {
|
||||
type Database = MySql;
|
||||
|
||||
type Row = super::MySqlRow;
|
||||
}
|
||||
|
||||
impl<'a> HasCursor<'a> for MySql {
|
||||
type Database = MySql;
|
||||
|
||||
type Cursor = super::MySqlCursor<'a>;
|
||||
}
|
||||
|
||||
impl<'a> HasRawValue<'a> for MySql {
|
||||
type RawValue = Option<&'a [u8]>;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ impl<C> Connection for PoolConnection<C>
|
|||
where
|
||||
C: Connect,
|
||||
{
|
||||
type Database = C::Database;
|
||||
|
||||
/// Detach the connection from the pool and close it nicely.
|
||||
fn close(mut self) -> BoxFuture<'static, crate::Result<()>> {
|
||||
Box::pin(async move {
|
||||
|
@ -166,7 +168,8 @@ impl<'s, C> Floating<'s, Idle<C>> {
|
|||
where
|
||||
C: Connection,
|
||||
{
|
||||
self.live.raw.ping().await
|
||||
// TODO self.live.raw.ping().await
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn into_live(self) -> Floating<'s, Live<C>> {
|
||||
|
|
|
@ -3,148 +3,89 @@ use std::ops::DerefMut;
|
|||
use futures_core::{future::BoxFuture, stream::BoxStream};
|
||||
use futures_util::StreamExt;
|
||||
|
||||
use crate::{connection::Connect, describe::Describe, executor::Executor, pool::Pool, Database};
|
||||
use crate::{
|
||||
connection::{Connect, Connection},
|
||||
describe::Describe,
|
||||
executor::Executor,
|
||||
pool::Pool,
|
||||
Database,
|
||||
};
|
||||
|
||||
use super::PoolConnection;
|
||||
use crate::database::HasCursor;
|
||||
use crate::executor::Execute;
|
||||
|
||||
impl<C> Executor for Pool<C>
|
||||
impl<'p, C> Executor<'p> for &'p Pool<C>
|
||||
where
|
||||
C: Connect,
|
||||
for<'con> &'con mut C: Executor<'con>,
|
||||
{
|
||||
type Database = <C as Executor>::Database;
|
||||
type Database = C::Database;
|
||||
|
||||
fn send<'e, 'q: 'e>(&'e mut self, commands: &'q str) -> BoxFuture<'e, crate::Result<()>> {
|
||||
Box::pin(async move { <&Pool<C> as Executor>::send(&mut &*self, commands).await })
|
||||
fn execute<'q, E>(self, query: E) -> <Self::Database as HasCursor<'p>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn execute<'e, 'q: 'e>(
|
||||
fn execute_by_ref<'q, 'e, E>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <<C as Executor>::Database as Database>::Arguments,
|
||||
) -> BoxFuture<'e, crate::Result<u64>> {
|
||||
Box::pin(async move { <&Pool<C> as Executor>::execute(&mut &*self, query, args).await })
|
||||
}
|
||||
|
||||
fn fetch<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <<C as Executor>::Database as Database>::Arguments,
|
||||
) -> BoxStream<'e, crate::Result<<<C as Executor>::Database as Database>::Row>> {
|
||||
Box::pin(async_stream::try_stream! {
|
||||
let mut self_ = &*self;
|
||||
let mut s = <&Pool<C> as Executor>::fetch(&mut self_, query, args);
|
||||
|
||||
while let Some(row) = s.next().await.transpose()? {
|
||||
yield row;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_optional<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <<C as Executor>::Database as Database>::Arguments,
|
||||
) -> BoxFuture<'e, crate::Result<Option<<<C as Executor>::Database as Database>::Row>>> {
|
||||
Box::pin(
|
||||
async move { <&Pool<C> as Executor>::fetch_optional(&mut &*self, query, args).await },
|
||||
)
|
||||
}
|
||||
|
||||
fn describe<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>> {
|
||||
Box::pin(async move { <&Pool<C> as Executor>::describe(&mut &*self, query).await })
|
||||
query: E,
|
||||
) -> <Self::Database as HasCursor<'_>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Executor for &'_ Pool<C>
|
||||
impl<'c, C> Executor<'c> for &'c mut PoolConnection<C>
|
||||
where
|
||||
C: Connect,
|
||||
for<'con> &'con mut C: Executor<'con>,
|
||||
{
|
||||
type Database = <C as Executor>::Database;
|
||||
type Database = C::Database;
|
||||
|
||||
fn send<'e, 'q: 'e>(&'e mut self, commands: &'q str) -> BoxFuture<'e, crate::Result<()>> {
|
||||
Box::pin(async move { self.acquire().await?.send(commands).await })
|
||||
fn execute<'q, E>(self, query: E) -> <Self::Database as HasCursor<'c>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn execute<'e, 'q: 'e>(
|
||||
fn execute_by_ref<'q, 'e, E>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <<C as Executor>::Database as Database>::Arguments,
|
||||
) -> BoxFuture<'e, crate::Result<u64>> {
|
||||
Box::pin(async move { self.acquire().await?.execute(query, args).await })
|
||||
}
|
||||
|
||||
fn fetch<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <<C as Executor>::Database as Database>::Arguments,
|
||||
) -> BoxStream<'e, crate::Result<<<C as Executor>::Database as Database>::Row>> {
|
||||
Box::pin(async_stream::try_stream! {
|
||||
let mut live = self.acquire().await?;
|
||||
let mut s = live.fetch(query, args);
|
||||
|
||||
while let Some(row) = s.next().await.transpose()? {
|
||||
yield row;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_optional<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <<C as Executor>::Database as Database>::Arguments,
|
||||
) -> BoxFuture<'e, crate::Result<Option<<<C as Executor>::Database as Database>::Row>>> {
|
||||
Box::pin(async move { self.acquire().await?.fetch_optional(query, args).await })
|
||||
}
|
||||
|
||||
fn describe<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>> {
|
||||
Box::pin(async move { self.acquire().await?.describe(query).await })
|
||||
query: E,
|
||||
) -> <Self::Database as HasCursor<'_>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Executor for PoolConnection<C>
|
||||
impl<C> Executor<'static> for PoolConnection<C>
|
||||
where
|
||||
C: Connect,
|
||||
// for<'con> &'con mut C: Executor<'con>,
|
||||
{
|
||||
type Database = <C as Executor>::Database;
|
||||
type Database = C::Database;
|
||||
|
||||
fn send<'e, 'q: 'e>(&'e mut self, commands: &'q str) -> BoxFuture<'e, crate::Result<()>> {
|
||||
self.deref_mut().send(commands)
|
||||
fn execute<'q, E>(self, query: E) -> <Self::Database as HasCursor<'static>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn execute<'e, 'q: 'e>(
|
||||
fn execute_by_ref<'q, 'e, E>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <<C as Executor>::Database as Database>::Arguments,
|
||||
) -> BoxFuture<'e, crate::Result<u64>> {
|
||||
self.deref_mut().execute(query, args)
|
||||
}
|
||||
|
||||
fn fetch<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <<C as Executor>::Database as Database>::Arguments,
|
||||
) -> BoxStream<'e, crate::Result<<<C as Executor>::Database as Database>::Row>> {
|
||||
self.deref_mut().fetch(query, args)
|
||||
}
|
||||
|
||||
fn fetch_optional<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <<C as Executor>::Database as Database>::Arguments,
|
||||
) -> BoxFuture<'e, crate::Result<Option<<<C as Executor>::Database as Database>::Row>>> {
|
||||
self.deref_mut().fetch_optional(query, args)
|
||||
}
|
||||
|
||||
fn describe<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>> {
|
||||
self.deref_mut().describe(query)
|
||||
query: E,
|
||||
) -> <Self::Database as HasCursor<'_>>::Cursor
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::postgres::protocol::{
|
|||
};
|
||||
use crate::postgres::PgError;
|
||||
use crate::url::Url;
|
||||
use crate::Result;
|
||||
use crate::{Postgres, Result};
|
||||
|
||||
/// An asynchronous connection to a [Postgres][super::Postgres] database.
|
||||
///
|
||||
|
@ -397,17 +397,6 @@ impl PgConnection {
|
|||
}
|
||||
}
|
||||
|
||||
impl PgConnection {
|
||||
#[deprecated(note = "please use 'connect' instead")]
|
||||
pub fn open<T>(url: T) -> BoxFuture<'static, Result<Self>>
|
||||
where
|
||||
T: TryInto<Url, Error = crate::Error>,
|
||||
Self: Sized,
|
||||
{
|
||||
Box::pin(PgConnection::establish(url.try_into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Connect for PgConnection {
|
||||
fn connect<T>(url: T) -> BoxFuture<'static, Result<PgConnection>>
|
||||
where
|
||||
|
@ -419,6 +408,8 @@ impl Connect for PgConnection {
|
|||
}
|
||||
|
||||
impl Connection for PgConnection {
|
||||
type Database = Postgres;
|
||||
|
||||
fn close(self) -> BoxFuture<'static, Result<()>> {
|
||||
Box::pin(self.terminate())
|
||||
}
|
||||
|
|
56
sqlx-core/src/postgres/cursor.rs
Normal file
56
sqlx-core/src/postgres/cursor.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::BoxStream;
|
||||
|
||||
use crate::cursor::Cursor;
|
||||
use crate::database::HasRow;
|
||||
use crate::postgres::protocol::StatementId;
|
||||
use crate::postgres::PgConnection;
|
||||
use crate::Postgres;
|
||||
|
||||
pub struct PgCursor<'a> {
|
||||
statement: StatementId,
|
||||
connection: &'a mut PgConnection,
|
||||
}
|
||||
|
||||
impl<'a> PgCursor<'a> {
|
||||
pub(super) fn from_connection(
|
||||
connection: &'a mut PgConnection,
|
||||
statement: StatementId,
|
||||
) -> Self {
|
||||
Self {
|
||||
connection,
|
||||
statement,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Cursor<'a> for PgCursor<'a> {
|
||||
type Database = Postgres;
|
||||
|
||||
fn first(self) -> BoxFuture<'a, crate::Result<Option<<Self::Database as HasRow>::Row>>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn next(&mut self) -> BoxFuture<crate::Result<Option<<Self::Database as HasRow>::Row>>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn map<T, F>(self, f: F) -> BoxStream<'a, crate::Result<T>>
|
||||
where
|
||||
F: Fn(<Self::Database as HasRow>::Row) -> T,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Future for PgCursor<'a> {
|
||||
type Output = crate::Result<u64>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::database::Database;
|
||||
use crate::database::{Database, HasCursor, HasRawValue, HasRow};
|
||||
|
||||
/// **Postgres** database driver.
|
||||
pub struct Postgres;
|
||||
|
@ -8,9 +8,25 @@ impl Database for Postgres {
|
|||
|
||||
type Arguments = super::PgArguments;
|
||||
|
||||
type Row = super::PgRow;
|
||||
|
||||
type TypeInfo = super::PgTypeInfo;
|
||||
|
||||
type TableId = u32;
|
||||
}
|
||||
|
||||
impl HasRow for Postgres {
|
||||
// TODO: Can we drop the `type Database = _`
|
||||
type Database = Postgres;
|
||||
|
||||
type Row = super::PgRow;
|
||||
}
|
||||
|
||||
impl<'a> HasCursor<'a> for Postgres {
|
||||
// TODO: Can we drop the `type Database = _`
|
||||
type Database = Postgres;
|
||||
|
||||
type Cursor = super::PgCursor<'a>;
|
||||
}
|
||||
|
||||
impl<'a> HasRawValue<'a> for Postgres {
|
||||
type RawValue = Option<&'a [u8]>;
|
||||
}
|
||||
|
|
|
@ -6,17 +6,9 @@ use futures_core::future::BoxFuture;
|
|||
use futures_core::stream::BoxStream;
|
||||
|
||||
use crate::describe::{Column, Describe};
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::postgres::protocol::{self, Encode, Message, StatementId, TypeFormat};
|
||||
use crate::postgres::{PgArguments, PgRow, PgTypeInfo, Postgres};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Step {
|
||||
Command(u64),
|
||||
NoData,
|
||||
Row(protocol::DataRow),
|
||||
ParamDesc(Box<protocol::ParameterDescription>),
|
||||
RowDesc(Box<protocol::RowDescription>),
|
||||
}
|
||||
use crate::postgres::{PgArguments, PgCursor, PgRow, PgTypeInfo, Postgres};
|
||||
|
||||
impl super::PgConnection {
|
||||
fn write_prepare(&mut self, query: &str, args: &PgArguments) -> StatementId {
|
||||
|
@ -63,274 +55,51 @@ impl super::PgConnection {
|
|||
fn write_sync(&mut self) {
|
||||
protocol::Sync.encode(self.stream.buffer_mut());
|
||||
}
|
||||
|
||||
async fn wait_until_ready(&mut self) -> crate::Result<()> {
|
||||
if !self.ready {
|
||||
while let Some(message) = self.receive().await? {
|
||||
match message {
|
||||
Message::ReadyForQuery(_) => {
|
||||
self.ready = true;
|
||||
break;
|
||||
}
|
||||
|
||||
_ => {
|
||||
// Drain the stream
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'e> Executor<'e> for &'e mut super::PgConnection {
|
||||
type Database = Postgres;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn execute<'q, E>(self, query: E) -> PgCursor<'e>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
let (query, arguments) = query.into_parts();
|
||||
|
||||
async fn step(&mut self) -> crate::Result<Option<Step>> {
|
||||
while let Some(message) = self.receive().await? {
|
||||
match message {
|
||||
Message::BindComplete
|
||||
| Message::ParseComplete
|
||||
| Message::PortalSuspended
|
||||
| Message::CloseComplete => {}
|
||||
// TODO: Handle [arguments] being None. This should be a SIMPLE query.
|
||||
let arguments = arguments.unwrap();
|
||||
|
||||
Message::CommandComplete(body) => {
|
||||
return Ok(Some(Step::Command(body.affected_rows)));
|
||||
}
|
||||
// Check the statement cache for a statement ID that matches the given query
|
||||
// If it doesn't exist, we generate a new statement ID and write out [Parse] to the
|
||||
// connection command buffer
|
||||
let statement = self.write_prepare(query, &arguments);
|
||||
|
||||
Message::NoData => {
|
||||
return Ok(Some(Step::NoData));
|
||||
}
|
||||
|
||||
Message::DataRow(body) => {
|
||||
return Ok(Some(Step::Row(body)));
|
||||
}
|
||||
|
||||
Message::ReadyForQuery(_) => {
|
||||
self.ready = true;
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Message::ParameterDescription(desc) => {
|
||||
return Ok(Some(Step::ParamDesc(desc)));
|
||||
}
|
||||
|
||||
Message::RowDescription(desc) => {
|
||||
return Ok(Some(Step::RowDesc(desc)));
|
||||
}
|
||||
|
||||
message => {
|
||||
return Err(protocol_err!("received unexpected message: {:?}", message).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Connection was (unexpectedly) closed
|
||||
Err(io::Error::from(io::ErrorKind::ConnectionAborted).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl super::PgConnection {
|
||||
async fn send<'e, 'q: 'e>(&'e mut self, command: &'q str) -> crate::Result<()> {
|
||||
protocol::Query(command).encode(self.stream.buffer_mut());
|
||||
|
||||
self.wait_until_ready().await?;
|
||||
|
||||
self.stream.flush().await?;
|
||||
self.ready = false;
|
||||
|
||||
while let Some(_step) = self.step().await? {
|
||||
// Drain the stream until ReadyForQuery
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: PgArguments,
|
||||
) -> crate::Result<u64> {
|
||||
let statement = self.write_prepare(query, &args);
|
||||
|
||||
self.write_bind("", statement, &args);
|
||||
self.write_execute("", 1);
|
||||
self.write_sync();
|
||||
|
||||
self.wait_until_ready().await?;
|
||||
|
||||
self.stream.flush().await?;
|
||||
self.ready = false;
|
||||
|
||||
let mut affected = 0;
|
||||
|
||||
while let Some(step) = self.step().await? {
|
||||
if let Step::Command(cnt) = step {
|
||||
affected = cnt;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(affected)
|
||||
}
|
||||
|
||||
// Initial part of [fetch]; write message to stream
|
||||
fn write_fetch(&mut self, query: &str, args: &PgArguments) -> StatementId {
|
||||
let statement = self.write_prepare(query, &args);
|
||||
|
||||
self.write_bind("", statement, &args);
|
||||
// Next, [Bind] attaches the arguments to the statement and creates a named portal
|
||||
self.write_bind("", statement, &arguments);
|
||||
|
||||
// Next, [Describe] will return the expected result columns and types
|
||||
// Conditionally run [Describe] only if the results have not been cached
|
||||
if !self.statement_cache.has_columns(statement) {
|
||||
self.write_describe(protocol::Describe::Portal(""));
|
||||
}
|
||||
|
||||
// Next, [Execute] then executes the named portal
|
||||
self.write_execute("", 0);
|
||||
|
||||
// Finally, [Sync] asks postgres to process the messages that we sent and respond with
|
||||
// a [ReadyForQuery] message when it's completely done. Theoretically, we could send
|
||||
// dozens of queries before a [Sync] and postgres can handle that. Execution on the server
|
||||
// is still serial but it would reduce round-trips. Some kind of builder pattern that is
|
||||
// termed batching might suit this.
|
||||
self.write_sync();
|
||||
|
||||
statement
|
||||
PgCursor::from_connection(self, statement)
|
||||
}
|
||||
|
||||
async fn get_columns(
|
||||
&mut self,
|
||||
statement: StatementId,
|
||||
) -> crate::Result<Arc<HashMap<Box<str>, usize>>> {
|
||||
if !self.statement_cache.has_columns(statement) {
|
||||
let desc: Option<_> = 'outer: loop {
|
||||
while let Some(step) = self.step().await? {
|
||||
match step {
|
||||
Step::RowDesc(desc) => break 'outer Some(desc),
|
||||
|
||||
Step::NoData => break 'outer None,
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
let mut columns = HashMap::new();
|
||||
|
||||
if let Some(desc) = desc {
|
||||
columns.reserve(desc.fields.len());
|
||||
|
||||
for (index, field) in desc.fields.iter().enumerate() {
|
||||
if let Some(name) = &field.name {
|
||||
columns.insert(name.clone(), index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.statement_cache.put_columns(statement, columns);
|
||||
}
|
||||
|
||||
Ok(self.statement_cache.get_columns(statement))
|
||||
}
|
||||
|
||||
fn fetch<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: PgArguments,
|
||||
) -> BoxStream<'e, crate::Result<PgRow>> {
|
||||
Box::pin(async_stream::try_stream! {
|
||||
let statement = self.write_fetch(query, &args);
|
||||
|
||||
self.wait_until_ready().await?;
|
||||
|
||||
self.stream.flush().await?;
|
||||
self.ready = false;
|
||||
|
||||
let columns = self.get_columns(statement).await?;
|
||||
|
||||
while let Some(step) = self.step().await? {
|
||||
if let Step::Row(data) = step {
|
||||
yield PgRow { data, columns: Arc::clone(&columns) };
|
||||
}
|
||||
}
|
||||
|
||||
// No more rows in the result set
|
||||
})
|
||||
}
|
||||
|
||||
async fn describe<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
) -> crate::Result<Describe<Postgres>> {
|
||||
let statement = self.write_prepare(query, &Default::default());
|
||||
|
||||
self.write_describe(protocol::Describe::Statement(statement));
|
||||
self.write_sync();
|
||||
|
||||
self.stream.flush().await?;
|
||||
self.wait_until_ready().await?;
|
||||
|
||||
let params = match self.step().await? {
|
||||
Some(Step::ParamDesc(desc)) => desc,
|
||||
|
||||
step => {
|
||||
return Err(
|
||||
protocol_err!("expected ParameterDescription; received {:?}", step).into(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let result = match self.step().await? {
|
||||
Some(Step::RowDesc(desc)) => Some(desc),
|
||||
Some(Step::NoData) => None,
|
||||
|
||||
step => {
|
||||
return Err(protocol_err!("expected RowDescription; received {:?}", step).into());
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Describe {
|
||||
param_types: params
|
||||
.ids
|
||||
.iter()
|
||||
.map(|id| PgTypeInfo::new(*id))
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
result_columns: result
|
||||
.map(|r| r.fields)
|
||||
.unwrap_or_default()
|
||||
.into_vec()
|
||||
.into_iter()
|
||||
// TODO: Should [Column] just wrap [protocol::Field] ?
|
||||
.map(|field| Column {
|
||||
name: field.name,
|
||||
table_id: field.table_id,
|
||||
type_info: PgTypeInfo::new(field.type_id),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Executor for super::PgConnection {
|
||||
type Database = super::Postgres;
|
||||
|
||||
fn send<'e, 'q: 'e>(&'e mut self, query: &'q str) -> BoxFuture<'e, crate::Result<()>> {
|
||||
Box::pin(self.send(query))
|
||||
}
|
||||
|
||||
fn execute<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: PgArguments,
|
||||
) -> BoxFuture<'e, crate::Result<u64>> {
|
||||
Box::pin(self.execute(query, args))
|
||||
}
|
||||
|
||||
fn fetch<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: PgArguments,
|
||||
) -> BoxStream<'e, crate::Result<PgRow>> {
|
||||
self.fetch(query, args)
|
||||
}
|
||||
|
||||
fn describe<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>> {
|
||||
Box::pin(self.describe(query))
|
||||
fn execute_by_ref<'q, E>(&mut self, query: E) -> PgCursor<'_>
|
||||
where
|
||||
E: Execute<'q, Self::Database>,
|
||||
{
|
||||
self.execute(query)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
pub use arguments::PgArguments;
|
||||
pub use connection::PgConnection;
|
||||
pub use cursor::PgCursor;
|
||||
pub use database::Postgres;
|
||||
pub use error::PgError;
|
||||
pub use row::PgRow;
|
||||
|
@ -9,6 +10,7 @@ pub use types::PgTypeInfo;
|
|||
|
||||
mod arguments;
|
||||
mod connection;
|
||||
mod cursor;
|
||||
mod database;
|
||||
mod error;
|
||||
mod executor;
|
||||
|
|
|
@ -1,88 +1,77 @@
|
|||
use crate::arguments::Arguments;
|
||||
use crate::arguments::IntoArguments;
|
||||
use crate::database::Database;
|
||||
use crate::cursor::Cursor;
|
||||
use crate::database::{Database, HasCursor, HasRow};
|
||||
use crate::encode::Encode;
|
||||
use crate::executor::Executor;
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::types::HasSqlType;
|
||||
use futures_core::stream::BoxStream;
|
||||
use futures_util::future::ready;
|
||||
use futures_util::TryFutureExt;
|
||||
use futures_util::TryStreamExt;
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Dynamic SQL query with bind parameters. Returned by [query].
|
||||
///
|
||||
/// The methods on this struct should be passed a reference to [crate::Pool] or one of
|
||||
/// the connection types.
|
||||
pub struct Query<'q, DB, T = <DB as Database>::Arguments>
|
||||
/// Raw SQL query with bind parameters. Returned by [`query`].
|
||||
pub struct Query<'a, DB, T = <DB as Database>::Arguments>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
query: &'q str,
|
||||
query: &'a str,
|
||||
arguments: T,
|
||||
database: PhantomData<DB>,
|
||||
}
|
||||
|
||||
impl<'q, DB, P> Query<'q, DB, P>
|
||||
impl<'a, DB, P> Execute<'a, DB> for Query<'a, DB, P>
|
||||
where
|
||||
DB: Database,
|
||||
P: IntoArguments<DB> + Send,
|
||||
{
|
||||
/// Execute the query for its side-effects.
|
||||
///
|
||||
/// Returns the number of rows affected, or 0 if not applicable.
|
||||
pub async fn execute<E>(self, executor: &mut E) -> crate::Result<u64>
|
||||
where
|
||||
E: Executor<Database = DB>,
|
||||
{
|
||||
executor
|
||||
.execute(self.query, self.arguments.into_arguments())
|
||||
.await
|
||||
fn into_parts(self) -> (&'a str, Option<<DB as Database>::Arguments>) {
|
||||
(self.query, Some(self.arguments.into_arguments()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute the query, returning the rows as a futures `Stream`.
|
||||
///
|
||||
/// Use [fetch_all] if you want a `Vec` instead.
|
||||
pub fn fetch<'e, E>(self, executor: &'e mut E) -> BoxStream<'e, crate::Result<DB::Row>>
|
||||
impl<'a, DB, P> Query<'a, DB, P>
|
||||
where
|
||||
E: Executor<Database = DB>,
|
||||
'q: 'e,
|
||||
DB: Database,
|
||||
P: IntoArguments<DB> + Send,
|
||||
{
|
||||
executor.fetch(self.query, self.arguments.into_arguments())
|
||||
pub fn execute<'b, E>(self, executor: E) -> impl Future<Output = crate::Result<u64>> + 'b
|
||||
where
|
||||
E: Executor<'b, Database = DB>,
|
||||
'a: 'b,
|
||||
{
|
||||
executor.execute(self)
|
||||
}
|
||||
|
||||
/// Execute the query and get all rows from the result as a `Vec`.
|
||||
pub async fn fetch_all<E>(self, executor: &mut E) -> crate::Result<Vec<DB::Row>>
|
||||
pub fn fetch<'b, E>(self, executor: E) -> <DB as HasCursor<'b>>::Cursor
|
||||
where
|
||||
E: Executor<Database = DB>,
|
||||
E: Executor<'b, Database = DB>,
|
||||
'a: 'b,
|
||||
{
|
||||
executor
|
||||
.fetch(self.query, self.arguments.into_arguments())
|
||||
.try_collect()
|
||||
.await
|
||||
executor.execute(self)
|
||||
}
|
||||
|
||||
/// Execute a query which should return either 0 or 1 rows.
|
||||
///
|
||||
/// Returns [crate::Error::FoundMoreThanOne] if more than 1 row is returned.
|
||||
/// Use `.fetch().try_next()` if you just want one row.
|
||||
pub async fn fetch_optional<E>(self, executor: &mut E) -> crate::Result<Option<DB::Row>>
|
||||
pub async fn fetch_optional<'b, E>(
|
||||
self,
|
||||
executor: E,
|
||||
) -> crate::Result<Option<<DB as HasRow>::Row>>
|
||||
where
|
||||
E: Executor<Database = DB>,
|
||||
E: Executor<'b, Database = DB>,
|
||||
{
|
||||
executor
|
||||
.fetch_optional(self.query, self.arguments.into_arguments())
|
||||
.await
|
||||
executor.execute(self).first().await
|
||||
}
|
||||
|
||||
/// Execute a query which should return exactly 1 row.
|
||||
///
|
||||
/// * Returns [crate::Error::NotFound] if 0 rows are returned.
|
||||
/// * Returns [crate::Error::FoundMoreThanOne] if more than one row is returned.
|
||||
pub async fn fetch_one<E>(self, executor: &mut E) -> crate::Result<DB::Row>
|
||||
pub async fn fetch_one<'b, E>(self, executor: E) -> crate::Result<<DB as HasRow>::Row>
|
||||
where
|
||||
E: Executor<Database = DB>,
|
||||
E: Executor<'b, Database = DB>,
|
||||
{
|
||||
executor
|
||||
.fetch_one(self.query, self.arguments.into_arguments())
|
||||
self.fetch_optional(executor)
|
||||
.and_then(|row| match row {
|
||||
Some(row) => ready(Ok(row)),
|
||||
None => ready(Err(crate::Error::NotFound)),
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
@ -92,12 +81,6 @@ where
|
|||
DB: Database,
|
||||
{
|
||||
/// Bind a value for use with this SQL query.
|
||||
///
|
||||
/// # 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<T>(mut self, value: T) -> Self
|
||||
where
|
||||
DB: HasSqlType<T>,
|
||||
|
@ -108,17 +91,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Construct a full SQL query that can be chained to bind parameters and executed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let names: Vec<String> = sqlx::query("SELECT name FROM users WHERE active = ?")
|
||||
/// .bind(false) // [active = ?]
|
||||
/// .fetch(&mut connection) // -> Stream<Item = impl Row>
|
||||
/// .map_ok(|row| row.name("name")) // -> Stream<Item = String>
|
||||
/// .try_collect().await?; // -> Vec<String>
|
||||
/// ```
|
||||
/// Construct a raw SQL query that can be chained to bind parameters and executed.
|
||||
pub fn query<DB>(sql: &str) -> Query<DB>
|
||||
where
|
||||
DB: Database,
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
use futures_core::Stream;
|
||||
use futures_util::{future, TryStreamExt};
|
||||
|
||||
use crate::arguments::{Arguments, ImmutableArguments};
|
||||
use crate::{
|
||||
arguments::IntoArguments, database::Database, encode::Encode, executor::Executor, row::FromRow,
|
||||
types::HasSqlType,
|
||||
};
|
||||
|
||||
/// SQL query with bind parameters, which maps rows to an explicit output type.
|
||||
///
|
||||
/// Returned by [query_as] and [query!] *et al*.
|
||||
///
|
||||
/// The methods on this struct should be passed a reference to [crate::Pool] or one of
|
||||
/// the connection types.
|
||||
pub struct QueryAs<'q, DB, R, P = <DB as Database>::Arguments>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
query: &'q str,
|
||||
args: P,
|
||||
map_row: fn(DB::Row) -> crate::Result<R>,
|
||||
}
|
||||
|
||||
/// The result of [query!] for SQL queries that does not return output.
|
||||
impl<DB, P> QueryAs<'_, DB, (), P>
|
||||
where
|
||||
DB: Database,
|
||||
P: IntoArguments<DB> + Send,
|
||||
{
|
||||
/// Execute the query for its side-effects.
|
||||
///
|
||||
/// Returns the number of rows affected, or 0 if not applicable.
|
||||
pub async fn execute<E>(self, executor: &mut E) -> crate::Result<u64>
|
||||
where
|
||||
E: Executor<Database = DB>,
|
||||
{
|
||||
executor
|
||||
.execute(self.query, self.args.into_arguments())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB, R, P> QueryAs<'q, DB, R, P>
|
||||
where
|
||||
DB: Database,
|
||||
P: IntoArguments<DB> + Send,
|
||||
R: Send + 'q,
|
||||
{
|
||||
/// Execute the query, returning the rows as a futures `Stream`.
|
||||
///
|
||||
/// Use [fetch_all] if you want a `Vec` instead.
|
||||
pub fn fetch<'e, E>(self, executor: &'e mut E) -> impl Stream<Item = crate::Result<R>> + 'e
|
||||
where
|
||||
E: Executor<Database = DB>,
|
||||
'q: 'e,
|
||||
{
|
||||
let Self {
|
||||
query,
|
||||
args,
|
||||
map_row,
|
||||
..
|
||||
} = self;
|
||||
executor
|
||||
.fetch(query, args.into_arguments())
|
||||
.and_then(move |row| future::ready(map_row(row)))
|
||||
}
|
||||
|
||||
/// Execute the query and get all rows from the result as a `Vec`.
|
||||
pub async fn fetch_all<E>(self, executor: &mut E) -> crate::Result<Vec<R>>
|
||||
where
|
||||
E: Executor<Database = DB>,
|
||||
{
|
||||
self.fetch(executor).try_collect().await
|
||||
}
|
||||
|
||||
/// Execute a query which should return either 0 or 1 rows.
|
||||
///
|
||||
/// Returns [crate::Error::FoundMoreThanOne] if more than 1 row is returned.
|
||||
/// Use `.fetch().try_next()` if you just want one row.
|
||||
pub async fn fetch_optional<E>(self, executor: &mut E) -> crate::Result<Option<R>>
|
||||
where
|
||||
E: Executor<Database = DB>,
|
||||
{
|
||||
executor
|
||||
.fetch_optional(self.query, self.args.into_arguments())
|
||||
.await?
|
||||
.map(self.map_row)
|
||||
.transpose()
|
||||
}
|
||||
|
||||
/// Execute a query which should return exactly 1 row.
|
||||
///
|
||||
/// * Returns [crate::Error::NotFound] if 0 rows are returned.
|
||||
/// * Returns [crate::Error::FoundMoreThanOne] if more than one row is returned.
|
||||
pub async fn fetch_one<E>(self, executor: &mut E) -> crate::Result<R>
|
||||
where
|
||||
E: Executor<Database = DB>,
|
||||
{
|
||||
(self.map_row)(
|
||||
executor
|
||||
.fetch_one(self.query, self.args.into_arguments())
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB, R> QueryAs<'q, DB, R>
|
||||
where
|
||||
DB: Database,
|
||||
DB::Arguments: Arguments<Database = DB>,
|
||||
{
|
||||
/// Bind a value for use with this SQL query.
|
||||
///
|
||||
/// # 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<T>(mut self, value: T) -> Self
|
||||
where
|
||||
DB: HasSqlType<T>,
|
||||
T: Encode<DB>,
|
||||
{
|
||||
self.args.add(value);
|
||||
self
|
||||
}
|
||||
|
||||
// used by query!() and friends
|
||||
#[doc(hidden)]
|
||||
pub fn bind_all(self, values: DB::Arguments) -> QueryAs<'q, DB, R, ImmutableArguments<DB>> {
|
||||
QueryAs {
|
||||
query: self.query,
|
||||
args: ImmutableArguments(values),
|
||||
map_row: self.map_row,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a dynamic SQL query with an explicit output type implementing [FromRow].
|
||||
#[inline]
|
||||
pub fn query_as<DB, T>(query: &str) -> QueryAs<DB, T>
|
||||
where
|
||||
DB: Database,
|
||||
T: FromRow<DB::Row>,
|
||||
{
|
||||
QueryAs {
|
||||
query,
|
||||
args: Default::default(),
|
||||
map_row: |row| Ok(T::from_row(row)),
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn query_as_mapped<DB, T>(
|
||||
query: &str,
|
||||
map_row: fn(DB::Row) -> crate::Result<T>,
|
||||
) -> QueryAs<DB, T>
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
QueryAs {
|
||||
query,
|
||||
args: Default::default(),
|
||||
map_row,
|
||||
}
|
||||
}
|
|
@ -1,17 +1,19 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::BoxStream;
|
||||
|
||||
use crate::connection::Connection;
|
||||
use crate::database::Database;
|
||||
use crate::describe::Describe;
|
||||
use crate::executor::Executor;
|
||||
use crate::database::HasCursor;
|
||||
use crate::executor::{Execute, Executor};
|
||||
use crate::runtime::spawn;
|
||||
use crate::Database;
|
||||
|
||||
// Transaction<PoolConnection<PgConnection>>
|
||||
// Transaction<PgConnection>
|
||||
pub struct Transaction<T>
|
||||
where
|
||||
T: Connection + Send + 'static,
|
||||
T: Connection,
|
||||
T: Executor<'static>,
|
||||
{
|
||||
inner: Option<T>,
|
||||
depth: u32,
|
||||
|
@ -19,15 +21,16 @@ where
|
|||
|
||||
impl<T> Transaction<T>
|
||||
where
|
||||
T: Connection + Send + 'static,
|
||||
T: Connection,
|
||||
T: Executor<'static>,
|
||||
{
|
||||
pub(crate) async fn new(depth: u32, mut inner: T) -> crate::Result<Self> {
|
||||
if depth == 0 {
|
||||
inner.send("BEGIN").await?;
|
||||
inner.execute_by_ref("BEGIN").await?;
|
||||
} else {
|
||||
let stmt = format!("SAVEPOINT _sqlx_savepoint_{}", depth);
|
||||
|
||||
inner.send(&stmt).await?;
|
||||
inner.execute_by_ref(&*stmt).await?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
|
@ -45,11 +48,11 @@ where
|
|||
let depth = self.depth;
|
||||
|
||||
if depth == 1 {
|
||||
inner.send("COMMIT").await?;
|
||||
inner.execute_by_ref("COMMIT").await?;
|
||||
} else {
|
||||
let stmt = format!("RELEASE SAVEPOINT _sqlx_savepoint_{}", depth - 1);
|
||||
|
||||
inner.send(&stmt).await?;
|
||||
inner.execute_by_ref(&*stmt).await?;
|
||||
}
|
||||
|
||||
Ok(inner)
|
||||
|
@ -60,11 +63,11 @@ where
|
|||
let depth = self.depth;
|
||||
|
||||
if depth == 1 {
|
||||
inner.send("ROLLBACK").await?;
|
||||
inner.execute_by_ref("ROLLBACK").await?;
|
||||
} else {
|
||||
let stmt = format!("ROLLBACK TO SAVEPOINT _sqlx_savepoint_{}", depth - 1);
|
||||
|
||||
inner.send(&stmt).await?;
|
||||
inner.execute_by_ref(&*stmt).await?;
|
||||
}
|
||||
|
||||
Ok(inner)
|
||||
|
@ -73,20 +76,22 @@ where
|
|||
|
||||
const ERR_FINALIZED: &str = "(bug) transaction already finalized";
|
||||
|
||||
impl<Conn> Deref for Transaction<Conn>
|
||||
impl<T> Deref for Transaction<T>
|
||||
where
|
||||
Conn: Connection,
|
||||
T: Connection,
|
||||
T: Executor<'static>,
|
||||
{
|
||||
type Target = Conn;
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner.as_ref().expect(ERR_FINALIZED)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Conn> DerefMut for Transaction<Conn>
|
||||
impl<T> DerefMut for Transaction<T>
|
||||
where
|
||||
Conn: Connection,
|
||||
T: Connection,
|
||||
T: Executor<'static>,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.inner.as_mut().expect(ERR_FINALIZED)
|
||||
|
@ -96,64 +101,52 @@ where
|
|||
impl<T> Connection for Transaction<T>
|
||||
where
|
||||
T: Connection,
|
||||
T: Executor<'static>,
|
||||
{
|
||||
type Database = <T as Connection>::Database;
|
||||
|
||||
// Close is equivalent to ROLLBACK followed by CLOSE
|
||||
fn close(self) -> BoxFuture<'static, crate::Result<()>> {
|
||||
Box::pin(async move { self.rollback().await?.close().await })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Executor for Transaction<T>
|
||||
impl<'a, DB, T> Executor<'a> for &'a mut Transaction<T>
|
||||
where
|
||||
DB: Database,
|
||||
T: Connection<Database = DB>,
|
||||
T: Executor<'static, Database = DB>,
|
||||
{
|
||||
type Database = <T as Connection>::Database;
|
||||
|
||||
fn execute<'b, E>(self, query: E) -> <<T as Connection>::Database as HasCursor<'a>>::Cursor
|
||||
where
|
||||
E: Execute<'b, Self::Database>,
|
||||
{
|
||||
(**self).execute_by_ref(query)
|
||||
}
|
||||
|
||||
fn execute_by_ref<'b, 'c, E>(
|
||||
&'c mut self,
|
||||
query: E,
|
||||
) -> <Self::Database as HasCursor<'c>>::Cursor
|
||||
where
|
||||
E: Execute<'b, Self::Database>,
|
||||
{
|
||||
(**self).execute_by_ref(query)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Transaction<T>
|
||||
where
|
||||
T: Connection,
|
||||
{
|
||||
type Database = T::Database;
|
||||
|
||||
fn send<'e, 'q: 'e>(&'e mut self, commands: &'q str) -> BoxFuture<'e, crate::Result<()>> {
|
||||
self.deref_mut().send(commands)
|
||||
}
|
||||
|
||||
fn execute<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <Self::Database as Database>::Arguments,
|
||||
) -> BoxFuture<'e, crate::Result<u64>> {
|
||||
self.deref_mut().execute(query, args)
|
||||
}
|
||||
|
||||
fn fetch<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <Self::Database as Database>::Arguments,
|
||||
) -> BoxStream<'e, crate::Result<<Self::Database as Database>::Row>> {
|
||||
self.deref_mut().fetch(query, args)
|
||||
}
|
||||
|
||||
fn fetch_optional<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
args: <Self::Database as Database>::Arguments,
|
||||
) -> BoxFuture<'e, crate::Result<Option<<Self::Database as Database>::Row>>> {
|
||||
self.deref_mut().fetch_optional(query, args)
|
||||
}
|
||||
|
||||
fn describe<'e, 'q: 'e>(
|
||||
&'e mut self,
|
||||
query: &'q str,
|
||||
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>> {
|
||||
self.deref_mut().describe(query)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Conn> Drop for Transaction<Conn>
|
||||
where
|
||||
Conn: Connection,
|
||||
T: Executor<'static>,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
if self.depth > 0 {
|
||||
if let Some(mut inner) = self.inner.take() {
|
||||
spawn(async move {
|
||||
let res = inner.send("ROLLBACK").await;
|
||||
let res = inner.execute_by_ref("ROLLBACK").await;
|
||||
|
||||
// If the rollback failed we need to close the inner connection
|
||||
if res.is_err() {
|
||||
|
|
|
@ -17,10 +17,7 @@ pub use sqlx_core::{
|
|||
};
|
||||
|
||||
// Functions
|
||||
pub use sqlx_core::{query, query_as};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use sqlx_core::query_as_mapped;
|
||||
pub use sqlx_core::query;
|
||||
|
||||
#[cfg(feature = "mysql")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "mysql")))]
|
||||
|
|
Loading…
Reference in a new issue