sqlx: MySQL: rust enum not compatible with SQL ENUM
Hi,
I’ve hit an issue with ENUMs in MySQL and, after reviewing docs and issues here, I can’t find anything that helps with this particular problem. Basically, the error says that type ‘ENUM’ is not compatible with type ‘ENUM’:
error occurred while decoding column \"kind\": mismatched types; Rust type `...::IssueKind` (as SQL type `ENUM`) is not compatible with SQL type `ENUM`
The relevant (abridged) code is this:
#[derive(sqlx::FromRow)]
pub struct Issue {
pub id: IssueId,
pub env_id: EnvId,
pub date: Date,
pub kind: IssueKind,
}
#[derive(sqlx::Type)]
#[sqlx(rename_all = "snake_case")]
pub enum IssueKind {
TypeOne,
TypeTwo,
TypeThree,
}
pub async fn find_by_env_date(env_id: EnvId, date: Date) -> Result<Vec<Issue>> {
Ok(
sqlx::query_as::<_, Issue>("SELECT * FROM `issue` WHERE `env_id` = ? AND `date` = ?")
.bind(&env_id)
.bind(&date)
.fetch_all(&ConnPool::clone())
.await?,
)
}
The DB schema looks like this (again, abridged):
CREATE TABLE `issue` (
`id` int(11) UNSIGNED NOT NULL,
`env_id` int(11) UNSIGNED NOT NULL,
`date` date NOT NULL,
`kind` enum('type_one','type_two','type_three') NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
This was happening with v0.5.1 and is still happening after upgrade to v.0.5.4
I’m guessing that I have hit a fairly obscure issue, or that I’m missing something pretty basic.
Any help would be appreciated.
Regards, Alfonso
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 10
- Comments: 17
Running into this issue in 2023:
Version:
0.6.2Has any progress been made on this issue?
@mitchrmiller 's discovery inspired me to come up with a relatively quiet workaround.
It could also be a macro.
I did some more digging and this is what I can see.
The DB server (or at least my version) is sending a
ColumnDefinitionfor the ENUM column withtype=String,enum_flag=trueandchar_set=utf8- the default for DB/table (these are not actual field names). In sqlx, the reference definition of an Enum type (inMySqlTypeInfo::__enum()) istype=Enum,binary_flag=true,char_set=63(binary). Obviously, when they are compared for compatibility it fails and throws the error.The reason that the error says “ENUM is not compatible with ENUM” is that the method that translates the column type into its name (
ColumnType::name()) returns “ENUM” iftype=EnumOR iftype=String and enum_flag=true. So both the reference Enum column type and the one coming from the server yield “ENUM” as the name, even though they have different definitions.Unfortunately, the method that decides if the two types are compatible checks if the two
typefields are the same, which in this case are not. So the two types have a different definition but the same name. The compatibility check fails - though it shouldn’t - and it prints that two types with the same name are not compatible.I hope this helps somebody decide what’s the correct fix for this issue. Unless my server is a very peculiar version of MariaDB, I would be surprised if this use case is working for anybody.
Cheers, Alfonso
Thank you for the workaround @amitavaghosh1
In fact, if you use the macro version (
query_as!), and set the right types for your enums, the error doesn’t come up. My guess is that it uses a different logic to check compatibility between data from DB and rust types - since the check is at compile time.However, the issue remains that, for the
query_as()method, sqlx fails to correctly detect that the data coming from the DB is an enum and that it matches the type in the struct.Regards, Alfonso
Another workaround is just to cast it as signed:
It’s not a great solution, but we use a mapper function with
try_get_uncheckedto get around this, and rely on integration tests to catch mistakes 😬For example: