From 27b0ebe399e23a507ea909f818c9f0bc9d510b20 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Fri, 1 May 2026 10:30:31 +0900 Subject: [PATCH] breaking(any+mysql): correctly convert text and blob types to `AnyTypeInfo` --- sqlx-mysql/src/any.rs | 11 +++----- tests/mysql/mysql.rs | 61 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/sqlx-mysql/src/any.rs b/sqlx-mysql/src/any.rs index b0950e0b41..57c895826f 100644 --- a/sqlx-mysql/src/any.rs +++ b/sqlx-mysql/src/any.rs @@ -16,6 +16,7 @@ use sqlx_core::database::Database; use sqlx_core::executor::Executor; use sqlx_core::sql_str::SqlStr; use sqlx_core::transaction::TransactionManager; +use sqlx_core::types::Type; use std::{future, pin::pin}; sqlx_core::declare_driver_with_optional_migrate!(DRIVER = MySql); @@ -164,13 +165,9 @@ impl<'a> TryFrom<&'a MySqlTypeInfo> for AnyTypeInfo { ColumnType::LongLong => AnyTypeInfoKind::BigInt, ColumnType::Float => AnyTypeInfoKind::Real, ColumnType::Double => AnyTypeInfoKind::Double, - ColumnType::Blob - | ColumnType::TinyBlob - | ColumnType::MediumBlob - | ColumnType::LongBlob => AnyTypeInfoKind::Blob, - ColumnType::String | ColumnType::VarString | ColumnType::VarChar => { - AnyTypeInfoKind::Text - } + // Checks for any applicable type and compatible collations + _ if >::compatible(type_info) => AnyTypeInfoKind::Text, + _ if <[u8] as Type>::compatible(type_info) => AnyTypeInfoKind::Blob, _ => { return Err(sqlx_core::Error::AnyDriverError( format!("Any driver does not support MySql type {type_info:?}").into(), diff --git a/tests/mysql/mysql.rs b/tests/mysql/mysql.rs index 8d89d74c70..5374e651c8 100644 --- a/tests/mysql/mysql.rs +++ b/tests/mysql/mysql.rs @@ -3,6 +3,7 @@ use futures_util::TryStreamExt; use sqlx::mysql::{MySql, MySqlConnection, MySqlPool, MySqlPoolOptions, MySqlRow}; use sqlx::{Column, Connection, Executor, Row, SqlSafeStr, Statement, TypeInfo}; use sqlx_core::connection::ConnectOptions; +use sqlx_core::types::Type; use sqlx_mysql::MySqlConnectOptions; use sqlx_test::{new, setup_if_needed}; use std::env; @@ -666,3 +667,63 @@ async fn it_can_name_columns_issue_2206() -> anyhow::Result<()> { Ok(()) } + +#[cfg(feature = "any")] +#[sqlx_macros::test] +async fn any_blob_conversions() -> anyhow::Result<()> { + use sqlx::Any; + use sqlx::Decode; + use sqlx::ValueRef; + + sqlx::any::install_default_drivers(); + + let mut conn = new::().await?; + + sqlx::raw_sql( + r#" + CREATE TEMPORARY TABLE any_blob_conversions( + id INTEGER PRIMARY KEY, + regular_text TEXT NOT NULL, + text_binary TEXT COLLATE "utf8mb4_bin" NOT NULL, + binary_blob BLOB NOT NULL + ); + + INSERT INTO any_blob_conversions(id, regular_text, text_binary, binary_blob) + VALUES (1, 'Hello, world!', 'Lorem ipsum dolor sit amet', X'01020304DEADBEEF'); + "#, + ) + .execute(&mut conn) + .await?; + + let row = sqlx::query("SELECT * FROM any_blob_conversions") + .fetch_one(&mut conn) + .await?; + + let id = row.try_get_raw("id")?; + let regular_text = row.try_get_raw("regular_text")?; + let text_binary = row.try_get_raw("text_binary")?; + let binary_blob = row.try_get_raw("binary_blob")?; + + assert_eq!(*id.type_info(), >::type_info()); + assert_eq!(>::decode(id).unwrap(), 1); + + assert_eq!(*regular_text.type_info(), >::type_info()); + assert_eq!( + <&str as Decode>::decode(regular_text).unwrap(), + "Hello, world!" + ); + + assert_eq!(*text_binary.type_info(), >::type_info()); + assert_eq!( + <&str as Decode>::decode(text_binary).unwrap(), + "Lorem ipsum dolor sit amet" + ); + + assert_eq!(*binary_blob.type_info(), <[u8] as Type>::type_info()); + assert_eq!( + <&[u8] as Decode>::decode(binary_blob).unwrap(), + [0x01, 0x02, 0x03, 0x04, 0xDE, 0xAD, 0xBE, 0xEF], + ); + + Ok(()) +}