mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-10-02 23:35:20 +00:00
decode: add lifetime to Decode; impl Decode for &str and &[u8]; remove DecodeError
This commit is contained in:
parent
d257c32946
commit
dd5f250e5e
@ -47,7 +47,7 @@ rand = { version = "0.7.3", default-features = false, optional = true, features
|
||||
sha-1 = { version = "0.8.2", default-features = false, optional = true }
|
||||
sha2 = { version = "0.8.1", default-features = false, optional = true }
|
||||
url = { version = "2.1.1", default-features = false }
|
||||
uuid = { version = "0.8.1", default-features = false, optional = true }
|
||||
uuid = { version = "0.8.1", default-features = false, optional = true, features = [ "std" ] }
|
||||
hmac = { version = "0.7.1", default-features = false, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -6,30 +6,22 @@ use std::fmt::{self, Display};
|
||||
use crate::database::Database;
|
||||
use crate::types::Type;
|
||||
|
||||
pub enum DecodeError {
|
||||
/// An unexpected `NULL` was encountered while decoding.
|
||||
UnexpectedNull,
|
||||
|
||||
Message(Box<dyn Display + Send + Sync>),
|
||||
|
||||
Other(Box<dyn StdError + Send + Sync>),
|
||||
}
|
||||
|
||||
/// Decode a single value from the database.
|
||||
pub trait Decode<DB>: Sized
|
||||
pub trait Decode<'de, DB>
|
||||
where
|
||||
DB: Database + ?Sized,
|
||||
Self: Sized,
|
||||
DB: Database,
|
||||
{
|
||||
fn decode(raw: &[u8]) -> Result<Self, DecodeError>;
|
||||
fn decode(raw: &'de [u8]) -> crate::Result<Self>;
|
||||
|
||||
/// Creates a new value of this type from a `NULL` SQL value.
|
||||
///
|
||||
/// The default implementation returns [DecodeError::UnexpectedNull].
|
||||
fn decode_null() -> Result<Self, DecodeError> {
|
||||
Err(DecodeError::UnexpectedNull)
|
||||
fn decode_null() -> crate::Result<Self> {
|
||||
Err(crate::Error::Decode(UnexpectedNullError.into()))
|
||||
}
|
||||
|
||||
fn decode_nullable(raw: Option<&[u8]>) -> Result<Self, DecodeError> {
|
||||
fn decode_nullable(raw: Option<&'de [u8]>) -> crate::Result<Self> {
|
||||
if let Some(raw) = raw {
|
||||
Self::decode(raw)
|
||||
} else {
|
||||
@ -38,50 +30,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, DB> Decode<DB> for Option<T>
|
||||
impl<'de, T, DB> Decode<'de, DB> for Option<T>
|
||||
where
|
||||
DB: Database,
|
||||
T: Type<DB>,
|
||||
T: Decode<DB>,
|
||||
T: Decode<'de, DB>,
|
||||
{
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<Self> {
|
||||
T::decode(buf).map(Some)
|
||||
}
|
||||
|
||||
fn decode_null() -> Result<Self, DecodeError> {
|
||||
fn decode_null() -> crate::Result<Self> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for DecodeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("DecodeError(")?;
|
||||
/// An unexpected `NULL` was encountered during decoding.
|
||||
///
|
||||
/// Returned from `Row::try_get` if the value from the database is `NULL`
|
||||
/// and you are not decoding into an `Option`.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct UnexpectedNullError;
|
||||
|
||||
match self {
|
||||
DecodeError::UnexpectedNull => write!(f, "unexpected null for non-null column")?,
|
||||
DecodeError::Message(err) => write!(f, "{}", err)?,
|
||||
DecodeError::Other(err) => write!(f, "{:?}", err)?,
|
||||
}
|
||||
|
||||
f.write_str(")")
|
||||
impl Display for UnexpectedNullError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("unexpected null; try decoding as an `Option`")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DecodeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
DecodeError::UnexpectedNull => f.write_str("unexpected null for non-null column"),
|
||||
DecodeError::Message(err) => write!(f, "{}", err),
|
||||
DecodeError::Other(err) => write!(f, "{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<E> for DecodeError
|
||||
where
|
||||
E: StdError + Send + Sync + 'static,
|
||||
{
|
||||
fn from(err: E) -> DecodeError {
|
||||
DecodeError::Other(Box::new(err))
|
||||
}
|
||||
}
|
||||
impl StdError for UnexpectedNullError {}
|
||||
|
@ -1,6 +1,5 @@
|
||||
//! Error and Result types.
|
||||
|
||||
use crate::decode::DecodeError;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::io;
|
||||
@ -28,10 +27,7 @@ pub enum Error {
|
||||
ColumnNotFound(Box<str>),
|
||||
|
||||
/// Column index was out of bounds (e.g., asking for column 4 in a 2-column row).
|
||||
ColumnIndexOutOfBounds {
|
||||
index: usize,
|
||||
len: usize,
|
||||
},
|
||||
ColumnIndexOutOfBounds { index: usize, len: usize },
|
||||
|
||||
/// Unexpected or invalid data was encountered. This would indicate that we received
|
||||
/// data that we were not expecting or it was in a format we did not understand. This
|
||||
@ -51,7 +47,8 @@ pub enum Error {
|
||||
/// An error occurred during a TLS upgrade.
|
||||
TlsUpgrade(Box<dyn StdError + Send + Sync>),
|
||||
|
||||
Decode(DecodeError),
|
||||
/// An error occurred decoding data received from the database.
|
||||
Decode(Box<dyn StdError + Send + Sync>),
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
@ -60,7 +57,7 @@ impl StdError for Error {
|
||||
Error::Io(error) => Some(error),
|
||||
Error::UrlParse(error) => Some(error),
|
||||
Error::PoolTimedOut(Some(error)) => Some(&**error),
|
||||
Error::Decode(DecodeError::Other(error)) => Some(&**error),
|
||||
Error::Decode(error) => Some(&**error),
|
||||
Error::TlsUpgrade(error) => Some(&**error),
|
||||
|
||||
_ => None,
|
||||
@ -124,13 +121,6 @@ impl From<io::ErrorKind> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DecodeError> for Error {
|
||||
#[inline]
|
||||
fn from(err: DecodeError) -> Self {
|
||||
Error::Decode(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<url::ParseError> for Error {
|
||||
#[inline]
|
||||
fn from(err: url::ParseError) -> Self {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::decode::{Decode, DecodeError};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::Encode;
|
||||
use crate::postgres::protocol::TypeId;
|
||||
use crate::postgres::types::PgTypeInfo;
|
||||
@ -23,10 +23,8 @@ impl Encode<Postgres> for bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for bool {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
buf.get(0).map(|&b| b != 0).ok_or_else(|| {
|
||||
DecodeError::Message(Box::new("Expected minimum 1 byte but received none."))
|
||||
})
|
||||
impl<'de> Decode<'de, Postgres> for bool {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<Self> {
|
||||
Ok(buf.get(0).map(|&b| b != 0).unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::decode::{Decode, DecodeError};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::Encode;
|
||||
use crate::postgres::protocol::TypeId;
|
||||
use crate::postgres::types::PgTypeInfo;
|
||||
@ -17,7 +17,6 @@ impl Type<Postgres> for [&'_ [u8]] {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Do we need the [HasSqlType] here on the Vec?
|
||||
impl Type<Postgres> for Vec<u8> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
<[u8] as Type<Postgres>>::type_info()
|
||||
@ -36,8 +35,14 @@ impl Encode<Postgres> for Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for Vec<u8> {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
impl<'de> Decode<'de, Postgres> for Vec<u8> {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<Self> {
|
||||
Ok(buf.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Decode<'de, Postgres> for &'de [u8] {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<&'de [u8]> {
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::mem;
|
||||
|
||||
use chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
|
||||
|
||||
use crate::decode::{Decode, DecodeError};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::Encode;
|
||||
use crate::postgres::protocol::TypeId;
|
||||
use crate::postgres::types::PgTypeInfo;
|
||||
@ -28,7 +28,7 @@ impl Type<Postgres> for NaiveDateTime {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz> Type<DateTime<Tz>> for Postgres
|
||||
impl<Tz> Type<Postgres> for DateTime<Tz>
|
||||
where
|
||||
Tz: TimeZone,
|
||||
{
|
||||
@ -55,7 +55,7 @@ impl Type<Postgres> for [NaiveDateTime] {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz> Type<[DateTime<Tz>]> for Postgres
|
||||
impl<Tz> Type<Postgres> for [DateTime<Tz>]
|
||||
where
|
||||
Tz: TimeZone,
|
||||
{
|
||||
@ -64,8 +64,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for NaiveTime {
|
||||
fn decode(raw: &[u8]) -> Result<Self, DecodeError> {
|
||||
impl<'de> Decode<'de, Postgres> for NaiveTime {
|
||||
fn decode(raw: &'de [u8]) -> crate::Result<Self> {
|
||||
let micros: i64 = Decode::<Postgres>::decode(raw)?;
|
||||
|
||||
Ok(NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(micros))
|
||||
@ -86,8 +86,8 @@ impl Encode<Postgres> for NaiveTime {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for NaiveDate {
|
||||
fn decode(raw: &[u8]) -> Result<Self, DecodeError> {
|
||||
impl<'de> Decode<'de, Postgres> for NaiveDate {
|
||||
fn decode(raw: &'de [u8]) -> crate::Result<Self> {
|
||||
let days: i32 = Decode::<Postgres>::decode(raw)?;
|
||||
|
||||
Ok(NaiveDate::from_ymd(2000, 1, 1) + Duration::days(days as i64))
|
||||
@ -111,18 +111,21 @@ impl Encode<Postgres> for NaiveDate {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for NaiveDateTime {
|
||||
fn decode(raw: &[u8]) -> Result<Self, DecodeError> {
|
||||
impl<'de> Decode<'de, Postgres> for NaiveDateTime {
|
||||
fn decode(raw: &'de [u8]) -> crate::Result<Self> {
|
||||
let micros: i64 = Decode::<Postgres>::decode(raw)?;
|
||||
|
||||
postgres_epoch()
|
||||
.naive_utc()
|
||||
.checked_add_signed(Duration::microseconds(micros))
|
||||
.ok_or_else(|| {
|
||||
DecodeError::Message(Box::new(format!(
|
||||
"Postgres timestamp out of range for NaiveDateTime: {:?}",
|
||||
micros
|
||||
)))
|
||||
crate::Error::Decode(
|
||||
format!(
|
||||
"Postgres timestamp out of range for NaiveDateTime: {:?}",
|
||||
micros
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -142,15 +145,15 @@ impl Encode<Postgres> for NaiveDateTime {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for DateTime<Utc> {
|
||||
fn decode(raw: &[u8]) -> Result<Self, DecodeError> {
|
||||
impl<'de> Decode<'de, Postgres> for DateTime<Utc> {
|
||||
fn decode(raw: &'de [u8]) -> crate::Result<Self> {
|
||||
let date_time = Decode::<Postgres>::decode(raw)?;
|
||||
Ok(DateTime::from_utc(date_time, Utc))
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for DateTime<Local> {
|
||||
fn decode(raw: &[u8]) -> Result<Self, DecodeError> {
|
||||
impl<'de> Decode<'de, Postgres> for DateTime<Local> {
|
||||
fn decode(raw: &'de [u8]) -> crate::Result<Self> {
|
||||
let date_time = Decode::<Postgres>::decode(raw)?;
|
||||
Ok(Local.from_utc_datetime(&date_time))
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::decode::{Decode, DecodeError};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::Encode;
|
||||
use crate::postgres::protocol::TypeId;
|
||||
use crate::postgres::types::PgTypeInfo;
|
||||
@ -23,8 +23,8 @@ impl Encode<Postgres> for f32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for f32 {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
impl<'de> Decode<'de, Postgres> for f32 {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<Self> {
|
||||
Ok(f32::from_bits(
|
||||
<i32 as Decode<Postgres>>::decode(buf)? as u32
|
||||
))
|
||||
@ -49,8 +49,8 @@ impl Encode<Postgres> for f64 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for f64 {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
impl<'de> Decode<'de, Postgres> for f64 {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<Self> {
|
||||
Ok(f64::from_bits(
|
||||
<i64 as Decode<Postgres>>::decode(buf)? as u64
|
||||
))
|
||||
|
@ -1,6 +1,6 @@
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
use crate::decode::{Decode, DecodeError};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::Encode;
|
||||
use crate::postgres::protocol::TypeId;
|
||||
use crate::postgres::types::PgTypeInfo;
|
||||
@ -25,8 +25,8 @@ impl Encode<Postgres> for i16 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for i16 {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
impl<'de> Decode<'de, Postgres> for i16 {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<Self> {
|
||||
Ok(NetworkEndian::read_i16(buf))
|
||||
}
|
||||
}
|
||||
@ -49,8 +49,8 @@ impl Encode<Postgres> for i32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for i32 {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
impl<'de> Decode<'de, Postgres> for i32 {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<Self> {
|
||||
Ok(NetworkEndian::read_i32(buf))
|
||||
}
|
||||
}
|
||||
@ -73,8 +73,8 @@ impl Encode<Postgres> for i64 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for i64 {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
impl<'de> Decode<'de, Postgres> for i64 {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<Self> {
|
||||
Ok(NetworkEndian::read_i64(buf))
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::str;
|
||||
|
||||
use crate::decode::{Decode, DecodeError};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::Encode;
|
||||
use crate::postgres::protocol::TypeId;
|
||||
use crate::postgres::types::PgTypeInfo;
|
||||
@ -19,7 +19,7 @@ impl Type<Postgres> for [&'_ str] {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Do we need [HasSqlType] on String here?
|
||||
// TODO: Do we need [Type] on String here?
|
||||
impl Type<Postgres> for String {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
<str as Type<Postgres>>::type_info()
|
||||
@ -46,8 +46,14 @@ impl Encode<Postgres> for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for String {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
Ok(str::from_utf8(buf)?.to_owned())
|
||||
impl<'de> Decode<'de, Postgres> for String {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<Self> {
|
||||
<&'de str>::decode(buf).map(ToOwned::to_owned)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Decode<'de, Postgres> for &'de str {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<Self> {
|
||||
str::from_utf8(buf).map_err(|err| crate::Error::Decode(Box::new(err)))
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::decode::{Decode, DecodeError};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::Encode;
|
||||
use crate::postgres::protocol::TypeId;
|
||||
use crate::postgres::types::PgTypeInfo;
|
||||
@ -25,8 +25,8 @@ impl Encode<Postgres> for Uuid {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<Postgres> for Uuid {
|
||||
fn decode(buf: &[u8]) -> Result<Self, DecodeError> {
|
||||
Uuid::from_slice(buf).map_err(|err| DecodeError::Message(Box::new(err)))
|
||||
impl<'de> Decode<'de, Postgres> for Uuid {
|
||||
fn decode(buf: &'de [u8]) -> crate::Result<Self> {
|
||||
Uuid::from_slice(buf).map_err(|err| crate::Error::Decode(Box::new(err)))
|
||||
}
|
||||
}
|
||||
|
@ -27,9 +27,8 @@ pub trait Row<'c>: Unpin + Send {
|
||||
where
|
||||
T: Type<Self::Database>,
|
||||
I: ColumnIndex<'c, Self>,
|
||||
T: Decode<Self::Database>,
|
||||
T: Decode<'c, Self::Database>,
|
||||
{
|
||||
// todo: use expect with a proper message
|
||||
self.try_get(index).unwrap()
|
||||
}
|
||||
|
||||
@ -37,7 +36,7 @@ pub trait Row<'c>: Unpin + Send {
|
||||
where
|
||||
T: Type<Self::Database>,
|
||||
I: ColumnIndex<'c, Self>,
|
||||
T: Decode<Self::Database>,
|
||||
T: Decode<'c, Self::Database>,
|
||||
{
|
||||
Ok(Decode::decode_nullable(self.try_get_raw(index)?)?)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::decode::DecodeError;
|
||||
use crate::decode::UnexpectedNullError;
|
||||
use crate::Error;
|
||||
|
||||
pub trait ResultExt<T>: Sized {
|
||||
@ -15,7 +15,15 @@ impl<T> ResultExt<Option<T>> for crate::Result<T> {
|
||||
fn try_unwrap_optional(self) -> crate::Result<Option<T>> {
|
||||
match self {
|
||||
Ok(val) => Ok(Some(val)),
|
||||
Err(Error::Decode(DecodeError::UnexpectedNull)) => Ok(None),
|
||||
|
||||
Err(Error::Decode(error)) => {
|
||||
if let Some(UnexpectedNullError) = error.downcast_ref() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(Error::Decode(error))
|
||||
}
|
||||
}
|
||||
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user