【PostgreSQL】よくあるPostgreSQLエラーまとめと対処法

PostgreSQL

こんにちは、かつコーチです。

PostgreSQLを触り始めると、赤い文字のERROR:メッセージに何度も出くわします。

最初は焦りますが、実は出てくるエラーの種類はある程度パターン化されています。

この記事では、初中級者がつまずきやすいPostgreSQLのエラーを実際のエラーメッセージ付きでまとめ、それぞれの原因と対処法を解説します。

「このエラー文でググってここに辿り着いた」という使い方もできるよう、エラーメッセージをそのまま載せています。

接続まわりのエラー

role “xxx” does not exist

psqlでログインしようとしたときに、最もよく見るエラーの1つです。

$ psql -U taro -d myapp
psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: FATAL:  role "taro" does not exist

これは、指定したロール(ユーザー)がPostgreSQLサーバーに存在しないために起こります。

インストール直後はpostgresロールしか存在しないため、独自のロールを使いたい場合は事前に作成しておく必要があります。

-- postgresロールで接続してから、必要なロールを作成する
CREATE ROLE taro WITH LOGIN PASSWORD 'change_this_password';

FATAL: password authentication failed for user

パスワードは合っているはずなのに、認証エラーになるケースです。

$ psql -U app_user -d myapp
psql: error: FATAL:  password authentication failed for user "app_user"

原因の多くは、pg_hba.conf(接続方式ごとの認証設定ファイル)の設定です。

# pg_hba.conf の設定例
# TYPE  DATABASE  USER  ADDRESS       METHOD
host    all       all   127.0.0.1/32  scram-sha-256

METHOD列がtrust(無条件許可)になっていると、逆にパスワードなしで通ってしまうため意図がずれます。

scram-sha-256(PostgreSQL 16での推奨方式)になっているか、パスワードの打ち間違いがないかを順に確認します。

設定変更後は、必ずpg_ctl reloadもしくはサーバーの再起動を忘れないようにしてください。

制約・型に関するエラー

duplicate key value violates unique constraint

一意制約(UNIQUE制約)に違反したときに出るエラーです。

INSERT INTO users (email, name) VALUES ('taro@example.com', '田中太郎');
ERROR:  duplicate key value violates unique constraint "users_email_key"
DETAIL:  Key (email)=(taro@example.com) already exists.

DETAIL行に、実際に重複した値まで表示されるのがPostgreSQLの親切なところです。

このエラーが出たら、まずDETAILを確認し、本当に重複を防ぎたいのか、あるいは既存行を更新したいのかを判断します。

「既存なら更新、なければ挿入」をしたい場合は、UPSERT(別記事で解説したON CONFLICT句)を使うとエラーを出さずに処理できます。

-- ❌ エラーになる可能性がある通常のINSERT
INSERT INTO users (email, name) VALUES ('taro@example.com', '田中太郎');

-- ✅ 重複時は更新するON CONFLICTを使う
INSERT INTO users (email, name) VALUES ('taro@example.com', '田中太郎')
ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name;

invalid input syntax for type integer

型が合わないデータを挿入・比較しようとしたときのエラーです。

SELECT * FROM orders WHERE id = 'abc';
ERROR:  invalid input syntax for type integer: "abc"
LINE 1: SELECT * FROM orders WHERE id = 'abc';
                                         ^

idカラムがinteger型なのに、文字列'abc'を渡してしまったために発生します。

アプリケーションのコードで、URLパラメータなどの文字列をそのままSQLに渡してしまうケースでよく起こります。

アプリ側で数値変換のバリデーション(PHPならis_numeric()ctype_digit()など)を通してからクエリに渡すようにすると防げます。

null value in column violates not-null constraint

NOT NULL制約が付いたカラムに、値を指定せずにINSERTしようとしたときのエラーです。

INSERT INTO products (name, price) VALUES ('ノートPC', NULL);
ERROR:  null value in column "price" of relation "products" violates not-null constraint
DETAIL:  Failing row contains (1, ノートPC, null, ...).

DETAIL行に、実際に挿入しようとしていた行全体が表示されるため、どのカラムが欠けていたかを特定しやすくなっています。

フォームのバリデーション漏れが原因であることが多いため、アプリ側の必須項目チェックを見直すのが根本対応です。

権限・スキーマに関するエラー

permission denied for table

権限不足で操作が拒否されたときのエラーです。

DELETE FROM orders WHERE id = 100;
ERROR:  permission denied for table orders

前回・前々回の記事(ロール権限管理、Row-Level Security)で扱った通り、PostgreSQLは操作ごとに細かく権限を制御できます。

このエラーが出たら、まず現在のロールに必要な権限が付与されているかを確認します。

-- 自分のロールが持つ orders テーブルへの権限を確認
SELECT privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'orders' AND grantee = current_user;

権限が足りない場合は、管理者ロールでGRANTを実行してもらう必要があります。

relation “xxx” does not exist

テーブル名のタイプミスや、想定と違うスキーマを見ているときに出るエラーです。

SELECT * FROM order;
ERROR:  relation "order" does not exist
LINE 1: SELECT * FROM order;
                       ^
HINT:  Perhaps you meant to reference the table "orders".

PostgreSQLは似た名前のテーブルがあるとHINTで候補を提示してくれることがあり、これは単純なタイポで気づける親切な機能です。

複数のスキーマを使っている場合は、search_path(スキーマの検索順序)の設定によって「存在するはずのテーブルが見えない」ということも起こります。

-- 現在のsearch_pathを確認する
SHOW search_path;

意図したスキーマが含まれているか確認し、必要であればSET search_path TO ...で調整します。

エラー調査を効率化するコツ

エラーコードで検索する

PostgreSQLのエラーには、23505(一意制約違反)のようなSQLSTATEコードが割り当てられています。

-- アプリケーションのログにエラーコードが出ている場合、公式ドキュメントの
-- 「Appendix A. PostgreSQL Error Codes」で意味を調べられる
-- 例: 23505 = unique_violation

アプリケーションのフレームワーク(Laravel、Djangoなど)によっては、生のエラーメッセージではなくSQLSTATEコードだけがログに残ることもあるため、コードと原因の対応を覚えておくと調査が速くなります。

ログレベルを上げて詳細を確認する

エラーメッセージだけでは原因が分からない場合、postgresql.conflog_statementallにして、実行されたSQL文自体をログに残す方法も有効です。

# postgresql.conf(調査時のみ一時的に設定、常時ONは非推奨)
log_statement = 'all'

本番環境で常時有効にするとログ量が膨大になるため、障害調査時だけ一時的に有効化し、調査が終わったら元に戻すことを忘れないようにしてください。

まとめ

この記事のポイント

  • 接続エラーの多くは、ロールの存在確認とpg_hba.confの認証方式チェックで解決する
  • 制約違反のエラーはDETAIL行に具体的な値が出るため、必ず確認してから対処する
  • 権限エラーはロール権限管理・RLSの設定を見直すきっかけとして活用する
  • SQLSTATEコードとログレベルの調整を覚えておくと、原因調査のスピードが上がる

次に読むべき記事

  • ロールベースの権限管理(CREATE ROLE)
  • UPSERT(ON CONFLICT)とRETURNINGで往復を減らす
  • PostgreSQLとMongoDB、JSONBはどこまでMongoDBの代わりになるか比較検証

タグ: PostgreSQL, 初心者向け, エラー解決

タイトルとURLをコピーしました