できない.dev

PostgreSQL で「permission denied for table」が出てテーブルを操作できない

接続できているのにテーブルを読めないのは、ロールにそのテーブルの権限が無いためである。
テーブル権限とスキーマの USAGE 権限は別物で、両方揃わないと参照できない。
既存テーブルへの GRANT と、今後作られるテーブル向けの既定権限を分けて設定する。

公開: 更新:

実行例あり(2026-09-25 に実環境で検証)

要約

ERROR: permission denied for table users は、接続そのものは成功していて、そのロールに当該テーブルの権限が無いという意味である。
接続エラーではないので、接続文字列やパスワードをいくら見直しても直らない。

つまずきやすいのは、PostgreSQL の権限が テーブル単位 と スキーマ単位 の二段構えになっている点である。
テーブルに SELECT を付けても、そのテーブルが属するスキーマに USAGE が無ければ到達できない。
逆にスキーマの USAGE だけでもテーブルは読めない。

myapp=> SELECT * FROM users;
ERROR:  permission denied for table users

実行例

公式イメージ postgres:17 で、migrator が作った users を app_user で読むと permission denied for table users になり(psql の -f 実行では ERROR でも終了コードは 0 のまま)、\dp users の権限欄は空だった。
スキーマの USAGE とテーブル・シーケンスへの GRANT の後は読めたが、その後に作った orders は再び拒否され、ALTER DEFAULT PRIVILEGES の後に作った items は最初から読め、orders は個別に GRANT してようやく読めている。

$ psql -U app_user -d myapp -f select_users.sql
psql:select_users.sql:1: ERROR:  permission denied for table users
$ echo $?
0
$ psql -U migrator -d myapp -c "\dp users"
Access privileges
 Schema | Name  | Type  | Access privileges | Column privileges | Policies 
--------+-------+-------+-------------------+-------------------+----------
 public | users | table |                   |                   | 
(1 row)
$ psql -U migrator -d myapp -a -f grant.sql
GRANT USAGE ON SCHEMA public TO app_user;
GRANT
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
GRANT
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO app_user;
GRANT
$ psql -U app_user -d myapp -f select_users.sql
id | name  
----+-------
  1 | alice
(1 row)
$ echo $?
0
$ psql -U app_user -d myapp -f select_orders.sql
psql:select_orders.sql:1: ERROR:  permission denied for table orders
$ echo $?
0
$ psql -U migrator -d myapp -a -f defaults.sql
ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA public
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES
ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA public
  GRANT USAGE ON SEQUENCES TO app_user;
ALTER DEFAULT PRIVILEGES
$ psql -U app_user -d myapp -f select_items.sql
id | sku 
----+-----
(0 rows)
$ echo $?
0
$ psql -U migrator -d myapp -c "GRANT SELECT ON orders TO app_user"
GRANT
$ psql -U app_user -d myapp -f select_orders.sql
id | total 
----+-------
(0 rows)
$ echo $?
0

— 2026-09-25 時点の出力

検証環境

検証日
実行環境
postgres:17Debian GNU/Linux 13 (trixie)

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. テーブル権限が無い: テーブルは所有者以外には自動で権限が付かない。
    マイグレーションを管理者ロールで流し、アプリは別ロールで接続している構成でよく起きる。
  2. スキーマの USAGE が無い: GRANT でテーブル権限だけ付けて満足しているケース。GRANT の公式ドキュメント(新しいタブで開く) のとおり、スキーマに対しては CREATE と USAGE を別に指定する。
  3. 所有者とアプリのロールが別: 誰がテーブルを作ったかで既定の権限が決まる。
    所有者を確認せずに権限を追いかけると原因を見失う。
  4. 新規テーブルだけ漏れる: GRANT ... ON ALL TABLES IN SCHEMA は実行時点で存在するテーブルにしか作用しない。
    翌週のマイグレーションで増えたテーブルは、また権限が無い状態から始まる。
  5. シーケンスの権限漏れ: SERIAL 列を持つテーブルへ INSERT すると裏でシーケンスを進める。
    テーブル権限だけでは permission denied for sequence users_id_seq になる。

解決策

1. 現状の権限を確認する

\dp users

Access privileges 列が空なら所有者以外は何もできない。
ロール名と付いている権限記号(r = SELECT、a = INSERT など)を先に把握する。

2. スキーマとテーブルの両方に付与する

GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO app_user;

3 行をセットで実行する。
シーケンスを忘れると読めるのに書けない状態になる。

3. これから作られるテーブルにも効かせる

ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA public
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA public
  GRANT USAGE ON SEQUENCES TO app_user;

FOR ROLE には テーブルを作るロール を指定する。
ここを実行者自身のロールと取り違えると、マイグレーションで増えたテーブルには反映されない。

4. 権限設計を単純にする

読み取り専用ロールと書き込みロールを分け、アプリは必要な方だけを使う。
ロールを増やすより、既存ロールに GRANT app_readonly TO app_user; のように束ねる方が管理しやすい。

この記事は役立ちましたか?