postgres: have PgValue remember its type OID

this is in preparation of doing type compatibility checks
This commit is contained in:
Ryan Leckey 2020-03-25 01:46:13 -07:00
parent 918a797581
commit 1dc582edd0
29 changed files with 365 additions and 291 deletions

View File

@ -1,5 +1,8 @@
use crate::database::{Database, HasCursor, HasRawValue, HasRow}; use crate::cursor::HasCursor;
use crate::database::Database;
use crate::mysql::error::MySqlError; use crate::mysql::error::MySqlError;
use crate::row::HasRow;
use crate::value::HasRawValue;
/// **MySQL** database driver. /// **MySQL** database driver.
#[derive(Debug)] #[derive(Debug)]
@ -32,5 +35,7 @@ impl<'c, 'q> HasCursor<'c, 'q> for MySql {
} }
impl<'c> HasRawValue<'c> for MySql { impl<'c> HasRawValue<'c> for MySql {
type Database = MySql;
type RawValue = Option<super::MySqlValue<'c>>; type RawValue = Option<super::MySqlValue<'c>>;
} }

View File

@ -11,8 +11,9 @@ use crate::executor::Executor;
use crate::postgres::database::Postgres; use crate::postgres::database::Postgres;
use crate::postgres::protocol::{ use crate::postgres::protocol::{
Authentication, AuthenticationMd5, AuthenticationSasl, BackendKeyData, Message, Authentication, AuthenticationMd5, AuthenticationSasl, BackendKeyData, Message,
PasswordMessage, StartupMessage, StatementId, Terminate, TypeFormat, PasswordMessage, StartupMessage, StatementId, Terminate,
}; };
use crate::postgres::row::Statement;
use crate::postgres::stream::PgStream; use crate::postgres::stream::PgStream;
use crate::postgres::{sasl, tls}; use crate::postgres::{sasl, tls};
use crate::url::Url; use crate::url::Url;
@ -82,9 +83,11 @@ pub struct PgConnection {
pub(super) next_statement_id: u32, pub(super) next_statement_id: u32,
pub(super) is_ready: bool, pub(super) is_ready: bool,
pub(super) cache_statement: HashMap<Box<str>, StatementId>, // cache query -> statement ID
pub(super) cache_statement_columns: HashMap<StatementId, Arc<HashMap<Box<str>, usize>>>, pub(super) cache_statement_id: HashMap<Box<str>, StatementId>,
pub(super) cache_statement_formats: HashMap<StatementId, Arc<[TypeFormat]>>,
// cache statement ID -> statement description
pub(super) cache_statement: HashMap<StatementId, Arc<Statement>>,
// Work buffer for the value ranges of the current row // Work buffer for the value ranges of the current row
// This is used as the backing memory for each Row's value indexes // This is used as the backing memory for each Row's value indexes
@ -250,9 +253,8 @@ impl PgConnection {
current_row_values: Vec::with_capacity(10), current_row_values: Vec::with_capacity(10),
next_statement_id: 1, next_statement_id: 1,
is_ready: true, is_ready: true,
cache_statement: HashMap::new(), cache_statement_id: HashMap::with_capacity(10),
cache_statement_columns: HashMap::new(), cache_statement: HashMap::with_capacity(10),
cache_statement_formats: HashMap::new(),
process_id: key_data.process_id, process_id: key_data.process_id,
secret_key: key_data.secret_key, secret_key: key_data.secret_key,
}) })

View File

@ -7,16 +7,14 @@ use crate::connection::ConnectionSource;
use crate::cursor::Cursor; use crate::cursor::Cursor;
use crate::executor::Execute; use crate::executor::Execute;
use crate::pool::Pool; use crate::pool::Pool;
use crate::postgres::protocol::{ use crate::postgres::protocol::{DataRow, Message, ReadyForQuery, RowDescription, StatementId};
DataRow, Message, ReadyForQuery, RowDescription, StatementId, TypeFormat, use crate::postgres::row::{Column, Statement};
};
use crate::postgres::{PgArguments, PgConnection, PgRow, Postgres}; use crate::postgres::{PgArguments, PgConnection, PgRow, Postgres};
pub struct PgCursor<'c, 'q> { pub struct PgCursor<'c, 'q> {
source: ConnectionSource<'c, PgConnection>, source: ConnectionSource<'c, PgConnection>,
query: Option<(&'q str, Option<PgArguments>)>, query: Option<(&'q str, Option<PgArguments>)>,
columns: Arc<HashMap<Box<str>, usize>>, statement: Arc<Statement>,
formats: Arc<[TypeFormat]>,
} }
impl crate::cursor::private::Sealed for PgCursor<'_, '_> {} impl crate::cursor::private::Sealed for PgCursor<'_, '_> {}
@ -32,8 +30,7 @@ impl<'c, 'q> Cursor<'c, 'q> for PgCursor<'c, 'q> {
{ {
Self { Self {
source: ConnectionSource::Pool(pool.clone()), source: ConnectionSource::Pool(pool.clone()),
columns: Arc::default(), statement: Arc::default(),
formats: Arc::new([] as [TypeFormat; 0]),
query: Some(query.into_parts()), query: Some(query.into_parts()),
} }
} }
@ -46,8 +43,7 @@ impl<'c, 'q> Cursor<'c, 'q> for PgCursor<'c, 'q> {
{ {
Self { Self {
source: ConnectionSource::ConnectionRef(conn), source: ConnectionSource::ConnectionRef(conn),
columns: Arc::default(), statement: Arc::default(),
formats: Arc::new([] as [TypeFormat; 0]),
query: Some(query.into_parts()), query: Some(query.into_parts()),
} }
} }
@ -57,29 +53,33 @@ impl<'c, 'q> Cursor<'c, 'q> for PgCursor<'c, 'q> {
} }
} }
fn parse_row_description(rd: RowDescription) -> (HashMap<Box<str>, usize>, Vec<TypeFormat>) { fn parse_row_description(rd: RowDescription) -> Statement {
let mut columns = HashMap::new(); let mut names = HashMap::new();
let mut formats = Vec::new(); let mut columns = Vec::new();
columns.reserve(rd.fields.len()); columns.reserve(rd.fields.len());
formats.reserve(rd.fields.len()); names.reserve(rd.fields.len());
for (index, field) in rd.fields.iter().enumerate() { for (index, field) in rd.fields.iter().enumerate() {
if let Some(name) = &field.name { if let Some(name) = &field.name {
columns.insert(name.clone(), index); names.insert(name.clone(), index);
} }
formats.push(field.type_format); columns.push(Column {
type_oid: field.type_id.0,
format: field.type_format,
});
} }
(columns, formats) Statement {
columns: columns.into_boxed_slice(),
names,
}
} }
// Used to describe the incoming results // Used to describe the incoming results
// We store the column map in an Arc and share it among all rows // We store the column map in an Arc and share it among all rows
async fn expect_desc( async fn expect_desc(conn: &mut PgConnection) -> crate::Result<Postgres, Statement> {
conn: &mut PgConnection,
) -> crate::Result<Postgres, (HashMap<Box<str>, usize>, Vec<TypeFormat>)> {
let description: Option<_> = loop { let description: Option<_> = loop {
match conn.stream.receive().await? { match conn.stream.receive().await? {
Message::ParseComplete | Message::BindComplete => {} Message::ParseComplete | Message::BindComplete => {}
@ -106,24 +106,15 @@ async fn expect_desc(
// A form of describe that uses the statement cache // A form of describe that uses the statement cache
async fn get_or_describe( async fn get_or_describe(
conn: &mut PgConnection, conn: &mut PgConnection,
statement: StatementId, id: StatementId,
) -> crate::Result<Postgres, (Arc<HashMap<Box<str>, usize>>, Arc<[TypeFormat]>)> { ) -> crate::Result<Postgres, Arc<Statement>> {
if !conn.cache_statement_columns.contains_key(&statement) if !conn.cache_statement.contains_key(&id) {
|| !conn.cache_statement_formats.contains_key(&statement) let statement = expect_desc(conn).await?;
{
let (columns, formats) = expect_desc(conn).await?;
conn.cache_statement_columns conn.cache_statement.insert(id, Arc::new(statement));
.insert(statement, Arc::new(columns));
conn.cache_statement_formats
.insert(statement, Arc::from(formats));
} }
Ok(( Ok(Arc::clone(&conn.cache_statement[&id]))
Arc::clone(&conn.cache_statement_columns[&statement]),
Arc::clone(&conn.cache_statement_formats[&statement]),
))
} }
async fn next<'a, 'c: 'a, 'q: 'a>( async fn next<'a, 'c: 'a, 'q: 'a>(
@ -141,10 +132,7 @@ async fn next<'a, 'c: 'a, 'q: 'a>(
if let Some(statement) = statement { if let Some(statement) = statement {
// A prepared statement will re-use the previous column map if // A prepared statement will re-use the previous column map if
// this query has been executed before // this query has been executed before
let (columns, formats) = get_or_describe(&mut *conn, statement).await?; cursor.statement = get_or_describe(&mut *conn, statement).await?;
cursor.columns = columns;
cursor.formats = formats;
} }
// A non-prepared query must be described each time // A non-prepared query must be described each time
@ -171,18 +159,14 @@ async fn next<'a, 'c: 'a, 'q: 'a>(
Message::RowDescription => { Message::RowDescription => {
let rd = RowDescription::read(conn.stream.buffer())?; let rd = RowDescription::read(conn.stream.buffer())?;
let (columns, formats) = parse_row_description(rd); cursor.statement = Arc::new(parse_row_description(rd));
cursor.columns = Arc::new(columns);
cursor.formats = Arc::from(formats);
} }
Message::DataRow => { Message::DataRow => {
let data = DataRow::read(conn.stream.buffer(), &mut conn.current_row_values)?; let data = DataRow::read(conn.stream.buffer(), &mut conn.current_row_values)?;
return Ok(Some(PgRow { return Ok(Some(PgRow {
columns: Arc::clone(&cursor.columns), statement: Arc::clone(&cursor.statement),
formats: Arc::clone(&cursor.formats),
data, data,
})); }));
} }

View File

@ -1,19 +1,21 @@
//! Types which represent various database drivers. //! Types which represent various database drivers.
use crate::database::{Database, HasCursor, HasRawValue, HasRow}; use crate::cursor::HasCursor;
use crate::postgres::error::PgError; use crate::database::Database;
use crate::postgres::row::PgValue; use crate::postgres::{PgArguments, PgConnection, PgCursor, PgError, PgRow, PgTypeInfo, PgValue};
use crate::row::HasRow;
use crate::value::HasRawValue;
/// **Postgres** database driver. /// **Postgres** database driver.
#[derive(Debug)] #[derive(Debug)]
pub struct Postgres; pub struct Postgres;
impl Database for Postgres { impl Database for Postgres {
type Connection = super::PgConnection; type Connection = PgConnection;
type Arguments = super::PgArguments; type Arguments = PgArguments;
type TypeInfo = super::PgTypeInfo; type TypeInfo = PgTypeInfo;
type TableId = u32; type TableId = u32;
@ -25,15 +27,17 @@ impl Database for Postgres {
impl<'a> HasRow<'a> for Postgres { impl<'a> HasRow<'a> for Postgres {
type Database = Postgres; type Database = Postgres;
type Row = super::PgRow<'a>; type Row = PgRow<'a>;
} }
impl<'s, 'q> HasCursor<'s, 'q> for Postgres { impl<'s, 'q> HasCursor<'s, 'q> for Postgres {
type Database = Postgres; type Database = Postgres;
type Cursor = super::PgCursor<'s, 'q>; type Cursor = PgCursor<'s, 'q>;
} }
impl<'a> HasRawValue<'a> for Postgres { impl<'a> HasRawValue<'a> for Postgres {
type RawValue = Option<PgValue<'a>>; type Database = Postgres;
type RawValue = PgValue<'a>;
} }

View File

@ -22,7 +22,7 @@ impl PgConnection {
} }
pub(crate) fn write_prepare(&mut self, query: &str, args: &PgArguments) -> StatementId { pub(crate) fn write_prepare(&mut self, query: &str, args: &PgArguments) -> StatementId {
if let Some(&id) = self.cache_statement.get(query) { if let Some(&id) = self.cache_statement_id.get(query) {
id id
} else { } else {
let id = StatementId(self.next_statement_id); let id = StatementId(self.next_statement_id);
@ -35,7 +35,7 @@ impl PgConnection {
param_types: &*args.types, param_types: &*args.types,
}); });
self.cache_statement.insert(query.into(), id); self.cache_statement_id.insert(query.into(), id);
id id
} }
@ -106,7 +106,7 @@ impl PgConnection {
// Next, [Describe] will return the expected result columns and types // Next, [Describe] will return the expected result columns and types
// Conditionally run [Describe] only if the results have not been cached // Conditionally run [Describe] only if the results have not been cached
if !self.cache_statement_columns.contains_key(&statement) { if !self.cache_statement.contains_key(&statement) {
self.write_describe(protocol::Describe::Portal("")); self.write_describe(protocol::Describe::Portal(""));
} }

View File

@ -6,8 +6,9 @@ pub use cursor::PgCursor;
pub use database::Postgres; pub use database::Postgres;
pub use error::PgError; pub use error::PgError;
pub use listen::{PgListener, PgNotification}; pub use listen::{PgListener, PgNotification};
pub use row::{PgRow, PgValue}; pub use row::PgRow;
pub use types::PgTypeInfo; pub use types::PgTypeInfo;
pub use value::{PgData, PgValue};
mod arguments; mod arguments;
mod connection; mod connection;
@ -22,6 +23,7 @@ mod sasl;
mod stream; mod stream;
mod tls; mod tls;
pub mod types; pub mod types;
mod value;
/// An alias for [`Pool`][crate::pool::Pool], specialized for **Postgres**. /// An alias for [`Pool`][crate::pool::Pool], specialized for **Postgres**.
#[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))]
@ -29,5 +31,4 @@ pub type PgPool = crate::pool::Pool<PgConnection>;
make_query_as!(PgQueryAs, Postgres, PgRow); make_query_as!(PgQueryAs, Postgres, PgRow);
impl_map_row_for_row!(Postgres, PgRow); impl_map_row_for_row!(Postgres, PgRow);
impl_column_index_for_row!(PgRow);
impl_from_row_for_tuples!(Postgres, PgRow); impl_from_row_for_tuples!(Postgres, PgRow);

View File

@ -26,9 +26,6 @@ impl<'c> DataRow<'c> {
buffer: &'c [u8], buffer: &'c [u8],
values: &'c mut Vec<Option<Range<u32>>>, values: &'c mut Vec<Option<Range<u32>>>,
) -> crate::Result<Postgres, Self> { ) -> crate::Result<Postgres, Self> {
// let buffer = connection.stream.buffer();
// let values = &mut connection.current_row_values;
values.clear(); values.clear();
let mut buf = buffer; let mut buf = buffer;

View File

@ -1,38 +1,37 @@
use core::str::{from_utf8, Utf8Error};
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom;
use std::sync::Arc; use std::sync::Arc;
use crate::error::UnexpectedNullError;
use crate::postgres::protocol::{DataRow, TypeFormat}; use crate::postgres::protocol::{DataRow, TypeFormat};
use crate::postgres::value::PgValue;
use crate::postgres::Postgres; use crate::postgres::Postgres;
use crate::row::{ColumnIndex, Row}; use crate::row::{ColumnIndex, Row};
/// A value from Postgres. This may be in a BINARY or TEXT format depending // A statement has 0 or more columns being returned from the database
/// on the data type and if the query was prepared or not. // For Postgres, each column has an OID and a format (binary or text)
#[derive(Debug)] // For simple (unprepared) queries, format will always be text
pub enum PgValue<'c> { // For prepared queries, format will _almost_ always be binary
Binary(&'c [u8]), pub(crate) struct Column {
Text(&'c str), pub(crate) type_oid: u32,
pub(crate) format: TypeFormat,
} }
impl<'c> TryFrom<Option<PgValue<'c>>> for PgValue<'c> { // A statement description containing the column information used to
type Error = crate::Error<Postgres>; // properly decode data
#[derive(Default)]
pub(crate) struct Statement {
// column name -> position
pub(crate) names: HashMap<Box<str>, usize>,
#[inline] // all columns
fn try_from(value: Option<PgValue<'c>>) -> Result<Self, Self::Error> { pub(crate) columns: Box<[Column]>,
match value {
Some(value) => Ok(value),
None => Err(crate::Error::decode(UnexpectedNullError)),
}
}
} }
pub struct PgRow<'c> { pub struct PgRow<'c> {
pub(super) data: DataRow<'c>, pub(super) data: DataRow<'c>,
pub(super) columns: Arc<HashMap<Box<str>, usize>>,
pub(super) formats: Arc<[TypeFormat]>, // shared reference to the statement this row is coming from
// allows us to get the column information on demand
pub(super) statement: Arc<Statement>,
} }
impl crate::row::private_row::Sealed for PgRow<'_> {} impl crate::row::private_row::Sealed for PgRow<'_> {}
@ -40,24 +39,47 @@ impl crate::row::private_row::Sealed for PgRow<'_> {}
impl<'c> Row<'c> for PgRow<'c> { impl<'c> Row<'c> for PgRow<'c> {
type Database = Postgres; type Database = Postgres;
#[inline]
fn len(&self) -> usize { fn len(&self) -> usize {
self.data.len() self.data.len()
} }
#[doc(hidden)] #[doc(hidden)]
fn try_get_raw<I>(&self, index: I) -> crate::Result<Postgres, Option<PgValue<'c>>> fn try_get_raw<I>(&self, index: I) -> crate::Result<Postgres, PgValue<'c>>
where where
I: ColumnIndex<'c, Self>, I: ColumnIndex<'c, Self>,
{ {
let index = index.index(self)?; let index = index.index(self)?;
let column = &self.statement.columns[index];
let buffer = self.data.get(index); let buffer = self.data.get(index);
let value = match (column.format, buffer) {
(_, None) => PgValue::null(column.type_oid),
(TypeFormat::Binary, Some(buf)) => PgValue::bytes(column.type_oid, buf),
(TypeFormat::Text, Some(buf)) => PgValue::utf8(column.type_oid, buf)?,
};
buffer Ok(value)
.map(|buf| match self.formats[index] { }
TypeFormat::Binary => Ok(PgValue::Binary(buf)), }
TypeFormat::Text => Ok(PgValue::Text(from_utf8(buf)?)),
}) impl<'c> ColumnIndex<'c, PgRow<'c>> for usize {
.transpose() fn index(&self, row: &PgRow<'c>) -> crate::Result<Postgres, usize> {
.map_err(|err: Utf8Error| crate::Error::Decode(Box::new(err))) let len = Row::len(row);
if *self >= len {
return Err(crate::Error::ColumnIndexOutOfBounds { len, index: *self });
}
Ok(*self)
}
}
impl<'c> ColumnIndex<'c, PgRow<'c>> for str {
fn index(&self, row: &PgRow<'c>) -> crate::Result<Postgres, usize> {
row.statement
.names
.get(self)
.ok_or_else(|| crate::Error::ColumnNotFound((*self).into()))
.map(|&index| index as usize)
} }
} }

View File

@ -41,7 +41,7 @@ where
[T]: Type<Postgres>, [T]: Type<Postgres>,
T: Type<Postgres>, T: Type<Postgres>,
{ {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
PgArrayDecoder::<T>::new(value)?.collect() PgArrayDecoder::<T>::new(value)?.collect()
} }
} }

View File

@ -6,7 +6,7 @@ use num_bigint::{BigInt, Sign};
use crate::decode::Decode; use crate::decode::Decode;
use crate::encode::Encode; use crate::encode::Encode;
use crate::postgres::{PgTypeInfo, PgValue, Postgres}; use crate::postgres::{PgData, PgTypeInfo, PgValue, Postgres};
use crate::types::Type; use crate::types::Type;
use super::raw::{PgNumeric, PgNumericSign}; use super::raw::{PgNumeric, PgNumericSign};
@ -152,10 +152,10 @@ impl Encode<Postgres> for BigDecimal {
} }
impl Decode<'_, Postgres> for BigDecimal { impl Decode<'_, Postgres> for BigDecimal {
fn decode(value: Option<PgValue>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(binary) => PgNumeric::from_bytes(binary)?.try_into(), PgData::Binary(binary) => PgNumeric::from_bytes(binary)?.try_into(),
PgValue::Text(text) => text PgData::Text(text) => text
.parse::<BigDecimal>() .parse::<BigDecimal>()
.map_err(|e| crate::Error::Decode(e.into())), .map_err(|e| crate::Error::Decode(e.into())),
} }

View File

@ -1,11 +1,7 @@
use std::convert::TryInto;
use crate::decode::Decode; use crate::decode::Decode;
use crate::encode::Encode; use crate::encode::Encode;
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::row::PgValue; use crate::postgres::{PgData, PgTypeInfo, PgValue, Postgres};
use crate::postgres::types::PgTypeInfo;
use crate::postgres::Postgres;
use crate::types::Type; use crate::types::Type;
impl Type<Postgres> for bool { impl Type<Postgres> for bool {
@ -32,18 +28,14 @@ impl Encode<Postgres> for bool {
} }
impl<'de> Decode<'de, Postgres> for bool { impl<'de> Decode<'de, Postgres> for bool {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(buf) => Ok(buf.get(0).map(|&b| b != 0).unwrap_or_default()), PgData::Binary(buf) => Ok(buf.get(0).map(|&b| b != 0).unwrap_or_default()),
PgValue::Text("t") => Ok(true), PgData::Text("t") => Ok(true),
PgValue::Text("f") => Ok(false), PgData::Text("f") => Ok(false),
PgValue::Text(s) => { PgData::Text(s) => Err(decode_err!("unexpected value {:?} for boolean", s)),
return Err(crate::Error::Decode(
format!("unexpected value {:?} for boolean", s).into(),
));
}
} }
} }
} }

View File

@ -1,10 +1,8 @@
use std::convert::TryInto;
use crate::decode::Decode; use crate::decode::Decode;
use crate::encode::Encode; use crate::encode::Encode;
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::types::PgTypeInfo; use crate::postgres::types::PgTypeInfo;
use crate::postgres::{PgValue, Postgres}; use crate::postgres::{PgData, PgValue, Postgres};
use crate::types::Type; use crate::types::Type;
impl Type<Postgres> for [u8] { impl Type<Postgres> for [u8] {
@ -44,10 +42,10 @@ impl Encode<Postgres> for Vec<u8> {
} }
impl<'de> Decode<'de, Postgres> for Vec<u8> { impl<'de> Decode<'de, Postgres> for Vec<u8> {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(buf) => Ok(buf.to_vec()), PgData::Binary(buf) => Ok(buf.to_vec()),
PgValue::Text(s) => { PgData::Text(s) => {
// BYTEA is formatted as \x followed by hex characters // BYTEA is formatted as \x followed by hex characters
hex::decode(&s[2..]).map_err(crate::Error::decode) hex::decode(&s[2..]).map_err(crate::Error::decode)
} }
@ -56,10 +54,10 @@ impl<'de> Decode<'de, Postgres> for Vec<u8> {
} }
impl<'de> Decode<'de, Postgres> for &'de [u8] { impl<'de> Decode<'de, Postgres> for &'de [u8] {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(buf) => Ok(buf), PgData::Binary(buf) => Ok(buf),
PgValue::Text(_s) => Err(crate::Error::Decode( PgData::Text(_s) => Err(crate::Error::Decode(
"unsupported decode to `&[u8]` of BYTEA in a simple query; \ "unsupported decode to `&[u8]` of BYTEA in a simple query; \
use a prepared query or decode to `Vec<u8>`" use a prepared query or decode to `Vec<u8>`"
.into(), .into(),

View File

@ -7,9 +7,8 @@ use chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, Tim
use crate::decode::Decode; use crate::decode::Decode;
use crate::encode::Encode; use crate::encode::Encode;
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::row::PgValue;
use crate::postgres::types::PgTypeInfo; use crate::postgres::types::PgTypeInfo;
use crate::postgres::Postgres; use crate::postgres::{PgData, PgValue, Postgres};
use crate::types::Type; use crate::types::Type;
use crate::Error; use crate::Error;
@ -95,15 +94,15 @@ where
} }
impl<'de> Decode<'de, Postgres> for NaiveTime { impl<'de> Decode<'de, Postgres> for NaiveTime {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(mut buf) => { PgData::Binary(mut buf) => {
let micros = buf.read_i64::<NetworkEndian>().map_err(Error::decode)?; let micros = buf.read_i64::<NetworkEndian>().map_err(Error::decode)?;
Ok(NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(micros)) Ok(NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(micros))
} }
PgValue::Text(s) => NaiveTime::parse_from_str(s, "%H:%M:%S%.f").map_err(Error::decode), PgData::Text(s) => NaiveTime::parse_from_str(s, "%H:%M:%S%.f").map_err(Error::decode),
} }
} }
} }
@ -123,15 +122,15 @@ impl Encode<Postgres> for NaiveTime {
} }
impl<'de> Decode<'de, Postgres> for NaiveDate { impl<'de> Decode<'de, Postgres> for NaiveDate {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(mut buf) => { PgData::Binary(mut buf) => {
let days: i32 = buf.read_i32::<NetworkEndian>().map_err(Error::decode)?; let days: i32 = buf.read_i32::<NetworkEndian>().map_err(Error::decode)?;
Ok(NaiveDate::from_ymd(2000, 1, 1) + Duration::days(days as i64)) Ok(NaiveDate::from_ymd(2000, 1, 1) + Duration::days(days as i64))
} }
PgValue::Text(s) => NaiveDate::parse_from_str(s, "%Y-%m-%d").map_err(Error::decode), PgData::Text(s) => NaiveDate::parse_from_str(s, "%Y-%m-%d").map_err(Error::decode),
} }
} }
} }
@ -154,9 +153,9 @@ impl Encode<Postgres> for NaiveDate {
} }
impl<'de> Decode<'de, Postgres> for NaiveDateTime { impl<'de> Decode<'de, Postgres> for NaiveDateTime {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(mut buf) => { PgData::Binary(mut buf) => {
let micros = buf.read_i64::<NetworkEndian>().map_err(Error::decode)?; let micros = buf.read_i64::<NetworkEndian>().map_err(Error::decode)?;
postgres_epoch() postgres_epoch()
@ -173,7 +172,7 @@ impl<'de> Decode<'de, Postgres> for NaiveDateTime {
}) })
} }
PgValue::Text(s) => { PgData::Text(s) => {
NaiveDateTime::parse_from_str( NaiveDateTime::parse_from_str(
s, s,
if s.contains('+') { if s.contains('+') {
@ -207,14 +206,14 @@ impl Encode<Postgres> for NaiveDateTime {
} }
impl<'de> Decode<'de, Postgres> for DateTime<Utc> { impl<'de> Decode<'de, Postgres> for DateTime<Utc> {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
let date_time = Decode::<Postgres>::decode(value)?; let date_time = Decode::<Postgres>::decode(value)?;
Ok(DateTime::from_utc(date_time, Utc)) Ok(DateTime::from_utc(date_time, Utc))
} }
} }
impl<'de> Decode<'de, Postgres> for DateTime<Local> { impl<'de> Decode<'de, Postgres> for DateTime<Local> {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
let date_time = Decode::<Postgres>::decode(value)?; let date_time = Decode::<Postgres>::decode(value)?;
Ok(Local.from_utc_datetime(&date_time)) Ok(Local.from_utc_datetime(&date_time))
} }
@ -265,18 +264,18 @@ fn test_encode_time() {
#[test] #[test]
fn test_decode_time() { fn test_decode_time() {
let buf = [0u8; 8]; let buf = [0u8; 8];
let time: NaiveTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let time: NaiveTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(time, NaiveTime::from_hms(0, 0, 0),); assert_eq!(time, NaiveTime::from_hms(0, 0, 0),);
// half an hour // half an hour
let buf = (1_000_000i64 * 60 * 30).to_be_bytes(); let buf = (1_000_000i64 * 60 * 30).to_be_bytes();
let time: NaiveTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let time: NaiveTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(time, NaiveTime::from_hms(0, 30, 0),); assert_eq!(time, NaiveTime::from_hms(0, 30, 0),);
// 12:53:05.125305 // 12:53:05.125305
let buf = (1_000_000i64 * 60 * 60 * 12 + 1_000_000i64 * 60 * 53 + 1_000_000i64 * 5 + 125305) let buf = (1_000_000i64 * 60 * 60 * 12 + 1_000_000i64 * 60 * 53 + 1_000_000i64 * 5 + 125305)
.to_be_bytes(); .to_be_bytes();
let time: NaiveTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let time: NaiveTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(time, NaiveTime::from_hms_micro(12, 53, 5, 125305),); assert_eq!(time, NaiveTime::from_hms_micro(12, 53, 5, 125305),);
} }
@ -308,15 +307,15 @@ fn test_encode_datetime() {
#[test] #[test]
fn test_decode_datetime() { fn test_decode_datetime() {
let buf = [0u8; 8]; let buf = [0u8; 8];
let date: NaiveDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: NaiveDateTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(date.to_string(), "2000-01-01 00:00:00"); assert_eq!(date.to_string(), "2000-01-01 00:00:00");
let buf = 3_600_000_000i64.to_be_bytes(); let buf = 3_600_000_000i64.to_be_bytes();
let date: NaiveDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: NaiveDateTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(date.to_string(), "2000-01-01 01:00:00"); assert_eq!(date.to_string(), "2000-01-01 01:00:00");
let buf = 629_377_265_000_000i64.to_be_bytes(); let buf = 629_377_265_000_000i64.to_be_bytes();
let date: NaiveDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: NaiveDateTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(date.to_string(), "2019-12-11 11:01:05"); assert_eq!(date.to_string(), "2019-12-11 11:01:05");
} }
@ -344,14 +343,14 @@ fn test_encode_date() {
#[test] #[test]
fn test_decode_date() { fn test_decode_date() {
let buf = [0; 4]; let buf = [0; 4];
let date: NaiveDate = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: NaiveDate = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(date.to_string(), "2000-01-01"); assert_eq!(date.to_string(), "2000-01-01");
let buf = 366i32.to_be_bytes(); let buf = 366i32.to_be_bytes();
let date: NaiveDate = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: NaiveDate = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(date.to_string(), "2001-01-01"); assert_eq!(date.to_string(), "2001-01-01");
let buf = 7284i32.to_be_bytes(); let buf = 7284i32.to_be_bytes();
let date: NaiveDate = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: NaiveDate = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(date.to_string(), "2019-12-11"); assert_eq!(date.to_string(), "2019-12-11");
} }

View File

@ -1,4 +1,3 @@
use std::convert::TryInto;
use std::str::FromStr; use std::str::FromStr;
use byteorder::{NetworkEndian, ReadBytesExt}; use byteorder::{NetworkEndian, ReadBytesExt};
@ -8,7 +7,7 @@ use crate::encode::Encode;
use crate::error::Error; use crate::error::Error;
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::types::PgTypeInfo; use crate::postgres::types::PgTypeInfo;
use crate::postgres::{PgValue, Postgres}; use crate::postgres::{PgData, PgValue, Postgres};
use crate::types::Type; use crate::types::Type;
impl Type<Postgres> for f32 { impl Type<Postgres> for f32 {
@ -35,14 +34,14 @@ impl Encode<Postgres> for f32 {
} }
impl<'de> Decode<'de, Postgres> for f32 { impl<'de> Decode<'de, Postgres> for f32 {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(mut buf) => buf PgData::Binary(mut buf) => buf
.read_i32::<NetworkEndian>() .read_i32::<NetworkEndian>()
.map_err(Error::decode) .map_err(Error::decode)
.map(|value| f32::from_bits(value as u32)), .map(|value| f32::from_bits(value as u32)),
PgValue::Text(s) => f32::from_str(s).map_err(Error::decode), PgData::Text(s) => f32::from_str(s).map_err(Error::decode),
} }
} }
} }
@ -71,14 +70,14 @@ impl Encode<Postgres> for f64 {
} }
impl<'de> Decode<'de, Postgres> for f64 { impl<'de> Decode<'de, Postgres> for f64 {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(mut buf) => buf PgData::Binary(mut buf) => buf
.read_i64::<NetworkEndian>() .read_i64::<NetworkEndian>()
.map_err(Error::decode) .map_err(Error::decode)
.map(|value| f64::from_bits(value as u64)), .map(|value| f64::from_bits(value as u64)),
PgValue::Text(s) => f64::from_str(s).map_err(Error::decode), PgData::Text(s) => f64::from_str(s).map_err(Error::decode),
} }
} }
} }

View File

@ -1,4 +1,3 @@
use std::convert::TryInto;
use std::str::FromStr; use std::str::FromStr;
use byteorder::{NetworkEndian, ReadBytesExt}; use byteorder::{NetworkEndian, ReadBytesExt};
@ -7,7 +6,7 @@ use crate::decode::Decode;
use crate::encode::Encode; use crate::encode::Encode;
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::types::PgTypeInfo; use crate::postgres::types::PgTypeInfo;
use crate::postgres::{PgValue, Postgres}; use crate::postgres::{PgData, PgValue, Postgres};
use crate::types::Type; use crate::types::Type;
use crate::Error; use crate::Error;
@ -35,10 +34,10 @@ impl Encode<Postgres> for i16 {
} }
impl<'de> Decode<'de, Postgres> for i16 { impl<'de> Decode<'de, Postgres> for i16 {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(mut buf) => buf.read_i16::<NetworkEndian>().map_err(Error::decode), PgData::Binary(mut buf) => buf.read_i16::<NetworkEndian>().map_err(Error::decode),
PgValue::Text(s) => i16::from_str(s).map_err(Error::decode), PgData::Text(s) => i16::from_str(s).map_err(Error::decode),
} }
} }
} }
@ -67,10 +66,10 @@ impl Encode<Postgres> for i32 {
} }
impl<'de> Decode<'de, Postgres> for i32 { impl<'de> Decode<'de, Postgres> for i32 {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(mut buf) => buf.read_i32::<NetworkEndian>().map_err(Error::decode), PgData::Binary(mut buf) => buf.read_i32::<NetworkEndian>().map_err(Error::decode),
PgValue::Text(s) => i32::from_str(s).map_err(Error::decode), PgData::Text(s) => i32::from_str(s).map_err(Error::decode),
} }
} }
} }
@ -99,10 +98,10 @@ impl Encode<Postgres> for i64 {
} }
impl<'de> Decode<'de, Postgres> for i64 { impl<'de> Decode<'de, Postgres> for i64 {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(mut buf) => buf.read_i64::<NetworkEndian>().map_err(Error::decode), PgData::Binary(mut buf) => buf.read_i64::<NetworkEndian>().map_err(Error::decode),
PgValue::Text(s) => i64::from_str(s).map_err(Error::decode), PgData::Text(s) => i64::from_str(s).map_err(Error::decode),
} }
} }
} }

View File

@ -1,4 +1,3 @@
use std::convert::TryInto;
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network}; use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
@ -6,9 +5,9 @@ use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
use crate::decode::Decode; use crate::decode::Decode;
use crate::encode::Encode; use crate::encode::Encode;
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::row::PgValue;
use crate::postgres::types::PgTypeInfo; use crate::postgres::types::PgTypeInfo;
use crate::postgres::Postgres; use crate::postgres::value::PgValue;
use crate::postgres::{PgData, Postgres};
use crate::types::Type; use crate::types::Type;
use crate::Error; use crate::Error;
@ -67,10 +66,10 @@ impl Encode<Postgres> for IpNetwork {
} }
impl<'de> Decode<'de, Postgres> for IpNetwork { impl<'de> Decode<'de, Postgres> for IpNetwork {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(buf) => decode(buf), PgData::Binary(buf) => decode(buf),
PgValue::Text(s) => s.parse().map_err(|err| crate::Error::decode(err)), PgData::Text(s) => s.parse().map_err(crate::Error::decode),
} }
} }
} }

View File

@ -26,7 +26,7 @@ impl Encode<Postgres> for JsonValue {
} }
impl<'de> Decode<'de, Postgres> for JsonValue { impl<'de> Decode<'de, Postgres> for JsonValue {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
<PgJsonb<Self> as Decode<Postgres>>::decode(value).map(|item| item.0) <PgJsonb<Self> as Decode<Postgres>>::decode(value).map(|item| item.0)
} }
} }
@ -44,7 +44,7 @@ impl Encode<Postgres> for &'_ JsonRawValue {
} }
impl<'de> Decode<'de, Postgres> for &'de JsonRawValue { impl<'de> Decode<'de, Postgres> for &'de JsonRawValue {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
<PgJsonb<Self> as Decode<Postgres>>::decode(value).map(|item| item.0) <PgJsonb<Self> as Decode<Postgres>>::decode(value).map(|item| item.0)
} }
} }
@ -69,7 +69,7 @@ where
T: 'de, T: 'de,
T: Deserialize<'de>, T: Deserialize<'de>,
{ {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
<PgJsonb<T> as Decode<Postgres>>::decode(value).map(|item| Self(item.0)) <PgJsonb<T> as Decode<Postgres>>::decode(value).map(|item| Self(item.0))
} }
} }

View File

@ -281,10 +281,12 @@ impl<'de, T> Decode<'de, Postgres> for Option<T>
where where
T: Decode<'de, Postgres>, T: Decode<'de, Postgres>,
{ {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
value Ok(if value.get().is_some() {
.map(|value| <T as Decode<Postgres>>::decode(Some(value))) Some(<T as Decode<Postgres>>::decode(value)?)
.transpose() } else {
None
})
} }
} }

View File

@ -2,10 +2,9 @@ use crate::decode::Decode;
use crate::encode::{Encode, IsNull}; use crate::encode::{Encode, IsNull};
use crate::io::{Buf, BufMut}; use crate::io::{Buf, BufMut};
use crate::postgres::types::raw::sequence::PgSequenceDecoder; use crate::postgres::types::raw::sequence::PgSequenceDecoder;
use crate::postgres::{PgValue, Postgres}; use crate::postgres::{PgData, PgValue, Postgres};
use crate::types::Type; use crate::types::Type;
use byteorder::BE; use byteorder::BE;
use std::convert::TryInto;
use std::marker::PhantomData; use std::marker::PhantomData;
// https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/utils/array.h;h=7f7e744cb12bc872f628f90dad99dfdf074eb314;hb=master#l6 // https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/utils/array.h;h=7f7e744cb12bc872f628f90dad99dfdf074eb314;hb=master#l6
@ -94,17 +93,17 @@ where
T: for<'arr> Decode<'arr, Postgres>, T: for<'arr> Decode<'arr, Postgres>,
T: Type<Postgres>, T: Type<Postgres>,
{ {
pub(crate) fn new(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { pub(crate) fn new(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
let mut value = value.try_into()?; let mut data = value.try_get()?;
match value { match data {
PgValue::Binary(ref mut buf) => { PgData::Binary(ref mut buf) => {
// number of dimensions of the array // number of dimensions of the array
let ndim = buf.get_i32::<BE>()?; let ndim = buf.get_i32::<BE>()?;
if ndim == 0 { if ndim == 0 {
return Ok(Self { return Ok(Self {
inner: PgSequenceDecoder::new(PgValue::Binary(&[]), false), inner: PgSequenceDecoder::new(PgData::Binary(&[]), false),
phantom: PhantomData, phantom: PhantomData,
}); });
} }
@ -141,11 +140,11 @@ where
} }
} }
PgValue::Text(_) => {} PgData::Text(_) => {}
} }
Ok(Self { Ok(Self {
inner: PgSequenceDecoder::new(value, false), inner: PgSequenceDecoder::new(data, false),
phantom: PhantomData, phantom: PhantomData,
}) })
} }
@ -172,7 +171,7 @@ where
mod tests { mod tests {
use super::PgArrayDecoder; use super::PgArrayDecoder;
use super::PgArrayEncoder; use super::PgArrayEncoder;
use crate::postgres::{PgValue, Postgres}; use crate::postgres::{PgData, PgValue, Postgres};
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"; 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";
@ -193,7 +192,7 @@ mod tests {
#[test] #[test]
fn it_decodes_text_i32() -> crate::Result<Postgres, ()> { fn it_decodes_text_i32() -> crate::Result<Postgres, ()> {
let s = "{1,152,-12412}"; let s = "{1,152,-12412}";
let mut decoder = PgArrayDecoder::<i32>::new(Some(PgValue::Text(s)))?; let mut decoder = PgArrayDecoder::<i32>::new(Some(PgData::Text(s)))?;
assert_eq!(decoder.decode()?, Some(1)); assert_eq!(decoder.decode()?, Some(1));
assert_eq!(decoder.decode()?, Some(152)); assert_eq!(decoder.decode()?, Some(152));
@ -206,7 +205,7 @@ mod tests {
#[test] #[test]
fn it_decodes_text_str() -> crate::Result<Postgres, ()> { fn it_decodes_text_str() -> crate::Result<Postgres, ()> {
let s = "{\"\",\"\\\"\"}"; let s = "{\"\",\"\\\"\"}";
let mut decoder = PgArrayDecoder::<String>::new(Some(PgValue::Text(s)))?; let mut decoder = PgArrayDecoder::<String>::new(Some(PgData::Text(s)))?;
assert_eq!(decoder.decode()?, Some("".to_string())); assert_eq!(decoder.decode()?, Some("".to_string()));
assert_eq!(decoder.decode()?, Some("\"".to_string())); assert_eq!(decoder.decode()?, Some("\"".to_string()));
@ -217,8 +216,8 @@ mod tests {
#[test] #[test]
fn it_decodes_binary_nulls() -> crate::Result<Postgres, ()> { fn it_decodes_binary_nulls() -> crate::Result<Postgres, ()> {
let mut decoder = PgArrayDecoder::<Option<bool>>::new(Some(PgValue::Binary( let mut decoder = PgArrayDecoder::<Option<bool>>::new(Some(PgData::Binary(
b"\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x01\xff\xff\xff\xff\x00\x00\x00\x01\x01\xff\xff\xff\xff\x00\x00\x00\x01\x00" b"\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x01\xff\xff\xff\xff\x00\x00\x00\x01\x01\xff\xff\xff\xff\x00\x00\x00\x01\x00", 0,
)))?; )))?;
assert_eq!(decoder.decode()?, Some(None)); assert_eq!(decoder.decode()?, Some(None));
@ -231,7 +230,7 @@ mod tests {
#[test] #[test]
fn it_decodes_binary_i32() -> crate::Result<Postgres, ()> { fn it_decodes_binary_i32() -> crate::Result<Postgres, ()> {
let mut decoder = PgArrayDecoder::<i32>::new(Some(PgValue::Binary(BUF_BINARY_I32)))?; let mut decoder = PgArrayDecoder::<i32>::new(Some(PgData::Binary(BUF_BINARY_I32, 0)))?;
let val_1 = decoder.decode()?; let val_1 = decoder.decode()?;
let val_2 = decoder.decode()?; let val_2 = decoder.decode()?;

View File

@ -3,10 +3,9 @@ use crate::encode::Encode;
use crate::io::{Buf, BufMut}; use crate::io::{Buf, BufMut};
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::types::PgTypeInfo; use crate::postgres::types::PgTypeInfo;
use crate::postgres::{PgValue, Postgres}; use crate::postgres::{PgData, PgValue, Postgres};
use crate::types::Type; use crate::types::Type;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::TryInto;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct PgJson<T>(pub T); pub struct PgJson<T>(pub T);
@ -32,10 +31,10 @@ where
T: 'de, T: 'de,
T: Deserialize<'de>, T: Deserialize<'de>,
{ {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
(match value.try_into()? { (match value.try_get()? {
PgValue::Text(s) => serde_json::from_str(s), PgData::Text(s) => serde_json::from_str(s),
PgValue::Binary(buf) => serde_json::from_slice(buf), PgData::Binary(buf) => serde_json::from_slice(buf),
}) })
.map(PgJson) .map(PgJson)
.map_err(crate::Error::decode) .map_err(crate::Error::decode)
@ -71,10 +70,10 @@ where
T: 'de, T: 'de,
T: Deserialize<'de>, T: Deserialize<'de>,
{ {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
(match value.try_into()? { (match value.try_get()? {
PgValue::Text(s) => serde_json::from_str(s), PgData::Text(s) => serde_json::from_str(s),
PgValue::Binary(mut buf) => { PgData::Binary(mut buf) => {
let version = buf.get_u8()?; let version = buf.get_u8()?;
assert_eq!( assert_eq!(

View File

@ -1,4 +1,4 @@
use std::convert::TryInto; use core::convert::TryInto;
use byteorder::BigEndian; use byteorder::BigEndian;
@ -6,7 +6,7 @@ use crate::decode::Decode;
use crate::encode::Encode; use crate::encode::Encode;
use crate::io::{Buf, BufMut}; use crate::io::{Buf, BufMut};
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::{PgTypeInfo, PgValue, Postgres}; use crate::postgres::{PgData, PgTypeInfo, PgValue, Postgres};
use crate::types::Type; use crate::types::Type;
use crate::Error; use crate::Error;
@ -109,8 +109,8 @@ impl PgNumeric {
/// Receiving `PgNumeric` is currently only supported for the Postgres /// Receiving `PgNumeric` is currently only supported for the Postgres
/// binary (prepared statements) protocol. /// binary (prepared statements) protocol.
impl Decode<'_, Postgres> for PgNumeric { impl Decode<'_, Postgres> for PgNumeric {
fn decode(value: Option<PgValue>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue) -> crate::Result<Postgres, Self> {
if let PgValue::Binary(bytes) = value.try_into()? { if let PgData::Binary(bytes) = value.try_get()? {
Self::from_bytes(bytes) Self::from_bytes(bytes)
} else { } else {
Err(Error::Decode( Err(Error::Decode(
@ -119,6 +119,7 @@ impl Decode<'_, Postgres> for PgNumeric {
} }
} }
} }
/// ### Panics /// ### Panics
/// ///
/// * If `digits.len()` overflows `i16` /// * If `digits.len()` overflows `i16`

View File

@ -2,10 +2,9 @@ use crate::decode::Decode;
use crate::encode::{Encode, IsNull}; use crate::encode::{Encode, IsNull};
use crate::io::Buf; use crate::io::Buf;
use crate::postgres::types::raw::sequence::PgSequenceDecoder; use crate::postgres::types::raw::sequence::PgSequenceDecoder;
use crate::postgres::{PgValue, Postgres}; use crate::postgres::{PgData, PgValue, Postgres};
use crate::types::Type; use crate::types::Type;
use byteorder::BigEndian; use byteorder::BigEndian;
use std::convert::TryInto;
pub struct PgRecordEncoder<'a> { pub struct PgRecordEncoder<'a> {
buf: &'a mut Vec<u8>, buf: &'a mut Vec<u8>,
@ -62,17 +61,17 @@ impl<'a> PgRecordEncoder<'a> {
pub struct PgRecordDecoder<'de>(PgSequenceDecoder<'de>); pub struct PgRecordDecoder<'de>(PgSequenceDecoder<'de>);
impl<'de> PgRecordDecoder<'de> { impl<'de> PgRecordDecoder<'de> {
pub fn new(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { pub fn new(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
let mut value: PgValue = value.try_into()?; let mut data = value.try_get()?;
match value { match data {
PgValue::Text(_) => {} PgData::Text(_) => {}
PgValue::Binary(ref mut buf) => { PgData::Binary(ref mut buf) => {
let _expected_len = buf.get_u32::<BigEndian>()?; let _expected_len = buf.get_u32::<BigEndian>()?;
} }
} }
Ok(Self(PgSequenceDecoder::new(value, true))) Ok(Self(PgSequenceDecoder::new(data, true)))
} }
#[inline] #[inline]
@ -119,7 +118,7 @@ fn test_decode_field() {
encoder.encode(&value); encoder.encode(&value);
let buf = buf.as_slice(); let buf = buf.as_slice();
let mut decoder = PgRecordDecoder::new(Some(PgValue::Binary(buf))).unwrap(); let mut decoder = PgRecordDecoder::new(Some(PgData::Binary(buf, 0))).unwrap();
let value_decoded: String = decoder.decode().unwrap(); let value_decoded: String = decoder.decode().unwrap();
assert_eq!(value_decoded, value); assert_eq!(value_decoded, value);

View File

@ -1,31 +1,31 @@
use crate::decode::Decode; use crate::decode::Decode;
use crate::io::Buf; use crate::io::Buf;
use crate::postgres::{PgValue, Postgres}; use crate::postgres::{PgData, PgValue, Postgres};
use crate::types::Type; use crate::types::Type;
use byteorder::BigEndian; use byteorder::BigEndian;
pub(crate) struct PgSequenceDecoder<'de> { pub(crate) struct PgSequenceDecoder<'de> {
value: PgValue<'de>, data: PgData<'de>,
len: usize, len: usize,
mixed: bool, mixed: bool,
} }
impl<'de> PgSequenceDecoder<'de> { impl<'de> PgSequenceDecoder<'de> {
pub(crate) fn new(mut value: PgValue<'de>, mixed: bool) -> Self { pub(crate) fn new(mut data: PgData<'de>, mixed: bool) -> Self {
match value { match data {
PgValue::Binary(_) => { PgData::Binary(_) => {
// assume that this has already gotten tweaked by the caller as // assume that this has already gotten tweaked by the caller as
// tuples and arrays have a very different header // tuples and arrays have a very different header
} }
PgValue::Text(ref mut s) => { PgData::Text(ref mut s) => {
// remove the outer ( ... ) or { ... } // remove the outer ( ... ) or { ... }
*s = &s[1..(s.len() - 1)]; *s = &s[1..(s.len() - 1)];
} }
} }
Self { Self {
value, data,
mixed, mixed,
len: 0, len: 0,
} }
@ -40,8 +40,8 @@ impl<'de> PgSequenceDecoder<'de> {
T: for<'seq> Decode<'seq, Postgres>, T: for<'seq> Decode<'seq, Postgres>,
T: Type<Postgres>, T: Type<Postgres>,
{ {
match self.value { match self.data {
PgValue::Binary(ref mut buf) => { PgData::Binary(ref mut buf) => {
if buf.is_empty() { if buf.is_empty() {
return Ok(None); return Ok(None);
} }
@ -59,13 +59,15 @@ impl<'de> PgSequenceDecoder<'de> {
let len = buf.get_i32::<BigEndian>()? as isize; let len = buf.get_i32::<BigEndian>()? as isize;
let value = if len < 0 { let value = if len < 0 {
T::decode(None)? // TODO: Grab the correct element OID
T::decode(PgValue::null(0))?
} else { } else {
let value_buf = &buf[..(len as usize)]; let value_buf = &buf[..(len as usize)];
*buf = &buf[(len as usize)..]; *buf = &buf[(len as usize)..];
T::decode(Some(PgValue::Binary(value_buf)))? // TODO: Grab the correct element OID
T::decode(PgValue::bytes(0, value_buf))?
}; };
self.len += 1; self.len += 1;
@ -73,7 +75,7 @@ impl<'de> PgSequenceDecoder<'de> {
Ok(Some(value)) Ok(Some(value))
} }
PgValue::Text(ref mut s) => { PgData::Text(ref mut s) => {
if s.is_empty() { if s.is_empty() {
return Ok(None); return Ok(None);
} }
@ -134,12 +136,15 @@ impl<'de> PgSequenceDecoder<'de> {
}; };
let value = T::decode(if end == Some(0) { let value = T::decode(if end == Some(0) {
None // TODO: Grab the correct element OID
PgValue::null(0)
} else if !self.mixed && value == "NULL" { } else if !self.mixed && value == "NULL" {
// Yes, in arrays the text encoding of a NULL is just NULL // Yes, in arrays the text encoding of a NULL is just NULL
None // TODO: Grab the correct element OID
PgValue::null(0)
} else { } else {
Some(PgValue::Text(&value)) // TODO: Grab the correct element OID
PgValue::str(0, &*value)
})?; })?;
*s = if let Some(end) = end { *s = if let Some(end) = end {
@ -158,7 +163,7 @@ impl<'de> PgSequenceDecoder<'de> {
impl<'de> From<&'de str> for PgSequenceDecoder<'de> { impl<'de> From<&'de str> for PgSequenceDecoder<'de> {
fn from(s: &'de str) -> Self { fn from(s: &'de str) -> Self {
Self::new(PgValue::Text(s), false) Self::new(PgData::Text(s), false)
} }
} }

View File

@ -1,8 +1,8 @@
use crate::decode::Decode; use crate::decode::Decode;
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::row::PgValue;
use crate::postgres::types::raw::PgRecordDecoder; use crate::postgres::types::raw::PgRecordDecoder;
use crate::postgres::types::PgTypeInfo; use crate::postgres::types::PgTypeInfo;
use crate::postgres::value::PgValue;
use crate::postgres::Postgres; use crate::postgres::Postgres;
use crate::types::Type; use crate::types::Type;
@ -42,7 +42,7 @@ macro_rules! impl_pg_record_for_tuple {
$($T: Type<Postgres>,)+ $($T: Type<Postgres>,)+
$($T: for<'tup> Decode<'tup, Postgres>,)+ $($T: for<'tup> Decode<'tup, Postgres>,)+
{ {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
let mut decoder = PgRecordDecoder::new(value)?; let mut decoder = PgRecordDecoder::new(value)?;
$(let $idx: $T = decoder.decode()?;)+ $(let $idx: $T = decoder.decode()?;)+

View File

@ -1,11 +1,10 @@
use std::convert::TryInto;
use std::str::from_utf8; use std::str::from_utf8;
use crate::decode::Decode; use crate::decode::Decode;
use crate::encode::Encode; use crate::encode::Encode;
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::row::PgValue;
use crate::postgres::types::PgTypeInfo; use crate::postgres::types::PgTypeInfo;
use crate::postgres::value::{PgData, PgValue};
use crate::postgres::Postgres; use crate::postgres::Postgres;
use crate::types::Type; use crate::types::Type;
use crate::Error; use crate::Error;
@ -67,16 +66,16 @@ impl Encode<Postgres> for String {
} }
impl<'de> Decode<'de, Postgres> for String { impl<'de> Decode<'de, Postgres> for String {
fn decode(buf: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
<&'de str as Decode<Postgres>>::decode(buf).map(ToOwned::to_owned) <&'de str as Decode<Postgres>>::decode(value).map(ToOwned::to_owned)
} }
} }
impl<'de> Decode<'de, Postgres> for &'de str { impl<'de> Decode<'de, Postgres> for &'de str {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(buf) => from_utf8(buf).map_err(Error::decode), PgData::Binary(buf) => from_utf8(buf).map_err(Error::decode),
PgValue::Text(s) => Ok(s), PgData::Text(s) => Ok(s),
} }
} }
} }

View File

@ -10,7 +10,7 @@ use crate::encode::Encode;
use crate::io::Buf; use crate::io::Buf;
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::types::PgTypeInfo; use crate::postgres::types::PgTypeInfo;
use crate::postgres::{PgValue, Postgres}; use crate::postgres::{PgData, PgValue, Postgres};
use crate::types::Type; use crate::types::Type;
const POSTGRES_EPOCH: PrimitiveDateTime = date!(2000 - 1 - 1).midnight(); const POSTGRES_EPOCH: PrimitiveDateTime = date!(2000 - 1 - 1).midnight();
@ -109,15 +109,15 @@ fn from_microseconds_since_midnight(mut microsecond: u64) -> crate::Result<Postg
} }
impl<'de> Decode<'de, Postgres> for Time { impl<'de> Decode<'de, Postgres> for Time {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(mut buf) => { PgData::Binary(mut buf) => {
let micros: i64 = buf.get_i64::<BigEndian>()?; let micros: i64 = buf.get_i64::<BigEndian>()?;
from_microseconds_since_midnight(micros as u64) from_microseconds_since_midnight(micros as u64)
} }
PgValue::Text(s) => { PgData::Text(s) => {
// If there are less than 9 digits after the decimal point // If there are less than 9 digits after the decimal point
// We need to zero-pad // We need to zero-pad
// TODO: Ask [time] to add a parse % for less-than-fixed-9 nanos // TODO: Ask [time] to add a parse % for less-than-fixed-9 nanos
@ -147,15 +147,15 @@ impl Encode<Postgres> for Time {
} }
impl<'de> Decode<'de, Postgres> for Date { impl<'de> Decode<'de, Postgres> for Date {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(mut buf) => { PgData::Binary(mut buf) => {
let n: i32 = buf.get_i32::<BigEndian>()?; let n: i32 = buf.get_i32::<BigEndian>()?;
Ok(date!(2000 - 1 - 1) + n.days()) Ok(date!(2000 - 1 - 1) + n.days())
} }
PgValue::Text(s) => Date::parse(s, "%Y-%m-%d").map_err(crate::Error::decode), PgData::Text(s) => Date::parse(s, "%Y-%m-%d").map_err(crate::Error::decode),
} }
} }
} }
@ -177,16 +177,16 @@ impl Encode<Postgres> for Date {
} }
impl<'de> Decode<'de, Postgres> for PrimitiveDateTime { impl<'de> Decode<'de, Postgres> for PrimitiveDateTime {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(mut buf) => { PgData::Binary(mut buf) => {
let n: i64 = buf.get_i64::<BigEndian>()?; let n: i64 = buf.get_i64::<BigEndian>()?;
Ok(POSTGRES_EPOCH + n.microseconds()) Ok(POSTGRES_EPOCH + n.microseconds())
} }
// TODO: Try and fix duplication between here and MySQL // TODO: Try and fix duplication between here and MySQL
PgValue::Text(s) => { PgData::Text(s) => {
// If there are less than 9 digits after the decimal point // If there are less than 9 digits after the decimal point
// We need to zero-pad // We need to zero-pad
// TODO: Ask [time] to add a parse % for less-than-fixed-9 nanos // TODO: Ask [time] to add a parse % for less-than-fixed-9 nanos
@ -233,7 +233,7 @@ impl Encode<Postgres> for PrimitiveDateTime {
} }
impl<'de> Decode<'de, Postgres> for OffsetDateTime { impl<'de> Decode<'de, Postgres> for OffsetDateTime {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
let primitive: PrimitiveDateTime = Decode::<Postgres>::decode(value)?; let primitive: PrimitiveDateTime = Decode::<Postgres>::decode(value)?;
Ok(primitive.assume_utc()) Ok(primitive.assume_utc())
@ -285,18 +285,18 @@ fn test_encode_time() {
#[test] #[test]
fn test_decode_time() { fn test_decode_time() {
let buf = [0u8; 8]; let buf = [0u8; 8];
let time: Time = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let time: Time = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(time, time!(0:00)); assert_eq!(time, time!(0:00));
// half an hour // half an hour
let buf = (1_000_000i64 * 60 * 30).to_be_bytes(); let buf = (1_000_000i64 * 60 * 30).to_be_bytes();
let time: Time = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let time: Time = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(time, time!(0:30)); assert_eq!(time, time!(0:30));
// 12:53:05.125305 // 12:53:05.125305
let buf = (1_000_000i64 * 60 * 60 * 12 + 1_000_000i64 * 60 * 53 + 1_000_000i64 * 5 + 125305) let buf = (1_000_000i64 * 60 * 60 * 12 + 1_000_000i64 * 60 * 53 + 1_000_000i64 * 5 + 125305)
.to_be_bytes(); .to_be_bytes();
let time: Time = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let time: Time = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(time, time!(12:53:05.125305)); assert_eq!(time, time!(12:53:05.125305));
} }
@ -325,21 +325,21 @@ fn test_encode_datetime() {
#[test] #[test]
fn test_decode_datetime() { fn test_decode_datetime() {
let buf = [0u8; 8]; let buf = [0u8; 8];
let date: PrimitiveDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: PrimitiveDateTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!( assert_eq!(
date, date,
PrimitiveDateTime::new(date!(2000 - 01 - 01), time!(00:00:00)) PrimitiveDateTime::new(date!(2000 - 01 - 01), time!(00:00:00))
); );
let buf = 3_600_000_000i64.to_be_bytes(); let buf = 3_600_000_000i64.to_be_bytes();
let date: PrimitiveDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: PrimitiveDateTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!( assert_eq!(
date, date,
PrimitiveDateTime::new(date!(2000 - 01 - 01), time!(01:00:00)) PrimitiveDateTime::new(date!(2000 - 01 - 01), time!(01:00:00))
); );
let buf = 629_377_265_000_000i64.to_be_bytes(); let buf = 629_377_265_000_000i64.to_be_bytes();
let date: PrimitiveDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: PrimitiveDateTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!( assert_eq!(
date, date,
PrimitiveDateTime::new(date!(2019 - 12 - 11), time!(11:01:05)) PrimitiveDateTime::new(date!(2019 - 12 - 11), time!(11:01:05))
@ -372,21 +372,21 @@ fn test_encode_offsetdatetime() {
#[test] #[test]
fn test_decode_offsetdatetime() { fn test_decode_offsetdatetime() {
let buf = [0u8; 8]; let buf = [0u8; 8];
let date: OffsetDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: OffsetDateTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!( assert_eq!(
date, date,
PrimitiveDateTime::new(date!(2000 - 01 - 01), time!(00:00:00)).assume_utc() PrimitiveDateTime::new(date!(2000 - 01 - 01), time!(00:00:00)).assume_utc()
); );
let buf = 3_600_000_000i64.to_be_bytes(); let buf = 3_600_000_000i64.to_be_bytes();
let date: OffsetDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: OffsetDateTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!( assert_eq!(
date, date,
PrimitiveDateTime::new(date!(2000 - 01 - 01), time!(01:00:00)).assume_utc() PrimitiveDateTime::new(date!(2000 - 01 - 01), time!(01:00:00)).assume_utc()
); );
let buf = 629_377_265_000_000i64.to_be_bytes(); let buf = 629_377_265_000_000i64.to_be_bytes();
let date: OffsetDateTime = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: OffsetDateTime = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!( assert_eq!(
date, date,
PrimitiveDateTime::new(date!(2019 - 12 - 11), time!(11:01:05)).assume_utc() PrimitiveDateTime::new(date!(2019 - 12 - 11), time!(11:01:05)).assume_utc()
@ -417,14 +417,14 @@ fn test_encode_date() {
#[test] #[test]
fn test_decode_date() { fn test_decode_date() {
let buf = [0; 4]; let buf = [0; 4];
let date: Date = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: Date = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(date, date!(2000 - 01 - 01)); assert_eq!(date, date!(2000 - 01 - 01));
let buf = 366i32.to_be_bytes(); let buf = 366i32.to_be_bytes();
let date: Date = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: Date = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(date, date!(2001 - 01 - 01)); assert_eq!(date, date!(2001 - 01 - 01));
let buf = 7284i32.to_be_bytes(); let buf = 7284i32.to_be_bytes();
let date: Date = Decode::<Postgres>::decode(Some(PgValue::Binary(&buf))).unwrap(); let date: Date = Decode::<Postgres>::decode(Some(PgData::Binary(&buf))).unwrap();
assert_eq!(date, date!(2019 - 12 - 11)); assert_eq!(date, date!(2019 - 12 - 11));
} }

View File

@ -1,4 +1,3 @@
use std::convert::TryInto;
use std::str::FromStr; use std::str::FromStr;
use uuid::Uuid; use uuid::Uuid;
@ -6,8 +5,8 @@ use uuid::Uuid;
use crate::decode::Decode; use crate::decode::Decode;
use crate::encode::Encode; use crate::encode::Encode;
use crate::postgres::protocol::TypeId; use crate::postgres::protocol::TypeId;
use crate::postgres::row::PgValue;
use crate::postgres::types::PgTypeInfo; use crate::postgres::types::PgTypeInfo;
use crate::postgres::value::{PgData, PgValue};
use crate::postgres::Postgres; use crate::postgres::Postgres;
use crate::types::Type; use crate::types::Type;
@ -36,10 +35,10 @@ impl Encode<Postgres> for Uuid {
} }
impl<'de> Decode<'de, Postgres> for Uuid { impl<'de> Decode<'de, Postgres> for Uuid {
fn decode(value: Option<PgValue<'de>>) -> crate::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
match value.try_into()? { match value.try_get()? {
PgValue::Binary(buf) => Uuid::from_slice(buf).map_err(|err| crate::Error::decode(err)), PgData::Binary(buf) => Uuid::from_slice(buf).map_err(crate::Error::decode),
PgValue::Text(s) => Uuid::from_str(s).map_err(|err| crate::Error::decode(err)), PgData::Text(s) => Uuid::from_str(s).map_err(crate::Error::decode),
} }
} }
} }

View File

@ -0,0 +1,70 @@
use crate::error::UnexpectedNullError;
use crate::postgres::{PgTypeInfo, Postgres};
use crate::value::RawValue;
use std::str::from_utf8;
#[derive(Debug, Copy, Clone)]
pub enum PgData<'c> {
Binary(&'c [u8]),
Text(&'c str),
}
#[derive(Debug)]
pub struct PgValue<'c> {
type_oid: u32,
data: Option<PgData<'c>>,
}
impl<'c> PgValue<'c> {
/// Gets the binary or text data for this value; or, `UnexpectedNullError` if this
/// is a `NULL` value.
pub(crate) fn try_get(&self) -> crate::Result<Postgres, PgData<'c>> {
match self.data {
Some(data) => Ok(data),
None => Err(crate::Error::decode(UnexpectedNullError)),
}
}
/// Gets the binary or text data for this value; or, `None` if this
/// is a `NULL` value.
#[inline]
pub fn get(&self) -> Option<PgData<'c>> {
self.data
}
pub(crate) fn null(type_oid: u32) -> Self {
Self {
type_oid,
data: None,
}
}
pub(crate) fn bytes(type_oid: u32, buf: &'c [u8]) -> Self {
Self {
type_oid,
data: Some(PgData::Binary(buf)),
}
}
pub(crate) fn utf8(type_oid: u32, buf: &'c [u8]) -> crate::Result<Postgres, Self> {
Ok(Self {
type_oid,
data: Some(PgData::Text(from_utf8(&buf).map_err(crate::Error::decode)?)),
})
}
pub(crate) fn str(type_oid: u32, s: &'c str) -> Self {
Self {
type_oid,
data: Some(PgData::Text(s)),
}
}
}
impl<'c> RawValue<'c> for PgValue<'c> {
type Database = Postgres;
fn type_info(&self) -> PgTypeInfo {
PgTypeInfo::with_oid(self.type_oid)
}
}

View File

@ -474,7 +474,7 @@ END $$;
} }
impl<'de> Decode<'de, Postgres> for RecordEmpty { impl<'de> Decode<'de, Postgres> for RecordEmpty {
fn decode(_value: Option<PgValue<'de>>) -> sqlx::Result<Postgres, Self> { fn decode(_value: PgValue<'de>) -> sqlx::Result<Postgres, Self> {
Ok(RecordEmpty {}) Ok(RecordEmpty {})
} }
} }
@ -506,7 +506,7 @@ END $$;
} }
impl<'de> Decode<'de, Postgres> for Record1 { impl<'de> Decode<'de, Postgres> for Record1 {
fn decode(value: Option<PgValue<'de>>) -> sqlx::Result<Postgres, Self> { fn decode(value: PgValue<'de>) -> sqlx::Result<Postgres, Self> {
let mut decoder = PgRecordDecoder::new(value)?; let mut decoder = PgRecordDecoder::new(value)?;
let _1 = decoder.decode()?; let _1 = decoder.decode()?;