diff --git a/sqlx-core/src/any/type_info.rs b/sqlx-core/src/any/type_info.rs index a056f3bd..3d3c914f 100644 --- a/sqlx-core/src/any/type_info.rs +++ b/sqlx-core/src/any/type_info.rs @@ -33,6 +33,22 @@ pub(crate) enum AnyTypeInfoKind { } impl TypeInfo for AnyTypeInfo { + fn is_null(&self) -> bool { + match &self.0 { + #[cfg(feature = "postgres")] + AnyTypeInfoKind::Postgres(ty) => ty.is_null(), + + #[cfg(feature = "mysql")] + AnyTypeInfoKind::MySql(ty) => ty.is_null(), + + #[cfg(feature = "sqlite")] + AnyTypeInfoKind::Sqlite(ty) => ty.is_null(), + + #[cfg(feature = "mssql")] + AnyTypeInfoKind::Mssql(ty) => ty.is_null(), + } + } + fn name(&self) -> &str { match &self.0 { #[cfg(feature = "postgres")] diff --git a/sqlx-core/src/mssql/type_info.rs b/sqlx-core/src/mssql/type_info.rs index 4d903fc2..2fb12492 100644 --- a/sqlx-core/src/mssql/type_info.rs +++ b/sqlx-core/src/mssql/type_info.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use crate::mssql::protocol::type_info::TypeInfo as ProtocolTypeInfo; +use crate::mssql::protocol::type_info::{TypeInfo as ProtocolTypeInfo, DataType}; use crate::type_info::TypeInfo; #[derive(Debug, Clone, PartialEq, Eq)] @@ -8,6 +8,10 @@ use crate::type_info::TypeInfo; pub struct MssqlTypeInfo(pub(crate) ProtocolTypeInfo); impl TypeInfo for MssqlTypeInfo { + fn is_null(&self) -> bool { + matches!(self.0.ty, DataType::Null) + } + fn name(&self) -> &str { self.0.name() } diff --git a/sqlx-core/src/mysql/type_info.rs b/sqlx-core/src/mysql/type_info.rs index 29542874..4111d81c 100644 --- a/sqlx-core/src/mysql/type_info.rs +++ b/sqlx-core/src/mysql/type_info.rs @@ -60,6 +60,10 @@ impl Display for MySqlTypeInfo { } impl TypeInfo for MySqlTypeInfo { + fn is_null(&self) -> bool { + matches!(self.r#type, ColumnType::Null) + } + fn name(&self) -> &str { self.r#type.name(self.char_set, self.flags) } diff --git a/sqlx-core/src/postgres/type_info.rs b/sqlx-core/src/postgres/type_info.rs index 10bc8557..7f33a25e 100644 --- a/sqlx-core/src/postgres/type_info.rs +++ b/sqlx-core/src/postgres/type_info.rs @@ -741,6 +741,10 @@ impl TypeInfo for PgTypeInfo { fn name(&self) -> &str { self.0.display_name() } + + fn is_null(&self) -> bool { + false + } } impl PartialEq for PgCustomType { diff --git a/sqlx-core/src/postgres/types/record.rs b/sqlx-core/src/postgres/types/record.rs index b58ab72c..758f3927 100644 --- a/sqlx-core/src/postgres/types/record.rs +++ b/sqlx-core/src/postgres/types/record.rs @@ -6,6 +6,7 @@ use crate::error::{mismatched_types, BoxDynError}; use crate::postgres::type_info::{PgType, PgTypeKind}; use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres}; use crate::types::Type; +use crate::type_info::TypeInfo; #[doc(hidden)] pub struct PgRecordEncoder<'a> { @@ -126,7 +127,7 @@ impl<'r> PgRecordDecoder<'r> { self.ind += 1; if let Some(ty) = &element_type_opt { - if !T::compatible(ty) { + if !ty.is_null() && !T::compatible(ty) { return Err(mismatched_types::(ty)); } } diff --git a/sqlx-core/src/row.rs b/sqlx-core/src/row.rs index 488cff30..12837323 100644 --- a/sqlx-core/src/row.rs +++ b/sqlx-core/src/row.rs @@ -4,6 +4,7 @@ use crate::database::{Database, HasValueRef}; use crate::decode::Decode; use crate::error::{mismatched_types, Error}; use crate::types::Type; +use crate::type_info::TypeInfo; use crate::value::ValueRef; /// A type that can be used to index into a [`Row`]. @@ -171,7 +172,7 @@ pub trait Row: private_row::Sealed + Unpin + Send + Sync + 'static { if !value.is_null() { let ty = value.type_info(); - if !T::compatible(&ty) { + if !ty.is_null() && !T::compatible(&ty) { return Err(Error::ColumnDecode { index: format!("{:?}", index), source: mismatched_types::(&ty), diff --git a/sqlx-core/src/sqlite/type_info.rs b/sqlx-core/src/sqlite/type_info.rs index c983d693..70695ee6 100644 --- a/sqlx-core/src/sqlite/type_info.rs +++ b/sqlx-core/src/sqlite/type_info.rs @@ -40,6 +40,10 @@ impl Display for SqliteTypeInfo { } impl TypeInfo for SqliteTypeInfo { + fn is_null(&self) -> bool { + matches!(self.0, DataType::Null) + } + fn name(&self) -> &str { match self.0 { DataType::Null => "NULL", diff --git a/sqlx-core/src/type_info.rs b/sqlx-core/src/type_info.rs index 2ce59b07..93bfd3d3 100644 --- a/sqlx-core/src/type_info.rs +++ b/sqlx-core/src/type_info.rs @@ -2,6 +2,8 @@ use std::fmt::{Debug, Display}; /// Provides information about a SQL type for the database driver. pub trait TypeInfo: Debug + Display + Clone + PartialEq { + fn is_null(&self) -> bool; + /// 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 /// should be a rough approximation of how they are written in SQL in the given database. diff --git a/sqlx-core/src/value.rs b/sqlx-core/src/value.rs index f800814d..2f117d59 100644 --- a/sqlx-core/src/value.rs +++ b/sqlx-core/src/value.rs @@ -2,6 +2,7 @@ use crate::database::{Database, HasValueRef}; use crate::decode::Decode; use crate::error::{mismatched_types, Error}; use crate::types::Type; +use crate::type_info::TypeInfo; /// An owned value from the database. pub trait Value { @@ -65,7 +66,7 @@ pub trait Value { if !self.is_null() { let ty = self.type_info(); - if !T::compatible(&ty) { + if !ty.is_null() && !T::compatible(&ty) { return Err(Error::Decode(mismatched_types::(&ty))); } }