Postgres中NUMERIC类型如何序列化?Rust中u128转numeric(40)遇格式难题
解决Rust中u128序列化为Postgres numeric(40)的问题
Postgres的numeric类型有特定的二进制格式,不是简单的大小端整数序列化,需要按照其结构组装字节流。以下是具体实现步骤和代码:
Postgres numeric二进制格式核心结构(大端字节序)
所有多字节字段均采用大端(网络字节序),结构如下:
- 4字节uint32:整个numeric的总字节长度(包含此字段)
- 2字节int16:十进制数字的总位数(如
1234为4位) - 2字节int16:权重(weight),最高有效digit对应的
10^(3*weight),计算方式为(总位数-1)/3(整数除法) - 2字节int16:符号位(0=正,1=负)
- 2字节int16:精度(如
numeric(40)的精度为40) - 2字节int16:小数位(scale,
numeric(40,0)为0) - 后续字节:每个digit为2字节uint16,对应3个十进制数字(从最高位到最低位存储,不足3位的组直接存实际数值)
Rust实现ToSql trait
针对u128实现Postgres的ToSql trait,完成序列化:
use postgres::{types::{ToSql, Type}, Error}; use std::io::Write; impl ToSql for u128 { fn to_sql(&self, ty: &Type, out: &mut dyn Write) -> Result<usize, Error> { if *ty != Type::NUMERIC { return Err(Error::ToSqlConversionFailed); } // 将u128转为十进制字符串 let s = self.to_string(); let total_digits = s.len() as i16; // 从右往左拆分每3位一组,再反转得到高位到低位的顺序 let mut chars = s.chars().rev().collect::<Vec<_>>(); let mut digits = Vec::new(); while !chars.is_empty() { let chunk = chars.drain(0..3.min(chars.len())).rev().collect::<String>(); digits.push(chunk.parse::<u16>().unwrap()); } digits.reverse(); // 计算numeric字段值 let weight = (total_digits - 1) / 3; let sign = 0; // u128为无符号,固定正 let precision = 40; // 对应numeric(40)的精度 let scale = 0; // numeric(40,0)的小数位 let total_len = 4 + 2 * 5 + 2 * digits.len() as u32; // 按格式写入字节流(大端) out.write_all(&total_len.to_be_bytes())?; out.write_all(&total_digits.to_be_bytes())?; out.write_all(&weight.to_be_bytes())?; out.write_all(&sign.to_be_bytes())?; out.write_all(&precision.to_be_bytes())?; out.write_all(&scale.to_be_bytes())?; for &digit in &digits { out.write_all(&digit.to_be_bytes())?; } Ok(total_len as usize) } fn accepts(ty: &Type) -> bool { *ty == Type::NUMERIC } to_sql_checked!(); }
关键注意事项
- 字节序:所有多字节字段必须用大端写入,Postgres不支持小端格式。
- digit顺序:拆分后必须反转数组,确保高位digit在前,符合Postgres的存储要求。
- 精度适配:如果需要适配不同精度的
numeric类型,可以通过解析Type的参数动态调整precision和scale,上述代码针对numeric(40,0)做了固定处理。 - 测试验证:插入
u128::MAX后,在Postgres中查询验证数值是否正确:SELECT col FROM your_table; -- 应返回340282366920938463463374607431768211455
内容的提问来源于stack exchange,提问作者João A. Veiga
相关产品推荐
相关产品推荐

