mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-29 21:00:54 +00:00
mysql: tweak type equivalence rules to try and support both rust best practices but still be compatible with the loose types of mysql
This commit is contained in:
parent
50928b06b8
commit
ad2cf1676f
@ -95,6 +95,16 @@ impl MySqlTypeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub const fn r#enum() -> Self {
|
||||
Self {
|
||||
id: TypeId::ENUM,
|
||||
is_unsigned: false,
|
||||
is_binary: false,
|
||||
char_set: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_nullable_column_def(def: &ColumnDefinition) -> Self {
|
||||
Self {
|
||||
id: def.type_id,
|
||||
@ -157,6 +167,51 @@ impl Display for MySqlTypeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<MySqlTypeInfo> for MySqlTypeInfo {
|
||||
fn eq(&self, other: &MySqlTypeInfo) -> bool {
|
||||
match self.id {
|
||||
TypeId::VAR_CHAR
|
||||
| TypeId::TEXT
|
||||
| TypeId::CHAR
|
||||
| TypeId::TINY_BLOB
|
||||
| TypeId::MEDIUM_BLOB
|
||||
| TypeId::LONG_BLOB
|
||||
| TypeId::ENUM
|
||||
if (self.is_binary == other.is_binary)
|
||||
&& match other.id {
|
||||
TypeId::VAR_CHAR
|
||||
| TypeId::TEXT
|
||||
| TypeId::CHAR
|
||||
| TypeId::TINY_BLOB
|
||||
| TypeId::MEDIUM_BLOB
|
||||
| TypeId::LONG_BLOB
|
||||
| TypeId::ENUM => true,
|
||||
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if self.id.0 != other.id.0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
match self.id {
|
||||
TypeId::TINY_INT | TypeId::SMALL_INT | TypeId::INT | TypeId::BIG_INT => {
|
||||
return self.is_unsigned == other.is_unsigned;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeInfo for MySqlTypeInfo {
|
||||
fn compatible(&self, other: &Self) -> bool {
|
||||
// NOTE: MySQL is weakly typed so much of this may be surprising to a Rust developer.
|
||||
@ -190,19 +245,45 @@ impl TypeInfo for MySqlTypeInfo {
|
||||
| TypeId::TINY_BLOB
|
||||
| TypeId::MEDIUM_BLOB
|
||||
| TypeId::LONG_BLOB
|
||||
| TypeId::ENUM
|
||||
if (self.is_binary == other.is_binary)
|
||||
&& match other.id {
|
||||
TypeId::VAR_CHAR
|
||||
| TypeId::TEXT
|
||||
| TypeId::CHAR
|
||||
| TypeId::TINY_BLOB
|
||||
| TypeId::MEDIUM_BLOB
|
||||
| TypeId::LONG_BLOB
|
||||
| TypeId::ENUM => true,
|
||||
if match other.id {
|
||||
TypeId::VAR_CHAR
|
||||
| TypeId::TEXT
|
||||
| TypeId::CHAR
|
||||
| TypeId::TINY_BLOB
|
||||
| TypeId::MEDIUM_BLOB
|
||||
| TypeId::LONG_BLOB => true,
|
||||
|
||||
_ => false,
|
||||
} =>
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
true
|
||||
}
|
||||
|
||||
// Enums are considered compatible with other text/binary types
|
||||
TypeId::ENUM
|
||||
if match other.id {
|
||||
TypeId::VAR_CHAR
|
||||
| TypeId::TEXT
|
||||
| TypeId::CHAR
|
||||
| TypeId::TINY_BLOB
|
||||
| TypeId::MEDIUM_BLOB
|
||||
| TypeId::LONG_BLOB
|
||||
| TypeId::ENUM => true,
|
||||
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
true
|
||||
}
|
||||
|
||||
TypeId::VAR_CHAR
|
||||
| TypeId::TEXT
|
||||
| TypeId::CHAR
|
||||
| TypeId::TINY_BLOB
|
||||
| TypeId::MEDIUM_BLOB
|
||||
| TypeId::LONG_BLOB
|
||||
| TypeId::ENUM
|
||||
if other.id == TypeId::ENUM =>
|
||||
{
|
||||
true
|
||||
}
|
||||
@ -227,8 +308,7 @@ impl TypeInfo for MySqlTypeInfo {
|
||||
true
|
||||
}
|
||||
|
||||
// Fallback to equality of only [id] and [is_unsigned]
|
||||
_ => self.id.0 == other.id.0 && self.is_unsigned == other.is_unsigned,
|
||||
_ => self.eq(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ pub mod ipnetwork {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Json<T>(pub T);
|
||||
|
||||
pub trait TypeInfo: Debug + Display + Clone {
|
||||
pub trait TypeInfo: PartialEq<Self> + Debug + Display + Clone {
|
||||
/// Compares type information to determine if `other` is compatible at the Rust level
|
||||
/// with `self`.
|
||||
fn compatible(&self, other: &Self) -> bool;
|
||||
|
||||
@ -45,7 +45,10 @@ macro_rules! impl_database_ext {
|
||||
fn param_type_for_id(info: &Self::TypeInfo) -> Option<&'static str> {
|
||||
match () {
|
||||
$(
|
||||
// `if` statements cannot have attributes but these can
|
||||
$(#[$meta])?
|
||||
_ if <$ty as sqlx::types::Type<$database>>::type_info() == *info => Some(input_ty!($ty $(, $input)?)),
|
||||
)*
|
||||
$(
|
||||
$(#[$meta])?
|
||||
_ if sqlx::types::TypeInfo::compatible(&<$ty as sqlx::types::Type<$database>>::type_info(), &info) => Some(input_ty!($ty $(, $input)?)),
|
||||
)*
|
||||
@ -55,6 +58,10 @@ macro_rules! impl_database_ext {
|
||||
|
||||
fn return_type_for_id(info: &Self::TypeInfo) -> Option<&'static str> {
|
||||
match () {
|
||||
$(
|
||||
$(#[$meta])?
|
||||
_ if <$ty as sqlx::types::Type<$database>>::type_info() == *info => return Some(stringify!($ty)),
|
||||
)*
|
||||
$(
|
||||
$(#[$meta])?
|
||||
_ if sqlx::types::TypeInfo::compatible(&<$ty as sqlx::types::Type<$database>>::type_info(), &info) => return Some(stringify!($ty)),
|
||||
|
||||
@ -71,7 +71,7 @@ fn expand_derive_decode_transparent(
|
||||
|
||||
let tts = quote!(
|
||||
impl #impl_generics sqlx::decode::Decode<'de, DB> for #ident #ty_generics #where_clause {
|
||||
fn decode(value: <DB as sqlx::database::HasRawValue<'de>>::RawValue) -> sqlx::Result<DB, Self> {
|
||||
fn decode(value: <DB as sqlx::value::HasRawValue<'de>>::RawValue) -> sqlx::Result<DB, Self> {
|
||||
<#ty as sqlx::decode::Decode<'de, DB>>::decode(value).map(Self)
|
||||
}
|
||||
}
|
||||
@ -100,7 +100,7 @@ fn expand_derive_decode_weak_enum(
|
||||
|
||||
Ok(quote!(
|
||||
impl<'de, DB: sqlx::Database> sqlx::decode::Decode<'de, DB> for #ident where #repr: sqlx::decode::Decode<'de, DB> {
|
||||
fn decode(value: <DB as sqlx::database::HasRawValue<'de>>::RawValue) -> sqlx::Result<DB, Self> {
|
||||
fn decode(value: <DB as sqlx::value::HasRawValue<'de>>::RawValue) -> sqlx::Result<DB, Self> {
|
||||
let value = <#repr as sqlx::decode::Decode<'de, DB>>::decode(value)?;
|
||||
|
||||
match value {
|
||||
@ -140,7 +140,7 @@ fn expand_derive_decode_strong_enum(
|
||||
|
||||
Ok(quote!(
|
||||
impl<'de, DB: sqlx::Database> sqlx::decode::Decode<'de, DB> for #ident where &'de str: sqlx::decode::Decode<'de, DB> {
|
||||
fn decode(value: <DB as sqlx::database::HasRawValue<'de>>::RawValue) -> sqlx::Result<DB, Self> {
|
||||
fn decode(value: <DB as sqlx::value::HasRawValue<'de>>::RawValue) -> sqlx::Result<DB, Self> {
|
||||
let value = <&'de str as sqlx::decode::Decode<'de, DB>>::decode(value)?;
|
||||
match value {
|
||||
#(#value_arms)*
|
||||
@ -195,7 +195,7 @@ fn expand_derive_decode_struct(
|
||||
|
||||
tts.extend(quote!(
|
||||
impl #impl_generics sqlx::decode::Decode<'de, sqlx::Postgres> for #ident #ty_generics #where_clause {
|
||||
fn decode(value: <sqlx::Postgres as sqlx::database::HasRawValue<'de>>::RawValue) -> sqlx::Result<sqlx::Postgres, Self> {
|
||||
fn decode(value: <sqlx::Postgres as sqlx::value::HasRawValue<'de>>::RawValue) -> sqlx::Result<sqlx::Postgres, Self> {
|
||||
let mut decoder = sqlx::postgres::types::raw::PgRecordDecoder::new(value)?;
|
||||
|
||||
#(#reads)*
|
||||
|
||||
@ -110,9 +110,7 @@ fn expand_derive_has_sql_type_strong_enum(
|
||||
tts.extend(quote!(
|
||||
impl sqlx::Type< sqlx::MySql > for #ident {
|
||||
fn type_info() -> sqlx::mysql::MySqlTypeInfo {
|
||||
// This is really fine, MySQL is loosely typed and
|
||||
// we don't nede to be specific here
|
||||
<str as sqlx::Type<sqlx::MySql>>::type_info()
|
||||
sqlx::mysql::MySqlTypeInfo::r#enum()
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
pub use sqlx_core::arguments;
|
||||
pub use sqlx_core::connection::{Connect, Connection};
|
||||
pub use sqlx_core::cursor::Cursor;
|
||||
pub use sqlx_core::cursor::{self, Cursor};
|
||||
pub use sqlx_core::database::{self, Database};
|
||||
pub use sqlx_core::executor::{self, Execute, Executor};
|
||||
pub use sqlx_core::pool::{self, Pool};
|
||||
@ -10,6 +10,7 @@ pub use sqlx_core::query::{self, query, Query};
|
||||
pub use sqlx_core::query_as::{query_as, QueryAs};
|
||||
pub use sqlx_core::row::{self, FromRow, Row};
|
||||
pub use sqlx_core::transaction::Transaction;
|
||||
pub use sqlx_core::value;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use sqlx_core::describe;
|
||||
|
||||
@ -58,3 +58,17 @@ async fn test_query_as_raw() -> anyhow::Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn test_query_bytes() -> anyhow::Result<()> {
|
||||
let mut conn = new::<MySql>().await?;
|
||||
|
||||
let rec = sqlx::query!("SELECT X'01AF' as _1")
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert_eq!(rec._1, &[0x01_u8, 0xAF_u8]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user