From f7a9b77f6b4450b04ab9f98d51d231dab09a0acf Mon Sep 17 00:00:00 2001 From: Zachery Gyurkovitz Date: Thu, 18 Jul 2019 11:22:47 -0700 Subject: [PATCH 1/3] Decode ParameterDescription and PortalSuspended --- sqlx-postgres-protocol/src/bind.rs | 2 - sqlx-postgres-protocol/src/lib.rs | 2 + sqlx-postgres-protocol/src/message.rs | 7 +- .../src/parameter_description.rs | 64 +++++++++++++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 sqlx-postgres-protocol/src/parameter_description.rs diff --git a/sqlx-postgres-protocol/src/bind.rs b/sqlx-postgres-protocol/src/bind.rs index 3633f73c..4b67b4af 100644 --- a/sqlx-postgres-protocol/src/bind.rs +++ b/sqlx-postgres-protocol/src/bind.rs @@ -1,6 +1,4 @@ -use crate::Encode; use byteorder::{BigEndian, ByteOrder}; -use std::io; // FIXME: Having structs here is breaking down. I think front-end messages should be // simple functions that take the wbuf as a mut Vec diff --git a/sqlx-postgres-protocol/src/lib.rs b/sqlx-postgres-protocol/src/lib.rs index 326b697c..9ee84807 100644 --- a/sqlx-postgres-protocol/src/lib.rs +++ b/sqlx-postgres-protocol/src/lib.rs @@ -10,6 +10,7 @@ mod encode; mod execute; mod message; mod notification_response; +mod parameter_description; mod parameter_status; mod parse; mod password_message; @@ -31,6 +32,7 @@ pub use self::{ execute::Execute, message::Message, notification_response::NotificationResponse, + parameter_description::ParameterDescription, parameter_status::ParameterStatus, parse::Parse, password_message::PasswordMessage, diff --git a/sqlx-postgres-protocol/src/message.rs b/sqlx-postgres-protocol/src/message.rs index d0c846d3..2895c74b 100644 --- a/sqlx-postgres-protocol/src/message.rs +++ b/sqlx-postgres-protocol/src/message.rs @@ -1,6 +1,6 @@ use crate::{ Authentication, BackendKeyData, CommandComplete, DataRow, Decode, NotificationResponse, - ParameterStatus, ReadyForQuery, Response, RowDescription, + ParameterStatus, ReadyForQuery, Response, RowDescription, ParameterDescription, }; use byteorder::{BigEndian, ByteOrder}; use bytes::BytesMut; @@ -20,6 +20,8 @@ pub enum Message { ParseComplete, BindComplete, NoData, + PortalSuspended, + ParameterDescription(ParameterDescription), } impl Message { @@ -65,7 +67,8 @@ impl Message { b'1' => Message::ParseComplete, b'2' => Message::BindComplete, b'n' => Message::NoData, - + b's' => Message::PortalSuspended, + b't' => Message::ParameterDescription(ParameterDescription::decode(src)?), _ => unimplemented!("decode not implemented for token: {}", token as char), }; diff --git a/sqlx-postgres-protocol/src/parameter_description.rs b/sqlx-postgres-protocol/src/parameter_description.rs new file mode 100644 index 00000000..086da015 --- /dev/null +++ b/sqlx-postgres-protocol/src/parameter_description.rs @@ -0,0 +1,64 @@ +use crate::{Decode}; +use byteorder::{BigEndian, ByteOrder}; +use bytes::Bytes; + +use std::io; + +type ObjectId = u32; + +#[derive(Debug)] +pub struct ParameterDescription { + ids: Vec, +} + +impl Decode for ParameterDescription { + fn decode(src: Bytes) -> io::Result { + let count = BigEndian::read_u16(&*src) as usize; + + // todo: error handling + assert_eq!(src.len(), count * 4 + 2); + + let mut ids = Vec::with_capacity(count); + for i in 0..count { + let offset = i * 4 + 2; // 4==size_of(u32), 2==size_of(u16) + ids.push(BigEndian::read_u32(&src[offset..])); + } + + Ok(ParameterDescription {ids}) + } +} + +#[cfg(test)] +mod test { + use super::ParameterDescription; + use crate::Decode; + use bytes::Bytes; + use std::io; + + #[test] + fn it_decodes_parameter_description() -> io::Result<()> { + let src = Bytes::from_static(b"\x00\x02\x00\x00\x00\x00\x00\x00\x05\x00"); + let desc = ParameterDescription::decode(src)?; + + assert_eq!(desc.ids.len(), 2); + assert_eq!(desc.ids[0], 0x0000_0000); + assert_eq!(desc.ids[1], 0x0000_0500); + Ok(()) + } + + #[test] + fn it_decodes_empty_parameter_description() -> io::Result<()> { + let src = Bytes::from_static(b"\x00\x00"); + let desc = ParameterDescription::decode(src)?; + + assert_eq!(desc.ids.len(), 0); + Ok(()) + } + + #[test] + #[should_panic] + fn parameter_description_wrong_length_fails() -> () { + let src = Bytes::from_static(b"\x00\x00\x00\x01\x02\x03"); + ParameterDescription::decode(src).unwrap(); + } +} \ No newline at end of file From 3f8508c0a43cb47bbf78b4f64054ba07725faa4a Mon Sep 17 00:00:00 2001 From: Zachery Gyurkovitz Date: Thu, 18 Jul 2019 11:54:24 -0700 Subject: [PATCH 2/3] Encode Describe messages --- sqlx-postgres-protocol/src/describe.rs | 64 ++++++++++++++++++++++++++ sqlx-postgres-protocol/src/lib.rs | 2 + 2 files changed, 66 insertions(+) create mode 100644 sqlx-postgres-protocol/src/describe.rs diff --git a/sqlx-postgres-protocol/src/describe.rs b/sqlx-postgres-protocol/src/describe.rs new file mode 100644 index 00000000..afc25cd0 --- /dev/null +++ b/sqlx-postgres-protocol/src/describe.rs @@ -0,0 +1,64 @@ +use crate::Encode; +use std::io; + +#[derive(Debug)] +pub enum DescribeKind { + Portal, + PreparedStatement, +} + +#[derive(Debug)] +pub struct Describe<'a> { + kind: DescribeKind, + name: &'a str, +} + +impl<'a> Describe<'a> { + pub fn new(kind: DescribeKind, name: &'a str) -> Self { + Self { kind, name } + } +} + +impl<'a> Encode for Describe<'a> { + fn encode(&self, buf: &mut Vec) -> io::Result<()> { + buf.push(b'D'); + + let len = 4 + self.name.len() + 1 + 4; + buf.extend_from_slice(&(len as i32).to_be_bytes()); + + match &self.kind { + DescribeKind::Portal => buf.push(b'P'), + DescribeKind::PreparedStatement => buf.push(b'S'), + }; + + buf.extend_from_slice(self.name.as_bytes()); + buf.push(b'\0'); + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::{Describe, DescribeKind}; + use crate::Encode; + use std::io; + + #[test] + fn it_encodes_describe_portal() -> io::Result<()> { + let mut buf = vec![]; + Describe::new(DescribeKind::Portal, "ABC123").encode(&mut buf)?; + assert_eq!(&buf, b"D\x00\x00\x00\x0fPABC123\x00"); + + Ok(()) + } + + #[test] + fn it_encodes_describe_statement() -> io::Result<()> { + let mut buf = vec![]; + Describe::new(DescribeKind::PreparedStatement, "95 apples").encode(&mut buf)?; + assert_eq!(&buf, b"D\x00\x00\x00\x12S95 apples\x00"); + + Ok(()) + } +} \ No newline at end of file diff --git a/sqlx-postgres-protocol/src/lib.rs b/sqlx-postgres-protocol/src/lib.rs index 9ee84807..21156e12 100644 --- a/sqlx-postgres-protocol/src/lib.rs +++ b/sqlx-postgres-protocol/src/lib.rs @@ -6,6 +6,7 @@ pub mod bind; mod command_complete; mod data_row; mod decode; +mod describe; mod encode; mod execute; mod message; @@ -28,6 +29,7 @@ pub use self::{ command_complete::CommandComplete, data_row::DataRow, decode::Decode, + describe::{Describe, DescribeKind}, encode::Encode, execute::Execute, message::Message, From 6e7b18bc004e6f946bd8d732cb64cd2dc170a9e5 Mon Sep 17 00:00:00 2001 From: Zachery Gyurkovitz Date: Thu, 18 Jul 2019 11:59:33 -0700 Subject: [PATCH 3/3] cargo fmt --- sqlx-postgres-protocol/src/describe.rs | 4 ++-- sqlx-postgres-protocol/src/message.rs | 2 +- sqlx-postgres-protocol/src/parameter_description.rs | 10 +++++----- sqlx-postgres/src/connection/prepare.rs | 13 +++++++++++-- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/sqlx-postgres-protocol/src/describe.rs b/sqlx-postgres-protocol/src/describe.rs index afc25cd0..bf654063 100644 --- a/sqlx-postgres-protocol/src/describe.rs +++ b/sqlx-postgres-protocol/src/describe.rs @@ -25,7 +25,7 @@ impl<'a> Encode for Describe<'a> { let len = 4 + self.name.len() + 1 + 4; buf.extend_from_slice(&(len as i32).to_be_bytes()); - + match &self.kind { DescribeKind::Portal => buf.push(b'P'), DescribeKind::PreparedStatement => buf.push(b'S'), @@ -61,4 +61,4 @@ mod test { Ok(()) } -} \ No newline at end of file +} diff --git a/sqlx-postgres-protocol/src/message.rs b/sqlx-postgres-protocol/src/message.rs index 2895c74b..23e42324 100644 --- a/sqlx-postgres-protocol/src/message.rs +++ b/sqlx-postgres-protocol/src/message.rs @@ -1,6 +1,6 @@ use crate::{ Authentication, BackendKeyData, CommandComplete, DataRow, Decode, NotificationResponse, - ParameterStatus, ReadyForQuery, Response, RowDescription, ParameterDescription, + ParameterDescription, ParameterStatus, ReadyForQuery, Response, RowDescription, }; use byteorder::{BigEndian, ByteOrder}; use bytes::BytesMut; diff --git a/sqlx-postgres-protocol/src/parameter_description.rs b/sqlx-postgres-protocol/src/parameter_description.rs index 086da015..ab94d984 100644 --- a/sqlx-postgres-protocol/src/parameter_description.rs +++ b/sqlx-postgres-protocol/src/parameter_description.rs @@ -1,4 +1,4 @@ -use crate::{Decode}; +use crate::Decode; use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; @@ -14,17 +14,17 @@ pub struct ParameterDescription { impl Decode for ParameterDescription { fn decode(src: Bytes) -> io::Result { let count = BigEndian::read_u16(&*src) as usize; - + // todo: error handling assert_eq!(src.len(), count * 4 + 2); - + let mut ids = Vec::with_capacity(count); for i in 0..count { let offset = i * 4 + 2; // 4==size_of(u32), 2==size_of(u16) ids.push(BigEndian::read_u32(&src[offset..])); } - Ok(ParameterDescription {ids}) + Ok(ParameterDescription { ids }) } } @@ -61,4 +61,4 @@ mod test { let src = Bytes::from_static(b"\x00\x00\x00\x01\x02\x03"); ParameterDescription::decode(src).unwrap(); } -} \ No newline at end of file +} diff --git a/sqlx-postgres/src/connection/prepare.rs b/sqlx-postgres/src/connection/prepare.rs index ebd5b149..336e682b 100644 --- a/sqlx-postgres/src/connection/prepare.rs +++ b/sqlx-postgres/src/connection/prepare.rs @@ -16,7 +16,11 @@ pub fn prepare<'a, 'b>(connection: &'a mut Connection, query: &'b str) -> Prepar let bind_state = proto::bind::header(&mut connection.wbuf, "", "", &[]); - Prepare { connection, bind_state, bind_values: 0 } + Prepare { + connection, + bind_state, + bind_values: 0, + } } impl<'a> Prepare<'a> { @@ -36,7 +40,12 @@ impl<'a> Prepare<'a> { #[inline] pub async fn execute(self) -> io::Result { - proto::bind::trailer(&mut self.connection.wbuf, self.bind_state, self.bind_values, &[]); + proto::bind::trailer( + &mut self.connection.wbuf, + self.bind_state, + self.bind_values, + &[], + ); self.connection.send(Execute::new("", 0)); self.connection.send(Sync);