Rust如何实现AStruct到tokio_postgres::Row的From转换及表插入
解答
直接结论
- 无法合法实现
impl From<AStruct> for tokio_postgres::Row - 手动构造的
tokio_postgres::Row实例没有任何可用于插入数据的公开API,完全不能用来做插入操作
原因说明
tokio_postgres::Row从设计上就是数据库查询结果的只读载体,只在执行SELECT等查询语句后由客户端内部构造返回给用户,它的所有内部字段(你代码里写的statement/body/ranges)都是私有的,且没有提供任何公开的构造方法。这些字段存储的是数据库返回的原始二进制结果、关联的查询语句元数据、字段值偏移量,本质是绑定了一次查询返回上下文的只读结构,根本不是用来承载待写入数据的类型。
你尝试实现ToSql/FromSql的方向是对的,但搞错了Row的定位:tokio-postgres的写入逻辑完全不涉及Row类型,写入时只需要传入SQL语句,以及按顺序排列、实现了ToSql trait的参数列表即可。
正确实现方式
你已经为自定义类型实现了ToSql/FromSql,不需要做任何额外的Row转换,直接传参执行插入语句即可:
use chrono::{DateTime, Utc}; use tokio_postgres::{NoTls, Error, Row}; use tokio_postgres::types::{ToSql, FromSql}; // 注意:derive ToSql/FromSql时需要通过属性标记匹配数据库中定义的类型名 #[derive(ToSql, FromSql, Debug)] #[postgres(name = "e")] // 对应数据库中提前创建的枚举类型 enum E { A, B, } #[derive(ToSql, FromSql, Debug)] struct AStruct { i: i32, e: E, d: DateTime<Utc> } impl From<Row> for AStruct { fn from(row: Row) -> Self { Self { i: row.get(0), e: row.get(1), d: row.get(2), } } } // 可选:给AStruct加个辅助方法,快速生成插入参数列表,避免重复写参数 impl AStruct { fn insert_params(&self) -> Vec<&(dyn ToSql + Sync)> { vec![&self.i, &self.e, &self.d] } } #[tokio::main] async fn main() -> Result<(), Error> { let (client, _) = tokio_postgres::connect("database_url", NoTls).await?; let id = 1; let s = AStruct { i: 1, e: E::A, d: Utc::now(), }; // 查询逻辑不需要改动 let s: AStruct = client .query("SELECT i, e, d FROM structs WHERE id = $1", &[&id]) .await? .into_iter() .next() .unwrap() .into(); // 直接执行插入,传入参数即可,不需要构造Row client .execute( "INSERT INTO structs (i, e, d) VALUES ($1, $2, $3)", &s.insert_params() ) .await?; Ok(()) }
如果你在数据库中把某张表的字段定义为和AStruct匹配的复合类型,甚至可以直接把整个结构体作为参数传入,不需要拆分成单独字段。
内容的提问来源于stack exchange,提问作者Neotenic Primate
相关产品推荐
相关产品推荐

