エラーの内容
CSVファイルをインポートするために COPY コマンドを実行したところ、以下のエラーが表示された:
ERROR: could not open file "/var/data/import.csv" for reading: Permission denied
ファイルはそこに存在している。cat で確認しても問題ない。それでも PostgreSQL は拒否する。なぜなら、PostgreSQL はファイルを postgres OSユーザーとして読み込むからだ — あなたのユーザーとしてではない。そして postgres はそのパスにアクセスする権限を持っていない。
なぜこのエラーが起きるのか
サーバーサイドの COPY FROM は postgres システムアカウントで実行される。よくある落とし穴が2つある:
- ファイルが
postgresによって読み取れない。 - パス中の親ディレクトリが
postgresのトラバースをブロックしている — 実行(x)ビットがない。ファイルが 777 でも、/var/data/が別ユーザー所有でモード700なら動かない。
注意:これはサーバーサイドの COPY であり、\copy ではない。psql クライアントコマンド \copy はあなたのユーザーとして実行される。全く別物だ — 詳しくは後述する。
ステップごとの解決方法
ステップ 1 — ファイルの所有者を確認する
ls -la /var/data/import.csv
ls -la /var/data/
Ubuntu サーバーの初期状態での典型的な出力:
-rw------- 1 ubuntu ubuntu 204800 Apr 27 10:00 /var/data/import.csv
drwx------ 2 ubuntu ubuntu 4096 Apr 27 09:55 /var/data/
ディレクトリのモードが 700 の場合、ubuntu のみが入れる。postgres ユーザーはファイルに到達する前にブロックされる。
ステップ 2 — PostgreSQL が実行されているユーザーを確認する
ps aux | grep postgres | head -3
最初のカラムが OSユーザーだ。ほとんどのシステムでは postgres だが、カスタムインストールでは別のアカウントを使う場合もある — 確認する価値がある。
ステップ 3 — ファイルの読み取り権限を付与する
chmod o+r /var/data/import.csv
数値形式での同等コマンド:
chmod 644 /var/data/import.csv
ステップ 4 — パス上の全ディレクトリに実行権限を付与する
PostgreSQL はパス上のすべてのディレクトリをトラバースする必要がある。最後のディレクトリだけではない。/var/data/import.csv の場合、/var と /var/data の両方に postgres の実行ビットが必要だ。多くのシステムでは /var はデフォルトで全ユーザーに実行権限があるが、/var/data はしばしばロックされている:
chmod o+x /var/data/
/home/ubuntu/data/imports/ のような深いパスは、各セグメントに o+x が必要だ。一つずつ確認していこう。
ステップ 5 — COPY コマンドを再実行する
COPY your_table FROM '/var/data/import.csv' WITH (FORMAT csv, HEADER true);
権限エラーは出なくなる。
代替案:ファイルを /tmp に移動する
ディレクトリを開放したくない場合は、PostgreSQL がすでにアクセスできる場所にファイルをコピーするだけでよい:
cp /var/data/import.csv /tmp/import.csv
chmod 644 /tmp/import.csv
そこからインポートを実行する:
COPY your_table FROM '/tmp/import.csv' WITH (FORMAT csv, HEADER true);
完了したら後片付けをする:
rm /tmp/import.csv
一回限りのインポートには手軽でリスクが低い方法だ。
代替案:\copy に切り替える
psql でクエリを実行しているなら? SQL の COPY をやめて代わりに \copy を使おう:
\copy your_table FROM '/var/data/import.csv' WITH (FORMAT csv, HEADER true);
クライアントサイドで自分のユーザーとして実行される。サーバーサイドの権限問題は発生しない。大きなファイル — たとえば 500 MB 以上 — の場合、データがクライアント接続を経由するためわずかなパフォーマンスコストがある。100 MB 未満なら差を感じることはないだろう。
ローカルマシン上の CSV を使ってリモートの PostgreSQL サーバーに接続する場合、サーバーサイドの COPY はノートパソコンのファイルシステムにアクセスできないため、いずれにしても \copy 一択となる。
インポート前に確認する
最速のサニティチェック — postgres ユーザーに成り代わり、ファイルを直接読み込んでみる:
sudo -u postgres cat /var/data/import.csv | head -5
エラーなしで5行が表示されたなら、PostgreSQL は読み込める。それでも Permission denied が出る場合、パス上のどこかのディレクトリの権限が誤っている — ステップ 4 を見直そう。
Tips
chmod の値を視覚的に確認する
644 と 640 のような8進数モードは、所有者・グループ・その他で異なるアクセス権が必要な場合に混同しやすい。ToolCraft の Unix Permissions Calculator を使えば、適用前に chmod の値が何を許可するかを正確に確認できる — 頭の中で8進数を解読するより圧倒的に速い。
world-readable よりグループ所有権を優先する
ファイルを o+r にするとシステム上の全ユーザーが読み取れるようになる。より厳密なアプローチ:共有グループを作成し、postgres を追加して、グループ権限を使う。
# Create a shared group and add postgres to it
sudo groupadd dataimport
sudo usermod -aG dataimport postgres
# Set group ownership
chown :dataimport /var/data/
chown :dataimport /var/data/import.csv
# Only owner and group can access
chmod 750 /var/data/
chmod 640 /var/data/import.csv
サーバー上の他のユーザーはファイルにアクセスできない。共有環境では圧倒的にクリーンだ。
定期的なインポートには専用のステージングディレクトリを使う
定期的にインポートを行う場合は、postgres が完全に所有するディレクトリを用意しよう:
sudo mkdir -p /var/pgimport
sudo chown postgres:postgres /var/pgimport
sudo chmod 700 /var/pgimport
インポートのたびにそこにファイルを配置する。postgres がディレクトリ全体を所有しているため、権限エラーは起きなくなる。
COPY と \copy — クイックリファレンス
COPY— SQL コマンド。データベースサーバー上で実行される。postgresOSユーザーの権限を使用する。ファイルはサーバー上に存在する必要がある。\copy— psql メタコマンド。クライアントマシン上で実行される。自分の OSユーザーの権限を使用する。ファイルはローカルマシン上のどこにあってもよい。

