fix Executor::describe() for MariaDB

prevent exhaustive match of `describe` structs
This commit is contained in:
Austin Bonander 2019-12-04 14:37:56 -08:00 committed by Ryan Leckey
parent 4d28424af5
commit b0a36ddd5c
4 changed files with 85 additions and 46 deletions

View file

@ -10,6 +10,7 @@ pub struct Describe<DB: Backend> {
pub param_types: Vec<<DB as HasTypeMetadata>::TypeId>,
///
pub result_fields: Vec<ResultField<DB>>,
pub(crate) _backcompat: (),
}
impl<DB: Backend> fmt::Debug for Describe<DB>
@ -30,6 +31,7 @@ pub struct ResultField<DB: Backend> {
pub table_id: Option<<DB as Backend>::TableIdent>,
/// The type ID of this result column.
pub type_id: <DB as HasTypeMetadata>::TypeId,
pub(crate) _backcompat: (),
}
impl<DB: Backend> fmt::Debug for ResultField<DB>

View file

@ -1,17 +1,12 @@
use super::establish;
use crate::{
io::{Buf, BufMut, BufStream},
mariadb::{
protocol::{
Capabilities, ColumnCountPacket, ColumnDefinitionPacket, ComPing, ComQuit,
ComStmtExecute, ComStmtPrepare, ComStmtPrepareOk, Encode, EofPacket, ErrPacket,
OkPacket, ResultRow, StmtExecFlag,
},
query::MariaDbQueryParameters,
use crate::{io::{Buf, BufMut, BufStream}, mariadb::{
protocol::{
Capabilities, ColumnCountPacket, ColumnDefinitionPacket, ComPing, ComQuit,
ComStmtExecute, ComStmtPrepare, ComStmtPrepareOk, Encode, EofPacket, ErrPacket,
OkPacket, ResultRow, StmtExecFlag,
},
url::Url,
Error, Result,
};
query::MariaDbQueryParameters,
}, url::Url, Error, Result, Describe, ResultField};
use async_std::net::TcpStream;
use byteorder::{ByteOrder, LittleEndian};
use std::{
@ -19,6 +14,8 @@ use std::{
net::{IpAddr, SocketAddr},
};
pub type StatementId = u32;
pub struct MariaDb {
pub(crate) stream: BufStream<TcpStream>,
pub(crate) rbuf: Vec<u8>,
@ -157,7 +154,20 @@ impl MariaDb {
})
}
pub(super) async fn send_prepare<'c>(
async fn check_eof(&mut self) -> Result<()> {
// When (legacy) EOFs are enabled, the fixed number column definitions are further
// terminated by an EOF packet
if !self
.capabilities
.contains(Capabilities::CLIENT_DEPRECATE_EOF)
{
let _eof = EofPacket::decode(self.receive().await?)?;
}
Ok(())
}
async fn send_prepare<'c>(
&'c mut self,
statement: &'c str,
) -> Result<ComStmtPrepareOk> {
@ -177,19 +187,21 @@ impl MariaDb {
let ok = ComStmtPrepareOk::decode(packet)?;
Ok(ok)
}
// MySQL/MariaDB responds with statement metadata for every PREPARE command
// sometimes we care, sometimes we don't
pub(super) async fn prepare_ignore_describe(&mut self, statement: &str) -> Result<StatementId> {
let ok = self.send_prepare(statement).await?;
// Input parameters
for _ in 0..ok.params {
// TODO: Maybe do something with this data ?
let _column = ColumnDefinitionPacket::decode(self.receive().await?)?;
}
// TODO: De-duplicate this
if !self
.capabilities
.contains(Capabilities::CLIENT_DEPRECATE_EOF)
{
let _eof = EofPacket::decode(self.receive().await?)?;
}
self.check_eof().await?;
// Output parameters
for _ in 0..ok.columns {
@ -197,18 +209,46 @@ impl MariaDb {
let _column = ColumnDefinitionPacket::decode(self.receive().await?)?;
}
// TODO: De-duplicate this
if !self
.capabilities
.contains(Capabilities::CLIENT_DEPRECATE_EOF)
{
let _eof = EofPacket::decode(self.receive().await?)?;
}
self.check_eof().await?;
Ok(ok)
Ok(ok.statement_id)
}
pub(super) async fn column_definitions(&mut self) -> Result<Vec<ColumnDefinitionPacket>> {
pub(super) async fn prepare_describe(&mut self, statement: &str) -> Result<Describe<Self>> {
let ok = self.send_prepare(statement).await?;
let mut param_types = Vec::with_capacity(ok.params as usize);
let mut result_fields= Vec::with_capacity(ok.columns as usize);
// Input parameters
for _ in 0..ok.params {
let param = ColumnDefinitionPacket::decode(self.receive().await?)?;
param_types.push(param.field_type.0);
}
self.check_eof().await?;
// Output parameters
for _ in 0..ok.columns {
let column = ColumnDefinitionPacket::decode(self.receive().await?)?;
result_fields.push(ResultField {
name: column.column_alias.or(column.column),
table_id: column.table_alias.or(column.table),
type_id: column.field_type.0,
_backcompat: ()
});
}
self.check_eof().await?;
Ok(Describe {
param_types,
result_fields,
_backcompat: (),
})
}
pub(super) async fn result_column_defs(&mut self) -> Result<Vec<ColumnDefinitionPacket>> {
let packet = self.receive().await?;
// A Resultset starts with a [ColumnCountPacket] which is a single field that encodes
@ -224,14 +264,7 @@ impl MariaDb {
columns.push(column);
}
// When (legacy) EOFs are enabled, the fixed number column definitions are further terminated by
// an EOF packet
if !self
.capabilities
.contains(Capabilities::CLIENT_DEPRECATE_EOF)
{
let _eof = EofPacket::decode(self.receive().await?)?;
}
self.check_eof().await?;
Ok(columns)
}

View file

@ -25,10 +25,10 @@ impl Executor for MariaDb {
params: MariaDbQueryParameters,
) -> BoxFuture<'e, crate::Result<u64>> {
Box::pin(async move {
let prepare = self.send_prepare(query).await?;
self.send_execute(prepare.statement_id, params).await?;
let statement_id = self.prepare_ignore_describe(query).await?;
self.send_execute(statement_id, params).await?;
let columns = self.column_definitions().await?;
let columns = self.result_column_defs().await?;
let capabilities = self.capabilities;
// For each row in the result set we will receive a ResultRow packet.
@ -109,10 +109,10 @@ impl Executor for MariaDb {
T: FromRow<Self::Backend> + Send,
{
Box::pin(async move {
let prepare = self.send_prepare(query).await?;
self.send_execute(prepare.statement_id, params).await?;
let statement_id = self.prepare_ignore_describe(query).await?;
self.send_execute(statement_id, params).await?;
let columns = self.column_definitions().await?;
let columns = self.result_column_defs().await?;
let capabilities = self.capabilities;
let mut row: Option<_> = None;
@ -142,6 +142,8 @@ impl Executor for MariaDb {
fn describe<'e, 'q: 'e>(
&'e mut self,
_query: &'q str,
) -> BoxFuture<'e, crate::Result<Describe<Self::Backend>>> { unimplemented!(); }
query: &'q str,
) -> BoxFuture<'e, crate::Result<Describe<Self::Backend>>> {
Box::pin(self.prepare_describe(query))
}
}

View file

@ -128,10 +128,12 @@ impl Executor for Postgres {
.into_iter()
.map(|field| ResultField {
name: if field.name == "?column?" { None } else { Some(field.name) },
table_id: Some(field.table_id),
table_id: if field.table_id > 0 { Some(field.table_id) } else { None },
type_id: field.type_id,
_backcompat: (),
})
.collect(),
_backcompat: (),
})
})
}