TL;DR
ロールにテーブルの権限がありません。スーパーユーザーまたはテーブルオーナーとして接続して以下を実行してください:
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE your_table TO your_role;
フルアクセスの場合:
GRANT ALL PRIVILEGES ON TABLE your_table TO your_role;
また、ロールにスキーマレベルのアクセス権があることも確認してください — これがよく忘れられる部分です:
GRANT USAGE ON SCHEMA public TO your_role;
エラーについて
ERROR: permission denied for table users
このエラーは、PostgreSQL のロールが権限のないテーブルに対してクエリや変更を試みた際に発生します。新しいアプリユーザーの作成、データベースの切り替え、ダンプからのリストア、またはアプリが実際に接続するロールとは異なるロールでマイグレーションを実行した後によく遭遇します。
なぜこうなるのか
PostgreSQL の権限モデルは厳格です。すべてのロールはテーブルアクセスなしからスタートします — データベースに接続しても、その中で何ができるかは関係ありません。データベースを所有していても、その中のオブジェクトを所有しているわけではありません。
よくある原因:
- 新しいアプリユーザーが作成されたが、テーブル権限が付与されていない
postgresまたはマイグレーションユーザーによってテーブルが作成された — アプリロールがアクセスできないpg_dumpからリストアしたが、ダンプに GRANT 文が含まれていなかった- スキーマの USAGE 権限がない(PostgreSQL がテーブルを確認する前に権限チェックが失敗する)
ステップ1:不足している権限を確認する
スーパーユーザーとして実行して、テーブルの現在の権限を確認します:
-- psql 内で
\dp your_table
-- または SQL 経由で
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'your_table';
スキーマレベルのアクセスも確認します:
SELECT nspname, nspacl
FROM pg_namespace
WHERE nspname = 'public';
ロールが一覧にない場合、それが原因です。
修正1:テーブル権限を付与する
テーブルオーナーまたはスーパーユーザーとして接続し、ロールに必要な権限を付与します:
-- 読み取り専用
GRANT SELECT ON TABLE your_table TO your_role;
-- 読み取り + 書き込み
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE your_table TO your_role;
-- スキーマ内のすべてのテーブルに一括で
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO your_role;
修正2:スキーマの USAGE を付与する
テーブルレベルの権限だけでは不十分です。ロールはテーブルを含むスキーマの USAGE も必要です:
GRANT USAGE ON SCHEMA public TO your_role;
これを省略すると、PostgreSQL はテーブル権限を確認する前にクエリを拒否しますが、エラーメッセージは permission denied for table のままです。このズレがスキーマ権限の不足を見落としやすくしています。
修正3:INSERT のためのシーケンスアクセスを修正する
serial、bigserial、または GENERATED 列を使用するテーブルは、基盤となるシーケンスに依存しています。そのシーケンスへのアクセスがないと、テーブル権限を付与した後でも INSERT が失敗します:
-- 特定のシーケンス
GRANT USAGE, SELECT ON SEQUENCE your_table_id_seq TO your_role;
-- スキーマ内のすべてのシーケンス
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO your_role;
修正4:デフォルト権限(再発防止)
マイグレーションのたびに GRANT を再実行することにうんざりしていますか?デフォルト権限でそれを解決できます。これ以降、新しいテーブルとシーケンスには自動的にアクセス権が付与されます:
-- テーブルを作成するロール(例:マイグレーションユーザー)として実行する
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO your_role;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE, SELECT ON SEQUENCES TO your_role;
注意:これらはこのコマンド実行後に作成されたオブジェクトにのみ適用されます。また、コマンドを実行したユーザーのスコープに限定されます。マイグレーションが postgres として接続しているのに app_admin として ALTER DEFAULT PRIVILEGES を実行した場合、PostgreSQL はそれらの新しいテーブルには適用されません。
完全なセットアップスクリプト
新しいアプリロールをオンボーディングする際にスーパーユーザーとして実行する完全なスクリプトです:
-- 最初に適切なデータベースに接続する
\c your_database
-- スキーマアクセス
GRANT USAGE ON SCHEMA public TO app_user;
-- 既存のテーブル
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
-- 既存のシーケンス
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_user;
-- 将来のテーブルとシーケンス(マイグレーションユーザーとして実行)
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE, SELECT ON SEQUENCES TO app_user;
修正の確認
ロールを切り替えて直接テストします:
-- スーパーユーザーセッションから
SET ROLE your_role;
SELECT * FROM your_table LIMIT 1;
RESET ROLE;
またはコマンドラインからロールとして接続します:
psql -U your_role -d your_database -c "SELECT * FROM your_table LIMIT 1;"
エラーがなければ成功です。
補足
Linux では、PostgreSQL には2つの独立した権限レイヤーがあります。SQL 権限はロールがデータベース内でできることをカバーします — 上記のすべてがこのカテゴリに含まれます。データディレクトリ、ソケットファイル、pg_hba.conf のファイルレベルのアクセス権は OS レベルで処理されます。その側については、ToolCraft の Unix 権限計算ツールが chmod 値を頭の中で8進数計算せずに変換するのに便利です。
pg_dump/pg_restore を使用していますか?ダンプから権限を意図的に削除したい場合にのみ --no-acl を渡してください。省略すれば pg_dump はすべての GRANT 文を自動的に含めます。とはいえ、リストアパイプラインに誤って残された --no-acl フラグは、「なぜリストア後に何も権限がないのか?」という事例の驚くほど多くの原因となっています。

