我与 Sea ORM 的奇妙冒险:PostgreSQL schema 设置引发的血案

28次阅读
没有评论

共计 3666 个字符,预计需要花费 10 分钟才能阅读完成。

这两天在开发 ACGHub 社区平台时,遇到了一个诡异的问题 —— 数据库里明明白白有表,Sea ORM 愣是报错说表不存在。搞了半天才发现是个超级隐蔽的配置问题。把这次踩坑记录下来,以后自己或者其他小伙伴再碰到类似问题就能快速定位了。

问题描述

在我的 ACGHub 项目中,我需要实现一个获取板块列表的接口,之前用的是 sqlx 的原生 SQL 查询,现在我想把它改成使用 Sea ORM 的方式。数据结构很简单,就是查询 board 表的信息,同时关联 section 表获取分区信息。

我的代码大致是这样的:

pub async fn list_boards(
    &self,
    offset: i32,
    limit: i32,
    section_oxid: Option<String>,
) -> Result<ListBoardVo> {
    // 构建基本查询
    let mut query = board::Entity::find()
        .join(JoinType::InnerJoin, board::Relation::Section.def())
        .filter(board::Column::Status.eq(1))
        .filter(section::Column::Status.eq(1));

    // 处理可选的分区过滤
    if let Some(oxid) = section_oxid {query = query.filter(section::Column::Oxid.eq(oxid));
    }

    // 添加排序和分页
    let board_models = query
        .order_by_desc(board::Column::Weight)
        .offset(offset as u64)
        .limit(limit as u64)
        .all(&self.pool)
        .await?;
        
    // ... 后续处理代码
}

结果却收到了这样的错误:

Database error: Query Error: error returned from database: relation \"board\" does not exist

排查过程

第一步:确认表是否存在

首先,我直接在数据库中执行了查询,确认表是否真的存在:

SELECT tablename FROM pg_tables WHERE schemaname = 'public';

结果显示 board 表确实存在,而且在 public schema 中。

第二步:验证原始 SQL 查询

我直接用原始 SQL 查询测试,看看是否能正常获取数据:

SELECT
    b.id, b.name, b.description, b.oxid, b.icon,
    b.post_count, b.today_post_count, b.follower_count, b.is_official, b.updated_at,
    s.oxid as section_oxid, s.name as section_name, s.description as section_description, s.icon as section_icon
FROM
    board b
INNER JOIN
    section s ON b.section_id = s.oxid
WHERE
    b.status = 1 AND s.status = 1
ORDER BY
    b.weight DESC
LIMIT 10 OFFSET 0;

这个查询可以正常执行,返回了预期的结果。

第三步:检查 Sea ORM 实体定义

我仔细检查了 Sea ORM 的实体定义,确保表名和字段名都是正确的:

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "board")]
pub struct Model {#[sea_orm(primary_key)]
    pub id: i64,
    #[sea_orm(unique)]
    pub name: String,
    #[sea_orm(column_type = "Text")]
    pub description: String,
    #[sea_orm(unique)]
    pub oxid: String,
    pub icon: String,
    pub section_id: String,
    pub weight: i32,
    pub status: i16,
    pub is_official: bool,
    pub post_count: i64,
    pub today_post_count: i64,
    pub week_post_count: i64,
    pub month_post_count: i64,
    pub follower_count: i64,
    pub created_by: Option<String>,
    pub created_at: DateTime,
    pub updated_at: DateTime,
}

一切看起来都正确。

第四步:尝试各种可能的解决方案

我尝试了各种可能的解决方案,包括:

  1. 修改 Sea ORM 的关系定义
  2. 使用不同的查询方式
  3. 直接使用原始 SQL 查询代替 Sea ORM 的查询构建器

但是问题依然存在…

柳暗花明又一村

折腾了大半天后,我灵光一闪,想到是不是连接配置出了问题。检查了一下数据库连接配置,果然在这里发现了问题:

pub async fn create_db_pool_v2(&self) -> Result<DatabaseConnection> {let mut opt = ConnectOptions::new(&self.database_url);
    opt.max_connections(100)
        .min_connections(5)
        .connect_timeout(Duration::from_secs(8))
        .acquire_timeout(Duration::from_secs(8))
        .idle_timeout(Duration::from_secs(8))
        .max_lifetime(Duration::from_secs(8))
        .sqlx_logging(true)
        .sqlx_logging_level(log::LevelFilter::Info)
        .set_schema_search_path("my_schema") // 这货就是罪魁祸首!;

    Ok(Database::connect(opt).await?)
}

哦豁!原来是 .set_schema_search_path("my_schema") 这行惹的祸。我设置了一个名为“my_schema”的 schema 搜索路径,而我的表其实都在 PostgreSQL 的默认“public”schema 中。

把这行注释掉或者改成 .set_schema_search_path("public") 后,问题立马解决!

我后来还在 Rust 论坛找到了同样问题的讨论:https://users.rust-lang.org/t/postgres-sea-orm-relation-table-name-does-not-exist-at-character-64/103858,看来不止我一个人踩过这个坑。

Schema 搜索路径是个啥玩意儿?

这次问题的关键在于 PostgreSQL 的 schema 搜索路径机制。简单来说,schema 就像是表的命名空间或者文件夹,用来组织和管理表。

当你在 SQL 中引用一个表但没有明确指定 schema 时(比如直接写 SELECT * FROM users 而不是 SELECT * FROM public.users),PostgreSQL 会按照一定的顺序在不同的 schema 中找这个表。这个顺序就是由搜索路径决定的。

如果你把搜索路径设成了“my_schema”,而你的表其实都在“public”schema 里,那 PostgreSQL 就只会在“my_schema”里找表,找不到就报错了,即使这个表明明白白地存在于“public”schema 中。

正确的设置应该是这样的:

// 如果表在 public schema 中(PostgreSQL 的默认 schema).set_schema_search_path("public")

// 如果你有多个 schema 需要查询
.set_schema_search_path("my_schema,public")

Sea ORM 实际上是把这个搜索路径的设置传给了底层的 SQLx 和 PostgreSQL 连接,所以一旦设置错了,整个查询机制就崩了。

几点血泪教训

这次排错的过程真是既痛苦又有趣,总结一下几点经验:

不要轻信错误提示!" 表不存在 " 不一定是真的不存在,而可能是找错了地方。

对付 PostgreSQL 的问题,schema 永远是值得怀疑的对象。

ORM 再好用,也挡不住配置出问题。有时候最简单的原生 SQL 反而能帮你快速定位问题。

如果怀疑是数据库连接配置问题,可以先用最简单的 SQL 查询测试一下,能省下不少排错时间。

还有就是,多看开源社区的讨论!你遇到的问题,大概率有人已经踩过坑了。


好了,希望这篇踩坑记录能帮到其他在深夜被 Sea ORM 问题折磨的同学们。如果你有任何想法或者更好的解决方案,欢迎在评论区交流,或者来 ACGHub 的开发者社区灌水~

正文完
 0
历史的配角
版权声明:本站原创文章,由 历史的配角 于2025-04-22发表,共计3666字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
验证码

您无法复制此页面的内容

了解 未来日记 的更多信息

立即订阅以继续阅读并访问完整档案。

继续阅读