mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-29 21:00:54 +00:00
refactor: move Decode::accepts to Type::compatible
This commit is contained in:
parent
5ac7601fe2
commit
aaa475cc33
@ -1,29 +1,11 @@
|
||||
//! Provides [`Decode`](trait.Decode.html) for decoding values from the database.
|
||||
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use crate::database::{Database, HasValueRef};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::types::Type;
|
||||
use crate::value::ValueRef;
|
||||
|
||||
/// A specialized result type representing the result of decoding a value from the database.
|
||||
pub type Result<T> = StdResult<T, BoxDynError>;
|
||||
|
||||
/// A type that can be decoded from the database.
|
||||
///
|
||||
/// ## Derivable
|
||||
///
|
||||
/// This trait can be derived to provide user-defined types where supported by
|
||||
/// the database driver.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // `UserId` can now be decoded from the database where
|
||||
/// // an `i64` was expected.
|
||||
/// #[derive(Decode)]
|
||||
/// struct UserId(i64);
|
||||
/// ```
|
||||
///
|
||||
/// ## How can I implement `Decode`?
|
||||
///
|
||||
/// A manual implementation of `Decode` can be useful when adding support for
|
||||
@ -58,14 +40,6 @@ pub type Result<T> = StdResult<T, BoxDynError>;
|
||||
/// // are supported by the database
|
||||
/// &'r str: Decode<'r, DB>
|
||||
/// {
|
||||
/// fn accepts(ty: &DB::TypeInfo) -> bool {
|
||||
/// // accepts is intended to provide runtime type checking and assert that our decode
|
||||
/// // function can handle the incoming value from the database
|
||||
///
|
||||
/// // as we are delegating to String
|
||||
/// <&str as Decode<DB>>::accepts(ty)
|
||||
/// }
|
||||
///
|
||||
/// fn decode(
|
||||
/// value: <DB as HasValueRef<'r>>::ValueRef,
|
||||
/// ) -> Result<MyType, Box<dyn Error + 'static + Send + Sync>> {
|
||||
@ -84,12 +58,8 @@ pub type Result<T> = StdResult<T, BoxDynError>;
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Decode<'r, DB: Database>: Sized {
|
||||
/// Determines if a value of this type can be created from a value with the
|
||||
/// given type information.
|
||||
fn accepts(ty: &DB::TypeInfo) -> bool;
|
||||
|
||||
/// Decode a new value of this type using a raw value from the database.
|
||||
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self>;
|
||||
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError>;
|
||||
}
|
||||
|
||||
// implement `Decode` for Option<T> for all SQL types
|
||||
@ -98,11 +68,7 @@ where
|
||||
DB: Database,
|
||||
T: Decode<'r, DB>,
|
||||
{
|
||||
fn accepts(ty: &DB::TypeInfo) -> bool {
|
||||
T::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self> {
|
||||
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> {
|
||||
if value.is_null() {
|
||||
Ok(None)
|
||||
} else {
|
||||
@ -110,10 +76,3 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default implementation of `accepts`
|
||||
// this can be trivially removed once min_specialization is stable
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn accepts<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> bool {
|
||||
*ty == T::type_info()
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@ use std::io;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::type_info::TypeInfo;
|
||||
use crate::types::Type;
|
||||
|
||||
/// A specialized `Result` type for SQLx.
|
||||
pub type Result<T> = StdResult<T, Error>;
|
||||
@ -119,12 +121,12 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mismatched_types<DB: Database, T>(expected: &DB::TypeInfo) -> BoxDynError {
|
||||
let ty_name = type_name::<T>();
|
||||
|
||||
pub(crate) fn mismatched_types<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> BoxDynError {
|
||||
format!(
|
||||
"mismatched types; Rust type `{}` is not compatible with SQL type `{}`",
|
||||
ty_name, expected
|
||||
"mismatched types; Rust type `{}` (as SQL type `{}`) is not compatible with SQL type `{}`",
|
||||
type_name::<T>(),
|
||||
T::type_info().name(),
|
||||
ty.name()
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ use std::sync::Arc;
|
||||
// a micro-string is either a reference-counted string or a static string
|
||||
// this guarantees these are cheap to clone everywhere
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub(crate) enum UStr {
|
||||
pub enum UStr {
|
||||
Static(&'static str),
|
||||
Shared(Arc<str>),
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::row::Row;
|
||||
///
|
||||
/// In order to use [`query_as`] the output type must implement `FromRow`.
|
||||
///
|
||||
/// # Deriving
|
||||
/// ## Derivable
|
||||
///
|
||||
/// This trait can be automatically derived by SQLx for any struct. The generated implementation
|
||||
/// will consist of a sequence of calls to [`Row::try_get`] using the name from each
|
||||
|
||||
@ -482,6 +482,43 @@ impl TypeInfo {
|
||||
buf[offset..(offset + 4)].copy_from_slice(&size.to_le_bytes());
|
||||
}
|
||||
|
||||
pub(crate) fn name(&self) -> &'static str {
|
||||
match self.ty {
|
||||
DataType::Null => "NULL",
|
||||
DataType::TinyInt => "TINYINT",
|
||||
DataType::SmallInt => "SMALLINT",
|
||||
DataType::Int => "INT",
|
||||
DataType::BigInt => "BIGINT",
|
||||
DataType::Real => "REAL",
|
||||
DataType::Float => "FLOAT",
|
||||
|
||||
DataType::IntN => match self.size {
|
||||
1 => "TINYINT",
|
||||
2 => "SMALLINT",
|
||||
4 => "INT",
|
||||
8 => "BIGINT",
|
||||
|
||||
_ => unreachable!("invalid size {} for int"),
|
||||
},
|
||||
|
||||
DataType::FloatN => match self.size {
|
||||
4 => "REAL",
|
||||
8 => "FLOAT",
|
||||
|
||||
_ => unreachable!("invalid size {} for float"),
|
||||
},
|
||||
|
||||
DataType::VarChar => "VARCHAR",
|
||||
DataType::NVarChar => "NVARCHAR",
|
||||
DataType::BigVarChar => "BIGVARCHAR",
|
||||
DataType::Char => "CHAR",
|
||||
DataType::BigChar => "BIGCHAR",
|
||||
DataType::NChar => "NCHAR",
|
||||
|
||||
_ => unimplemented!("name: unsupported data type {:?}", self.ty),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fmt(&self, s: &mut String) {
|
||||
match self.ty {
|
||||
DataType::Null => s.push_str("nvarchar(1)"),
|
||||
|
||||
@ -7,13 +7,14 @@ use crate::type_info::TypeInfo;
|
||||
#[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct MssqlTypeInfo(pub(crate) ProtocolTypeInfo);
|
||||
|
||||
impl TypeInfo for MssqlTypeInfo {}
|
||||
impl TypeInfo for MssqlTypeInfo {
|
||||
fn name(&self) -> &str {
|
||||
self.0.name()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MssqlTypeInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let mut buf = String::new();
|
||||
self.0.fmt(&mut buf);
|
||||
|
||||
f.pad(&*buf)
|
||||
f.pad(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,10 @@ impl Type<Mssql> for f32 {
|
||||
fn type_info() -> MssqlTypeInfo {
|
||||
MssqlTypeInfo(TypeInfo::new(DataType::FloatN, 4))
|
||||
}
|
||||
|
||||
fn compatible(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::Real | DataType::FloatN) && ty.0.size == 4
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Mssql> for f32 {
|
||||
@ -22,10 +26,6 @@ impl Encode<'_, Mssql> for f32 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Mssql> for f32 {
|
||||
fn accepts(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::Real | DataType::FloatN) && ty.0.size == 4
|
||||
}
|
||||
|
||||
fn decode(value: MssqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(LittleEndian::read_f32(value.as_bytes()?))
|
||||
}
|
||||
@ -35,6 +35,10 @@ impl Type<Mssql> for f64 {
|
||||
fn type_info() -> MssqlTypeInfo {
|
||||
MssqlTypeInfo(TypeInfo::new(DataType::FloatN, 8))
|
||||
}
|
||||
|
||||
fn compatible(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::Float | DataType::FloatN) && ty.0.size == 8
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Mssql> for f64 {
|
||||
@ -46,10 +50,6 @@ impl Encode<'_, Mssql> for f64 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Mssql> for f64 {
|
||||
fn accepts(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::Float | DataType::FloatN) && ty.0.size == 8
|
||||
}
|
||||
|
||||
fn decode(value: MssqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(LittleEndian::read_f64(value.as_bytes()?))
|
||||
}
|
||||
|
||||
@ -11,6 +11,10 @@ impl Type<Mssql> for i8 {
|
||||
fn type_info() -> MssqlTypeInfo {
|
||||
MssqlTypeInfo(TypeInfo::new(DataType::IntN, 1))
|
||||
}
|
||||
|
||||
fn compatible(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::TinyInt | DataType::IntN) && ty.0.size == 1
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Mssql> for i8 {
|
||||
@ -22,10 +26,6 @@ impl Encode<'_, Mssql> for i8 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Mssql> for i8 {
|
||||
fn accepts(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::TinyInt | DataType::IntN) && ty.0.size == 1
|
||||
}
|
||||
|
||||
fn decode(value: MssqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(value.as_bytes()?[0] as i8)
|
||||
}
|
||||
@ -35,6 +35,10 @@ impl Type<Mssql> for i16 {
|
||||
fn type_info() -> MssqlTypeInfo {
|
||||
MssqlTypeInfo(TypeInfo::new(DataType::IntN, 2))
|
||||
}
|
||||
|
||||
fn compatible(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::SmallInt | DataType::IntN) && ty.0.size == 2
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Mssql> for i16 {
|
||||
@ -46,10 +50,6 @@ impl Encode<'_, Mssql> for i16 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Mssql> for i16 {
|
||||
fn accepts(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::SmallInt | DataType::IntN) && ty.0.size == 2
|
||||
}
|
||||
|
||||
fn decode(value: MssqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(LittleEndian::read_i16(value.as_bytes()?))
|
||||
}
|
||||
@ -59,6 +59,10 @@ impl Type<Mssql> for i32 {
|
||||
fn type_info() -> MssqlTypeInfo {
|
||||
MssqlTypeInfo(TypeInfo::new(DataType::IntN, 4))
|
||||
}
|
||||
|
||||
fn compatible(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::Int | DataType::IntN) && ty.0.size == 4
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Mssql> for i32 {
|
||||
@ -70,10 +74,6 @@ impl Encode<'_, Mssql> for i32 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Mssql> for i32 {
|
||||
fn accepts(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::Int | DataType::IntN) && ty.0.size == 4
|
||||
}
|
||||
|
||||
fn decode(value: MssqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(LittleEndian::read_i32(value.as_bytes()?))
|
||||
}
|
||||
@ -83,6 +83,10 @@ impl Type<Mssql> for i64 {
|
||||
fn type_info() -> MssqlTypeInfo {
|
||||
MssqlTypeInfo(TypeInfo::new(DataType::IntN, 8))
|
||||
}
|
||||
|
||||
fn compatible(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::BigInt | DataType::IntN) && ty.0.size == 8
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Mssql> for i64 {
|
||||
@ -94,10 +98,6 @@ impl Encode<'_, Mssql> for i64 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Mssql> for i64 {
|
||||
fn accepts(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(ty.0.ty, DataType::BigInt | DataType::IntN) && ty.0.size == 8
|
||||
}
|
||||
|
||||
fn decode(value: MssqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(LittleEndian::read_i64(value.as_bytes()?))
|
||||
}
|
||||
|
||||
@ -8,15 +8,6 @@ mod int;
|
||||
mod str;
|
||||
|
||||
impl<'q, T: 'q + Encode<'q, Mssql>> Encode<'q, Mssql> for Option<T> {
|
||||
fn produces(&self) -> Option<MssqlTypeInfo> {
|
||||
if let Some(v) = self {
|
||||
v.produces()
|
||||
} else {
|
||||
// MSSQL requires a special NULL type ID
|
||||
Some(MssqlTypeInfo(TypeInfo::new(DataType::Null, 0)))
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(self, buf: &mut Vec<u8>) -> IsNull {
|
||||
if let Some(v) = self {
|
||||
v.encode(buf)
|
||||
@ -33,6 +24,15 @@ impl<'q, T: 'q + Encode<'q, Mssql>> Encode<'q, Mssql> for Option<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn produces(&self) -> Option<MssqlTypeInfo> {
|
||||
if let Some(v) = self {
|
||||
v.produces()
|
||||
} else {
|
||||
// MSSQL requires a special NULL type ID
|
||||
Some(MssqlTypeInfo(TypeInfo::new(DataType::Null, 0)))
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
self.as_ref().map_or(0, Encode::size_hint)
|
||||
}
|
||||
|
||||
@ -10,12 +10,28 @@ impl Type<Mssql> for str {
|
||||
fn type_info() -> MssqlTypeInfo {
|
||||
MssqlTypeInfo(TypeInfo::new(DataType::NVarChar, 0))
|
||||
}
|
||||
|
||||
fn compatible(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(
|
||||
ty.0.ty,
|
||||
DataType::NVarChar
|
||||
| DataType::NChar
|
||||
| DataType::BigVarChar
|
||||
| DataType::VarChar
|
||||
| DataType::BigChar
|
||||
| DataType::Char
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Mssql> for String {
|
||||
fn type_info() -> MssqlTypeInfo {
|
||||
<str as Type<Mssql>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &MssqlTypeInfo) -> bool {
|
||||
<str as Type<Mssql>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Mssql> for &'_ str {
|
||||
@ -55,18 +71,6 @@ impl Encode<'_, Mssql> for String {
|
||||
}
|
||||
|
||||
impl Decode<'_, Mssql> for String {
|
||||
fn accepts(ty: &MssqlTypeInfo) -> bool {
|
||||
matches!(
|
||||
ty.0.ty,
|
||||
DataType::NVarChar
|
||||
| DataType::NChar
|
||||
| DataType::BigVarChar
|
||||
| DataType::VarChar
|
||||
| DataType::BigChar
|
||||
| DataType::Char
|
||||
)
|
||||
}
|
||||
|
||||
fn decode(value: MssqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(value
|
||||
.type_info
|
||||
|
||||
@ -159,18 +159,25 @@ impl Decode<'_, Capabilities> for ColumnDefinition {
|
||||
}
|
||||
|
||||
impl ColumnType {
|
||||
pub(crate) fn name(self, char_set: u16) -> &'static str {
|
||||
pub(crate) fn name(self, char_set: u16, flags: ColumnFlags) -> &'static str {
|
||||
let is_binary = char_set == 63;
|
||||
let is_unsigned = flags.contains(ColumnFlags::UNSIGNED);
|
||||
|
||||
match self {
|
||||
ColumnType::Tiny if is_unsigned => "TINYINT UNSIGNED",
|
||||
ColumnType::Short if is_unsigned => "SMALLINT UNSIGNED",
|
||||
ColumnType::Long if is_unsigned => "INT UNSIGNED",
|
||||
ColumnType::Int24 if is_unsigned => "MEDIUMINT UNSIGNED",
|
||||
ColumnType::LongLong if is_unsigned => "BIGINT UNSIGNED",
|
||||
ColumnType::Tiny => "TINYINT",
|
||||
ColumnType::Short => "SMALLINT",
|
||||
ColumnType::Long => "INT",
|
||||
ColumnType::Int24 => "MEDIUMINT",
|
||||
ColumnType::LongLong => "BIGINT",
|
||||
ColumnType::Float => "FLOAT",
|
||||
ColumnType::Double => "DOUBLE",
|
||||
ColumnType::Null => "NULL",
|
||||
ColumnType::Timestamp => "TIMESTAMP",
|
||||
ColumnType::LongLong => "BIGINT",
|
||||
ColumnType::Int24 => "MEDIUMINT",
|
||||
ColumnType::Date => "DATE",
|
||||
ColumnType::Time => "TIME",
|
||||
ColumnType::Datetime => "DATETIME",
|
||||
|
||||
@ -59,21 +59,15 @@ impl MySqlTypeInfo {
|
||||
|
||||
impl Display for MySqlTypeInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.r#type.name(self.char_set))?;
|
||||
|
||||
// NOTE: MariaDB flags timestamp columns as UNSIGNED but the type name
|
||||
// does not have that suffix
|
||||
if self.flags.contains(ColumnFlags::UNSIGNED)
|
||||
&& !self.flags.contains(ColumnFlags::TIMESTAMP)
|
||||
{
|
||||
f.write_str(" UNSIGNED")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
f.pad(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeInfo for MySqlTypeInfo {}
|
||||
impl TypeInfo for MySqlTypeInfo {
|
||||
fn name(&self) -> &str {
|
||||
self.r#type.name(self.char_set, self.flags)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<MySqlTypeInfo> for MySqlTypeInfo {
|
||||
fn eq(&self, other: &MySqlTypeInfo) -> bool {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use bigdecimal::BigDecimal;
|
||||
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::mysql::io::MySqlBufMutExt;
|
||||
@ -23,10 +23,6 @@ impl Encode<'_, MySql> for BigDecimal {
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for BigDecimal {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
accepts::<MySql, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(value.as_str()?.parse()?)
|
||||
}
|
||||
|
||||
@ -9,6 +9,10 @@ impl Type<MySql> for bool {
|
||||
// MySQL has no actual `BOOLEAN` type, the type is an alias of `TINYINT(1)`
|
||||
<i8 as Type<MySql>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
<i8 as Type<MySql>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for bool {
|
||||
@ -18,10 +22,6 @@ impl Encode<'_, MySql> for bool {
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for bool {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
<i8 as Decode<MySql>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(<i8 as Decode<MySql>>::decode(value)? != 0)
|
||||
}
|
||||
|
||||
@ -10,18 +10,8 @@ impl Type<MySql> for [u8] {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::binary(ColumnType::Blob)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for &'_ [u8] {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
buf.put_bytes_lenenc(self);
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for &'r [u8] {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(
|
||||
ty.r#type,
|
||||
ColumnType::VarChar
|
||||
@ -34,7 +24,17 @@ impl<'r> Decode<'r, MySql> for &'r [u8] {
|
||||
| ColumnType::Enum
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for &'_ [u8] {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
buf.put_bytes_lenenc(self);
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for &'r [u8] {
|
||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
value.as_bytes()
|
||||
}
|
||||
@ -44,6 +44,10 @@ impl Type<MySql> for Vec<u8> {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
<[u8] as Type<MySql>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
<&[u8] as Type<MySql>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for Vec<u8> {
|
||||
@ -53,10 +57,6 @@ impl Encode<'_, MySql> for Vec<u8> {
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for Vec<u8> {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
<&[u8] as Decode<MySql>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
<&[u8] as Decode<MySql>>::decode(value).map(ToOwned::to_owned)
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ use std::convert::TryFrom;
|
||||
use bytes::Buf;
|
||||
use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
|
||||
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::mysql::protocol::text::ColumnType;
|
||||
@ -15,6 +15,10 @@ impl Type<MySql> for DateTime<Utc> {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::binary(ColumnType::Timestamp)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(ty.r#type, ColumnType::Datetime | ColumnType::Timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for DateTime<Utc> {
|
||||
@ -24,10 +28,6 @@ impl Encode<'_, MySql> for DateTime<Utc> {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for DateTime<Utc> {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(ty.r#type, ColumnType::Datetime | ColumnType::Timestamp)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
let naive: NaiveDateTime = Decode::<MySql>::decode(value)?;
|
||||
|
||||
@ -70,10 +70,6 @@ impl Encode<'_, MySql> for NaiveTime {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for NaiveTime {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
accepts::<MySql, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
match value.format() {
|
||||
MySqlValueFormat::Binary => {
|
||||
@ -122,10 +118,6 @@ impl Encode<'_, MySql> for NaiveDate {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for NaiveDate {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
accepts::<MySql, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
match value.format() {
|
||||
MySqlValueFormat::Binary => Ok(decode_date(&value.as_bytes()?[1..])),
|
||||
@ -181,10 +173,6 @@ impl Encode<'_, MySql> for NaiveDateTime {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for NaiveDateTime {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(ty.r#type, ColumnType::Datetime | ColumnType::Timestamp)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
match value.format() {
|
||||
MySqlValueFormat::Binary => {
|
||||
|
||||
@ -7,7 +7,7 @@ use crate::mysql::protocol::text::ColumnType;
|
||||
use crate::mysql::{MySql, MySqlTypeInfo, MySqlValueFormat, MySqlValueRef};
|
||||
use crate::types::Type;
|
||||
|
||||
fn real_accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
fn real_compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(ty.r#type, ColumnType::Float | ColumnType::Double)
|
||||
}
|
||||
|
||||
@ -15,12 +15,20 @@ impl Type<MySql> for f32 {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::binary(ColumnType::Float)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
real_compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<MySql> for f64 {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::binary(ColumnType::Double)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
real_compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for f32 {
|
||||
@ -40,10 +48,6 @@ impl Encode<'_, MySql> for f64 {
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for f32 {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
real_accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
MySqlValueFormat::Binary => {
|
||||
@ -64,10 +68,6 @@ impl Decode<'_, MySql> for f32 {
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for f64 {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
real_accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
MySqlValueFormat::Binary => LittleEndian::read_f64(value.as_bytes()?),
|
||||
|
||||
@ -9,28 +9,55 @@ use crate::mysql::protocol::text::{ColumnFlags, ColumnType};
|
||||
use crate::mysql::{MySql, MySqlTypeInfo, MySqlValueFormat, MySqlValueRef};
|
||||
use crate::types::Type;
|
||||
|
||||
fn int_compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(
|
||||
ty.r#type,
|
||||
ColumnType::Tiny
|
||||
| ColumnType::Short
|
||||
| ColumnType::Long
|
||||
| ColumnType::Int24
|
||||
| ColumnType::LongLong
|
||||
) && !ty.flags.contains(ColumnFlags::UNSIGNED)
|
||||
}
|
||||
|
||||
impl Type<MySql> for i8 {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::binary(ColumnType::Tiny)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
int_compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<MySql> for i16 {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::binary(ColumnType::Short)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
int_compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<MySql> for i32 {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::binary(ColumnType::Long)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
int_compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<MySql> for i64 {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::binary(ColumnType::LongLong)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
int_compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for i8 {
|
||||
@ -65,17 +92,6 @@ impl Encode<'_, MySql> for i64 {
|
||||
}
|
||||
}
|
||||
|
||||
fn int_accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(
|
||||
ty.r#type,
|
||||
ColumnType::Tiny
|
||||
| ColumnType::Short
|
||||
| ColumnType::Long
|
||||
| ColumnType::Int24
|
||||
| ColumnType::LongLong
|
||||
) && !ty.flags.contains(ColumnFlags::UNSIGNED)
|
||||
}
|
||||
|
||||
fn int_decode(value: MySqlValueRef<'_>) -> Result<i64, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
MySqlValueFormat::Text => value.as_str()?.parse()?,
|
||||
@ -87,40 +103,24 @@ fn int_decode(value: MySqlValueRef<'_>) -> Result<i64, BoxDynError> {
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for i8 {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
int_accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
int_decode(value)?.try_into().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for i16 {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
int_accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
int_decode(value)?.try_into().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for i32 {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
int_accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
int_decode(value)?.try_into().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for i64 {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
int_accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
int_decode(value)?.try_into().map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -15,6 +15,12 @@ impl<T> Type<MySql> for Json<T> {
|
||||
// and has nothing to do with the native storage ability of MySQL v8+
|
||||
MySqlTypeInfo::binary(ColumnType::String)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
ty.r#type == ColumnType::Json
|
||||
|| <&str as Type<MySql>>::compatible(ty)
|
||||
|| <&[u8] as Type<MySql>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Encode<'_, MySql> for Json<T>
|
||||
@ -33,12 +39,6 @@ impl<'r, T> Decode<'r, MySql> for Json<T>
|
||||
where
|
||||
T: 'r + DeserializeOwned,
|
||||
{
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
ty.r#type == ColumnType::Json
|
||||
|| <&str as Decode<MySql>>::accepts(ty)
|
||||
|| <&[u8] as Decode<MySql>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
let string_value = <&str as Decode<MySql>>::decode(value)?;
|
||||
|
||||
|
||||
@ -14,18 +14,8 @@ impl Type<MySql> for str {
|
||||
flags: ColumnFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for &'_ str {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
buf.put_str_lenenc(self);
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for &'r str {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(
|
||||
ty.r#type,
|
||||
ColumnType::VarChar
|
||||
@ -38,7 +28,17 @@ impl<'r> Decode<'r, MySql> for &'r str {
|
||||
| ColumnType::Enum
|
||||
) && ty.char_set == 224
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for &'_ str {
|
||||
fn encode_by_ref(&self, buf: &mut Vec<u8>) -> IsNull {
|
||||
buf.put_str_lenenc(self);
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for &'r str {
|
||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
value.as_str()
|
||||
}
|
||||
@ -48,6 +48,10 @@ impl Type<MySql> for String {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
<str as Type<MySql>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
<str as Type<MySql>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for String {
|
||||
@ -57,10 +61,6 @@ impl Encode<'_, MySql> for String {
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for String {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
<&str as Decode<MySql>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
<&str as Decode<MySql>>::decode(value).map(ToOwned::to_owned)
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ use byteorder::{ByteOrder, LittleEndian};
|
||||
use bytes::Buf;
|
||||
use time::{Date, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};
|
||||
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::mysql::protocol::text::ColumnType;
|
||||
@ -17,6 +17,10 @@ impl Type<MySql> for OffsetDateTime {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
MySqlTypeInfo::binary(ColumnType::Timestamp)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(ty.r#type, ColumnType::Datetime | ColumnType::Timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for OffsetDateTime {
|
||||
@ -29,10 +33,6 @@ impl Encode<'_, MySql> for OffsetDateTime {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for OffsetDateTime {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(ty.r#type, ColumnType::Datetime | ColumnType::Timestamp)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
let primitive: PrimitiveDateTime = Decode::<MySql>::decode(value)?;
|
||||
|
||||
@ -75,10 +75,6 @@ impl Encode<'_, MySql> for Time {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for Time {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
accepts::<MySql, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
match value.format() {
|
||||
MySqlValueFormat::Binary => {
|
||||
@ -138,10 +134,6 @@ impl Encode<'_, MySql> for Date {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for Date {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
accepts::<MySql, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
match value.format() {
|
||||
MySqlValueFormat::Binary => decode_date(&value.as_bytes()?[1..]),
|
||||
@ -191,10 +183,6 @@ impl Encode<'_, MySql> for PrimitiveDateTime {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, MySql> for PrimitiveDateTime {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(ty.r#type, ColumnType::Datetime | ColumnType::Timestamp)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
match value.format() {
|
||||
MySqlValueFormat::Binary => {
|
||||
|
||||
@ -17,28 +17,55 @@ fn uint_type_info(ty: ColumnType) -> MySqlTypeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
fn uint_compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(
|
||||
ty.r#type,
|
||||
ColumnType::Tiny
|
||||
| ColumnType::Short
|
||||
| ColumnType::Long
|
||||
| ColumnType::Int24
|
||||
| ColumnType::LongLong
|
||||
) && ty.flags.contains(ColumnFlags::UNSIGNED)
|
||||
}
|
||||
|
||||
impl Type<MySql> for u8 {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
uint_type_info(ColumnType::Tiny)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
uint_compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<MySql> for u16 {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
uint_type_info(ColumnType::Short)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
uint_compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<MySql> for u32 {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
uint_type_info(ColumnType::Long)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
uint_compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<MySql> for u64 {
|
||||
fn type_info() -> MySqlTypeInfo {
|
||||
uint_type_info(ColumnType::LongLong)
|
||||
}
|
||||
|
||||
fn compatible(ty: &MySqlTypeInfo) -> bool {
|
||||
uint_compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, MySql> for u8 {
|
||||
@ -73,17 +100,6 @@ impl Encode<'_, MySql> for u64 {
|
||||
}
|
||||
}
|
||||
|
||||
fn uint_accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
matches!(
|
||||
ty.r#type,
|
||||
ColumnType::Tiny
|
||||
| ColumnType::Short
|
||||
| ColumnType::Long
|
||||
| ColumnType::Int24
|
||||
| ColumnType::LongLong
|
||||
) && ty.flags.contains(ColumnFlags::UNSIGNED)
|
||||
}
|
||||
|
||||
fn uint_decode(value: MySqlValueRef<'_>) -> Result<u64, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
MySqlValueFormat::Text => value.as_str()?.parse()?,
|
||||
@ -95,40 +111,24 @@ fn uint_decode(value: MySqlValueRef<'_>) -> Result<u64, BoxDynError> {
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for u8 {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
uint_accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
uint_decode(value)?.try_into().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for u16 {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
uint_accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
uint_decode(value)?.try_into().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for u32 {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
uint_accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
uint_decode(value)?.try_into().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<'_, MySql> for u64 {
|
||||
fn accepts(ty: &MySqlTypeInfo) -> bool {
|
||||
uint_accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
uint_decode(value)?.try_into().map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ pub use message::PgSeverity;
|
||||
pub use options::{PgConnectOptions, PgSslMode};
|
||||
pub use row::PgRow;
|
||||
pub use transaction::PgTransactionManager;
|
||||
pub use type_info::{PgTypeInfo, PgTypeKind};
|
||||
pub use type_info::PgTypeInfo;
|
||||
pub use value::{PgValue, PgValueFormat, PgValueRef};
|
||||
|
||||
/// An alias for [`Pool`][crate::pool::Pool], specialized for Postgres.
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::ext::ustr::UStr;
|
||||
@ -11,10 +12,18 @@ use crate::type_info::TypeInfo;
|
||||
#[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct PgTypeInfo(pub(crate) PgType);
|
||||
|
||||
impl Deref for PgTypeInfo {
|
||||
type Target = PgType;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[repr(u32)]
|
||||
pub(crate) enum PgType {
|
||||
pub enum PgType {
|
||||
Bool,
|
||||
Bytea,
|
||||
Char,
|
||||
@ -118,7 +127,7 @@ pub(crate) enum PgType {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub(crate) struct PgCustomType {
|
||||
pub struct PgCustomType {
|
||||
pub(crate) oid: u32,
|
||||
pub(crate) name: UStr,
|
||||
pub(crate) kind: PgTypeKind,
|
||||
@ -418,6 +427,102 @@ impl PgType {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn display_name(&self) -> &str {
|
||||
match self {
|
||||
PgType::Bool => "BOOL",
|
||||
PgType::Bytea => "BYTEA",
|
||||
PgType::Char => "\"CHAR\"",
|
||||
PgType::Name => "NAME",
|
||||
PgType::Int8 => "INT8",
|
||||
PgType::Int2 => "INT2",
|
||||
PgType::Int4 => "INT4",
|
||||
PgType::Text => "TEXT",
|
||||
PgType::Oid => "OID",
|
||||
PgType::Json => "JSON",
|
||||
PgType::JsonArray => "JSON[]",
|
||||
PgType::Point => "POINT",
|
||||
PgType::Lseg => "LSEG",
|
||||
PgType::Path => "PATH",
|
||||
PgType::Box => "BOX",
|
||||
PgType::Polygon => "POLYGON",
|
||||
PgType::Line => "LINE",
|
||||
PgType::LineArray => "LINE[]",
|
||||
PgType::Cidr => "CIDR",
|
||||
PgType::CidrArray => "CIDR[]",
|
||||
PgType::Float4 => "FLOAT4",
|
||||
PgType::Float8 => "FLOAT8",
|
||||
PgType::Unknown => "UNKNOWN",
|
||||
PgType::Circle => "CIRCLE",
|
||||
PgType::CircleArray => "CIRCLE[]",
|
||||
PgType::Macaddr8 => "MACADDR8",
|
||||
PgType::Macaddr8Array => "MACADDR8[]",
|
||||
PgType::Macaddr => "MACADDR",
|
||||
PgType::Inet => "INET",
|
||||
PgType::BoolArray => "BOOL[]",
|
||||
PgType::ByteaArray => "BYTEA[]",
|
||||
PgType::CharArray => "\"CHAR\"[]",
|
||||
PgType::NameArray => "NAME[]",
|
||||
PgType::Int2Array => "INT2[]",
|
||||
PgType::Int4Array => "INT4[]",
|
||||
PgType::TextArray => "TEXT[]",
|
||||
PgType::BpcharArray => "CHAR[]",
|
||||
PgType::VarcharArray => "VARCHAR[]",
|
||||
PgType::Int8Array => "INT8[]",
|
||||
PgType::PointArray => "POINT[]",
|
||||
PgType::LsegArray => "LSEG[]",
|
||||
PgType::PathArray => "PATH[]",
|
||||
PgType::BoxArray => "BOX[]",
|
||||
PgType::Float4Array => "FLOAT4[]",
|
||||
PgType::Float8Array => "FLOAT8[]",
|
||||
PgType::PolygonArray => "POLYGON[]",
|
||||
PgType::OidArray => "OID[]",
|
||||
PgType::MacaddrArray => "MACADDR[]",
|
||||
PgType::InetArray => "INET[]",
|
||||
PgType::Bpchar => "CHAR",
|
||||
PgType::Varchar => "VARCHAR",
|
||||
PgType::Date => "DATE",
|
||||
PgType::Time => "TIME",
|
||||
PgType::Timestamp => "TIMESTAMP",
|
||||
PgType::TimestampArray => "TIMESTAMP[]",
|
||||
PgType::DateArray => "DATE[]",
|
||||
PgType::TimeArray => "TIME[]",
|
||||
PgType::Timestamptz => "TIMESTAMPTZ",
|
||||
PgType::TimestamptzArray => "TIMESTAMPTZ[]",
|
||||
PgType::NumericArray => "NUMERIC[]",
|
||||
PgType::Timetz => "TIMETZ",
|
||||
PgType::TimetzArray => "TIMETZ[]",
|
||||
PgType::Bit => "BIT",
|
||||
PgType::BitArray => "BIT[]",
|
||||
PgType::Varbit => "VARBIT",
|
||||
PgType::VarbitArray => "VARBIT[]",
|
||||
PgType::Numeric => "NUMERIC",
|
||||
PgType::Record => "RECORD",
|
||||
PgType::Interval => "INTERVAL",
|
||||
PgType::RecordArray => "RECORD[]",
|
||||
PgType::Uuid => "UUID",
|
||||
PgType::UuidArray => "UUID[]",
|
||||
PgType::Jsonb => "JSONB",
|
||||
PgType::JsonbArray => "JSONB[]",
|
||||
PgType::Int4Range => "INT4RANGE",
|
||||
PgType::Int4RangeArray => "INT4RANGE[]",
|
||||
PgType::NumRange => "NUMRANGE",
|
||||
PgType::NumRangeArray => "NUMRANGE[]",
|
||||
PgType::TsRange => "TSRANGE",
|
||||
PgType::TsRangeArray => "TSRANGE[]",
|
||||
PgType::TstzRange => "TSTZRANGE",
|
||||
PgType::TstzRangeArray => "TSTZRANGE[]",
|
||||
PgType::DateRange => "DATERANGE",
|
||||
PgType::DateRangeArray => "DATERANGE[]",
|
||||
PgType::Int8Range => "INT8RANGE",
|
||||
PgType::Int8RangeArray => "INT8RANGE[]",
|
||||
PgType::Jsonpath => "JSONPATH",
|
||||
PgType::JsonpathArray => "JSONPATH[]",
|
||||
PgType::Custom(ty) => &*ty.name,
|
||||
PgType::DeclareWithOid(_) => "?",
|
||||
PgType::DeclareWithName(name) => name,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn name(&self) -> &str {
|
||||
match self {
|
||||
PgType::Bool => "bool",
|
||||
@ -613,7 +718,11 @@ impl PgType {
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeInfo for PgTypeInfo {}
|
||||
impl TypeInfo for PgTypeInfo {
|
||||
fn name(&self) -> &str {
|
||||
self.0.display_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<PgCustomType> for PgCustomType {
|
||||
fn eq(&self, other: &PgCustomType) -> bool {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use bytes::Buf;
|
||||
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::type_info::PgType;
|
||||
@ -14,6 +14,10 @@ where
|
||||
fn type_info() -> PgTypeInfo {
|
||||
<[T] as Type<Postgres>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
<[T] as Type<Postgres>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Type<Postgres> for Vec<Option<T>>
|
||||
@ -23,6 +27,10 @@ where
|
||||
fn type_info() -> PgTypeInfo {
|
||||
<Vec<T> as Type<Postgres>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
<Vec<T> as Type<Postgres>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, T> Encode<'q, Postgres> for Vec<T>
|
||||
@ -66,18 +74,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Array decoding in PostgreSQL *could* allow 'r (row) lifetime of elements if we can figure
|
||||
// out a way for the TEXT encoding to use some shared memory somewhere.
|
||||
|
||||
impl<'r, T> Decode<'r, Postgres> for Vec<T>
|
||||
where
|
||||
T: for<'a> Decode<'a, Postgres> + Type<Postgres>,
|
||||
Self: Type<Postgres>,
|
||||
{
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
let element_type_info = T::type_info();
|
||||
let format = value.format();
|
||||
|
||||
@ -4,7 +4,7 @@ use std::convert::{TryFrom, TryInto};
|
||||
use bigdecimal::BigDecimal;
|
||||
use num_bigint::{BigInt, Sign};
|
||||
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::types::numeric::{PgNumeric, PgNumericSign};
|
||||
@ -165,10 +165,6 @@ impl Encode<'_, Postgres> for BigDecimal {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for BigDecimal {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
match value.format() {
|
||||
PgValueFormat::Binary => PgNumeric::decode(value.as_bytes()?)?.try_into(),
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
@ -31,10 +31,6 @@ impl Encode<'_, Postgres> for bool {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for bool {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => value.as_bytes()?[0] != 0,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
@ -55,10 +55,6 @@ impl Encode<'_, Postgres> for Vec<u8> {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Postgres> for &'r [u8] {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
match value.format() {
|
||||
PgValueFormat::Binary => value.as_bytes(),
|
||||
@ -70,10 +66,6 @@ impl<'r> Decode<'r, Postgres> for &'r [u8] {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for Vec<u8> {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => value.as_bytes()?.to_owned(),
|
||||
|
||||
@ -2,7 +2,7 @@ use std::mem;
|
||||
|
||||
use chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
|
||||
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
@ -96,10 +96,6 @@ impl Encode<'_, Postgres> for NaiveTime {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Postgres> for NaiveTime {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => {
|
||||
@ -126,10 +122,6 @@ impl Encode<'_, Postgres> for NaiveDate {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Postgres> for NaiveDate {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => {
|
||||
@ -160,10 +152,6 @@ impl Encode<'_, Postgres> for NaiveDateTime {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Postgres> for NaiveDateTime {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => {
|
||||
@ -202,10 +190,6 @@ impl<Tz: TimeZone> Encode<'_, Postgres> for DateTime<Tz> {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Postgres> for DateTime<Local> {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
let naive = <NaiveDateTime as Decode<Postgres>>::decode(value)?;
|
||||
Ok(Local.from_utc_datetime(&naive))
|
||||
@ -213,10 +197,6 @@ impl<'r> Decode<'r, Postgres> for DateTime<Local> {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Postgres> for DateTime<Utc> {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
let naive = <NaiveDateTime as Decode<Postgres>>::decode(value)?;
|
||||
Ok(Utc.from_utc_datetime(&naive))
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
@ -33,10 +33,6 @@ impl Encode<'_, Postgres> for f32 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for f32 {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => BigEndian::read_f32(value.as_bytes()?),
|
||||
@ -72,10 +68,6 @@ impl Encode<'_, Postgres> for f64 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for f64 {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => BigEndian::read_f64(value.as_bytes()?),
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
@ -33,10 +33,6 @@ impl Encode<'_, Postgres> for i8 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for i8 {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
// note: in the TEXT encoding, a value of "0" here is encoded as an empty string
|
||||
Ok(value.as_bytes()?.get(0).copied().unwrap_or_default() as i8)
|
||||
@ -70,10 +66,6 @@ impl Encode<'_, Postgres> for i16 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for i16 {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => BigEndian::read_i16(value.as_bytes()?),
|
||||
@ -109,10 +101,6 @@ impl Encode<'_, Postgres> for u32 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for u32 {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => BigEndian::read_u32(value.as_bytes()?),
|
||||
@ -148,10 +136,6 @@ impl Encode<'_, Postgres> for i32 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for i32 {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => BigEndian::read_i32(value.as_bytes()?),
|
||||
@ -187,10 +171,6 @@ impl Encode<'_, Postgres> for i64 {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for i64 {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => BigEndian::read_i64(value.as_bytes()?),
|
||||
|
||||
@ -28,6 +28,10 @@ impl Type<Postgres> for IpNetwork {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::INET
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
*ty == PgTypeInfo::CIDR || *ty == PgTypeInfo::INET
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for [IpNetwork] {
|
||||
@ -40,6 +44,10 @@ impl Type<Postgres> for Vec<IpNetwork> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
<[IpNetwork] as Type<Postgres>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
<[IpNetwork] as Type<Postgres>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Postgres> for IpNetwork {
|
||||
@ -77,10 +85,6 @@ impl Encode<'_, Postgres> for IpNetwork {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for IpNetwork {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
*ty == PgTypeInfo::CIDR || *ty == PgTypeInfo::INET
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
let bytes = match value.format() {
|
||||
PgValueFormat::Binary => value.as_bytes()?,
|
||||
|
||||
@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::types::array_compatible;
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
use crate::types::{Json, Type};
|
||||
|
||||
@ -16,12 +17,20 @@ impl<T> Type<Postgres> for Json<T> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::JSONB
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
*ty == PgTypeInfo::JSON || *ty == PgTypeInfo::JSONB
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Type<Postgres> for [Json<T>] {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::JSONB_ARRAY
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
array_compatible::<Json<T>>(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Type<Postgres> for Vec<Json<T>> {
|
||||
@ -49,10 +58,6 @@ impl<'r, T: 'r> Decode<'r, Postgres> for Json<T>
|
||||
where
|
||||
T: Deserialize<'r>,
|
||||
{
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
*ty == PgTypeInfo::JSON || *ty == PgTypeInfo::JSONB
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
let mut buf = value.as_bytes()?;
|
||||
|
||||
|
||||
@ -128,6 +128,10 @@
|
||||
//! a potentially `NULL` value from Postgres.
|
||||
//!
|
||||
|
||||
use crate::postgres::type_info::PgTypeKind;
|
||||
use crate::postgres::{PgTypeInfo, Postgres};
|
||||
use crate::types::Type;
|
||||
|
||||
mod array;
|
||||
mod bool;
|
||||
mod bytes;
|
||||
@ -165,3 +169,14 @@ pub use range::PgRange;
|
||||
// but the interface is not considered part of the public API
|
||||
#[doc(hidden)]
|
||||
pub use record::{PgRecordDecoder, PgRecordEncoder};
|
||||
|
||||
// Type::compatible impl appropriate for arrays
|
||||
fn array_compatible<E: Type<Postgres>>(ty: &PgTypeInfo) -> bool {
|
||||
// we require the declared type to be an _array_ with an
|
||||
// element type that is acceptable
|
||||
if let PgTypeKind::Array(element) = &ty.kind() {
|
||||
return E::compatible(&element);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
@ -7,9 +7,8 @@ use bytes::Buf;
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::{
|
||||
PgArgumentBuffer, PgTypeInfo, PgTypeKind, PgValueFormat, PgValueRef, Postgres,
|
||||
};
|
||||
use crate::postgres::type_info::PgTypeKind;
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
use crate::types::Type;
|
||||
|
||||
// https://github.com/postgres/postgres/blob/2f48ede080f42b97b594fb14102c82ca1001b80c/src/include/utils/rangetypes.h#L35-L44
|
||||
@ -116,12 +115,20 @@ impl Type<Postgres> for PgRange<i32> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::INT4_RANGE
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
range_compatible::<i32>(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for PgRange<i64> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::INT8_RANGE
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
range_compatible::<i64>(ty)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "bigdecimal")]
|
||||
@ -129,6 +136,10 @@ impl Type<Postgres> for PgRange<bigdecimal::BigDecimal> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::NUM_RANGE
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
range_compatible::<bigdecimal::BigDecimal>(ty)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
@ -136,6 +147,10 @@ impl Type<Postgres> for PgRange<chrono::NaiveDate> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::DATE_RANGE
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
range_compatible::<chrono::NaiveDate>(ty)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
@ -143,6 +158,10 @@ impl Type<Postgres> for PgRange<chrono::NaiveDateTime> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::TS_RANGE
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
range_compatible::<chrono::NaiveDateTime>(ty)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
@ -150,6 +169,10 @@ impl<Tz: chrono::TimeZone> Type<Postgres> for PgRange<chrono::DateTime<Tz>> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::TSTZ_RANGE
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
range_compatible::<chrono::DateTime<Tz>>(ty)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
@ -157,6 +180,10 @@ impl Type<Postgres> for PgRange<time::Date> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::DATE_RANGE
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
range_compatible::<time::Date>(ty)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
@ -164,6 +191,10 @@ impl Type<Postgres> for PgRange<time::PrimitiveDateTime> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::TS_RANGE
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
range_compatible::<time::PrimitiveDateTime>(ty)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
@ -171,6 +202,10 @@ impl Type<Postgres> for PgRange<time::OffsetDateTime> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::TSTZ_RANGE
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
range_compatible::<time::OffsetDateTime>(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for [PgRange<i32>] {
|
||||
@ -335,16 +370,6 @@ impl<'r, T> Decode<'r, Postgres> for PgRange<T>
|
||||
where
|
||||
T: Type<Postgres> + for<'a> Decode<'a, Postgres>,
|
||||
{
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
// we require the declared type to be a _range_ with an
|
||||
// element type that is acceptable
|
||||
if let PgTypeKind::Range(element) = &ty.0.kind() {
|
||||
return T::accepts(&element);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
match value.format {
|
||||
PgValueFormat::Binary => {
|
||||
@ -528,3 +553,13 @@ where
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn range_compatible<E: Type<Postgres>>(ty: &PgTypeInfo) -> bool {
|
||||
// we require the declared type to be a _range_ with an
|
||||
// element type that is acceptable
|
||||
if let PgTypeKind::Range(element) = &ty.kind() {
|
||||
return E::compatible(&element);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
@ -3,10 +3,8 @@ use bytes::Buf;
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::Encode;
|
||||
use crate::error::{mismatched_types, BoxDynError};
|
||||
use crate::postgres::type_info::PgType;
|
||||
use crate::postgres::{
|
||||
PgArgumentBuffer, PgTypeInfo, PgTypeKind, PgValueFormat, PgValueRef, Postgres,
|
||||
};
|
||||
use crate::postgres::type_info::{PgType, PgTypeKind};
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
use crate::types::Type;
|
||||
|
||||
#[doc(hidden)]
|
||||
@ -128,8 +126,8 @@ impl<'r> PgRecordDecoder<'r> {
|
||||
self.ind += 1;
|
||||
|
||||
if let Some(ty) = &element_type_opt {
|
||||
if !T::accepts(ty) {
|
||||
return Err(mismatched_types::<Postgres, T>(&T::type_info(), ty));
|
||||
if !T::compatible(ty) {
|
||||
return Err(mismatched_types::<Postgres, T>(ty));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::types::array_compatible;
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueRef, Postgres};
|
||||
use crate::types::Type;
|
||||
|
||||
@ -8,18 +9,37 @@ impl Type<Postgres> for str {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::TEXT
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
[
|
||||
PgTypeInfo::TEXT,
|
||||
PgTypeInfo::NAME,
|
||||
PgTypeInfo::BPCHAR,
|
||||
PgTypeInfo::VARCHAR,
|
||||
PgTypeInfo::UNKNOWN,
|
||||
]
|
||||
.contains(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for [&'_ str] {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
PgTypeInfo::TEXT_ARRAY
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
array_compatible::<&str>(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for Vec<&'_ str> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
<[&str] as Type<Postgres>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
<[&str] as Type<Postgres>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode<'_, Postgres> for &'_ str {
|
||||
@ -37,17 +57,6 @@ impl Encode<'_, Postgres> for String {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Postgres> for &'r str {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
[
|
||||
PgTypeInfo::TEXT,
|
||||
PgTypeInfo::NAME,
|
||||
PgTypeInfo::BPCHAR,
|
||||
PgTypeInfo::VARCHAR,
|
||||
PgTypeInfo::UNKNOWN,
|
||||
]
|
||||
.contains(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(value.as_str()?)
|
||||
}
|
||||
@ -57,25 +66,33 @@ impl Type<Postgres> for String {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
<&str as Type<Postgres>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
<&str as Type<Postgres>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for [String] {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
<[&str] as Type<Postgres>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
<[&str] as Type<Postgres>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for Vec<String> {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
<[String] as Type<Postgres>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &PgTypeInfo) -> bool {
|
||||
<[String] as Type<Postgres>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for String {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
<&str as Decode<Postgres>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
Ok(value.as_str()?.to_owned())
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use time::{date, offset, Date, Duration, OffsetDateTime, PrimitiveDateTime, Time};
|
||||
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
@ -96,10 +96,6 @@ impl Encode<'_, Postgres> for Time {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Postgres> for Time {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => {
|
||||
@ -141,10 +137,6 @@ impl Encode<'_, Postgres> for Date {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Postgres> for Date {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => {
|
||||
@ -171,10 +163,6 @@ impl Encode<'_, Postgres> for PrimitiveDateTime {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Postgres> for PrimitiveDateTime {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(match value.format() {
|
||||
PgValueFormat::Binary => {
|
||||
@ -232,10 +220,6 @@ impl Encode<'_, Postgres> for OffsetDateTime {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Postgres> for OffsetDateTime {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(<PrimitiveDateTime as Decode<Postgres>>::decode(value)?.assume_utc())
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::types::PgRecordDecoder;
|
||||
use crate::postgres::{PgTypeInfo, PgValueRef, Postgres};
|
||||
@ -33,10 +33,6 @@ macro_rules! impl_type_for_tuple {
|
||||
$($T: Type<Postgres>,)*
|
||||
$($T: for<'a> Decode<'a, Postgres>,)*
|
||||
{
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
#[allow(unused)]
|
||||
let mut decoder = PgRecordDecoder::new(value)?;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::decode::{accepts, Decode};
|
||||
use crate::decode::Decode;
|
||||
use crate::encode::{Encode, IsNull};
|
||||
use crate::error::BoxDynError;
|
||||
use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
@ -33,10 +33,6 @@ impl Encode<'_, Postgres> for Uuid {
|
||||
}
|
||||
|
||||
impl Decode<'_, Postgres> for Uuid {
|
||||
fn accepts(ty: &PgTypeInfo) -> bool {
|
||||
accepts::<Postgres, Self>(ty)
|
||||
}
|
||||
|
||||
fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
|
||||
match value.format() {
|
||||
PgValueFormat::Binary => Uuid::from_slice(value.as_bytes()?),
|
||||
|
||||
@ -3,6 +3,7 @@ use std::fmt::Debug;
|
||||
use crate::database::{Database, HasValueRef};
|
||||
use crate::decode::Decode;
|
||||
use crate::error::{mismatched_types, Error};
|
||||
use crate::types::Type;
|
||||
use crate::value::ValueRef;
|
||||
|
||||
/// A type that can be used to index into a [`Row`].
|
||||
@ -16,6 +17,7 @@ use crate::value::ValueRef;
|
||||
/// [`Row`]: trait.Row.html
|
||||
/// [`get`]: trait.Row.html#method.get
|
||||
/// [`try_get`]: trait.Row.html#method.try_get
|
||||
///
|
||||
pub trait ColumnIndex<R: Row + ?Sized>: private_column_index::Sealed + Debug {
|
||||
/// Returns a valid positional index into the row, [`ColumnIndexOutOfBounds`], or,
|
||||
/// [`ColumnNotFound`].
|
||||
@ -89,7 +91,7 @@ pub trait Row: private_row::Sealed + Unpin + Send + Sync + 'static {
|
||||
fn get<'r, T, I>(&'r self, index: I) -> T
|
||||
where
|
||||
I: ColumnIndex<Self>,
|
||||
T: Decode<'r, Self::Database>,
|
||||
T: Decode<'r, Self::Database> + Type<Self::Database>,
|
||||
{
|
||||
self.try_get::<T, I>(index).unwrap()
|
||||
}
|
||||
@ -132,18 +134,18 @@ pub trait Row: private_row::Sealed + Unpin + Send + Sync + 'static {
|
||||
fn try_get<'r, T, I>(&'r self, index: I) -> Result<T, Error>
|
||||
where
|
||||
I: ColumnIndex<Self>,
|
||||
T: Decode<'r, Self::Database>,
|
||||
T: Decode<'r, Self::Database> + Type<Self::Database>,
|
||||
{
|
||||
let value = self.try_get_raw(&index)?;
|
||||
|
||||
if !value.is_null() {
|
||||
if let Some(actual_ty) = value.type_info() {
|
||||
if let Some(ty) = value.type_info() {
|
||||
// NOTE: we opt-out of asserting the type equivalency of NULL because of the
|
||||
// high false-positive rate (e.g., `NULL` in Postgres is `TEXT`).
|
||||
if !T::accepts(&actual_ty) {
|
||||
if !T::compatible(&ty) {
|
||||
return Err(Error::ColumnDecode {
|
||||
index: format!("{:?}", index),
|
||||
source: mismatched_types::<Self::Database, T>(&actual_ty),
|
||||
source: mismatched_types::<Self::Database, T>(&ty),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,15 +31,13 @@ pub struct SqliteTypeInfo(pub(crate) DataType);
|
||||
|
||||
impl Display for SqliteTypeInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
f.pad(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeInfo for SqliteTypeInfo {}
|
||||
|
||||
impl Display for DataType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
impl TypeInfo for SqliteTypeInfo {
|
||||
fn name(&self) -> &str {
|
||||
match self.0 {
|
||||
DataType::Text => "TEXT",
|
||||
DataType::Float => "FLOAT",
|
||||
DataType::Blob => "BLOB",
|
||||
@ -49,7 +47,7 @@ impl Display for DataType {
|
||||
// non-standard extensions
|
||||
DataType::Bool => "BOOLEAN",
|
||||
DataType::Int64 => "BIGINT",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,10 +20,6 @@ impl<'q> Encode<'q, Sqlite> for bool {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for bool {
|
||||
fn accepts(_ty: &SqliteTypeInfo) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<bool, BoxDynError> {
|
||||
Ok(value.int() != 0)
|
||||
}
|
||||
|
||||
@ -22,10 +22,6 @@ impl<'q> Encode<'q, Sqlite> for &'q [u8] {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for &'r [u8] {
|
||||
fn accepts(_ty: &SqliteTypeInfo) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(value.blob())
|
||||
}
|
||||
@ -52,10 +48,6 @@ impl<'q> Encode<'q, Sqlite> for Vec<u8> {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for Vec<u8> {
|
||||
fn accepts(_ty: &SqliteTypeInfo) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(value.blob().to_owned())
|
||||
}
|
||||
|
||||
@ -20,10 +20,6 @@ impl<'q> Encode<'q, Sqlite> for f32 {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for f32 {
|
||||
fn accepts(_ty: &SqliteTypeInfo) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<f32, BoxDynError> {
|
||||
Ok(value.double() as f32)
|
||||
}
|
||||
@ -44,10 +40,6 @@ impl<'q> Encode<'q, Sqlite> for f64 {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for f64 {
|
||||
fn accepts(_ty: &SqliteTypeInfo) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<f64, BoxDynError> {
|
||||
Ok(value.double())
|
||||
}
|
||||
|
||||
@ -20,10 +20,6 @@ impl<'q> Encode<'q, Sqlite> for i32 {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for i32 {
|
||||
fn accepts(_ty: &SqliteTypeInfo) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(value.int())
|
||||
}
|
||||
@ -44,10 +40,6 @@ impl<'q> Encode<'q, Sqlite> for i64 {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for i64 {
|
||||
fn accepts(_ty: &SqliteTypeInfo) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
Ok(value.int64())
|
||||
}
|
||||
|
||||
@ -19,10 +19,6 @@
|
||||
//! a potentially `NULL` value from SQLite.
|
||||
//!
|
||||
|
||||
// NOTE: all types are compatible with all other types in SQLite
|
||||
// so we explicitly opt-out of runtime type assertions by returning [true] for
|
||||
// all implementations of [Decode::accepts]
|
||||
|
||||
mod bool;
|
||||
mod bytes;
|
||||
mod float;
|
||||
|
||||
@ -22,10 +22,6 @@ impl<'q> Encode<'q, Sqlite> for &'q str {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for &'r str {
|
||||
fn accepts(_ty: &SqliteTypeInfo) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
value.text()
|
||||
}
|
||||
@ -52,10 +48,6 @@ impl<'q> Encode<'q, Sqlite> for String {
|
||||
}
|
||||
|
||||
impl<'r> Decode<'r, Sqlite> for String {
|
||||
fn accepts(_ty: &SqliteTypeInfo) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
|
||||
value.text().map(ToOwned::to_owned)
|
||||
}
|
||||
|
||||
@ -1,10 +1,6 @@
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
/// Provides information about a SQL type for the database driver.
|
||||
///
|
||||
/// Currently this only exposes type equality rules that should roughly match the interpretation
|
||||
/// in a given database (e.g., in PostgreSQL `VARCHAR` and `TEXT` are roughly equivalent
|
||||
/// apart from storage).
|
||||
pub trait TypeInfo: Debug + Display + Clone + PartialEq<Self> {
|
||||
/// Returns the database system name of the type. Length specifiers should not be included.
|
||||
/// Common type names are `VARCHAR`, `TEXT`, or `INT`. Type names should be uppercase. They
|
||||
|
||||
@ -34,6 +34,10 @@ where
|
||||
fn type_info() -> DB::TypeInfo {
|
||||
<Json<Self> as Type<DB>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &DB::TypeInfo) -> bool {
|
||||
<Json<Self> as Type<DB>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, DB> Encode<'q, DB> for JsonValue
|
||||
@ -53,10 +57,6 @@ where
|
||||
Json<Self>: Decode<'r, DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn accepts(ty: &DB::TypeInfo) -> bool {
|
||||
<Json<Self> as Decode<DB>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> {
|
||||
<Json<Self> as Decode<DB>>::decode(value).map(|item| item.0)
|
||||
}
|
||||
@ -70,6 +70,10 @@ where
|
||||
fn type_info() -> DB::TypeInfo {
|
||||
<Json<&Self> as Type<DB>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &DB::TypeInfo) -> bool {
|
||||
<Json<&Self> as Type<DB>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
// We don't have to implement Encode for JsonRawValue because that's covered by the default
|
||||
@ -80,10 +84,6 @@ where
|
||||
Json<Self>: Decode<'r, DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn accepts(ty: &DB::TypeInfo) -> bool {
|
||||
<Json<Self> as Decode<DB>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> {
|
||||
<Json<Self> as Decode<DB>>::decode(value).map(|item| item.0)
|
||||
}
|
||||
|
||||
@ -48,22 +48,45 @@ pub use json::Json;
|
||||
|
||||
/// Indicates that a SQL type is supported for a database.
|
||||
pub trait Type<DB: Database> {
|
||||
/// Returns the canonical type information on the database for the type `T`.
|
||||
/// Returns the canonical SQL type for this Rust type.
|
||||
///
|
||||
/// When binding arguments, this is used to tell the database what is about to be sent; which,
|
||||
/// the database then uses to guide query plans. This can be overridden by `Encode::produces`.
|
||||
///
|
||||
/// A map of SQL types to Rust types is populated with this and used
|
||||
/// to determine the type that is returned from the anonymous struct type from `query!`.
|
||||
fn type_info() -> DB::TypeInfo;
|
||||
|
||||
/// Determines if this Rust type is compatible with the given SQL type.
|
||||
///
|
||||
/// When decoding values from a row, this method is checked to determine if we should continue
|
||||
/// or raise a runtime type mismatch error.
|
||||
///
|
||||
/// When binding arguments with `query!` or `query_as!`, this method is consulted to determine
|
||||
/// if the Rust type is acceptable.
|
||||
fn compatible(ty: &DB::TypeInfo) -> bool {
|
||||
*ty == Self::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
// for references, the underlying SQL type is identical
|
||||
impl<T: ?Sized + Type<DB>, DB: Database> Type<DB> for &'_ T {
|
||||
#[inline]
|
||||
fn type_info() -> DB::TypeInfo {
|
||||
<T as Type<DB>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &DB::TypeInfo) -> bool {
|
||||
<T as Type<DB>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
// for optionals, the underlying SQL type is identical
|
||||
impl<T: Type<DB>, DB: Database> Type<DB> for Option<T> {
|
||||
#[inline]
|
||||
fn type_info() -> DB::TypeInfo {
|
||||
<T as Type<DB>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &DB::TypeInfo) -> bool {
|
||||
<T as Type<DB>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ use std::borrow::Cow;
|
||||
use crate::database::{Database, HasValueRef};
|
||||
use crate::decode::Decode;
|
||||
use crate::error::{mismatched_types, Error};
|
||||
use crate::types::Type;
|
||||
|
||||
/// An owned value from the database.
|
||||
pub trait Value {
|
||||
@ -30,7 +31,7 @@ pub trait Value {
|
||||
#[inline]
|
||||
fn decode<'r, T>(&'r self) -> T
|
||||
where
|
||||
T: Decode<'r, Self::Database>,
|
||||
T: Decode<'r, Self::Database> + Type<Self::Database>,
|
||||
{
|
||||
self.try_decode::<T>().unwrap()
|
||||
}
|
||||
@ -64,14 +65,12 @@ pub trait Value {
|
||||
#[inline]
|
||||
fn try_decode<'r, T>(&'r self) -> Result<T, Error>
|
||||
where
|
||||
T: Decode<'r, Self::Database>,
|
||||
T: Decode<'r, Self::Database> + Type<Self::Database>,
|
||||
{
|
||||
if !self.is_null() {
|
||||
if let Some(actual_ty) = self.type_info() {
|
||||
if !T::accepts(&actual_ty) {
|
||||
return Err(Error::Decode(mismatched_types::<Self::Database, T>(
|
||||
&actual_ty,
|
||||
)));
|
||||
if let Some(ty) = self.type_info() {
|
||||
if !T::compatible(&ty) {
|
||||
return Err(Error::Decode(mismatched_types::<Self::Database, T>(&ty)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ macro_rules! impl_database_ext {
|
||||
)*
|
||||
$(
|
||||
$(#[$meta])?
|
||||
_ if <$ty as sqlx_core::decode::Decode<$database>>::accepts(&info) => Some(input_ty!($ty $(, $input)?)),
|
||||
_ if <$ty as sqlx_core::types::Type<$database>>::compatible(info) => Some(input_ty!($ty $(, $input)?)),
|
||||
)*
|
||||
_ => None
|
||||
}
|
||||
@ -67,7 +67,7 @@ macro_rules! impl_database_ext {
|
||||
)*
|
||||
$(
|
||||
$(#[$meta])?
|
||||
_ if <$ty as sqlx_core::decode::Decode<$database>>::accepts(&info) => return Some(stringify!($ty)),
|
||||
_ if <$ty as sqlx_core::types::Type<$database>>::compatible(info) => return Some(stringify!($ty)),
|
||||
)*
|
||||
_ => None
|
||||
}
|
||||
|
||||
@ -71,10 +71,6 @@ fn expand_derive_decode_transparent(
|
||||
|
||||
let tts = quote!(
|
||||
impl #impl_generics sqlx::decode::Decode<'r, DB> for #ident #ty_generics #where_clause {
|
||||
fn accepts(ty: &DB::TypeInfo) -> bool {
|
||||
<#ty as sqlx::decode::Decode<'r, DB>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: <DB as sqlx::database::HasValueRef<'r>>::ValueRef) -> std::result::Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
|
||||
<#ty as sqlx::decode::Decode<'r, DB>>::decode(value).map(Self)
|
||||
}
|
||||
@ -104,10 +100,6 @@ fn expand_derive_decode_weak_enum(
|
||||
|
||||
Ok(quote!(
|
||||
impl<'r, DB: sqlx::Database> sqlx::decode::Decode<'r, DB> for #ident where #repr: sqlx::decode::Decode<'r, DB> {
|
||||
fn accepts(ty: &DB::TypeInfo) -> bool {
|
||||
<#repr as sqlx::decode::Decode<'r, DB>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: <DB as sqlx::database::HasValueRef<'r>>::ValueRef) -> std::result::Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
|
||||
let value = <#repr as sqlx::decode::Decode<'r, DB>>::decode(value)?;
|
||||
|
||||
@ -159,10 +151,6 @@ fn expand_derive_decode_strong_enum(
|
||||
if cfg!(feature = "mysql") {
|
||||
tts.extend(quote!(
|
||||
impl<'r> sqlx::decode::Decode<'r, sqlx::mysql::MySql> for #ident {
|
||||
fn accepts(ty: &sqlx::mysql::MySqlTypeInfo) -> bool {
|
||||
ty == sqlx::mysql::MySqlTypeInfo::__enum()
|
||||
}
|
||||
|
||||
fn decode(value: sqlx::mysql::MySqlValueRef<'r>) -> std::result::Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
|
||||
let value = <&'r str as sqlx::decode::Decode<'r, sqlx::mysql::MySql>>::decode(value)?;
|
||||
|
||||
@ -175,10 +163,6 @@ fn expand_derive_decode_strong_enum(
|
||||
if cfg!(feature = "postgres") {
|
||||
tts.extend(quote!(
|
||||
impl<'r> sqlx::decode::Decode<'r, sqlx::postgres::Postgres> for #ident {
|
||||
fn accepts(ty: &sqlx::postgres::PgTypeInfo) -> bool {
|
||||
*ty == <#ident as sqlx::Type<sqlx::postgres::Postgres>>::type_info()
|
||||
}
|
||||
|
||||
fn decode(value: sqlx::postgres::PgValueRef<'r>) -> std::result::Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
|
||||
let value = <&'r str as sqlx::decode::Decode<'r, sqlx::postgres::Postgres>>::decode(value)?;
|
||||
|
||||
@ -191,10 +175,6 @@ fn expand_derive_decode_strong_enum(
|
||||
if cfg!(feature = "sqlite") {
|
||||
tts.extend(quote!(
|
||||
impl<'r> sqlx::decode::Decode<'r, sqlx::sqlite::Sqlite> for #ident {
|
||||
fn accepts(ty: &sqlx::sqlite::SqliteTypeInfo) -> bool {
|
||||
<&str as sqlx::decode::Decode<'r, DB>>::accepts(ty)
|
||||
}
|
||||
|
||||
fn decode(value: sqlx::sqlite::SqliteValueRef<'r>) -> std::result::Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
|
||||
let value = <&'r str as sqlx::decode::Decode<'r, sqlx::sqlite::Sqlite>>::decode(value)?;
|
||||
|
||||
@ -250,10 +230,6 @@ fn expand_derive_decode_struct(
|
||||
|
||||
tts.extend(quote!(
|
||||
impl #impl_generics sqlx::decode::Decode<'r, sqlx::Postgres> for #ident #ty_generics #where_clause {
|
||||
fn accepts(ty: &sqlx::postgres::PgTypeInfo) -> bool {
|
||||
*ty == <Self as sqlx::Type<sqlx::postgres::Postgres>>::type_info()
|
||||
}
|
||||
|
||||
fn decode(value: sqlx::postgres::PgValueRef<'r>) -> std::result::Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
|
||||
let mut decoder = sqlx::postgres::types::PgRecordDecoder::new(value)?;
|
||||
|
||||
|
||||
@ -129,6 +129,10 @@ fn expand_derive_has_sql_type_strong_enum(
|
||||
fn type_info() -> sqlx::mysql::MySqlTypeInfo {
|
||||
sqlx::mysql::MySqlTypeInfo::__enum()
|
||||
}
|
||||
|
||||
fn compatible(ty: &sqlx::mysql::MySqlTypeInfo) -> bool {
|
||||
ty == sqlx::mysql::MySqlTypeInfo::__enum()
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
@ -151,6 +155,10 @@ fn expand_derive_has_sql_type_strong_enum(
|
||||
fn type_info() -> sqlx::sqlite::SqliteTypeInfo {
|
||||
<str as sqlx::Type<sqlx::Sqlite>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &sqlx::sqlite::SqliteTypeInfo) -> bool {
|
||||
<&str as sqlx::types::Type<sqlx::sqlite::Sqlite>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ pub mod encode {
|
||||
|
||||
/// Provides [`Decode`](decode/trait.Decode.html) for decoding values from the database.
|
||||
pub mod decode {
|
||||
pub use sqlx_core::decode::{Decode, Result};
|
||||
pub use sqlx_core::decode::Decode;
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
#[doc(hidden)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user