reactor(core): remove Rt param from Database trait and remove Connection associated type

- significantly cleans up the sqlx/ zero-prelude wrapping
This commit is contained in:
Ryan Leckey 2021-02-12 01:06:53 -08:00
parent 0ce4a6a418
commit 8203410e3f
No known key found for this signature in database
GPG Key ID: F8AA68C235AB08C9
28 changed files with 137 additions and 142 deletions

View File

@ -1,11 +1,11 @@
#[cfg(feature = "async")]
use futures_util::future::BoxFuture;
use crate::{Database, Runtime};
use crate::{Connection, Database, Runtime};
#[allow(clippy::type_complexity)]
pub trait Acquire<Rt: Runtime> {
type Database: Database<Rt>;
type Connection: Connection<Rt>;
/// Get a connection from the pool, make a new connection, or wait for one to become
/// available.
@ -13,33 +13,27 @@ pub trait Acquire<Rt: Runtime> {
/// Takes exclusive use of the connection until it is released.
///
#[cfg(feature = "async")]
fn acquire(
self,
) -> BoxFuture<'static, crate::Result<<Self::Database as Database<Rt>>::Connection>>
fn acquire(self) -> BoxFuture<'static, crate::Result<Self::Connection>>
where
<Self::Database as Database<Rt>>::Connection: Sized;
Self::Connection: Sized;
/// Get a connection from the pool, if available.
///
/// Returns `None` immediately if there are no connections available.
///
fn try_acquire(self) -> Option<<Self::Database as Database<Rt>>::Connection>
///
fn try_acquire(self) -> Option<Self::Connection>
where
<Self::Database as Database<Rt>>::Connection: Sized;
Self::Connection: Sized;
#[cfg(feature = "async")]
fn begin(
self,
) -> BoxFuture<'static, crate::Result<<Self::Database as Database<Rt>>::Connection>>
fn begin(self) -> BoxFuture<'static, crate::Result<Self::Connection>>
where
<Self::Database as Database<Rt>>::Connection: Sized;
Self::Connection: Sized;
#[cfg(feature = "async")]
fn try_begin(
self,
) -> BoxFuture<'static, crate::Result<Option<<Self::Database as Database<Rt>>::Connection>>>
fn try_begin(self) -> BoxFuture<'static, crate::Result<Option<Self::Connection>>>
where
<Self::Database as Database<Rt>>::Connection: Sized;
Self::Connection: Sized;
}
// TODO: impl Acquire for &Pool { ... }

View File

@ -6,14 +6,14 @@ mod acquire;
mod close;
mod connect;
mod connection;
mod options;
mod executor;
mod options;
pub(crate) mod runtime;
pub use executor::Executor;
pub use acquire::Acquire;
pub use close::Close;
pub use connect::Connect;
pub use connection::Connection;
pub use executor::Executor;
pub use options::ConnectOptions;
pub use runtime::Runtime;

View File

@ -13,17 +13,17 @@ where
/// For detailed information, refer to the async version of
/// this: [`acquire()`][crate::Acquire::acquire].
///
fn acquire(self) -> crate::Result<<Self::Database as Database<Rt>>::Connection>
fn acquire(self) -> crate::Result<Self::Connection>
where
<Self::Database as Database<Rt>>::Connection: Sized;
Self::Connection: Sized;
fn begin(self) -> crate::Result<<Self::Database as Database<Rt>>::Connection>
fn begin(self) -> crate::Result<Self::Connection>
where
<Self::Database as Database<Rt>>::Connection: Sized;
Self::Connection: Sized;
fn try_begin(self) -> crate::Result<Option<<Self::Database as Database<Rt>>::Connection>>
fn try_begin(self) -> crate::Result<Option<Self::Connection>>
where
<Self::Database as Database<Rt>>::Connection: Sized;
Self::Connection: Sized;
}
// TODO: impl Acquire for &Pool { ... }

View File

@ -3,12 +3,12 @@ use crate::Database;
pub trait Executor<Rt: Runtime>: crate::Executor<Rt>
where
Self::Database: Database<Rt>,
Self::Database: Database,
{
fn execute<'x, 'e, 'q>(
&'e mut self,
sql: &'q str,
) -> crate::Result<<Self::Database as Database<Rt>>::QueryResult>
) -> crate::Result<<Self::Database as Database>::QueryResult>
where
'e: 'x,
'q: 'x;
@ -16,7 +16,7 @@ where
fn fetch_all<'x, 'e, 'q>(
&'e mut self,
sql: &'q str,
) -> crate::Result<Vec<<Self::Database as Database<Rt>>::Row>>
) -> crate::Result<Vec<<Self::Database as Database>::Row>>
where
'e: 'x,
'q: 'x;
@ -24,7 +24,7 @@ where
fn fetch_optional<'x, 'e, 'q>(
&'e mut self,
sql: &'q str,
) -> crate::Result<Option<<Self::Database as Database<Rt>>::Row>>
) -> crate::Result<Option<<Self::Database as Database>::Row>>
where
'e: 'x,
'q: 'x;

View File

@ -16,7 +16,7 @@ pub trait Connection<Rt>: 'static + Send + Connect<Rt> + Close<Rt>
where
Rt: Runtime,
{
type Database: Database<Rt, Connection = Self>;
type Database: Database;
/// Checks if a connection to the database is still valid.
///

View File

@ -1,25 +1,21 @@
use std::fmt::Debug;
use crate::{Column, Connection, QueryResult, Row, Runtime};
use crate::{Column, QueryResult, Row};
/// A database driver.
///
/// This trait encapsulates a complete set of traits that implement a driver for a
/// specific database (e.g., MySQL, PostgreSQL).
/// Represents a family of traits for interacting with a database. This is
/// separate from [`Connection`][crate::Connection]. One database driver may
/// have multiple concrete `Connection` implementations.
///
pub trait Database<Rt>:
pub trait Database:
'static + Sized + Debug + for<'x> HasOutput<'x> + for<'r> HasRawValue<'r>
where
Rt: Runtime,
{
/// The concrete [`Connection`] implementation for this database.
type Connection: Connection<Rt, Database = Self> + ?Sized;
/// The concrete [`Column`] implementation for this database.
type Column: Column;
/// The concrete [`Row`] implementation for this database.
type Row: Row<Column = Self::Column>;
type Row: Row<Database = Self>;
/// The concrete [`QueryResult`] implementation for this database.
type QueryResult: QueryResult;

View File

@ -6,18 +6,15 @@ use crate::database::HasRawValue;
use crate::{Database, Runtime};
/// A type that can be decoded from a SQL value.
pub trait Decode<'r, Db: Database<Rt>, Rt: Runtime>: Sized + Send + Sync {
pub trait Decode<'r, Db: Database>: Sized + Send + Sync {
fn decode(value: <Db as HasRawValue<'r>>::RawValue) -> Result<Self>;
}
/// A type that can be decoded from a SQL value, without borrowing any data
/// from the row.
pub trait DecodeOwned<Db: Database<Rt>, Rt: Runtime>: for<'de> Decode<'de, Db, Rt> {}
pub trait DecodeOwned<Db: Database>: for<'r> Decode<'r, Db> {}
impl<T, Db: Database<Rt>, Rt: Runtime> DecodeOwned<Db, Rt> for T where
T: for<'de> Decode<'de, Db, Rt>
{
}
impl<T, Db: Database> DecodeOwned<Db> for T where T: for<'r> Decode<'r, Db> {}
/// Errors which can occur while decoding a SQL value.
#[derive(Debug)]

View File

@ -5,7 +5,7 @@ use crate::database::{HasOutput, HasRawValue};
use crate::{Database, Runtime};
/// A type that can be encoded into a SQL value.
pub trait Encode<Db: Database<Rt>, Rt: Runtime>: Send + Sync {
pub trait Encode<Db: Database>: Send + Sync {
/// Encode this value into a SQL value.
fn encode(&self, ty: &Db::TypeInfo, out: &mut <Db as HasOutput<'_>>::Output) -> Result<()>;
@ -16,7 +16,7 @@ pub trait Encode<Db: Database<Rt>, Rt: Runtime>: Send + Sync {
}
}
impl<T: Encode<Db, Rt>, Db: Database<Rt>, Rt: Runtime> Encode<Db, Rt> for &T {
impl<T: Encode<Db>, Db: Database> Encode<Db> for &T {
#[inline]
fn encode(&self, ty: &Db::TypeInfo, out: &mut <Db as HasOutput<'_>>::Output) -> Result<()> {
(*self).encode(ty, out)

View File

@ -33,6 +33,11 @@ pub enum Error {
Decode(DecodeError),
Encode(EncodeError),
ColumnIndexOutOfBounds {
index: usize,
len: usize,
},
}
impl Error {
@ -84,6 +89,14 @@ impl Display for Error {
Self::Encode(error) => {
write!(f, "{}", error)
}
Self::ColumnIndexOutOfBounds { index, len } => {
write!(
f,
"column index out of bounds: the len is {}, but the index is {}",
len, index
)
}
}
}
}
@ -92,7 +105,6 @@ impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Self::Configuration { source: Some(source), .. } => Some(&**source),
Self::Network(source) => Some(source),
_ => None,

View File

@ -13,7 +13,7 @@ use crate::{Database, Error, Result, Runtime};
///
#[allow(clippy::type_complexity)]
pub trait Executor<Rt: Runtime> {
type Database: Database<Rt>;
type Database: Database;
/// Execute the SQL query and return information about the result, including
/// the number of rows affected, if any.
@ -21,7 +21,7 @@ pub trait Executor<Rt: Runtime> {
fn execute<'x, 'e, 'q>(
&'e mut self,
sql: &'q str,
) -> BoxFuture<'x, Result<<Self::Database as Database<Rt>>::QueryResult>>
) -> BoxFuture<'x, Result<<Self::Database as Database>::QueryResult>>
where
Rt: crate::Async,
'e: 'x,
@ -31,7 +31,7 @@ pub trait Executor<Rt: Runtime> {
fn fetch_all<'x, 'e, 'q>(
&'e mut self,
sql: &'q str,
) -> BoxFuture<'x, Result<Vec<<Self::Database as Database<Rt>>::Row>>>
) -> BoxFuture<'x, Result<Vec<<Self::Database as Database>::Row>>>
where
Rt: crate::Async,
'e: 'x,
@ -41,7 +41,7 @@ pub trait Executor<Rt: Runtime> {
fn fetch_optional<'x, 'e, 'q>(
&'e mut self,
sql: &'q str,
) -> BoxFuture<'x, Result<Option<<Self::Database as Database<Rt>>::Row>>>
) -> BoxFuture<'x, Result<Option<<Self::Database as Database>::Row>>>
where
Rt: crate::Async,
'e: 'x,
@ -51,7 +51,7 @@ pub trait Executor<Rt: Runtime> {
fn fetch_one<'x, 'e, 'q>(
&'e mut self,
sql: &'q str,
) -> BoxFuture<'x, Result<<Self::Database as Database<Rt>>::Row>>
) -> BoxFuture<'x, Result<<Self::Database as Database>::Row>>
where
Rt: crate::Async,
'e: 'x,

View File

@ -29,9 +29,8 @@ pub mod encode;
mod error;
mod executor;
mod options;
mod pool;
mod query_result;
mod row;
pub mod row;
mod runtime;
#[doc(hidden)]
@ -60,7 +59,6 @@ pub use encode::Encode;
pub use error::{DatabaseError, Error, Result};
pub use executor::Executor;
pub use options::ConnectOptions;
pub use pool::Pool;
pub use query_result::QueryResult;
pub use row::Row;
#[cfg(feature = "actix")]

View File

@ -1,17 +0,0 @@
use std::marker::PhantomData;
use crate::{Database, Runtime};
/// A connection pool to enable the efficient reuse of a managed pool of SQL connections.
pub struct Pool<Db, Rt>
where
Rt: Runtime,
Db: Database<Rt>,
{
runtime: PhantomData<Rt>,
database: PhantomData<Db>,
}
// TODO: impl Acquire for &Pool
// TODO: impl Connect for Pool
// TODO: impl Close for Pool

View File

@ -1,7 +1,8 @@
use crate::{Column, Database, Runtime};
use crate::database::HasRawValue;
use crate::{Column, Database, Decode, Runtime};
pub trait Row: 'static + Send + Sync {
type Column: Column;
type Database: Database;
/// Returns `true` if the row contains only `NULL` values.
fn is_null(&self) -> bool;
@ -15,7 +16,7 @@ pub trait Row: 'static + Send + Sync {
}
/// Returns a reference to the columns in the row.
fn columns(&self) -> &[Self::Column];
fn columns(&self) -> &[<Self::Database as Database>::Column];
/// Returns the column name, given the ordinal (also known as index) of the column.
fn column_name_of(&self, ordinal: usize) -> &str;
@ -29,7 +30,17 @@ pub trait Row: 'static + Send + Sync {
/// Returns the column ordinal, given the name of the column.
fn try_ordinal_of(&self, name: &str) -> crate::Result<usize>;
fn try_get_raw(&self) -> crate::Result<&[u8]>;
/// Returns the decoded value at the index.
fn try_get<'r, T>(&'r self, index: usize) -> crate::Result<T>
where
T: Decode<'r, Self::Database>;
/// Returns the raw representation of the value at the index.
// noinspection RsNeedlessLifetimes
fn try_get_raw<'r>(
&'r self,
index: usize,
) -> crate::Result<<Self::Database as HasRawValue<'r>>::RawValue>;
}
// TODO: fn type_info_of(index)

View File

@ -31,7 +31,7 @@ macro_rules! impl_fetch_all {
// execute ignores any rows returned
// but we do increment affected rows
QueryStep::End(res) => break 'result res.into_result()?,
QueryStep::Row(row) => rows.push(MySqlRow(row.deserialize_with(&columns[..])?)),
QueryStep::Row(row) => rows.push(MySqlRow::new(row.deserialize_with(&columns[..])?)),
}
}
}

View File

@ -33,7 +33,7 @@ macro_rules! impl_fetch_optional {
// but we do increment affected rows
QueryStep::End(res) => break 'result res.into_result()?,
QueryStep::Row(row) => {
first_row = Some(MySqlRow(row.deserialize_with(&columns[..])?));
first_row = Some(MySqlRow::new(row.deserialize_with(&columns[..])?));
// get out as soon as possible after finding our one row
break 'results;

View File

@ -1,26 +1,23 @@
use sqlx_core::database::{HasOutput, HasRawValue};
use sqlx_core::{Database, Runtime};
use sqlx_core::Database;
use super::{
MySqlColumn, MySqlConnection, MySqlOutput, MySqlQueryResult, MySqlRawValue, MySqlRow,
MySqlTypeId, MySqlTypeInfo,
MySqlColumn, MySqlOutput, MySqlQueryResult, MySqlRawValue, MySqlRow, MySqlTypeId, MySqlTypeInfo,
};
#[derive(Debug)]
pub struct MySql;
impl<Rt: Runtime> Database<Rt> for MySql {
type Connection = MySqlConnection<Rt>;
impl Database for MySql {
type Column = MySqlColumn;
type Row = MySqlRow;
type QueryResult = MySqlQueryResult;
type TypeId = MySqlTypeId;
type TypeInfo = MySqlTypeInfo;
type TypeId = MySqlTypeId;
}
impl<'x> HasOutput<'x> for MySql {

View File

@ -8,20 +8,18 @@ mod eof;
mod err;
mod handshake;
mod handshake_response;
mod info;
mod ok;
mod packet;
mod ping;
mod query;
mod query_response;
mod info;
mod result;
mod query_step;
mod packet;
mod quit;
mod result;
mod row;
mod status;
pub(crate) use info::Info;
pub(crate) use packet::Packet;
pub(crate) use auth_plugin::AuthPlugin;
pub(crate) use auth_response::AuthResponse;
pub(crate) use auth_switch::AuthSwitch;
@ -30,14 +28,16 @@ pub(crate) use column_def::ColumnDefinition;
pub(crate) use command::{Command, MaybeCommand};
pub(crate) use eof::EofPacket;
pub(crate) use err::ErrPacket;
pub(crate) use result::ResultPacket;
pub(crate) use handshake::Handshake;
pub(crate) use handshake_response::HandshakeResponse;
pub(crate) use info::Info;
pub(crate) use ok::OkPacket;
pub(crate) use packet::Packet;
pub(crate) use ping::Ping;
pub(crate) use query::Query;
pub(crate) use query_response::QueryResponse;
pub(crate) use query_step::QueryStep;
pub(crate) use quit::Quit;
pub(crate) use result::ResultPacket;
pub(crate) use row::Row;
pub(crate) use status::Status;

View File

@ -35,11 +35,11 @@ impl Info {
// ignore records changed
// this is "rows affected" for UPDATE
"Changed" => {},
"Changed" => {}
// ignore warnings in info
// these are passed back differently
"Warnings" => {},
"Warnings" => {}
// unknown key
_ => failed = true,

View File

@ -1,14 +1,23 @@
use sqlx_core::Row;
use std::marker::PhantomData;
use crate::{protocol, MySqlColumn};
use bytes::Bytes;
use sqlx_core::{Decode, Error, Row, Runtime};
use crate::{protocol, MySql, MySqlColumn, MySqlRawValue, MySqlRawValueFormat};
#[allow(clippy::module_name_repetitions)]
pub struct MySqlRow(pub(crate) protocol::Row);
pub struct MySqlRow {
values: Vec<Option<Bytes>>,
}
impl MySqlRow {
pub(crate) fn new(row: protocol::Row) -> Self {
Self { values: row.values }
}
#[must_use]
pub fn len(&self) -> usize {
self.0.values.len()
self.values.len()
}
#[must_use]
@ -18,7 +27,7 @@ impl MySqlRow {
}
impl Row for MySqlRow {
type Column = MySqlColumn;
type Database = MySql;
fn is_null(&self) -> bool {
todo!()
@ -48,7 +57,22 @@ impl Row for MySqlRow {
todo!()
}
fn try_get_raw(&self) -> sqlx_core::Result<&[u8]> {
todo!()
fn try_get<'r, T>(&'r self, index: usize) -> sqlx_core::Result<T>
where
T: Decode<'r, Self::Database>,
{
Ok(self.try_get_raw(index)?.decode()?)
}
// noinspection RsNeedlessLifetimes
fn try_get_raw<'r>(&'r self, index: usize) -> sqlx_core::Result<MySqlRawValue<'r>> {
let format = MySqlRawValueFormat::Text;
let value = self
.values
.get(index)
.ok_or_else(|| Error::ColumnIndexOutOfBounds { len: self.len(), index })?;
Ok(MySqlRawValue::new(value, format))
}
}

View File

@ -16,13 +16,13 @@ impl MySqlTypeId {
/// directly used in an expression by itself, such as `SELECT NULL`.
///
pub const fn is_null(&self) -> bool {
matches!(self, MySqlTypeId::NULL)
matches!(*self, MySqlTypeId::NULL)
}
/// Returns `true` if this is an integer data type.
pub const fn is_integer(&self) -> bool {
matches!(
self,
*self,
MySqlTypeId::TINYINT
| MySqlTypeId::TINYINT_UNSIGNED
| MySqlTypeId::SMALLINT

View File

@ -35,4 +35,3 @@ impl MySqlTypeInfo {
self.id().name()
}
}
o

View File

@ -1,6 +1,6 @@
use bytes::BufMut;
use sqlx_core::{decode, encode};
use sqlx_core::{Decode, Encode, Runtime};
use sqlx_core::{Decode, Encode};
use crate::{MySql, MySqlOutput, MySqlRawValue, MySqlTypeId, MySqlTypeInfo};
@ -10,14 +10,14 @@ use crate::{MySql, MySqlOutput, MySqlRawValue, MySqlTypeId, MySqlTypeInfo};
// TODO: accepts(ty) -> ty.is_integer()
// TODO: compatible(ty) -> ty.is_integer()
impl<Rt: Runtime> Encode<MySql, Rt> for bool {
impl Encode<MySql> for bool {
fn encode(&self, ty: &MySqlTypeInfo, out: &mut MySqlOutput<'_>) -> encode::Result<()> {
<u8 as Encode<MySql, Rt>>::encode(&(*self as u8), ty, out)
<u8 as Encode<MySql>>::encode(&(*self as u8), ty, out)
}
}
impl<'r, Rt: Runtime> Decode<'r, MySql, Rt> for bool {
impl<'r> Decode<'r, MySql> for bool {
fn decode(raw: MySqlRawValue<'r>) -> decode::Result<Self> {
Ok(raw.decode::<u8, Rt>()? != 0)
Ok(raw.decode::<u8>()? != 0)
}
}

View File

@ -1,7 +1,7 @@
use bytes::Buf;
use sqlx_core::database::HasOutput;
use sqlx_core::{decode, encode};
use sqlx_core::{Database, Decode, Encode, Runtime};
use sqlx_core::{Database, Decode, Encode};
use crate::type_info::MySqlTypeInfo;
use crate::MySqlRawValueFormat::*;
@ -12,7 +12,7 @@ use crate::{MySql, MySqlOutput, MySqlRawValue, MySqlTypeId};
// TODO: accepts(ty) -> ty.is_integer()
// TODO: compatible(ty) -> ty.is_integer()
impl<Rt: Runtime> Encode<MySql, Rt> for u8 {
impl Encode<MySql> for u8 {
fn encode(&self, _: &MySqlTypeInfo, out: &mut MySqlOutput<'_>) -> encode::Result<()> {
out.buffer().push(*self);
@ -20,8 +20,10 @@ impl<Rt: Runtime> Encode<MySql, Rt> for u8 {
}
}
impl<'r, Rt: Runtime> Decode<'r, MySql, Rt> for u8 {
impl<'r> Decode<'r, MySql> for u8 {
fn decode(value: MySqlRawValue<'r>) -> decode::Result<Self> {
// FIXME: ensure that the SQL value fits within u8
Ok(match value.format() {
Binary => value.as_bytes()?.get_u8(),
Text => value.as_str()?.parse()?,

View File

@ -26,6 +26,10 @@ pub struct MySqlRawValue<'r> {
// 'r: row
impl<'r> MySqlRawValue<'r> {
pub(crate) fn new(value: &'r Option<Bytes>, format: MySqlRawValueFormat) -> Self {
Self { value: value.as_ref(), format }
}
/// Returns the format of this value.
pub const fn format(&self) -> MySqlRawValueFormat {
self.format
@ -43,7 +47,7 @@ impl<'r> MySqlRawValue<'r> {
}
/// Decode this value into the target type.
pub fn decode<T: Decode<'r, MySql, Rt>, Rt: Runtime>(self) -> DecodeResult<T> {
<T as Decode<'r, MySql, Rt>>::decode(self)
pub fn decode<T: Decode<'r, MySql>>(self) -> DecodeResult<T> {
<T as Decode<'r, MySql>>::decode(self)
}
}

View File

@ -66,5 +66,5 @@ pub use sqlx_core::AsyncStd;
#[cfg(feature = "tokio")]
pub use sqlx_core::Tokio;
pub use sqlx_core::{
Acquire, Close, Connect, ConnectOptions, Connection, Database, Error, Result, Runtime,
Acquire, Close, Connect, ConnectOptions, Connection, Database, Error, Result, Row, Runtime,
};

View File

@ -4,7 +4,6 @@
//!
mod connection;
mod database;
mod options;
#[cfg(feature = "blocking")]
@ -15,11 +14,10 @@ mod blocking;
// this is to provide runtime-specialized inherent methods by taking advantage
// of through crate-local negative reasoning
pub use connection::MySqlConnection;
pub use database::MySql;
pub use options::MySqlConnectOptions;
//
// re-export the remaining types from the driver
pub use sqlx_mysql::{
MySqlColumn, MySqlDatabaseError, MySqlQueryResult, MySqlRawValue, MySqlRawValueFormat,
MySqlRow, MySqlTypeId,
types, MySql, MySqlColumn, MySqlDatabaseError, MySqlQueryResult, MySqlRawValue,
MySqlRawValueFormat, MySqlRow, MySqlTypeId,
};

View File

@ -3,9 +3,8 @@ use std::fmt::{self, Debug, Formatter};
#[cfg(feature = "async")]
use futures_util::future::{BoxFuture, FutureExt};
use sqlx_core::Executor;
use sqlx_mysql::{MySqlQueryResult, MySqlRow};
use super::{MySql, MySqlConnectOptions};
use super::{MySql, MySqlConnectOptions, MySqlQueryResult, MySqlRow};
#[cfg(feature = "async")]
use crate::{Async, Result};
use crate::{Close, Connect, Connection, DefaultRuntime, Runtime};

View File

@ -1,19 +0,0 @@
use sqlx_core::HasOutput;
use sqlx_mysql::{MySqlColumn, MySqlQueryResult, MySqlRow};
use super::MySqlConnection;
use crate::{Database, Runtime};
#[derive(Debug)]
pub struct MySql;
impl<Rt: Runtime> Database<Rt> for MySql {
type Connection = MySqlConnection<Rt>;
type Column = MySqlColumn;
type Row = MySqlRow;
type QueryResult = MySqlQueryResult;
}
impl<'x> HasOutput<'x> for MySql {
type Output = &'x mut Vec<u8>;
}