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

使用Sqlx query_as查询PostgreSQL时类型不匹配问题求助

问题解决:Sqlx query_as函数动态查询类型不匹配,query_as!宏正常

错误原因

query_as!宏是编译时展开的,会根据结构体字段和查询语句自动生成适配的类型解码逻辑,能正确识别UuidWrapper与PostgreSQL bpchar(36)的映射关系。而直接调用query_as函数时,Sqlx需要自定义类型手动实现Decode<Postgres>和Type<Postgres> trait,否则会触发默认解码逻辑,导致类型不匹配错误。

另外你的FromRow实现未被调用,是因为Sqlx在尝试逐个解码字段时就已失败,未走到调用FromRow的流程。

解决方案

1. 为UuidWrapper实现必要的Sqlx trait

需要让Sqlx知道如何将数据库的bpchar(36)类型转换为UuidWrapper,实现Decode、Type(如需写入数据库还需实现Encode):

use sqlx::{postgres::PgTypeInfo, Decode, Encode, Postgres, Type};
use uuid::Uuid;

#[derive(PartialEq, Debug, Clone)]
pub struct UuidWrapper(pub(crate) Uuid);

// 实现解码:将bpchar(36)字符串转为Uuid
impl<'r> Decode<'r, Postgres> for UuidWrapper {
    fn decode(value: sqlx::postgres::PgValueRef<'r>) -> Result<Self, sqlx::Error> {
        let uuid_str: &str = Decode::decode(value)?;
        let uuid = Uuid::parse_str(uuid_str)
            .map_err(|e| sqlx::Error::Decode(e.into()))?;
        Ok(UuidWrapper(uuid))
    }

    fn decode_nullable(value: sqlx::postgres::PgValueRef<'r>) -> Result<Option<Self>, sqlx::Error> {
        value.is_null().then_some(None).unwrap_or_else(|| Ok(Some(Self::decode(value)?)))
    }
}

// 声明对应的SQL类型
impl Type<Postgres> for UuidWrapper {
    fn type_info() -> PgTypeInfo {
        // 对应PostgreSQL的character(bpchar)类型
        PgTypeInfo::with_name("character")
    }

    fn compatible(ty: &PgTypeInfo) -> bool {
        // 兼容char和character两种类型名
        ty.name() == "char" || ty.name() == "character"
    }
}

// 如需向数据库写入数据,实现Encode
impl Encode<'_, Postgres> for UuidWrapper {
    fn encode_by_ref(&self, buf: &mut sqlx::postgres::PgArgumentBuffer) -> Result<(), sqlx::Error> {
        let uuid_str = self.0.to_string();
        Encode::encode(uuid_str, buf)
    }
}

2. 优先使用参数化查询替代字符串拼接

动态拼接SQL存在SQL注入风险,建议使用参数化查询,同时保留query_as!宏的编译时检查优势:

async fn select_where(
    pool: &Pool<Postgres>,
    owner_id: &Uuid,
    create_date: &chrono::NaiveDate
) -> Vec<Self>
where
    Self: Sized,
{
    query_as!(
        Post,
        "SELECT * FROM posts WHERE owner_id = $1 AND CAST(create_time AS DATE) = $2",
        owner_id,
        create_date
    )
    .fetch_all(pool)
    .await
    .unwrap()
}

3. 确认动态查询语句的正确性

如果必须使用动态SQL,确保拼接后的语句与query_as!中硬编码的语句完全一致,避免列缺失或语法错误导致的解码异常。


内容的提问来源于stack exchange,提问作者radu781

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 19:45:10