add Cursor and rewrite Executor/Query over it

* this breaks a lot internally as-is
 * mysql needs a restructure
This commit is contained in:
Ryan Leckey 2020-02-19 02:18:44 -08:00
parent bb17ebfbbd
commit ea1a4fb042
17 changed files with 407 additions and 763 deletions

View file

@ -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
View 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;
}

View file

@ -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>;
}

View file

@ -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;
/// 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.
///
/// 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;
/// Send a raw SQL command to the database.
///
/// 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<()>>;
/// 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)
}
}

View file

@ -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;

View file

@ -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]>;
}

View file

@ -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>> {

View file

@ -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!()
}
}

View file

@ -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())
}

View 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!()
}
}

View file

@ -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]>;
}

View file

@ -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
}
}
}
}
Ok(())
}
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 => {}
Message::CommandComplete(body) => {
return Ok(Some(Step::Command(body.affected_rows)));
}
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());
impl<'e> Executor<'e> for &'e mut super::PgConnection {
type Database = Postgres;
self.wait_until_ready().await?;
fn execute<'q, E>(self, query: E) -> PgCursor<'e>
where
E: Execute<'q, Self::Database>,
{
let (query, arguments) = query.into_parts();
self.stream.flush().await?;
self.ready = false;
// TODO: Handle [arguments] being None. This should be a SIMPLE query.
let arguments = arguments.unwrap();
while let Some(_step) = self.step().await? {
// Drain the stream until ReadyForQuery
}
// 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);
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)
}
}

View file

@ -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;

View file

@ -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>
fn into_parts(self) -> (&'a str, Option<<DB as Database>::Arguments>) {
(self.query, Some(self.arguments.into_arguments()))
}
}
impl<'a, DB, P> Query<'a, DB, P>
where
DB: Database,
P: IntoArguments<DB> + Send,
{
pub fn execute<'b, E>(self, executor: E) -> impl Future<Output = crate::Result<u64>> + 'b
where
E: Executor<Database = DB>,
E: Executor<'b, Database = DB>,
'a: 'b,
{
executor
.execute(self.query, self.arguments.into_arguments())
.await
executor.execute(self)
}
/// 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>>
pub fn fetch<'b, E>(self, executor: E) -> <DB as HasCursor<'b>>::Cursor
where
E: Executor<Database = DB>,
'q: 'e,
E: Executor<'b, Database = DB>,
'a: 'b,
{
executor.fetch(self.query, self.arguments.into_arguments())
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 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(self.query, self.arguments.into_arguments())
.try_collect()
.await
executor.execute(self).first().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<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_optional(self.query, self.arguments.into_arguments())
.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>
where
E: Executor<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,

View file

@ -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,
}
}

View file

@ -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
T: Connection,
DB: Database,
T: Connection<Database = DB>,
T: Executor<'static, Database = DB>,
{
type Database = T::Database;
type Database = <T as Connection>::Database;
fn send<'e, 'q: 'e>(&'e mut self, commands: &'q str) -> BoxFuture<'e, crate::Result<()>> {
self.deref_mut().send(commands)
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<'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)
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<Conn> Drop for Transaction<Conn>
impl<T> Drop for Transaction<T>
where
Conn: Connection,
T: 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() {

View file

@ -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")))]