Azure Database for PostgreSQL フレキシブルサーバーで、Azure Upgrade Utility による PostgreSQL 14→17 のメジャーアップグレードが「CREATE TABLESPACE ‘temptblspace’ … Operation not permitted」で失敗する――実運用で遭遇しやすいこの事象を、背景から回避策、検証・再実行・最適化まで一気通貫で解説します。権限エラーを根本から排除し、安全にアップグレードを完了させましょう。
想定シナリオとエラーメッセージ
Azure Upgrade Utility を使って PostgreSQL 14 から 17 へメジャーアップグレードを実行すると、処理開始から約 15 分ほどで次のエラーが発生し失敗します。
CREATE TABLESPACE "temptblspace" OWNER "azure_pg_admin" LOCATION '/mnt/pg_tmp';
ERROR: could not set permissions on directory "/mnt/pg_tmp": Operation not permitted
アップグレードを実行したユーザーは Owner 権限と User Access Administrator 権限を持っており、一見すると十分に見えますが、フレキシブルサーバー特有の権限制約が影響しています。
失敗の本質:なぜテーブルスペース作成で止まるのか
- スーパーユーザー権限が使えない:フレキシブルサーバーの管理ロール
azure_pg_adminはスーパーユーザーではありません。OS レベルのファイル操作や一部のサーバー管理コマンドは不可です。 - OS 上の任意パスへテーブルスペースを作成できない:
CREATE TABLESPACEは DB 外のディレクトリ権限変更を伴います。Azure 管理下のパス(例:/mnt/pg_tmp)はユーザーが chmod/chown 相当を行えないため失敗します。 - Upgrade Utility が一時 TS を前提にするケース:高速化や互換性の都合で、一時テーブルスペース(例:
temptblspace)を作ろうとする振る舞いがあります。過去に作成・残存していた TS を参照/再作成しようとして失敗することもあります。
まず把握すべき制約と方針
| 観点 | フレキシブルサーバーの前提 | アップグレード方針 |
|---|---|---|
| 権限モデル | スーパーユーザー不可、azure_pg_admin は管理ロールに限定 | OS に依存する操作を排除。DB 内の論理操作で完結 |
| テーブルスペース | ユーザー任意パスの TS は事実上不可。pg_default / pg_global に統一 | TS 参照を整理/撤廃してからアップグレード |
| 一時領域 | /mnt/pg_tmp などは Azure 管理。権限変更不可 | 一時 TS の新規作成を行わない(ツール設定・ダンプオプションで抑止) |
最短で解決する対処手順(実行順)
1. 不要なテーブルスペース・拡張の洗い出し
まずは現状の TS と拡張機能を棚卸しします。未対応の拡張や過去に作った TS が残っていないかを確認してください。
-- 拡張機能の一覧(名称とバージョン)
SELECT extname, extversion FROM pg_extension ORDER BY extname;
-- テーブルスペースの一覧と物理パス
SELECT spcname, pg_tablespace_location(oid) FROM pg_tablespace ORDER BY spcname;
-- ユーザー定義テーブルスペースのみ抽出(存在するなら要対応)
SELECT spcname
FROM pg_tablespace
WHERE spcname NOT IN ('pg_default','pg_global');
2. 依存オブジェクトの確認
一時 TS(例:temptblspace)やユーザー定義 TS に依存しているオブジェクト(テーブル・インデックス・マテビュー)があるか調べます。
-- temptblspace にぶら下がるリレーションを確認
SELECT relkind,
relname
FROM pg_class
WHERE reltablespace = (SELECT oid FROM pg_tablespace WHERE spcname = 'temptblspace');
依存がなければ次へ進めます。依存がある場合は pg_default に退避します(次項)。
3. 依存の退避(TS を空にする)
テーブル・インデックスなどを一括で pg_default に移します。PostgreSQL には TS 単位の一括移動があり、個々のテーブル名に触れずに移行できます。
-- テーブルを一括移動
ALTER TABLE ALL IN TABLESPACE temptblspace SET TABLESPACE pg_default;
-- インデックスを一括移動
ALTER INDEX ALL IN TABLESPACE temptblspace SET TABLESPACE pg_default;
-- マテリアライズド・ビューを一括移動
ALTER MATERIALIZED VIEW ALL IN TABLESPACE temptblspace SET TABLESPACE pg_default;
上記がバージョン差分などで通らない場合は、カタログから動的 SQL を作る方法も有効です。
-- 例:テーブル個別に移す動的 SQL 生成
SELECT 'ALTER TABLE ' || quote_ident(nspname) || '.' || quote_ident(relname) ||
' SET TABLESPACE pg_default;'
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.reltablespace = (SELECT oid FROM pg_tablespace WHERE spcname = 'temptblspace')
AND c.relkind = 'r';
4. 問題のテーブルスペースを削除
TS に依存がないことを確認したら削除します。存在しない環境ではエラーにならないよう IF EXISTS を付けます。
DROP TABLESPACE IF EXISTS temptblspace;
5. 拡張機能・イベントトリガの整合性
メジャーアップグレードで互換性が崩れる拡張は、事前更新または一時的に削除します。イベントトリガが DDL をフックして失敗を誘発するケースもあるため確認します。
-- 拡張の更新候補を把握
SELECT extname, extversion FROM pg_extension ORDER BY extname;
-- 必要に応じて更新
ALTER EXTENSION postgis UPDATE;
ALTER EXTENSION citext UPDATE;
-- イベントトリガの確認(ある場合は一時 disable も検討)
SELECT evtname, evtevent, evtenabled FROM pg_event_trigger;
6. ストレージの整頓(空き領域・無駄の削減)
- 不要な Large Object の削除(
lo_unlinkを使用) VACUUM FULLまたはサイズと相談して通常VACUUMを徹底- 統計情報の鮮度確保のため後述の
ANALYZEを実行
7. Upgrade Utility を再実行
TS 参照の解消と拡張整合性が取れていれば、エラーは解消し 14→17 のアップグレードが完了します。
テーブルスペースが勝手に作られるのを防ぐコツ
発生を未然に防ぐ観点で、以下の対策が効果的です。
| 対策 | 概要 | コマンド例・ポイント |
|---|---|---|
| TS 無効化ダンプ | ダンプ時に TS 定義を含めない | pg_dump --no-tablespaces を指定(復元側で pg_default に集約) |
| デフォルト TS を空に | 新規オブジェクトが TS 指定されないようにする | ALTER DATABASE <db> SET default_tablespace = ''; |
| 一時 TS 不使用 | セッションの一時 TS を空にして強制しない | SET temp_tablespaces = '';(ツール側のオプションで上書きされないよう確認) |
| 事前クリーン | 残存 TS と依存を消しておく | 前章の一括移動 → DROP TABLESPACE IF EXISTS |
権限エラーを回避するための事前チェックリスト
| チェック | 実行例 | 合格基準 | NG 例 |
|---|---|---|---|
| TS 残存 | SELECT spcname FROM pg_tablespace WHERE spcname NOT IN ('pg_default','pg_global'); | 0 行 | ユーザー定義 TS が 1 つでも存在 |
| 依存オブジェクト | 前掲の依存確認クエリ | 0 行 | テーブルやインデックスがぶら下がっている |
| 拡張互換 | SELECT extname, extversion FROM pg_extension; | 未対応なし | 未対応バージョンの postgis 等が残存 |
| 一時 TS | SHOW temp_tablespaces; | 空(ブランク) | temptblspace などが入っている |
| ダンプ設定 | ユーティリティのオプション確認 | --no-tablespaces 指定 | TS 定義を含むダンプを作成 |
Azure 公式のメジャーアップグレード機能の活用
可能であれば、Azure Portal / CLI が提供するメジャーアップグレード機能を使うのが最も安全です。ツール内部で Azure 側の制約を踏まえた手順が実行されるため、ユーザー側でテーブルスペースを作成する必要はありません。CLI の一例(パラメータは環境に合わせて調整してください):
# 例:フレキシブルサーバーのメジャーアップグレードを CLI で実行
# 実行前にバックアップとメンテナンス時間の確保を
az postgres flexible-server upgrade \
--resource-group <RG_NAME> \
--name <SERVER_NAME> \
--target-server-version 17
この方法を採る場合でも、前述の「TS 参照をなくす」「拡張を対応版にする」などの事前整備は有効です。停止時間の短縮や、予期せぬリトライを避けるのに役立ちます。
Upgrade Utility 利用時のおすすめ構成
- ダンプ/リストア方式の場合:
pg_dumpに--no-tablespacesを付与し、リストアでpg_defaultに集約。 - 論理レプリケーション切替の場合:移行先(PG17)を先に作り、拡張互換とスキーマ整合性を取った上で
pub/subを構成し、短時間のカットオーバーで切替。TS 参照は初期スキーマ生成段階で排除。
いずれの方式でも「ユーザー定義 TS を使わない」方針を徹底することで、権限エラーの根を絶ちます。
アップグレード後の最適化(必須のアフターケア)
- 統計更新:全 DB・全スキーマで
ANALYZEを実行し、プランナー統計を PG17 に最適化。 - 再インデックス候補の見直し:一部の演算子クラスや ICU 照合の更新によりプランが変わる場合は
REINDEXを検討。 - 拡張とアプリ設定の再調整:
ALTER EXTENSION ... UPDATE、ID 列はGENERATED ALWAYS AS IDENTITYへ移行検討など。 - パラメータ再点検:
work_mem、maintenance_work_mem、effective_cache_size、random_page_cost等をワークロードに合わせて更新。
-- 全テーブルの統計更新(スーパーユーザー不要)
DO $$
DECLARE r RECORD;
BEGIN
FOR r IN SELECT quote_ident(nspname) AS n, quote_ident(relname) AS r
FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace
WHERE relkind IN ('r','m')
LOOP
EXECUTE 'ANALYZE ' || r.n || '.' || r.r;
END LOOP;
END$$;
ロール・権限まわりの要点整理
| ロール | スーパーユーザー | OS 依存操作 | 代表的に不可な例 |
|---|---|---|---|
| azure_pg_admin | いいえ | 不可 | CREATE TABLESPACE ... LOCATION '/mnt/...'、COPY ... PROGRAM |
| 一般ユーザー | いいえ | 不可 | 上記に同じ |
結論として、テーブルスペースを作らない/持ち込まないことが、フレキシブルサーバーのメジャーアップグレード成功の最短ルートです。
トラブルシューティングの実践フロー
- 失敗ログの確認:エラーが
CREATE TABLESPACE ... /mnt/pg_tmpで止まっていないか。 - TS の棚卸し:ユーザー定義 TS が 0 であることを確認。
- 依存除去:TS 配下のテーブル/インデックス/マテビューを
pg_defaultに移動。 - TS 削除:
DROP TABLESPACE IF EXISTS。 - ダンプ設定見直し:
--no-tablespacesを付ける、またはポータル/CLI の公式機能で実行。 - 拡張・トリガ整合:
ALTER EXTENSION ... UPDATE、イベントトリガを一時無効化。 - 再実行:Upgrade Utility もしくはポータル/CLI でメジャーアップグレード。
- アフターケア:
ANALYZE、VACUUM、アプリ設定見直し。
安全運用のためのバックアップとロールバック戦略
- 二系統バックアップ:Azure の自動バックアップに加え、
pg_dump/pg_dumpallを取得(スキーマ+ロール+重要データ)。 - PITR 可否:復旧目標時点(RPO)に応じてログ保持期間を再点検。
- リハーサル:同サイズのテスト環境で手順を検証。復元・検証に要する時間を実測。
- 切替計画:ダウンタイムを許容できない場合は論理レプリケーションのハイブリッド移行を検討。
サンプル:一連の確認・整理・復元のミニスクリプト集
TS 参照を一括で外す(安全版)
-- 1) 既知のユーザー TS を列挙
SELECT spcname FROM pg_tablespace WHERE spcname NOT IN ('pg_default','pg_global');
-- 2) 依存がある場合は一括移動
ALTER TABLE ALL IN TABLESPACE temptblspace SET TABLESPACE pg_default;
ALTER INDEX ALL IN TABLESPACE temptblspace SET TABLESPACE pg_default;
ALTER MATERIALIZED VIEW ALL IN TABLESPACE temptblspace SET TABLESPACE pg_default;
-- 3) TS を削除(存在しなければ無視)
DROP TABLESPACE IF EXISTS temptblspace;
-- 4) デフォルト/一時 TS を空に
ALTER DATABASE SET default_tablespace = '';
ALTER DATABASE SET temp_tablespaces = '';
ダンプ/リストア(TS 無効化)
# スキーマとデータを TS なしでダンプ
pg_dump -h <host> -p <port> -U <user> --format=custom --no-tablespaces -d <db> -f dump_no_ts.backup
# リストア(復元先は PG17)
pg_restore -h -p -U -d --create --clean --if-exists dump_no_ts.backup
Large Object の整理
-- 参照が切れた Large Object を抽出
SELECT lom.oid
FROM pg_largeobject_metadata lom
LEFT JOIN pg_depend d
ON d.classid = 'pg_largeobject'::regclass
AND d.objid = lom.oid
WHERE d.objid IS NULL;
-- 必要に応じて lo_unlink() で削除(慎重に!)
SELECT lo_unlink();
よくある質問(FAQ)
Q. Owner と User Access Administrator を持っているのに、なぜ権限エラー?
A. Azure の RBAC は Azure リソース管理の権限です。PostgreSQL のスーパーユーザーとは別概念であり、OS 依存の操作(テーブルスペース作成など)は許可されません。
Q. temptblspace が見当たりません。何を消せばよい?
A. まず TS 自体が存在しないかを確認し、存在しないならダンプ時に --no-tablespaces を付与、default_tablespace/temp_tablespaces を空に設定して「TS を使わない」状態で再実行します。
Q. ユーザー定義 TS が業務要件で必要です。どうすべき?
A. フレキシブルサーバーでは設計上難しいため、TS に依存しない物理配置(パーティショニングやスキーマ分割)を検討してください。TS 前提の運用要件は、サービス選択も含めた再設計が推奨です。
Q. 既存拡張がアップグレードの邪魔をします。
A. 影響の大きい拡張は一時的に外す/更新してから再適用するのが安全です。ALTER EXTENSION ... UPDATE を基本とし、バージョン互換を確認してください。
まとめ:権限エラーを回避して一発で上げるコツ
- ユーザー定義テーブルスペースを全廃し、
pg_default/pg_globalに統一する。 - ダンプ時は
--no-tablespaces、セッションではtemp_tablespaces=''を徹底する。 - 拡張・イベントトリガの整合を先に取る(
ALTER EXTENSION ... UPDATE)。 - 公式のメジャーアップグレード機能(Portal / CLI)を優先し、手動 TS 作成を避ける。
- バックアップ二系統+リハーサルで、想定外のやり直しに備える。
これらを実施すれば、CREATE TABLESPACE ... /mnt/pg_tmp に起因する権限エラーは回避でき、PostgreSQL 14→17 のメジャーアップグレードを安全かつ確実に完了できます。
付録:チェック&実行テンプレート(そのまま使える)
事前チェック
-- TS の存在確認
SELECT spcname FROM pg_tablespace WHERE spcname NOT IN ('pg_default','pg_global');
-- 一時 TS 無効化(セッション)
SET temp_tablespaces = '';
-- 拡張の棚卸し
SELECT extname, extversion FROM pg_extension ORDER BY extname;
-- イベントトリガ確認
SELECT evtname, evtevent, evtenabled FROM pg_event_trigger;
TS 参照の除去と削除
ALTER TABLE ALL IN TABLESPACE temptblspace SET TABLESPACE pg_default;
ALTER INDEX ALL IN TABLESPACE temptblspace SET TABLESPACE pg_default;
ALTER MATERIALIZED VIEW ALL IN TABLESPACE temptblspace SET TABLESPACE pg_default;
DROP TABLESPACE IF EXISTS temptblspace;
アップグレード後の初期チューニング
-- 統計更新
ANALYZE;
-- 必要に応じて再インデックス(例)
REINDEX DATABASE ;
本稿の手順を土台に、環境固有の要件(ダウンタイム制約、データ容量、拡張の種類)に合わせて微調整すれば、権限周りの落とし穴にハマらず、再現性の高いメジャーアップグレードが可能になります。

コメント