launchbadge / sqlx

🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, and SQLite.
Apache License 2.0
13.47k stars 1.28k forks source link

Allow ignoring enum repr #2396

Open fenhl opened 1 year ago

fenhl commented 1 year ago

Is your feature request related to a problem? Please describe. I have an enum that needs to be represented as a PostgreSQL enum in the database but also needs to be #[repr(u8)] for FFI purposes. Currently this is not possible with #[derive(sqlx::Type)] because the derive macro sees the repr attribute and changes the database representation as well.

Describe the solution you'd like A #[sqlx(repr(default))] attribute that can be used to have sqlx ignore the #[repr(u8)] attribute and instead use the default textual representation, which works with PostgreSQL enums.

Describe alternatives you've considered As a workaround, I defined two copies of the enum type, one with #[derive(sqlx::Type)] and one with #[repr(u8)], and am converting between the two as necessary. Another option would be to add attributes to abstract away conversions like this, similar to serde's #[serde(from = "...", into = "...")].

bonsairobo commented 2 weeks ago

I have the same issue.

bonsairobo commented 2 weeks ago

I think this is a valid workaround (at least for sqlite):

impl sqlx::Type<sqlx::Sqlite> for MyEnum {
    fn type_info() -> SqliteTypeInfo {
        <&'static str as sqlx::Type>::type_info()
    }
}
impl<'a> sqlx::Encode<'a, sqlx::Sqlite> for MyEnum {
    fn encode_by_ref(
        &self,
        buf: &mut <sqlx::Sqlite as sqlx::database::HasArguments<'a>>::ArgumentBuffer,
    ) -> sqlx::encode::IsNull {
        self.name().encode_by_ref(buf)
    }
}
impl<'a> sqlx::Decode<'a, sqlx::Sqlite> for MyEnum {
    fn decode(
        value: <sqlx::Sqlite as sqlx::database::HasValueRef<'a>>::ValueRef,
    ) -> Result<Self, sqlx::error::BoxDynError> {
        <&'static str as sqlx::Decode>::decode(value)
    }
}

You just need to provide a MyEnum::name method.

I'm less sure about whether this can work with Postgres enums.