PostgreSQL「cannot drop the currently open database」の原因と対処
- 作成日 2025.08.22
- 更新日 2025.10.06
- その他
概要。自分が今まさに接続中のデータベースを DROP しようとすると PostgreSQL は「現在開いている DB は削除できない」と拒否する。正しい手順は「別 DB に接続→新規接続を遮断→既存接続を切断→DROP」。接続プールやマネージド環境、権限・トランザクション、類似エラーとの違いまでを手順化。貼って使えるコマンド/SQL付き。
エラーの意味と発生条件(なぜ起きるか)
・実行セッションの current_database() が DROP 対象そのもの
・セッションが切り替えられない(\c / 新接続ができない)状態で DROP を実行
・サブエラーとして「DROP DATABASE cannot run inside a transaction block」や「must be owner of database」が同時に出ることもある
よく似たエラーとの違い(切り分け早見)
・cannot drop the currently open database … 自分がその DB に接続中
・is being accessed by other users … 他のセッションが接続中(自分は別 DB から実行している)
・must be owner of database … 所有者でない(権限不足)
・cannot run inside a transaction block … BEGIN 内で実行している
最短手順(まずはこれ)
— 1) まず「別のデータベース」に接続(postgres や template1)
\c postgres
— 2) 対象 DB への新規接続を止める(CONNECT 権限を剥奪)
REVOKE CONNECT ON DATABASE target_db FROM PUBLIC;
— 3) 既存接続を終了(superuser で)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = ‘target_db’ AND pid <> pg_backend_pid();
— 4) DROP を実行(トランザクション外で)
DROP DATABASE target_db;
psql/CLI 派向け(dropdb の活用)
[code]
他 DB に接続して強制削除(PostgreSQL 13+ のサーバなら WITH (FORCE) と同等挙動)
dropdb –force -h localhost -U postgres target_db
psql での FORCE 版(サーバが対応していれば)
\c postgres
DROP DATABASE target_db WITH (FORCE);
[/code]
・dropdb はデフォルトで「対象以外の DB(template1 等)」へ接続して DROP を投げるため、「currently open」の罠を回避しやすい
接続の遮断を確実にする(プール/自動再接続対策)
[code]
— 1) CONNECT 権限を剥奪(新規接続を防ぐ)
REVOKE CONNECT ON DATABASE target_db FROM PUBLIC;
REVOKE CONNECT ON DATABASE target_db FROM some_role;
— 2) 可能なら接続プールを一時停止(例:PgBouncer)
— * PgBouncer: pgbouncer 側で “PAUSE target_db” → “KILL” → “RESUME” を順に
— * アプリの自動再接続/ヘルスチェックを一時的に無効化
— 3) 既存接続を terminate
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname=’target_db’ AND pid <> pg_backend_pid();
[/code]
実行中の接続を見つけるクエリ(犯人特定)
[code]
SELECT pid, usename, application_name, client_addr, backend_start,
state, wait_event_type, wait_event, query
FROM pg_stat_activity
WHERE datname = ‘target_db’
ORDER BY backend_start;
[/code]
・接続元アプリやプール、バッチ名を把握して確実に止める
トランザクションの注意(BEGIN 内では不可)
[code]
— NG: これは失敗する
BEGIN;
DROP DATABASE target_db; — ERROR: DROP DATABASE cannot run inside a transaction block
— OK: トランザクション外で実行
DROP DATABASE target_db;
[/code]
権限と所有者の確認(must be owner の回避)
[code]
— 所有者を確認
SELECT d.datname, r.rolname AS owner
FROM pg_database d JOIN pg_roles r ON r.oid = d.datdba
WHERE d.datname = ‘target_db’;
— 必要に応じて所有者を切替(別 DB から実行)
ALTER DATABASE target_db OWNER TO new_owner;
— または superuser で DROP を実行
[/code]
テンプレート/システム DB の扱い(落とし穴)
・template0 はシステム保護のため接続不可(再作成のための素体)。DROP 対象にしない
・template1/postgres は管理・テンプレ用途。削除しても再作成できるが、過不足なく再構築できる準備が無い限り避ける
マネージド環境(RDS/Cloud SQL など)での要点
[code]
— RDS 例(rds_superuser で)
\c postgres
REVOKE CONNECT ON DATABASE target_db FROM PUBLIC;
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname=’target_db’ AND pid <> pg_backend_pid();
DROP DATABASE target_db;
[/code]
・サービス側のガードで WITH (FORCE) が無効なことがある。ドキュメントの推奨手順に従う
消した後に同名で作り直す(再作成の安全形)
[code]
— ロケール/エンコーディング/テンプレートを明示して再作成
CREATE DATABASE target_db
WITH OWNER = app_user
TEMPLATE = template1
ENCODING = ‘UTF8’
LC_COLLATE = ‘C’
LC_CTYPE = ‘C’
CONNECTION LIMIT = -1;
[/code]
自動化スニペット(安全停止→DROP)
[code]
— psql から \i で流せるスクリプト
\set db ‘target_db’
\c postgres
REVOKE CONNECT ON DATABASE :db FROM PUBLIC;
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = :’db’ AND pid <> pg_backend_pid();
DROP DATABASE :db;
[/code]
失敗時のチェックリスト(原因別の決め手)
・still “currently open” → 自分が :target_db に接続していないか / \c postgres へ
・“accessed by other users” → REVOKE CONNECT → pg_terminate_backend → プール停止を順に
・“transaction block” → BEGIN を使っていないか / autocommit を有効化
・“must be owner” → 所有者 or superuser で実行 / OWNER を付け替え
・落ち続ける → ジョブやヘルスチェックの再接続を無効化できているかを確認
小ネタ(覚えておくと速い)
・dropdb –force はサーバ側が対応していれば最短
・\conninfo で現在接続先を即確認
・pg_terminate_backend は superuser でのみ実行可能(対象は他セッション)
・REVOKE CONNECT は「新規接続」を止めるだけ。既存接続は terminate/cancel が必要
-
前の記事
MySQLのエラー『エラー1049: Unknown Database』の解決方法 2025.08.22
-
次の記事
PostgreSQLでの『column reference is ambiguous』の原因と対処法 2025.08.25
コメントを書く