Azure Database for PostgreSQL フレキシブルサーバーのメジャーアップグレード(14→17)で「CREATE TABLESPACE /mnt/pg_tmp」権限エラーが出る原因と解決策

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 等が残存
一時 TSSHOW temp_tablespaces;空(ブランク)temptblspace などが入っている
ダンプ設定ユーティリティのオプション確認--no-tablespaces 指定TS 定義を含むダンプを作成

Azure 公式のメジャーアップグレード機能の活用

可能であれば、Azure Portal / CLI が提供するメジャーアップグレード機能を使うのが最も安全です。ツール内部で Azure 側の制約を踏まえた手順が実行されるため、ユーザー側でテーブルスペースを作成する必要はありません。CLI の一例(パラメータは環境に合わせて調整してください):

# 例:フレキシブルサーバーのメジャーアップグレードを CLI で実行
# 実行前にバックアップとメンテナンス時間の確保を
az postgres flexible-server upgrade \
  --resource-group &lt;RG_NAME&gt; \
  --name &lt;SERVER_NAME&gt; \
  --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
一般ユーザーいいえ不可上記に同じ

結論として、テーブルスペースを作らない/持ち込まないことが、フレキシブルサーバーのメジャーアップグレード成功の最短ルートです。

トラブルシューティングの実践フロー

  1. 失敗ログの確認:エラーが CREATE TABLESPACE ... /mnt/pg_tmp で止まっていないか。
  2. TS の棚卸し:ユーザー定義 TS が 0 であることを確認。
  3. 依存除去:TS 配下のテーブル/インデックス/マテビューを pg_default に移動。
  4. TS 削除:DROP TABLESPACE IF EXISTS。
  5. ダンプ設定見直し:--no-tablespaces を付ける、またはポータル/CLI の公式機能で実行。
  6. 拡張・トリガ整合:ALTER EXTENSION ... UPDATE、イベントトリガを一時無効化。
  7. 再実行:Upgrade Utility もしくはポータル/CLI でメジャーアップグレード。
  8. アフターケア: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 ; 

本稿の手順を土台に、環境固有の要件(ダウンタイム制約、データ容量、拡張の種類)に合わせて微調整すれば、権限周りの落とし穴にハマらず、再現性の高いメジャーアップグレードが可能になります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次