From bbbc1811ce5f7695401cb2f7ee5f540a11404b62 Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Mon, 30 Mar 2020 18:31:00 -0700 Subject: [PATCH] postgres: tweak DataRow::read --- sqlx-core/Cargo.toml | 2 + sqlx-core/src/lib.rs | 8 +-- sqlx-core/src/postgres/connection.rs | 2 +- sqlx-core/src/postgres/protocol/data_row.rs | 58 ++++++++++++++------- sqlx-core/src/postgres/types/raw/array.rs | 2 +- 5 files changed, 48 insertions(+), 24 deletions(-) diff --git a/sqlx-core/Cargo.toml b/sqlx-core/Cargo.toml index 38a4d049fe..edf8d88070 100644 --- a/sqlx-core/Cargo.toml +++ b/sqlx-core/Cargo.toml @@ -29,6 +29,8 @@ sqlite = [ "libsqlite3-sys" ] tls = [ "async-native-tls" ] runtime-async-std = [ "async-native-tls/runtime-async-std", "async-std" ] runtime-tokio = [ "async-native-tls/runtime-tokio", "tokio" ] +# intended for internal benchmarking, do not use +bench = [] [dependencies] async-native-tls = { version = "0.3.2", default-features = false, optional = true } diff --git a/sqlx-core/src/lib.rs b/sqlx-core/src/lib.rs index 93598a9d75..024a1f91cc 100644 --- a/sqlx-core/src/lib.rs +++ b/sqlx-core/src/lib.rs @@ -7,8 +7,12 @@ #![cfg_attr(not(feature = "sqlite"), forbid(unsafe_code))] #![recursion_limit = "512"] #![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(all(test, feature = "bench"), feature(test))] // #![warn(missing_docs)] +#[cfg(all(test, feature = "bench"))] +extern crate test; + // HACK: Allow a feature name the same name as a dependency #[cfg(feature = "bigdecimal")] extern crate bigdecimal_ as bigdecimal; @@ -65,7 +69,3 @@ pub mod postgres; pub mod sqlite; pub use error::{Error, Result}; - -// Named Lifetimes: -// 'c: connection -// 'q: query string (and arguments) diff --git a/sqlx-core/src/postgres/connection.rs b/sqlx-core/src/postgres/connection.rs index 09e414b3a2..3c5300878e 100644 --- a/sqlx-core/src/postgres/connection.rs +++ b/sqlx-core/src/postgres/connection.rs @@ -98,7 +98,7 @@ pub struct PgConnection { // Work buffer for the value ranges of the current row // This is used as the backing memory for each Row's value indexes - pub(super) current_row_values: Vec>>, + pub(super) current_row_values: Vec>, // TODO: Find a use for these values. Perhaps in a debug impl of PgConnection? #[allow(dead_code)] diff --git a/sqlx-core/src/postgres/protocol/data_row.rs b/sqlx-core/src/postgres/protocol/data_row.rs index 579367e999..e36e652fb2 100644 --- a/sqlx-core/src/postgres/protocol/data_row.rs +++ b/sqlx-core/src/postgres/protocol/data_row.rs @@ -4,27 +4,26 @@ use byteorder::NetworkEndian; use std::ops::Range; pub(crate) struct DataRow<'c> { - len: u16, + values: &'c [Option<(u32, u32)>], buffer: &'c [u8], - values: &'c [Option>], } impl<'c> DataRow<'c> { pub(crate) fn len(&self) -> usize { - self.len as usize + self.values.len() } pub(crate) fn get(&self, index: usize) -> Option<&'c [u8]> { - let range = self.values[index].as_ref()?; - - Some(&self.buffer[(range.start as usize)..(range.end as usize)]) + self.values[index] + .as_ref() + .map(|(offset, size)| &self.buffer[(*offset as usize)..((*offset + *size) as usize)]) } } impl<'c> DataRow<'c> { pub(crate) fn read( buffer: &'c [u8], - values: &'c mut Vec>>, + values: &'c mut Vec>, ) -> crate::Result { values.clear(); @@ -32,30 +31,53 @@ impl<'c> DataRow<'c> { let len = buf.get_u16::()?; - let mut index = 6; + let mut offset = 6; while values.len() < (len as usize) { // The length of the column value, in bytes (this count does not include itself). // Can be zero. As a special case, -1 indicates a NULL column value. // No value bytes follow in the NULL case. - let size = buf.get_i32::()?; + let mut size = buf.get_i32::()?; - if size == -1 { + if size < 0 { values.push(None); - index += 4; + offset += 4; } else { - values.push(Some((index)..(index + (size as u32)))); + values.push(Some((offset, size as u32))); - index += (size as u32) + 4; + offset += (size as u32) + 4; buf.advance(size as usize); } } - Ok(Self { - len, - buffer, - values, - }) + Ok(Self { buffer, values }) } } + +#[cfg(feature = "bench")] +#[bench] +fn bench_get_data_row(b: &mut test::Bencher) { + let buffer = b"\x00\x08\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00\n\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00(\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00P"; + let mut values = Vec::with_capacity(10); + let row = DataRow::read(buffer, &mut values).unwrap(); + + b.iter(|| { + assert_eq!(row.get(0), None); + assert_eq!(row.get(1), Some(&[0, 0, 0, 10][..])); + assert_eq!(row.get(2), None); + assert_eq!(row.get(3), Some(&[0, 0, 0, 20][..])); + assert_eq!(row.get(4), None); + }); +} + +#[cfg(feature = "bench")] +#[bench] +fn bench_read_data_row(b: &mut test::Bencher) { + let buffer = b"\x00\x08\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00\n\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00(\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00P"; + let mut values = Vec::with_capacity(10); + + b.iter(|| { + let row = DataRow::read(buffer, &mut values).unwrap(); + }); +} diff --git a/sqlx-core/src/postgres/types/raw/array.rs b/sqlx-core/src/postgres/types/raw/array.rs index a9b7d64769..acf8f7314e 100644 --- a/sqlx-core/src/postgres/types/raw/array.rs +++ b/sqlx-core/src/postgres/types/raw/array.rs @@ -176,7 +176,7 @@ where mod tests { use super::PgArrayDecoder; use super::PgArrayEncoder; - use crate::postgres::{PgRawBuffer, PgValue, Postgres}; + use crate::postgres::{PgRawBuffer, PgValue}; const BUF_BINARY_I32: &[u8] = b"\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x04";