diff --git a/src/mariadb/connection/mod.rs b/src/mariadb/connection/mod.rs index b79c8b02..afcb7e5c 100644 --- a/src/mariadb/connection/mod.rs +++ b/src/mariadb/connection/mod.rs @@ -6,18 +6,7 @@ use futures::{ prelude::*, }; use runtime::net::TcpStream; - -use crate::ConnectOptions; - -use crate::mariadb::protocol::{ - deserialize::{DeContext, Deserialize}, - encode::Encoder, - packets::{com_init_db::ComInitDb, com_ping::ComPing, com_query::ComQuery, com_quit::ComQuit, ok::OkPacket}, - serialize::Serialize, - server::Message as ServerMessage, - types::{Capabilities, ServerStatusFlag}, -}; -use crate::mariadb::protocol::server::Message; +use crate::{ConnectOptions, mariadb::protocol::{DeContext, Deserialize, Encoder, ComInitDb, ComPing, ComQuery, ComQuit, OkPacket, Serialize, Message, Capabilities, ServerStatusFlag}}; mod establish; @@ -160,7 +149,7 @@ impl Connection { Ok(()) } - pub async fn next(&mut self) -> Result, Error> { + pub async fn next(&mut self) -> Result, Error> { let mut rbuf = BytesMut::new(); let mut len = 0; @@ -187,7 +176,7 @@ impl Connection { while len > 0 { let size = rbuf.len(); - let message = ServerMessage::deserialize(&mut DeContext::new( + let message = Message::deserialize(&mut DeContext::new( &mut self.context, &rbuf.as_ref().into(), ))?; diff --git a/src/mariadb/mod.rs b/src/mariadb/mod.rs index 08b2d699..913f6489 100644 --- a/src/mariadb/mod.rs +++ b/src/mariadb/mod.rs @@ -47,5 +47,3 @@ pub use protocol::FieldType; pub use protocol::FieldDetailFlag; pub use protocol::SessionChangeType; pub use protocol::StmtExecFlag; -pub use protocol::TextProtocol; -pub use protocol::BinaryProtocol; diff --git a/src/mariadb/protocol/client.rs b/src/mariadb/protocol/client.rs deleted file mode 100644 index 8f5fd34f..00000000 --- a/src/mariadb/protocol/client.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Reference: https://mariadb.com/kb/en/library/connection -// Packets: https://mariadb.com/kb/en/library/0-packet - -// TODO: Handle lengths which are greater than 3 bytes -// Either break the backet into several smaller ones, or -// return error -// TODO: Handle different Capabilities for server and client -// TODO: Handle when capability is set, but field is None - -use super::packets::{SetOptionOptions, ShutdownOptions}; - -// This is an enum of text protocol packet tags. -// Tags are the 5th byte of the packet (1st byte of packet body) -// and are used to determine which type of query was sent. -// The name of the enum variant represents the type of query, and -// the value is the byte value required by the server. -pub enum TextProtocol { - ComChangeUser = 0x11, - ComDebug = 0x0D, - ComInitDb = 0x02, - ComPing = 0x0e, - ComProcessKill = 0xC, - ComQuery = 0x03, - ComQuit = 0x01, - ComResetConnection = 0x1F, - ComSetOption = 0x1B, - ComShutdown = 0x0A, - ComSleep = 0x00, - ComStatistics = 0x09, -} - -// Helper method to easily transform into u8 -impl Into for TextProtocol { - fn into(self) -> u8 { - self as u8 - } -} - -pub enum BinaryProtocol { - ComStmtPrepare = 0x16, - ComStmtClose = 0x19, - ComStmtExec = 0x17, -} - -// Helper method to easily transform into u8 -impl Into for BinaryProtocol { - fn into(self) -> u8 { - self as u8 - } -} diff --git a/src/mariadb/protocol/encode.rs b/src/mariadb/protocol/encode.rs index ea455cdf..410994cc 100644 --- a/src/mariadb/protocol/encode.rs +++ b/src/mariadb/protocol/encode.rs @@ -231,7 +231,7 @@ impl Encoder { } #[inline] - pub fn encode_binary(&mut self, bytes: &Bytes, ty: &FieldType) { + pub fn encode_param(&mut self, bytes: &Bytes, ty: &FieldType) { match ty { FieldType::MysqlTypeDecimal => self.encode_string_lenenc(bytes), FieldType::MysqlTypeTiny => self.encode_int_1(bytes), diff --git a/src/mariadb/protocol/mod.rs b/src/mariadb/protocol/mod.rs index 7bc32159..5601c8ef 100644 --- a/src/mariadb/protocol/mod.rs +++ b/src/mariadb/protocol/mod.rs @@ -1,4 +1,12 @@ -pub mod client; +// Reference: https://mariadb.com/kb/en/library/connection +// Packets: https://mariadb.com/kb/en/library/0-packet + +// TODO: Handle lengths which are greater than 3 bytes +// Either break the packet into several smaller ones, or +// return error +// TODO: Handle different Capabilities for server and client +// TODO: Handle when capability is set, but field is None + pub mod decode; pub mod deserialize; pub mod encode; @@ -39,6 +47,8 @@ pub use packets::ComStmtPrepareOk; pub use packets::ComStmtPrepareResp; pub use packets::ComStmtClose; pub use packets::ComStmtExec; +pub use packets::ComStmtFetch; +pub use packets::ComStmtReset; pub use decode::Decoder; @@ -59,6 +69,3 @@ pub use types::FieldType; pub use types::FieldDetailFlag; pub use types::SessionChangeType; pub use types::StmtExecFlag; - -pub use client::TextProtocol; -pub use client::BinaryProtocol; diff --git a/src/mariadb/protocol/packets/binary/com_stmt_close.rs b/src/mariadb/protocol/packets/binary/com_stmt_close.rs new file mode 100644 index 00000000..efdcd1a9 --- /dev/null +++ b/src/mariadb/protocol/packets/binary/com_stmt_close.rs @@ -0,0 +1,40 @@ +use std::convert::TryInto; + +#[derive(Debug)] +pub struct ComStmtClose { + stmt_id: i32 +} + +impl crate::mariadb::Serialize for ComStmtClose { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), failure::Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::BinaryProtocol::ComStmtClose.into()); + encoder.encode_int_i32(self.stmt_id); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder, Serialize}; + + #[test] + fn it_encodes_com_stmt_close() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComStmtClose { + stmt_id: 1 + }.serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x05\0\0\x00\x19\x01\0\0\0"); + + Ok(()) + } +} diff --git a/src/mariadb/protocol/packets/binary/com_stmt_exec.rs b/src/mariadb/protocol/packets/binary/com_stmt_exec.rs new file mode 100644 index 00000000..99f33573 --- /dev/null +++ b/src/mariadb/protocol/packets/binary/com_stmt_exec.rs @@ -0,0 +1,111 @@ +use crate::mariadb::{StmtExecFlag, ColumnDefPacket, FieldDetailFlag}; +use bytes::Bytes; + +#[derive(Debug)] +pub struct ComStmtExec { + pub stmt_id: i32, + pub flags: StmtExecFlag, + pub params: Option>>, + pub param_defs: Option>, +} + +impl crate::mariadb::Serialize for ComStmtExec { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), failure::Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::BinaryProtocol::ComStmtExec.into()); + encoder.encode_int_i32(self.stmt_id); + encoder.encode_int_u8(self.flags as u8); + encoder.encode_int_u8(0); + + if let Some(params) = &self.params { + if let Some(param_defs) = &self.param_defs { + if params.len() != param_defs.len() { + failure::bail!("Unequal number of params and param definitions supplied"); + } + } + + let null_bitmap_size = (params.len() + 7) / 8; + let mut shift_amount = 0u8; + let mut bitmap = vec![0u8]; + + // Generate NULL-bitmap from params + for param in params { + if param.is_none() { + bitmap.push(bitmap.last().unwrap() & (1 << shift_amount)); + } + + shift_amount = (shift_amount + 1) % 8; + + if shift_amount % 8 == 0 { + bitmap.push(0u8); + } + } + + // Do not send the param types + encoder.encode_int_u8(if self.param_defs.is_some() { + 1u8 + } else { + 0u8 + }); + + if let Some(params_defs) = &self.param_defs { + for param in params_defs { + encoder.encode_int_u8(param.field_type as u8); + encoder.encode_int_u8(if (param.field_details & FieldDetailFlag::UNSIGNED).is_empty() { + 1u8 + } else { + 0u8 + }); + } + + // Encode params + for index in 0..params.len() { + if let Some(bytes) = ¶ms[index] { + encoder.encode_param(&bytes, ¶ms_defs[index].field_type); + } + } + } + } + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder, Serialize, FieldType, FieldDetailFlag}; + + #[test] + fn it_encodes_com_stmt_close() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComStmtExec { + stmt_id: 1, + flags: StmtExecFlag::NoCursor, + params: Some(vec![Some(Bytes::from_static(b"\x06daniel"))]), + param_defs: Some(vec![ColumnDefPacket { + catalog: Bytes::from_static(b"def"), + schema: Bytes::from_static(b"test"), + table_alias: Bytes::from_static(b"users"), + table: Bytes::from_static(b"users"), + column_alias: Bytes::from_static(b"username"), + column: Bytes::from_static(b"username"), + length_of_fixed_fields: Some(0x0Ci64), + char_set: 1, + max_columns: 1, + field_type: FieldType::MysqlTypeString, + field_details: FieldDetailFlag::NOT_NULL, + decimals: 0, + }]), + }.serialize(&mut ctx, &mut encoder)?; + + Ok(()) + } +} + diff --git a/src/mariadb/protocol/packets/binary/com_stmt_fetch.rs b/src/mariadb/protocol/packets/binary/com_stmt_fetch.rs new file mode 100644 index 00000000..bfd2130d --- /dev/null +++ b/src/mariadb/protocol/packets/binary/com_stmt_fetch.rs @@ -0,0 +1,41 @@ +#[derive(Debug)] +pub struct ComStmtFetch { + pub stmt_id: i32, + pub rows: u32, +} + +impl crate::mariadb::Serialize for ComStmtFetch { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), failure::Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::BinaryProtocol::ComStmtFetch.into()); + encoder.encode_int_i32(self.stmt_id); + encoder.encode_int_u32(self.rows); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder, Serialize}; + + #[test] + fn it_encodes_com_stmt_fetch() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComStmtFetch { + stmt_id: 1, + rows: 10, + }.serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x09\0\0\x00\x1C\x01\0\0\0\x0A\0\0\0"); + + Ok(()) + } +} diff --git a/src/mariadb/protocol/packets/binary/com_stmt_prepare.rs b/src/mariadb/protocol/packets/binary/com_stmt_prepare.rs new file mode 100644 index 00000000..3aa47ae5 --- /dev/null +++ b/src/mariadb/protocol/packets/binary/com_stmt_prepare.rs @@ -0,0 +1,41 @@ +use bytes::Bytes; + +#[derive(Debug)] +pub struct ComStmtPrepare { + statement: Bytes +} + +impl crate::mariadb::Serialize for ComStmtPrepare { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), failure::Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::BinaryProtocol::ComStmtPrepare.into()); + encoder.encode_string_eof(&self.statement); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder, Serialize}; + + #[test] + fn it_encodes_com_stmt_prepare() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComStmtPrepare { + statement: Bytes::from_static(b"SELECT * FROM users WHERE username = ?") + }.serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], Bytes::from_static(b"\x27\0\0\x00\x16SELECT * FROM users WHERE username = ?")); + + Ok(()) + } +} + diff --git a/src/mariadb/protocol/packets/binary/com_stmt_prepare_ok.rs b/src/mariadb/protocol/packets/binary/com_stmt_prepare_ok.rs new file mode 100644 index 00000000..47b24aef --- /dev/null +++ b/src/mariadb/protocol/packets/binary/com_stmt_prepare_ok.rs @@ -0,0 +1,79 @@ +use std::convert::TryFrom; + +#[derive(Debug, Default)] +pub struct ComStmtPrepareOk { + pub stmt_id: i32, + pub columns: i16, + pub params: i16, + pub warnings: i16, +} + +impl crate::mariadb::Deserialize for ComStmtPrepareOk { + fn deserialize(ctx: &mut crate::mariadb::DeContext) -> Result { + let decoder = &mut ctx.decoder; + let length = decoder.decode_length()?; + let seq_no = decoder.decode_int_u8(); + + let header = decoder.decode_int_u8(); + + let stmt_id = decoder.decode_int_i32(); + + let columns = decoder.decode_int_i16(); + let params = decoder.decode_int_i16(); + + // Skip 1 unused byte; + decoder.skip_bytes(1); + + let warnings = decoder.decode_int_i16(); + + Ok(ComStmtPrepareOk { + stmt_id, + columns, + params, + warnings + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{__bytes_builder, ConnectOptions, mariadb::{ConnContext, DeContext, Deserialize}}; + use bytes::Bytes; + + #[test] + fn it_decodes_com_stmt_prepare_ok() -> Result<(), failure::Error> { + #[rustfmt::skip] + let buf = __bytes_builder!( + // int<3> length + 0u8, 0u8, 0u8, + // int<1> seq_no + 0u8, + // int<1> 0x00 COM_STMT_PREPARE_OK header + 0u8, + // int<4> statement id + 1u8, 0u8, 0u8, 0u8, + // int<2> number of columns in the returned result set (or 0 if statement does not return result set) + 10u8, 0u8, + // int<2> number of prepared statement parameters ('?' placeholders) + 1u8, 0u8, + // string<1> -not used- + 0u8, + // int<2> number of warnings + 0u8, 0u8 + ); + + let mut context = ConnContext::new(); + let mut ctx = DeContext::new(&mut context, &buf); + + let message = ComStmtPrepareOk::deserialize(&mut ctx)?; + + assert_eq!(message.stmt_id, 1); + assert_eq!(message.columns, 10); + assert_eq!(message.params, 1); + assert_eq!(message.warnings, 0); + + Ok(()) + } +} + diff --git a/src/mariadb/protocol/packets/binary/com_stmt_prepare_resp.rs b/src/mariadb/protocol/packets/binary/com_stmt_prepare_resp.rs new file mode 100644 index 00000000..90231379 --- /dev/null +++ b/src/mariadb/protocol/packets/binary/com_stmt_prepare_resp.rs @@ -0,0 +1,162 @@ +use crate::mariadb::{ComStmtPrepareOk, ColumnDefPacket, Capabilities, EofPacket}; + +#[derive(Debug, Default)] +pub struct ComStmtPrepareResp { + pub ok: ComStmtPrepareOk, + pub param_defs: Option>, + pub res_columns: Option>, +} + +impl crate::mariadb::Deserialize for ComStmtPrepareResp { + fn deserialize(ctx: &mut crate::mariadb::DeContext) -> Result { + let ok = ComStmtPrepareOk::deserialize(ctx)?; + + let param_defs = if ok.params > 0 { + let param_defs = (0..ok.params).map(|_| ColumnDefPacket::deserialize(ctx)) + .filter(Result::is_ok) + .map(Result::unwrap) + .collect::>(); + + if !ctx.conn.capabilities.contains(Capabilities::CLIENT_DEPRECATE_EOF) { + EofPacket::deserialize(ctx)?; + } + + Some(param_defs) + } else { + None + }; + + let res_columns = if ok.columns > 0 { + let param_defs = (0..ok.columns).map(|_| ColumnDefPacket::deserialize(ctx)) + .filter(Result::is_ok) + .map(Result::unwrap) + .collect::>(); + + if !ctx.conn.capabilities.contains(Capabilities::CLIENT_DEPRECATE_EOF) { + EofPacket::deserialize(ctx)?; + } + + Some(param_defs) + } else { + None + }; + + Ok(ComStmtPrepareResp { + ok, + param_defs, + res_columns, + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{__bytes_builder, ConnectOptions, mariadb::{ConnContext, DeContext, Deserialize}}; + + #[test] + fn it_decodes_com_stmt_prepare_resp() -> Result<(), failure::Error> { + #[rustfmt::skip] + let buf = __bytes_builder!( + // ---------------------------- // + // Statement Prepared Ok Packet // + // ---------------------------- // + + // int<3> length + 0u8, 0u8, 0u8, + // int<1> seq_no + 0u8, + // int<1> 0x00 COM_STMT_PREPARE_OK header + 0u8, + // int<4> statement id + 1u8, 0u8, 0u8, 0u8, + // int<2> number of columns in the returned result set (or 0 if statement does not return result set) + 1u8, 0u8, + // int<2> number of prepared statement parameters ('?' placeholders) + 1u8, 0u8, + // string<1> -not used- + 0u8, + // int<2> number of warnings + 0u8, 0u8, + + // Param column definition + + // ------------------------ // + // Column Definition packet // + // ------------------------ // + // int<3> length + 52u8, 0u8, 0u8, + // int<1> seq_no + 3u8, + // string catalog (always 'def') + 3u8, b"def", + // string schema + 4u8, b"test", + // string table alias + 5u8, b"users", + // string table + 5u8, b"users", + // string column alias + 8u8, b"username", + // string column + 8u8, b"username", + // int length of fixed fields (=0xC) + 0x0C_u8, + // int<2> character set number + 8u8, 0u8, + // int<4> max. column size + 0xFF_u8, 0xFF_u8, 0u8, 0u8, + // int<1> Field types + 0xFC_u8, + // int<2> Field detail flag + 0x11_u8, 0x10_u8, + // int<1> decimals + 0u8, + // int<2> - unused - + 0u8, 0u8, + + // Result column definitions + + // ------------------------ // + // Column Definition packet // + // ------------------------ // + // int<3> length + 52u8, 0u8, 0u8, + // int<1> seq_no + 3u8, + // string catalog (always 'def') + 3u8, b"def", + // string schema + 4u8, b"test", + // string table alias + 5u8, b"users", + // string table + 5u8, b"users", + // string column alias + 8u8, b"username", + // string column + 8u8, b"username", + // int length of fixed fields (=0xC) + 0x0C_u8, + // int<2> character set number + 8u8, 0u8, + // int<4> max. column size + 0xFF_u8, 0xFF_u8, 0u8, 0u8, + // int<1> Field types + 0xFC_u8, + // int<2> Field detail flag + 0x11_u8, 0x10_u8, + // int<1> decimals + 0u8, + // int<2> - unused - + 0u8, 0u8 + ); + + let mut context = ConnContext::new(); + let mut ctx = DeContext::new(&mut context, &buf); + + let message = ComStmtPrepareResp::deserialize(&mut ctx)?; + + Ok(()) + } +} diff --git a/src/mariadb/protocol/packets/binary/com_stmt_reset.rs b/src/mariadb/protocol/packets/binary/com_stmt_reset.rs new file mode 100644 index 00000000..ddbcadee --- /dev/null +++ b/src/mariadb/protocol/packets/binary/com_stmt_reset.rs @@ -0,0 +1,38 @@ +#[derive(Debug)] +pub struct ComStmtReset { + pub stmt_id: i32 +} + +impl crate::mariadb::Serialize for ComStmtReset { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), failure::Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::BinaryProtocol::ComStmtReset.into()); + encoder.encode_int_i32(self.stmt_id); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder, Serialize}; + + #[test] + fn it_encodes_com_stmt_reset() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComStmtReset { + stmt_id: 1 + }.serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x05\0\0\x00\x1A\x01\0\0\0"); + + Ok(()) + } +} diff --git a/src/mariadb/protocol/packets/binary/mod.rs b/src/mariadb/protocol/packets/binary/mod.rs new file mode 100644 index 00000000..7fb6736f --- /dev/null +++ b/src/mariadb/protocol/packets/binary/mod.rs @@ -0,0 +1,31 @@ +pub mod com_stmt_prepare; +pub mod com_stmt_prepare_ok; +pub mod com_stmt_prepare_resp; +pub mod com_stmt_close; +pub mod com_stmt_exec; +pub mod com_stmt_fetch; +pub mod com_stmt_reset; + +pub use com_stmt_prepare::ComStmtPrepare; +pub use com_stmt_prepare_ok::ComStmtPrepareOk; +pub use com_stmt_prepare_resp::ComStmtPrepareResp; +pub use com_stmt_close::ComStmtClose; +pub use com_stmt_exec::ComStmtExec; +pub use com_stmt_fetch::ComStmtFetch; +pub use com_stmt_reset::ComStmtReset; + +pub enum BinaryProtocol { + ComStmtPrepare = 0x16, + ComStmtExec = 0x17, + ComStmtClose = 0x19, + ComStmtReset = 0x1A, + ComStmtFetch = 0x1C, +} + +// Helper method to easily transform into u8 +impl Into for BinaryProtocol { + fn into(self) -> u8 { + self as u8 + } +} + diff --git a/src/mariadb/protocol/packets/com_debug.rs b/src/mariadb/protocol/packets/com_debug.rs deleted file mode 100644 index 022e863d..00000000 --- a/src/mariadb/protocol/packets/com_debug.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::mariadb::{Connection, TextProtocol, Serialize}; -use failure::Error; - -pub struct ComDebug(); - -impl Serialize for ComDebug { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(TextProtocol::ComDebug.into()); - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_init_db.rs b/src/mariadb/protocol/packets/com_init_db.rs deleted file mode 100644 index 0bfafded..00000000 --- a/src/mariadb/protocol/packets/com_init_db.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::mariadb::{TextProtocol, Serialize, Connection}; -use bytes::Bytes; -use failure::Error; - -pub struct ComInitDb { - pub schema_name: Bytes, -} - -impl Serialize for ComInitDb { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(TextProtocol::ComInitDb.into()); - encoder.encode_string_null(&self.schema_name); - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_ping.rs b/src/mariadb/protocol/packets/com_ping.rs deleted file mode 100644 index 7f834183..00000000 --- a/src/mariadb/protocol/packets/com_ping.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::mariadb::{TextProtocol, Serialize, Connection}; -use failure::Error; - -pub struct ComPing(); - -impl Serialize for ComPing { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(TextProtocol::ComPing.into()); - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_process_kill.rs b/src/mariadb/protocol/packets/com_process_kill.rs deleted file mode 100644 index ab68918f..00000000 --- a/src/mariadb/protocol/packets/com_process_kill.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::mariadb::{TextProtocol, Serialize, Connection}; -use failure::Error; - -pub struct ComProcessKill { - pub process_id: u32, -} - -impl Serialize for ComProcessKill { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(TextProtocol::ComProcessKill.into()); - encoder.encode_int_u32(self.process_id); - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_query.rs b/src/mariadb/protocol/packets/com_query.rs deleted file mode 100644 index 512d2a69..00000000 --- a/src/mariadb/protocol/packets/com_query.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::mariadb::{TextProtocol, Serialize, Connection}; -use bytes::Bytes; -use failure::Error; - -pub struct ComQuery { - pub sql_statement: Bytes, -} - -impl Serialize for ComQuery { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(TextProtocol::ComQuery.into()); - encoder.encode_string_eof(&self.sql_statement); - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_quit.rs b/src/mariadb/protocol/packets/com_quit.rs deleted file mode 100644 index 2c8c8184..00000000 --- a/src/mariadb/protocol/packets/com_quit.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::mariadb::{TextProtocol, Serialize, Connection}; -use failure::Error; - -pub struct ComQuit(); - -impl Serialize for ComQuit { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(TextProtocol::ComQuit.into()); - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_reset_conn.rs b/src/mariadb/protocol/packets/com_reset_conn.rs deleted file mode 100644 index f6692b84..00000000 --- a/src/mariadb/protocol/packets/com_reset_conn.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::mariadb::{TextProtocol, Serialize, Connection}; -use failure::Error; - -pub struct ComResetConnection(); - -impl Serialize for ComResetConnection { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(TextProtocol::ComResetConnection.into()); - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_set_option.rs b/src/mariadb/protocol/packets/com_set_option.rs deleted file mode 100644 index cc23ca81..00000000 --- a/src/mariadb/protocol/packets/com_set_option.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::mariadb::{TextProtocol, Serialize, Connection}; -use failure::Error; - -#[derive(Clone, Copy)] -pub enum SetOptionOptions { - MySqlOptionMultiStatementsOn = 0x00, - MySqlOptionMultiStatementsOff = 0x01, -} - -pub struct ComSetOption { - pub option: SetOptionOptions, -} - -impl Serialize for ComSetOption { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(TextProtocol::ComSetOption.into()); - encoder.encode_int_u16(self.option.into()); - - encoder.encode_length(); - - Ok(()) - } -} - -// Helper method to easily transform into u16 -impl Into for SetOptionOptions { - fn into(self) -> u16 { - self as u16 - } -} diff --git a/src/mariadb/protocol/packets/com_shutdown.rs b/src/mariadb/protocol/packets/com_shutdown.rs deleted file mode 100644 index 8ef0e203..00000000 --- a/src/mariadb/protocol/packets/com_shutdown.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::mariadb::{TextProtocol, Serialize, Connection}; -use failure::Error; - -#[derive(Clone, Copy)] -pub enum ShutdownOptions { - ShutdownDefault = 0x00, -} - -pub struct ComShutdown { - pub option: ShutdownOptions, -} - -impl Serialize for ComShutdown { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(TextProtocol::ComShutdown.into()); - encoder.encode_int_u8(self.option.into()); - - encoder.encode_length(); - - Ok(()) - } -} - -// Helper method to easily transform into u8 -impl Into for ShutdownOptions { - fn into(self) -> u8 { - self as u8 - } -} diff --git a/src/mariadb/protocol/packets/com_sleep.rs b/src/mariadb/protocol/packets/com_sleep.rs deleted file mode 100644 index 30326816..00000000 --- a/src/mariadb/protocol/packets/com_sleep.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::mariadb::{TextProtocol, Serialize, Connection}; -use failure::Error; - -pub struct ComSleep(); - -impl Serialize for ComSleep { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(TextProtocol::ComSleep.into()); - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_statistics.rs b/src/mariadb/protocol/packets/com_statistics.rs deleted file mode 100644 index 722a8240..00000000 --- a/src/mariadb/protocol/packets/com_statistics.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::mariadb::{TextProtocol, Serialize, Connection}; -use failure::Error; - -pub struct ComStatistics(); - -impl Serialize for ComStatistics { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(TextProtocol::ComStatistics.into()); - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_stmt_close.rs b/src/mariadb/protocol/packets/com_stmt_close.rs deleted file mode 100644 index b816beee..00000000 --- a/src/mariadb/protocol/packets/com_stmt_close.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::convert::TryInto; - -#[derive(Debug)] -pub struct ComStmtClose { - stmt_id: i32 -} - -impl crate::mariadb::Serialize for ComStmtClose { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), failure::Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(crate::mariadb::BinaryProtocol::ComStmtClose.into()); - encoder.encode_int_i32(self.stmt_id); - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_stmt_exec.rs b/src/mariadb/protocol/packets/com_stmt_exec.rs deleted file mode 100644 index 797aee95..00000000 --- a/src/mariadb/protocol/packets/com_stmt_exec.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::mariadb::{StmtExecFlag, ColumnDefPacket, FieldDetailFlag}; -use bytes::Bytes; - -#[derive(Debug)] -pub struct ComStmtExec { - pub stmt_id: i32, - pub flags: StmtExecFlag, - pub params: Option>>, - pub param_defs: Option>, -} - -impl crate::mariadb::Serialize for ComStmtExec { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), failure::Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(crate::mariadb::BinaryProtocol::ComStmtExec.into()); - encoder.encode_int_i32(self.stmt_id); - encoder.encode_int_u8(self.flags as u8); - encoder.encode_int_u8(0); - - if let Some(params) = &self.params { - let null_bitmap_size = (params.len() + 7) / 8; - let mut shift_amount = 0u8; - let mut bitmap = vec![0u8]; - - // Generate NULL-bitmap from params - for param in params { - if param.is_none() { - bitmap.push(bitmap.last().unwrap() & (1 << shift_amount)); - } - - shift_amount = (shift_amount + 1) % 8; - - if shift_amount % 8 == 0 { - bitmap.push(0u8); - } - } - - // Do not send the param types - encoder.encode_int_u8(if self.param_defs.is_some() { - 1u8 - } else { - 0u8 - }); - - if let Some(params) = &self.param_defs { - for param in params { - encoder.encode_int_u8(param.field_type as u8); - encoder.encode_int_u8(if (param.field_details & FieldDetailFlag::UNSIGNED).is_empty() { - 1u8 - } else { - 0u8 - }); - } - } - - // Encode params - for param in params { - - } - } - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_stmt_prepare.rs b/src/mariadb/protocol/packets/com_stmt_prepare.rs deleted file mode 100644 index 56297830..00000000 --- a/src/mariadb/protocol/packets/com_stmt_prepare.rs +++ /dev/null @@ -1,20 +0,0 @@ -use bytes::Bytes; - -#[derive(Debug)] -pub struct ComStmtPrepare { - statement: Bytes -} - -impl crate::mariadb::Serialize for ComStmtPrepare { - fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::connection::ConnContext, encoder: &mut crate::mariadb::protocol::encode::Encoder) -> Result<(), failure::Error> { - encoder.alloc_packet_header(); - encoder.seq_no(0); - - encoder.encode_int_u8(crate::mariadb::BinaryProtocol::ComStmtPrepare.into()); - encoder.encode_string_eof(&self.statement); - - encoder.encode_length(); - - Ok(()) - } -} diff --git a/src/mariadb/protocol/packets/com_stmt_prepare_ok.rs b/src/mariadb/protocol/packets/com_stmt_prepare_ok.rs deleted file mode 100644 index f74a4b57..00000000 --- a/src/mariadb/protocol/packets/com_stmt_prepare_ok.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::convert::TryFrom; - -#[derive(Debug)] -pub struct ComStmtPrepareOk { - pub stmt_id: i32, - pub columns: i16, - pub params: i16, - pub warnings: i16, -} - -impl crate::mariadb::Deserialize for ComStmtPrepareOk { - fn deserialize(ctx: &mut crate::mariadb::DeContext) -> Result { - let decoder = &mut ctx.decoder; - let length = decoder.decode_length()?; - let seq_no = decoder.decode_int_u8(); - - let stmt_id = decoder.decode_int_i32(); - - let columns = decoder.decode_int_i16(); - let params = decoder.decode_int_i16(); - - // Skip 1 unused byte; - decoder.skip_bytes(1); - - let warnings = decoder.decode_int_i16(); - - Ok(ComStmtPrepareOk { - stmt_id, - columns, - params, - warnings - }) - } -} diff --git a/src/mariadb/protocol/packets/com_stmt_prepare_resp.rs b/src/mariadb/protocol/packets/com_stmt_prepare_resp.rs deleted file mode 100644 index b02bbb5f..00000000 --- a/src/mariadb/protocol/packets/com_stmt_prepare_resp.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::mariadb::{ComStmtPrepareOk, ColumnDefPacket, Capabilities, EofPacket}; - -#[derive(Debug)] -pub struct ComStmtPrepareResp { - pub ok: ComStmtPrepareOk, - pub param_defs: Option>, - pub res_columns: Option>, -} - -//int<1> 0x00 COM_STMT_PREPARE_OK header -//int<4> statement id -//int<2> number of columns in the returned result set (or 0 if statement does not return result set) -//int<2> number of prepared statement parameters ('?' placeholders) -//string<1> -not used- -//int<2> number of warnings - -impl crate::mariadb::Deserialize for ComStmtPrepareResp { - fn deserialize(ctx: &mut crate::mariadb::DeContext) -> Result { - let decoder = &mut ctx.decoder; - let length = decoder.decode_length()?; - - let ok = ComStmtPrepareOk::deserialize(ctx)?; - - let param_defs = if ok.params > 0 { - let param_defs = (0..ok.params).map(|_| ColumnDefPacket::deserialize(ctx)) - .filter(Result::is_ok) - .map(Result::unwrap) - .collect::>(); - - if (ctx.conn.capabilities & Capabilities::CLIENT_DEPRECATE_EOF).is_empty() { - EofPacket::deserialize(ctx)?; - } - - Some(param_defs) - } else { - None - }; - - let res_columns = if ok.columns > 0 { - let param_defs = (0..ok.columns).map(|_| ColumnDefPacket::deserialize(ctx)) - .filter(Result::is_ok) - .map(Result::unwrap) - .collect::>(); - - if (ctx.conn.capabilities & Capabilities::CLIENT_DEPRECATE_EOF).is_empty() { - EofPacket::deserialize(ctx)?; - } - - Some(param_defs) - } else { - None - }; - - Ok(ComStmtPrepareResp { - ok, - param_defs, - res_columns - }) - } -} diff --git a/src/mariadb/protocol/packets/mod.rs b/src/mariadb/protocol/packets/mod.rs index 34ca06c9..31799640 100644 --- a/src/mariadb/protocol/packets/mod.rs +++ b/src/mariadb/protocol/packets/mod.rs @@ -1,17 +1,6 @@ pub mod auth_switch_request; pub mod column; pub mod column_def; -pub mod com_debug; -pub mod com_init_db; -pub mod com_ping; -pub mod com_process_kill; -pub mod com_query; -pub mod com_quit; -pub mod com_reset_conn; -pub mod com_set_option; -pub mod com_shutdown; -pub mod com_sleep; -pub mod com_statistics; pub mod eof; pub mod err; pub mod handshake_response; @@ -21,28 +10,12 @@ pub mod packet_header; pub mod result_set; pub mod ssl_request; pub mod result_row; -pub mod com_stmt_prepare; -pub mod com_stmt_prepare_ok; -pub mod com_stmt_prepare_resp; -pub mod com_stmt_close; -pub mod com_stmt_exec; +pub mod binary; +pub mod text; pub use auth_switch_request::AuthenticationSwitchRequestPacket; pub use column::ColumnPacket; pub use column_def::ColumnDefPacket; -pub use com_debug::ComDebug; -pub use com_init_db::ComInitDb; -pub use com_ping::ComPing; -pub use com_process_kill::ComProcessKill; -pub use com_query::ComQuery; -pub use com_quit::ComQuit; -pub use com_reset_conn::ComResetConnection; -pub use com_set_option::ComSetOption; -pub use com_set_option::SetOptionOptions; -pub use com_shutdown::ShutdownOptions; -pub use com_shutdown::ComShutdown; -pub use com_sleep::ComSleep; -pub use com_statistics::ComStatistics; pub use eof::EofPacket; pub use err::ErrPacket; pub use handshake_response::HandshakeResponsePacket; @@ -52,8 +25,25 @@ pub use packet_header::PacketHeader; pub use result_set::ResultSet; pub use result_row::ResultRow; pub use ssl_request::SSLRequestPacket; -pub use com_stmt_prepare::ComStmtPrepare; -pub use com_stmt_prepare_ok::ComStmtPrepareOk; -pub use com_stmt_prepare_resp::ComStmtPrepareResp; -pub use com_stmt_close::ComStmtClose; -pub use com_stmt_exec::ComStmtExec; + +pub use text::ComDebug; +pub use text::ComInitDb; +pub use text::ComPing; +pub use text::ComProcessKill; +pub use text::ComQuery; +pub use text::ComQuit; +pub use text::ComResetConnection; +pub use text::ComSetOption; +pub use text::SetOptionOptions; +pub use text::ShutdownOptions; +pub use text::ComShutdown; +pub use text::ComSleep; +pub use text::ComStatistics; + +pub use binary::ComStmtPrepare; +pub use binary::ComStmtPrepareOk; +pub use binary::ComStmtPrepareResp; +pub use binary::ComStmtClose; +pub use binary::ComStmtExec; +pub use binary::ComStmtFetch; +pub use binary::ComStmtReset; diff --git a/src/mariadb/protocol/packets/result_set.rs b/src/mariadb/protocol/packets/result_set.rs index 83a78ae9..07565b91 100644 --- a/src/mariadb/protocol/packets/result_set.rs +++ b/src/mariadb/protocol/packets/result_set.rs @@ -31,7 +31,7 @@ impl Deserialize for ResultSet { Vec::new() }; - let eof_packet = if !(ctx.conn.capabilities & Capabilities::CLIENT_DEPRECATE_EOF).is_empty() { + let eof_packet = if !ctx.conn.capabilities.contains(Capabilities::CLIENT_DEPRECATE_EOF) { Some(EofPacket::deserialize(ctx)?) } else { None @@ -62,7 +62,7 @@ impl Deserialize for ResultSet { } } - if (ctx.conn.capabilities & Capabilities::CLIENT_DEPRECATE_EOF).is_empty() { + if ctx.conn.capabilities.contains(Capabilities::CLIENT_DEPRECATE_EOF) { OkPacket::deserialize(ctx)?; } else { EofPacket::deserialize(ctx)?; diff --git a/src/mariadb/protocol/packets/text/com_debug.rs b/src/mariadb/protocol/packets/text/com_debug.rs new file mode 100644 index 00000000..e7c526f2 --- /dev/null +++ b/src/mariadb/protocol/packets/text/com_debug.rs @@ -0,0 +1,35 @@ +use crate::mariadb::{Connection, Serialize}; +use failure::Error; + +pub struct ComDebug(); + +impl Serialize for ComDebug { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::TextProtocol::ComDebug.into()); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder}; + + #[test] + fn it_encodes_com_debug() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComDebug().serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x01\0\0\x00\x0D"); + + Ok(()) + } +} diff --git a/src/mariadb/protocol/packets/text/com_init_db.rs b/src/mariadb/protocol/packets/text/com_init_db.rs new file mode 100644 index 00000000..fe9ebbb3 --- /dev/null +++ b/src/mariadb/protocol/packets/text/com_init_db.rs @@ -0,0 +1,42 @@ +use crate::mariadb::{Connection, Serialize}; +use bytes::Bytes; +use failure::Error; + +pub struct ComInitDb { + pub schema_name: Bytes, +} + +impl Serialize for ComInitDb { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::TextProtocol::ComInitDb.into()); + encoder.encode_string_null(&self.schema_name); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder}; + + #[test] + fn it_encodes_com_init_db() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComInitDb { + schema_name: Bytes::from_static(b"portal"), + }.serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x08\0\0\x00\x02portal\0"); + + Ok(()) + } +} + diff --git a/src/mariadb/protocol/packets/text/com_ping.rs b/src/mariadb/protocol/packets/text/com_ping.rs new file mode 100644 index 00000000..19ae3f33 --- /dev/null +++ b/src/mariadb/protocol/packets/text/com_ping.rs @@ -0,0 +1,35 @@ +use crate::mariadb::{Connection, Serialize}; +use failure::Error; + +pub struct ComPing(); + +impl Serialize for ComPing { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::TextProtocol::ComPing.into()); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder}; + + #[test] + fn it_encodes_com_ping() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComPing().serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x01\0\0\x00\x0E"); + + Ok(()) + } +} diff --git a/src/mariadb/protocol/packets/text/com_process_kill.rs b/src/mariadb/protocol/packets/text/com_process_kill.rs new file mode 100644 index 00000000..582a03fd --- /dev/null +++ b/src/mariadb/protocol/packets/text/com_process_kill.rs @@ -0,0 +1,40 @@ +use crate::mariadb::{Connection, Serialize}; +use failure::Error; + +pub struct ComProcessKill { + pub process_id: u32, +} + +impl Serialize for ComProcessKill { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::TextProtocol::ComProcessKill.into()); + encoder.encode_int_u32(self.process_id); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder}; + + #[test] + fn it_encodes_com_process_kill() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComProcessKill { + process_id: 1, + }.serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x05\0\0\x00\x0C\x01\0\0\0"); + + Ok(()) + } +} diff --git a/src/mariadb/protocol/packets/text/com_query.rs b/src/mariadb/protocol/packets/text/com_query.rs new file mode 100644 index 00000000..8e6c31a7 --- /dev/null +++ b/src/mariadb/protocol/packets/text/com_query.rs @@ -0,0 +1,42 @@ +use crate::mariadb::{Connection, Serialize}; +use bytes::Bytes; +use failure::Error; + +pub struct ComQuery { + pub sql_statement: Bytes, +} + +impl Serialize for ComQuery { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::TextProtocol::ComQuery.into()); + encoder.encode_string_eof(&self.sql_statement); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder}; + + #[test] + fn it_encodes_com_query() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComQuery { + sql_statement: Bytes::from_static(b"SELECT * FROM users"), + }.serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x14\0\0\x00\x03SELECT * FROM users"); + + Ok(()) + } +} + diff --git a/src/mariadb/protocol/packets/text/com_quit.rs b/src/mariadb/protocol/packets/text/com_quit.rs new file mode 100644 index 00000000..64cf4f1f --- /dev/null +++ b/src/mariadb/protocol/packets/text/com_quit.rs @@ -0,0 +1,36 @@ +use crate::mariadb::{Connection, Serialize}; +use failure::Error; + +pub struct ComQuit(); + +impl Serialize for ComQuit { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::TextProtocol::ComQuit.into()); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder}; + + #[test] + fn it_encodes_com_quit() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComQuit().serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x01\0\0\x00\x01"); + + Ok(()) + } +} + diff --git a/src/mariadb/protocol/packets/text/com_reset_conn.rs b/src/mariadb/protocol/packets/text/com_reset_conn.rs new file mode 100644 index 00000000..ceb1a07e --- /dev/null +++ b/src/mariadb/protocol/packets/text/com_reset_conn.rs @@ -0,0 +1,35 @@ +use crate::mariadb::{Connection, Serialize}; +use failure::Error; + +pub struct ComResetConnection(); + +impl Serialize for ComResetConnection { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::TextProtocol::ComResetConnection.into()); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder}; + + #[test] + fn it_encodes_com_reset_conn() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComResetConnection().serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x01\0\0\x00\x1F"); + + Ok(()) + } +} diff --git a/src/mariadb/protocol/packets/text/com_set_option.rs b/src/mariadb/protocol/packets/text/com_set_option.rs new file mode 100644 index 00000000..3ab90918 --- /dev/null +++ b/src/mariadb/protocol/packets/text/com_set_option.rs @@ -0,0 +1,55 @@ +use crate::mariadb::{Connection, Serialize}; +use failure::Error; + +#[derive(Clone, Copy)] +pub enum SetOptionOptions { + MySqlOptionMultiStatementsOn = 0x00, + MySqlOptionMultiStatementsOff = 0x01, +} + +pub struct ComSetOption { + pub option: SetOptionOptions, +} + +impl Serialize for ComSetOption { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::TextProtocol::ComSetOption.into()); + encoder.encode_int_u16(self.option.into()); + + encoder.encode_length(); + + Ok(()) + } +} + +// Helper method to easily transform into u16 +impl Into for SetOptionOptions { + fn into(self) -> u16 { + self as u16 + } +} + + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder}; + + #[test] + fn it_encodes_com_set_option() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComSetOption { + option: SetOptionOptions::MySqlOptionMultiStatementsOff + }.serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x03\0\0\x00\x1B\x01\0"); + + Ok(()) + } +} + diff --git a/src/mariadb/protocol/packets/text/com_shutdown.rs b/src/mariadb/protocol/packets/text/com_shutdown.rs new file mode 100644 index 00000000..855ee8ec --- /dev/null +++ b/src/mariadb/protocol/packets/text/com_shutdown.rs @@ -0,0 +1,53 @@ +use crate::mariadb::{Connection, Serialize}; +use failure::Error; + +#[derive(Clone, Copy)] +pub enum ShutdownOptions { + ShutdownDefault = 0x00, +} + +pub struct ComShutdown { + pub option: ShutdownOptions, +} + +impl Serialize for ComShutdown { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::TextProtocol::ComShutdown.into()); + encoder.encode_int_u8(self.option.into()); + + encoder.encode_length(); + + Ok(()) + } +} + +// Helper method to easily transform into u8 +impl Into for ShutdownOptions { + fn into(self) -> u8 { + self as u8 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder}; + + #[test] + fn it_encodes_com_shutdown() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComShutdown { + option: ShutdownOptions::ShutdownDefault + }.serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x02\0\0\x00\x0A\x00"); + + Ok(()) + } +} + diff --git a/src/mariadb/protocol/packets/text/com_sleep.rs b/src/mariadb/protocol/packets/text/com_sleep.rs new file mode 100644 index 00000000..df9d2bad --- /dev/null +++ b/src/mariadb/protocol/packets/text/com_sleep.rs @@ -0,0 +1,35 @@ +use crate::mariadb::{Connection, Serialize}; +use failure::Error; + +pub struct ComSleep(); + +impl Serialize for ComSleep { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::TextProtocol::ComSleep.into()); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder}; + + #[test] + fn it_encodes_com_sleep() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComSleep().serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x01\0\0\x00\x00"); + + Ok(()) + } +} diff --git a/src/mariadb/protocol/packets/text/com_statistics.rs b/src/mariadb/protocol/packets/text/com_statistics.rs new file mode 100644 index 00000000..4bb0e2fd --- /dev/null +++ b/src/mariadb/protocol/packets/text/com_statistics.rs @@ -0,0 +1,35 @@ +use crate::mariadb::{Connection, Serialize}; +use failure::Error; + +pub struct ComStatistics(); + +impl Serialize for ComStatistics { + fn serialize<'a, 'b>(&self, ctx: &mut crate::mariadb::ConnContext, encoder: &mut crate::mariadb::Encoder) -> Result<(), Error> { + encoder.alloc_packet_header(); + encoder.seq_no(0); + + encoder.encode_int_u8(super::TextProtocol::ComStatistics.into()); + + encoder.encode_length(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mariadb::{ConnContext, Encoder}; + + #[test] + fn it_encodes_com_statistics() -> Result<(), failure::Error> { + let mut encoder = Encoder::new(128); + let mut ctx = ConnContext::new(); + + ComStatistics().serialize(&mut ctx, &mut encoder)?; + + assert_eq!(&encoder.buf[..], b"\x01\0\0\x00\x09"); + + Ok(()) + } +} diff --git a/src/mariadb/protocol/packets/text/mod.rs b/src/mariadb/protocol/packets/text/mod.rs new file mode 100644 index 00000000..3f7d28d5 --- /dev/null +++ b/src/mariadb/protocol/packets/text/mod.rs @@ -0,0 +1,52 @@ +pub mod com_debug; +pub mod com_init_db; +pub mod com_ping; +pub mod com_process_kill; +pub mod com_query; +pub mod com_quit; +pub mod com_reset_conn; +pub mod com_set_option; +pub mod com_shutdown; +pub mod com_sleep; +pub mod com_statistics; + +pub use com_debug::ComDebug; +pub use com_init_db::ComInitDb; +pub use com_ping::ComPing; +pub use com_process_kill::ComProcessKill; +pub use com_query::ComQuery; +pub use com_quit::ComQuit; +pub use com_reset_conn::ComResetConnection; +pub use com_set_option::ComSetOption; +pub use com_set_option::SetOptionOptions; +pub use com_shutdown::ShutdownOptions; +pub use com_shutdown::ComShutdown; +pub use com_sleep::ComSleep; +pub use com_statistics::ComStatistics; + +// This is an enum of text protocol packet tags. +// Tags are the 5th byte of the packet (1st byte of packet body) +// and are used to determine which type of query was sent. +// The name of the enum variant represents the type of query, and +// the value is the byte value required by the server. +pub enum TextProtocol { + ComChangeUser = 0x11, + ComDebug = 0x0D, + ComInitDb = 0x02, + ComPing = 0x0e, + ComProcessKill = 0x0C, + ComQuery = 0x03, + ComQuit = 0x01, + ComResetConnection = 0x1F, + ComSetOption = 0x1B, + ComShutdown = 0x0A, + ComSleep = 0x00, + ComStatistics = 0x09, +} + +// Helper method to easily transform into u8 +impl Into for TextProtocol { + fn into(self) -> u8 { + self as u8 + } +}