mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-30 05:11:13 +00:00
Fix DataRow decode to make values accessible
This commit is contained in:
parent
6cfd3a57d1
commit
470804f542
@ -3,13 +3,16 @@ extern crate criterion;
|
||||
|
||||
use bytes::Bytes;
|
||||
use criterion::{black_box, Criterion};
|
||||
use sqlx_postgres_protocol::{BackendKeyData, Decode, ParameterStatus, ReadyForQuery, Response};
|
||||
use sqlx_postgres_protocol::{
|
||||
BackendKeyData, DataRow, Decode, ParameterStatus, ReadyForQuery, Response,
|
||||
};
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
const NOTICE_RESPONSE: &[u8] = b"SNOTICE\0VNOTICE\0C42710\0Mextension \"uuid-ossp\" already exists, skipping\0Fextension.c\0L1656\0RCreateExtension\0\0";
|
||||
const PARAM_STATUS: &[u8] = b"session_authorization\0postgres\0";
|
||||
const BACKEND_KEY_DATA: &[u8] = b"\0\0'\xc6\x89R\xc5+";
|
||||
const READY_FOR_QUERY: &[u8] = b"E";
|
||||
const DATA_ROW: &[u8] = b"\0\x03\0\0\0\x011\0\0\0\x012\0\0\0\x013";
|
||||
|
||||
c.bench_function("decode Response", |b| {
|
||||
b.iter(|| {
|
||||
@ -35,6 +38,12 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
let _ = ReadyForQuery::decode(black_box(Bytes::from_static(READY_FOR_QUERY))).unwrap();
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("decode DataRow", |b| {
|
||||
b.iter(|| {
|
||||
let _ = DataRow::decode(black_box(Bytes::from_static(DATA_ROW))).unwrap();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use crate::Decode;
|
||||
use bytes::Bytes;
|
||||
use std::io;
|
||||
use std::convert::TryInto;
|
||||
use std::{convert::TryInto, io};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BackendKeyData {
|
||||
|
||||
@ -1,70 +1,82 @@
|
||||
use crate::Decode;
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use bytes::Bytes;
|
||||
use std::io;
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
fmt::{self, Debug},
|
||||
io,
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
// TODO: Custom Debug for DataRow
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DataRow {
|
||||
len: u16,
|
||||
data: Bytes,
|
||||
}
|
||||
|
||||
impl DataRow {
|
||||
pub fn values(&self) -> DataValues<'_> {
|
||||
DataValues {
|
||||
rem: self.len,
|
||||
buf: &*self.data,
|
||||
}
|
||||
}
|
||||
ranges: Vec<Option<Range<usize>>>,
|
||||
buf: Bytes,
|
||||
}
|
||||
|
||||
impl Decode for DataRow {
|
||||
fn decode(src: Bytes) -> io::Result<Self> {
|
||||
let len = BigEndian::read_u16(&src[..2]);
|
||||
let len = u16::from_be_bytes(src.as_ref()[..2].try_into().unwrap());
|
||||
|
||||
Ok(Self {
|
||||
len,
|
||||
data: src.slice_from(2),
|
||||
})
|
||||
}
|
||||
}
|
||||
let mut ranges = Vec::with_capacity(len as usize);
|
||||
let mut rem = len;
|
||||
let mut index = 2;
|
||||
|
||||
pub struct DataValues<'a> {
|
||||
rem: u16,
|
||||
buf: &'a [u8],
|
||||
}
|
||||
while rem > 0 {
|
||||
// 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 value_len =
|
||||
i32::from_be_bytes(src.as_ref()[index..(index + 4)].try_into().unwrap());
|
||||
index += 4;
|
||||
|
||||
impl<'a> Iterator for DataValues<'a> {
|
||||
type Item = Option<&'a [u8]>;
|
||||
if value_len == -1 {
|
||||
ranges.push(None);
|
||||
} else {
|
||||
let value_beg = index;
|
||||
let value_end = value_beg + (value_len as usize);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.rem == 0 {
|
||||
return None;
|
||||
ranges.push(Some(value_beg..(value_end as usize)));
|
||||
|
||||
index += value_len as usize;
|
||||
}
|
||||
|
||||
rem -= 1;
|
||||
}
|
||||
|
||||
let len = BigEndian::read_i32(self.buf);
|
||||
let size = (if len < 0 { 0 } else { len }) as usize;
|
||||
|
||||
let value = if len == -1 {
|
||||
None
|
||||
} else {
|
||||
Some(&self.buf[4..(4 + len) as usize])
|
||||
};
|
||||
|
||||
self.rem -= 1;
|
||||
self.buf = &self.buf[(size + 4)..];
|
||||
|
||||
Some(value)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.rem as usize, Some(self.rem as usize))
|
||||
Ok(Self { ranges, buf: src })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExactSizeIterator for DataValues<'a> {}
|
||||
impl DataRow {
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.ranges.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.ranges.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<&[u8]> {
|
||||
Some(&self.buf[self.ranges[index].clone()?])
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DataRow {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("DataRow")
|
||||
.field(
|
||||
"values",
|
||||
&self
|
||||
.ranges
|
||||
.iter()
|
||||
.map(|range| Some(Bytes::from(&self.buf[range.clone()?])))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@ -79,12 +91,14 @@ mod tests {
|
||||
fn it_decodes_data_row() -> io::Result<()> {
|
||||
let src = Bytes::from_static(DATA_ROW);
|
||||
let message = DataRow::decode(src)?;
|
||||
assert_eq!(message.values().len(), 3);
|
||||
|
||||
for (index, value) in message.values().enumerate() {
|
||||
// "1", "2", "3"
|
||||
assert_eq!(value, Some(&[(index + 1 + 48) as u8][..]));
|
||||
}
|
||||
println!("{:?}", message);
|
||||
|
||||
assert_eq!(message.len(), 3);
|
||||
|
||||
assert_eq!(message.get(0), Some(&b"1"[..]));
|
||||
assert_eq!(message.get(1), Some(&b"2"[..]));
|
||||
assert_eq!(message.get(2), Some(&b"3"[..]));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ pub use self::{
|
||||
authentication::Authentication,
|
||||
backend_key_data::BackendKeyData,
|
||||
command_complete::CommandComplete,
|
||||
data_row::{DataRow, DataValues},
|
||||
data_row::DataRow,
|
||||
decode::Decode,
|
||||
encode::Encode,
|
||||
message::Message,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user