如何在Rust SQLx中为Postgres CHAR(1)列使用&str或枚举类型?
解决方案
1. 切换为Option<&str>类型(最简单高效)
直接将sex字段改为Option<&str>,无需额外实现任何trait,SQLx可直接将Postgres的CHAR(1)类型(包括null值)映射到该类型。注意结构体需要添加生命周期参数:
#[derive(sqlx::FromRow)] struct PersonSql<'r> { // 其他字段... sex: Option<&'r str>, }
2. 自定义Sex枚举类型(类型安全最优)
若想要类型安全的枚举,需修正SQLx的类型映射配置,解决之前的类型不兼容问题:
步骤1:定义枚举并配置sqlx::Type
指定枚举对应的Postgres类型为char(而非VARCHAR),并为每个枚举值绑定对应的SQL值:
#[derive(sqlx::Type, Debug, Clone, Copy, PartialEq, Eq)] #[sqlx(type_name = "char")] // 匹配Postgres的CHAR(1)类型 enum Sex { #[sqlx(rename = "M")] Male, #[sqlx(rename = "F")] Female, }
步骤2:结构体中使用Option<Sex>
由于数据库中sex允许为null,结构体字段需声明为Option<Sex>:
#[derive(sqlx::FromRow)] struct PersonSql { // 其他字段... sex: Option<Sex>, }
(可选)手动实现FromSql和ToSql(更灵活)
若自动派生的sqlx::Type仍有问题,可手动实现trait完全控制转换逻辑:
use sqlx::{postgres::{PgArgumentValue, PgTypeInfo, PgValueRef}, FromSql, ToSql, Error}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Sex { Male, Female, } impl<'a> FromSql<'a, sqlx::Postgres> for Sex { fn from_sql(value: PgValueRef<'a>) -> Result<Self, Error> { let s: &str = FromSql::from_sql(value)?; match s { "M" => Ok(Sex::Male), "F" => Ok(Sex::Female), _ => Err(Error::Decode(format!("无效的性别值: {}", s).into())), } } fn from_sql_nullable(value: PgValueRef<'a>) -> Result<Option<Self>, Error> { if value.is_null() { Ok(None) } else { Self::from_sql(value).map(Some) } } } impl ToSql<'_, sqlx::Postgres> for Sex { fn to_sql(&self) -> Result<PgArgumentValue<'_>, Error> { let s = match self { Sex::Male => "M", Sex::Female => "F", }; Ok(PgArgumentValue::Text(s.as_bytes())) } fn accepts(ty: &PgTypeInfo) -> bool { ty.name() == "char" || ty.name() == "character" } fn to_sql_checked(&self, ty: &PgTypeInfo) -> Result<PgArgumentValue<'_>, Error> { if !Self::accepts(ty) { return Err(Error::TypeMismatch { expected: "char".into(), actual: ty.name().to_string(), }); } self.to_sql() } }
3. 使用Option<char>类型
若偏好更紧凑的char类型,需手动实现FromSql将Postgres的CHAR(1)字符串转换为Rust的char:
use sqlx::{postgres::PgValueRef, FromSql, Error}; impl<'a> FromSql<'a, sqlx::Postgres> for char { fn from_sql(value: PgValueRef<'a>) -> Result<Self, Error> { let s: &str = FromSql::from_sql(value)?; s.chars().next().ok_or_else(|| { Error::Decode("预期非空字符串以转换为char".into()) }) } fn from_sql_nullable(value: PgValueRef<'a>) -> Result<Option<Self>, Error> { if value.is_null() { Ok(None) } else { Self::from_sql(value).map(Some) } } }
之后结构体即可使用Option<char>:
#[derive(sqlx::FromRow)] struct PersonSql { // 其他字段... sex: Option<char>, }
内容的提问来源于stack exchange,提问作者billy voiderer
相关产品推荐
相关产品推荐

