From b0a36ddd5c48e1cefcc8a9a92e9598f6b90d0259 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Wed, 4 Dec 2019 14:37:56 -0800 Subject: [PATCH] fix `Executor::describe()` for MariaDB prevent exhaustive match of `describe` structs --- sqlx-core/src/describe.rs | 2 + sqlx-core/src/mariadb/connection.rs | 107 ++++++++++++++++++---------- sqlx-core/src/mariadb/executor.rs | 18 ++--- sqlx-core/src/postgres/executor.rs | 4 +- 4 files changed, 85 insertions(+), 46 deletions(-) diff --git a/sqlx-core/src/describe.rs b/sqlx-core/src/describe.rs index 07282d0c..92b5eb10 100644 --- a/sqlx-core/src/describe.rs +++ b/sqlx-core/src/describe.rs @@ -10,6 +10,7 @@ pub struct Describe { pub param_types: Vec<::TypeId>, /// pub result_fields: Vec>, + pub(crate) _backcompat: (), } impl fmt::Debug for Describe @@ -30,6 +31,7 @@ pub struct ResultField { pub table_id: Option<::TableIdent>, /// The type ID of this result column. pub type_id: ::TypeId, + pub(crate) _backcompat: (), } impl fmt::Debug for ResultField diff --git a/sqlx-core/src/mariadb/connection.rs b/sqlx-core/src/mariadb/connection.rs index 1de390f2..2cdb2bc7 100644 --- a/sqlx-core/src/mariadb/connection.rs +++ b/sqlx-core/src/mariadb/connection.rs @@ -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, pub(crate) rbuf: Vec, @@ -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 { @@ -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 { + 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> { + pub(super) async fn prepare_describe(&mut self, statement: &str) -> Result> { + 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> { 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) } diff --git a/sqlx-core/src/mariadb/executor.rs b/sqlx-core/src/mariadb/executor.rs index e2946651..2679131a 100644 --- a/sqlx-core/src/mariadb/executor.rs +++ b/sqlx-core/src/mariadb/executor.rs @@ -25,10 +25,10 @@ impl Executor for MariaDb { params: MariaDbQueryParameters, ) -> BoxFuture<'e, crate::Result> { 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 + 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>> { unimplemented!(); } + query: &'q str, + ) -> BoxFuture<'e, crate::Result>> { + Box::pin(self.prepare_describe(query)) + } } diff --git a/sqlx-core/src/postgres/executor.rs b/sqlx-core/src/postgres/executor.rs index 85bff554..54fe4735 100644 --- a/sqlx-core/src/postgres/executor.rs +++ b/sqlx-core/src/postgres/executor.rs @@ -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: (), }) }) }