最近在使用 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
不要第一时间给权限。
先检查:
- 当前用户是谁
SELECT current_user;
- 数据库 owner
SELECT pg_get_userbyid(datdba)
FROM pg_database;
- 表 owner
SELECT tablename, tableowner
FROM pg_tables;
很多权限问题,本质不是权限不足,而是对象归属混乱。
这也是 PostgreSQL 相比 MySQL 更精细、更容易踩坑的地方。