actix-web+tokio-postgres插入PostgreSQL时类型转换错误求助
解决tokio-postgres插入PostgreSQL数组类型时的类型转换错误
问题描述
使用actix-web 4.4框架结合tokio-postgres向PostgreSQL的users表插入数据时,出现以下类型转换错误:
error serializing parameter 8: cannot convert between the Rust type `alloc::string::String` and the Postgres type `_text`
涉及表结构
id: text fullname: text nickname: text password: text email: text bucket: integer transactions: jsonb[] files: text[] is_verified: integer
错误根源
- 数组类型处理错误:手动将
Vec<String>(对应text[])和Vec<Purchase>(对应jsonb[])序列化为JSON字符串传入数据库,但PostgreSQL的数组类型需要接收原生Rust数组/集合类型,而非JSON格式字符串。 - INSERT字段错位:原INSERT语句包含表结构中不存在的
role字段,导致参数位置与表字段不匹配,进一步触发类型转换异常。
修复方案
- 移除手动JSON序列化步骤,直接传入对应Rust集合类型
- 修正INSERT语句的字段列表,与实际表结构完全对齐
- 利用tokio-postgres的
with-serde_json-1特性自动处理jsonb[]类型转换
修正后的代码
核心插入函数(insert_user_row)
pub async fn insert_user_row(pool: &Pool, user: &models::UserModel) -> Result<(), models::CustomError> { let client: Client = pool.get().await.map_err(|e| models::CustomError::new(&e.to_string()))?; // 修正字段列表,移除不存在的role字段,与表结构对齐 let query = "INSERT INTO public.users (id, fullname, nickname, password, email, bucket, files, transactions, is_verified) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)".to_string(); // 将transactions转换为Vec<serde_json::Value>,适配jsonb[]类型 let transactions_values: Vec<serde_json::Value> = user.transactions .iter() .map(|purchase| serde_json::to_value(purchase)) .collect::<Result<_, _>>() .map_err(|e| models::CustomError::new(&e.to_string()))?; client .execute( &query, &[ &user.id, &user.fullname, &user.nickname, &user.password, &user.email, &user.bucket, &user.files, // 直接传入Vec<String>,自动映射为text[] &transactions_values, // 传入Vec<serde_json::Value>,自动映射为jsonb[] &user.is_verified, ], ) .await .map_err(|e| models::CustomError::new(&e.to_string()))?; Ok(()) }
可选优化:为Purchase实现ToSql
如果不想每次手动转换Vec<Purchase>,可以为Purchase实现ToSql trait,直接传入集合:
impl ToSql for Purchase { fn to_sql(&self, ty: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> { let json_val = serde_json::to_value(self)?; json_val.to_sql(ty, out) } fn accepts(ty: &Type) -> bool { ty.name() == "jsonb" } fn to_sql_checked(&self, ty: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> { if !Self::accepts(ty) { return Err(format!("unsupported type: {}", ty.name()).into()); } self.to_sql(ty, out) } }
实现后可简化插入参数:
// ... client .execute( &query, &[ // ...其他参数 &user.transactions, // 直接传入Vec<Purchase>,自动映射为jsonb[] &user.is_verified, ], ) .await?; // ...
依赖确认
确保tokio-postgres已启用with-serde_json-1特性,现有配置无需修改:
tokio-postgres = { version = "0.7.10", features = ["with-serde_json-1"] }
内容的提问来源于stack exchange,提问作者Necoo33
相关产品推荐
相关产品推荐

