PostgreSQL 权限踩坑:数据库 owner 为什么不能查询自己的表

0 阅读3分钟

最近在使用 PostgreSQL 迁移 AI 应用数据库时,遇到了一个非常容易踩坑的问题:

数据库明明是当前用户的:

SELECT 
    datname,
    pg_get_userbyid(datdba) AS owner
FROM pg_database
WHERE datname = current_database();

结果:

datname      | owner
-------------+-----------
sf_ai_demo   | sf_ai_demo

但是执行查询:

SELECT * FROM ai_chat_message;

却报错:

ERROR: permission denied for table ai_chat_message

一开始会觉得很奇怪:

数据库都是我的了,为什么表还不给我访问?

实际上,这是 PostgreSQL 权限模型和很多人的认知差异导致的。

PostgreSQL 的权限是对象级别的

很多 MySQL 用户习惯:

用户
 ↓
数据库
 ↓
所有对象

拥有数据库权限,基本等于拥有数据库里的所有东西。

但是 PostgreSQL 更细:

数据库
 ├── Schema
 │    ├── Table
 │    ├── View
 │    ├── Sequence
 │    ├── Function
 │    └── Type

每一个对象都有自己的 owner 和权限。

数据库 owner:

sf_ai_demo

并不代表:

所有表 owner = sf_ai_demo

查看表的实际 owner

可以执行:

SELECT
    schemaname,
    tablename,
    tableowner
FROM pg_tables
WHERE schemaname='public';

例如:

结果:

schemaname | tablename              | tableowner
-----------+------------------------+------------
public     | ai_chat_message        | ki_admin
public     | recipe_embedding       | ki_admin
public     | recipes                | ki_admin

问题就找到了:

数据库:

owner = sf_ai_demo

但是表:

owner = ki_admin

当前用户并不是这些表的拥有者。

为什么会出现这种情况?

通常发生在数据库迁移。

例如:

源环境:

用户:
ki_admin

表:
ai_chat_message
owner:
ki_admin

迁移到新环境:

创建数据库:

sf_ai_demo
owner:
sf_ai_demo

但是数据迁移工具保留了原来的对象归属:

ai_chat_message
owner:
ki_admin

最终:

数据库 owner:
sf_ai_demo

 owner:
ki_admin

两个身份不一致。

如何修复?

方法1:修改表 owner

使用原表 owner 用户执行:

ALTER TABLE public.ai_chat_message 
OWNER TO sf_ai_demo;

批量处理:

ALTER TABLE public.recipes OWNER TO sf_ai_demo;
ALTER TABLE public.tags OWNER TO sf_ai_demo;
ALTER TABLE public.ai_chat_message OWNER TO sf_ai_demo;

方法2:授权访问

如果不希望修改 owner:

GRANT SELECT 
ON TABLE public.ai_chat_message 
TO sf_ai_demo;

或者:

GRANT ALL PRIVILEGES
ON ALL TABLES IN SCHEMA public
TO sf_ai_demo;

但是长期维护更推荐统一 owner。

PostgreSQL 云数据库还有一个额外坑

在云厂商环境:

例如:

  • 阿里云 PolarDB PostgreSQL
  • 云数据库 PostgreSQL

所谓“高权限账号”:

管理员账号

通常:

≠ PostgreSQL superuser

因为云厂商不会开放真正超级权限。

所以遇到:

must be superuser

不要继续加权限。

很多时候应该调整操作方式。

PostgreSQL 迁移建议

迁移 PostgreSQL 数据时:

不要直接迁所有对象。

尤其注意:

  • extension
  • type
  • function
  • operator
  • owner

例如 pgvector:

正确流程:

目标数据库

CREATE EXTENSION vector;

↓

迁移业务表

↓

创建索引

不要让迁移工具重新创建:

vector
halfvec
sparsevec
vector_cosine_ops

这些属于扩展对象,不应该当普通业务对象迁移。

总结

PostgreSQL 权限模型一句话:

数据库属于你,不代表数据库里的对象属于你。

遇到:

permission denied for table xxx

不要第一时间给权限。

先检查:

  1. 当前用户是谁
SELECT current_user;
  1. 数据库 owner
SELECT pg_get_userbyid(datdba)
FROM pg_database;
  1. 表 owner
SELECT tablename, tableowner
FROM pg_tables;

很多权限问题,本质不是权限不足,而是对象归属混乱。

这也是 PostgreSQL 相比 MySQL 更精细、更容易踩坑的地方。