Realtime: "must be owner of table messages" when setting up Authorization
Last edited: 10/14/2026
向 realtime.messages 添加 RLS 策略的迁移失败了:
🌐 A migration that adds an RLS policy to realtime.messages fails:
ERROR: 42501: must be owner of table messages原因 #
🌐 Cause
realtime.messages 由内部角色 supabase_realtime_admin 拥有。postgres 角色不是其成员,也不是超级用户。
策略仍然有效是因为supautils,这个扩展给了postgres角色在 Supabase 项目中的提升权限。它允许postgres在一个固定的、它不拥有的表列表上运行create policy、alter policy和drop policy,而realtime.messages就在这个列表上。列表只涵盖策略语句,所以ALTER TABLE仍然会被拒绝。
🌐 Policies still work because of supautils, the extension that gives the postgres role its elevated permissions on a Supabase project. It lets postgres run create policy, alter policy, and drop policy on a fixed list of tables it does not own, and realtime.messages is on that list. The list covers policy statements only, so ALTER TABLE is still refused.
出错的语句通常不是 create policy,而是同一次迁移中更早的 ALTER TABLE:
🌐 The failing statement is usually not the create policy. It's an ALTER TABLE earlier in the same migration:
alter table realtime.messages enable row level security;alter table realtime.messages add column ...;alter table realtime.messages owner to postgres;第一个情况是常见的情况。RLS 已经在 realtime.messages 上启用,并且 Postgres 在检查设置是否会更改之前会先检查所有权,所以这个语句在每个项目上都会失败。
🌐 The first one is the common case. RLS is already enabled on realtime.messages, and Postgres checks ownership before it checks whether the setting would change, so the statement fails on every project.
一个失败的语句会中止事务,所以 Postgres 会跳过它之后的所有内容,包括 create policy。supabase db push 会在一个事务中运行每个迁移文件,因此文件会回滚,supabase_migrations.schema_migrations 什么都不会写入。
🌐 A failed statement aborts the transaction, so Postgres skips everything after it, including the create policy. supabase db push runs each migration file in one transaction, so the file rolls back and nothing is written to supabase_migrations.schema_migrations.
修复 #
🌐 Fix
删除 ALTER TABLE 行。保留策略:
🌐 Remove the ALTER TABLE line. Keep the policy:
create policy "authenticated can read messages on room-1"on "realtime"."messages"for selectto authenticatedusing ( (select realtime.topic()) = 'room-1');在 SQL 编辑器里运行它,或者用 supabase db push。两者都以 postgres 身份连接。
🌐 Run it from the SQL editor or with supabase db push. Both connect as postgres.
要检查 RLS 是否开启,读目录而不是去设置它:
🌐 To check that RLS is on, read the catalog instead of setting it:
select relrowsecurity from pg_class where oid = 'realtime.messages'::regclass;不起作用 #
🌐 What doesn't work
授予拥有者角色失败,支持团队也无法授予:
🌐 Granting the owning role fails, and support can't grant it either:
grant supabase_realtime_admin to postgres;-- ERROR: 42501: "supabase_realtime_admin" role memberships are reserved, only superusers can grant them重新启动项目什么都不会改变。
🌐 Restarting the project changes nothing.
把 ALTER TABLE 包在一个函数或 do 块里也没什么变化。所有权检查仍然会运行,而一个属于 postgres 的 security definer 函数还是不是表的拥有者。
🌐 Wrapping the ALTER TABLE in a function or a do block changes nothing either. The ownership check still runs, and a security definer function owned by postgres is still not the table owner.
Postgres 在 realtime.messages 上能做什么 #
🌐 What postgres can do on realtime.messages
Yes 行是 supautils 委派的内容,加上直接授予 postgres 的权限。
🌐 The Yes rows are what supautils delegates, plus the privileges granted to postgres directly.
| 声明 | 允许 |
|---|---|
create policy、alter policy、drop policy | 是 |
comment on policy | 是 |
select、insert | 是 |
grant select、grant insert 给你自己的角色 | 是 |
alter table 以任何形式 | 否 |
drop table | 否 |
在 realtime 模式下创建表或函数 | 否 |
查看 实时授权 以编写策略。
🌐 See Realtime Authorization for writing the policies.