You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

错误根源

  1. 数组类型处理错误:手动将Vec<String>(对应text[])和Vec<Purchase>(对应jsonb[])序列化为JSON字符串传入数据库,但PostgreSQL的数组类型需要接收原生Rust数组/集合类型,而非JSON格式字符串。
  2. INSERT字段错位:原INSERT语句包含表结构中不存在的role字段,导致参数位置与表字段不匹配,进一步触发类型转换异常。

修复方案

  1. 移除手动JSON序列化步骤,直接传入对应Rust集合类型
  2. 修正INSERT语句的字段列表,与实际表结构完全对齐
  3. 利用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 01:17:02