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

PostgreSQL 12中PL/pgSQL函数内语句是否顺序执行?竞态问题咨询

关于PL/pgSQL函数竞态条件与RLS限制的问题解答

嘿,我来帮你理清这个问题,结合你的PostgreSQL 12环境和RLS场景给你拆解清楚:

核心事实先明确

首先要打消你两个顾虑:

  1. PL/pgSQL语句是严格按顺序执行的,而且整个函数默认跑在一个原子事务里——也就是说,函数里的INSERT一定会先执行companies的插入,再执行departments的,完全不会出现乱序的情况。
  2. 针对你提到的RLS导致无法用RETURNING的场景,你手动生成UUID的方案(第一个函数)是完全可行的,而且竞态条件的风险几乎可以忽略。

逐个分析你的函数

1. create_company_maybe_race_condition() 函数

你担心这个函数会有竞态,但实际上:

  • gen_random_uuid()是加密安全的随机生成器,生成重复UUID的概率低到可以忽略不计(远低于服务器硬件故障的概率),所以几乎不会出现两个并发调用生成相同company_id的情况。
  • 两个INSERT属于同一个事务,PostgreSQL会保证事务的原子性:要么两个插入都成功,要么都回滚。而且在事务内部,第一个INSERT写入的companies行对当前事务是可见的,所以第二个INSERT的外键约束肯定能通过——PostgreSQL知道这两个操作的依赖关系,外键检查会正常工作。
  • 你注释里写的“Postgres doesn't know that it depends on companies”是多虑了,外键约束本身就已经明确了这种依赖,事务内的可见性也保证了第二个插入能找到对应的公司行。

2. create_company_no_race_condition() 函数

这个函数的逻辑本身没问题,但在你的RLS场景下会直接失效:

  • 因为RLS依赖权限表的访问控制列表,当你插入companies行后,权限表还没有允许当前用户读取该行的条目,RETURNING子句会被RLS规则拦截,导致函数执行失败。这也是你说无法用RETURNING的根本原因,所以这个方案对你的场景不适用。

针对RLS场景的优化建议

既然不能用RETURNING,手动生成UUID的方案是最优解,不过可以加一点小优化让它更稳妥:

  • 先把companies表的company_id字段加上默认值,保持表结构的一致性:
    CREATE TABLE companies (
     company_id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    );
    
  • 如果你极端担心UUID重复(虽然完全没必要,但求个安心的话),可以在函数里加个简单的检查:
    CREATE FUNCTION create_company_safe () RETURNS void AS $$
    DECLARE
     v_company_id uuid := gen_random_uuid();
    BEGIN
     -- 极端场景下检查UUID是否已存在(可选)
     WHILE EXISTS (SELECT 1 FROM companies WHERE company_id = v_company_id) LOOP
       v_company_id := gen_random_uuid();
     END LOOP;
     
     INSERT INTO companies (company_id) VALUES (v_company_id);
     -- 关键:先插入权限表的访问控制条目,确保后续操作符合RLS规则
     INSERT INTO your_permission_table (user_id, company_id, permission) 
     VALUES (current_user, v_company_id, 'read');
     INSERT INTO departments (company_id, name) VALUES (v_company_id, 'My Department');
    END;
    $$ LANGUAGE plpgsql;
    
    这里一定要注意:必须在插入departments之前完成权限表的插入,否则departments的插入可能因为RLS对关联companies的权限检查而被拒绝。

总结

  • PL/pgSQL内部的语句绝对不会乱序执行,事务的原子性已经帮你保障了数据一致性;
  • 手动生成UUID的方案在你的RLS场景下完全可行,竞态条件的风险可以忽略;
  • 记得在插入业务表之前先搞定权限表的条目,确保所有操作都符合RLS的规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 20:27:41