【PL/SQL】依存オブジェクトとINVALID再コンパイルの制御|USER_DEPENDENCIES・UTL_RECOMP

【PL/SQL】依存オブジェクトとINVALID再コンパイルの制御 PL/SQL

PL/SQLを本番へデプロイするとき、意外と事故になりやすいのが依存オブジェクトのINVALID化です。パッケージ仕様、ビュー、型、シノニム、権限、参照先テーブルの変更によって、関連するプロシージャやパッケージが無効化され、次回実行時に自動再コンパイルが走ることがあります。

自動再コンパイルで直ることもありますが、本番の初回アクセスで遅延したり、権限不足・参照切れ・シグネチャ不一致で失敗したりすると障害になります。この記事では、USER_DEPENDENCIESUSER_OBJECTSUSER_ERRORSUTL_RECOMPDBMS_UTILITY.COMPILE_SCHEMA を使って、PL/SQLの依存関係とINVALID再コンパイルを制御する方法を整理します。

コンパイルエラーの読み方は PL/SQLコンパイル時エラーと警告の完全対処ガイド、パッケージ仕様と本体の分け方は PL/SQLパッケージ仕様部と本体部の分け方 もあわせて確認してください。

この記事で扱うこと

  • 依存オブジェクトがINVALIDになる主な原因
  • USER_DEPENDENCIES / ALL_DEPENDENCIES / DBA_DEPENDENCIES の使い方
  • USER_ERRORS / DBA_ERRORS / SHOW ERRORS による診断
  • パッケージSPEC/BODY、TYPE、VIEW、TRIGGERのコンパイル順序
  • UTL_RECOMPDBMS_UTILITY.COMPILE_SCHEMA の使い分け
  • デプロイ前後の確認、ウォームアップ、ORA-04068対策
スポンサーリンク

最初に結論:INVALIDは本番前に収束させる

INVALIDオブジェクトは、実行時に自動で再コンパイルされることがあります。しかし、本番ではその自動再コンパイルに頼り切らない方が安全です。デプロイ直後に依存関係を確認し、必要な順序で再コンパイルし、残ったエラーを確認してから代表処理を軽く実行します。

変更前変更対象の下流依存を確認します。パッケージSPEC、TYPE、VIEW、権限の変更は影響が広くなりやすいです。
変更中TYPE、PACKAGE SPEC、PACKAGE BODY、VIEW、TRIGGERの順序を意識してコンパイルします。
変更後USER_OBJECTSUSER_ERRORS でINVALIDとエラーを確認し、必要なら UTL_RECOMP を実行します。
公開前代表パッケージやAPIを一度呼び、初回再コンパイルやパッケージ状態破棄の影響を先に出します。

依存関係を確認する

まず、変更対象を参照しているオブジェクトを確認します。自分のスキーマ内なら USER_DEPENDENCIES、権限がある範囲を広く見るなら ALL_DEPENDENCIES、DBA視点なら DBA_DEPENDENCIES を使います。

下流依存を確認する

dependency-check.sql
-- 指定オブジェクトを参照している下流オブジェクト
SELECT name,
       type,
       referenced_name,
       referenced_type
  FROM user_dependencies
 WHERE referenced_name = UPPER(:object_name)
 ORDER BY type, name;

-- 自分が依存している上流オブジェクト
SELECT referenced_name,
       referenced_type,
       name,
       type
  FROM user_dependencies
 WHERE name = UPPER(:object_name)
 ORDER BY referenced_type, referenced_name;

パッケージ仕様、オブジェクト型、ビュー、シノニム、権限の変更は影響範囲が広くなりがちです。特に公開APIとして使われるパッケージ仕様は、安易に引数や戻り値を変えない設計が重要です。パッケージ設計全体は PL/SQLパッケージ設計 も参考になります。

INVALID一覧とエラー内容を確認する

再コンパイル前後では、どのオブジェクトがINVALIDなのか、なぜ失敗しているのかを分けて見ます。USER_OBJECTS は状態の一覧、USER_ERRORS はコンパイルエラーの中身を見るために使います。

INVALIDとコンパイルエラーを確認する

invalid-error-check.sql
-- INVALIDオブジェクト一覧
SELECT object_name,
       object_type,
       status,
       last_ddl_time
  FROM user_objects
 WHERE status = 'INVALID'
 ORDER BY object_type, object_name;

-- コンパイルエラーの詳細
SELECT name,
       type,
       line,
       position,
       text
  FROM user_errors
 ORDER BY name, sequence;

SQL*PlusやSQLclでは、直前にコンパイルしたオブジェクトに対して SHOW ERRORS も使えます。CI/CDや自動デプロイでは、USER_ERRORS をSQLで取得し、エラーが残っていれば失敗扱いにするのが実務的です。

INVALIDが発生する代表パターン

INVALIDは単に「コンパイルに失敗した」という意味だけではありません。依存先が変わったため、次回実行前に再検証が必要になっている状態も含みます。

パッケージSPEC変更引数、戻り値、公開型、公開定数を変えると、呼び出し側が広くINVALID化しやすくなります。
パッケージBODY変更通常は呼び出し側への影響を抑えやすいです。公開仕様を変えずに内部実装だけ差し替えるのが基本です。
テーブル・ビュー変更参照列の削除、型変更、ビュー定義変更で、依存するビューやPL/SQLがINVALIDになることがあります。
権限変更直接GRANTが外れた、ロール依存だった、Definer Rightsでロールが無効、などでコンパイル不能になることがあります。
シノニム・DBリンク変更参照先が変わる、存在しない、リモート接続不能などで再コンパイルに失敗することがあります。

AUTHIDと権限の関係は AUTHIDと権限管理の設計、Invoker Rights / Definer Rights の使い分けは Invoker RightsとDefiner Rights完全ガイド が関連します。

再コンパイル順序の基本

INVALIDが複数残っている場合、手当たり次第にコンパイルするより、依存の上流から順に直す方が早く収束します。型、パッケージ仕様、パッケージ本体、単体プロシージャ/関数、ビュー、トリガーの順序を意識します。

1. TYPE / OBJECT TYPEオブジェクト型やコレクション型は、パッケージや表関数の前提になります。先に通します。
2. PACKAGE SPEC公開インターフェースを先に通します。ここが失敗するとBODYや呼び出し側も安定しません。
3. PACKAGE BODY仕様が通ったあとに実装をコンパイルします。内部エラーはUSER_ERRORSで潰します。
4. PROCEDURE / FUNCTIONパッケージ外の単体プログラムをコンパイルします。依存先が通っているか確認します。
5. VIEW / TRIGGERビューやトリガーは参照先・権限・列変更の影響を受けやすいため、最後に残ったエラーを確認します。

単体再コンパイルの例

manual-compile.sql
ALTER TYPE customer_obj COMPILE;

ALTER PACKAGE app_api COMPILE SPECIFICATION;
ALTER PACKAGE app_api COMPILE BODY;

ALTER PROCEDURE import_orders COMPILE;
ALTER FUNCTION calc_tax COMPILE;

ALTER VIEW v_sales_summary COMPILE;
ALTER TRIGGER trg_orders_biu COMPILE;

パッケージ仕様と本体の役割は PL/SQLパッケージ仕様部と本体部の分け方、OBJECT TYPEが絡む場合は OBJECT TYPEとメンバーメソッドの使い方 も確認してください。

UTL_RECOMPで一括再コンパイルする

大量のINVALIDがある場合は、手作業より UTL_RECOMP を使う方が安全です。RECOMP_SERIAL は直列で負荷を抑えやすく、RECOMP_PARALLEL はメンテナンス時間帯に短時間で収束させたい場合に使います。

スキーマ単位で再コンパイルする

utl-recomp.sql
-- 負荷を抑えて直列再コンパイル
BEGIN
  UTL_RECOMP.RECOMP_SERIAL(schema => USER);
END;
/

-- メンテナンス時間帯に並列再コンパイル
BEGIN
  UTL_RECOMP.RECOMP_PARALLEL(
    threads => 4,
    schema  => USER
  );
END;
/

DB全体を対象にする場合はDBA権限や実行タイミングに注意します。本番では、業務時間中に高並列で流すのではなく、リリース枠やメンテナンス枠で実行するのが安全です。

DBMS_UTILITY.COMPILE_SCHEMAを使う場面

DBMS_UTILITY.COMPILE_SCHEMA は、スキーマ内オブジェクトをまとめてコンパイルする手軽な方法です。細かい依存順の制御や大規模環境では UTL_RECOMP を優先し、単純なスキーマ内収束には DBMS_UTILITY を使う、という切り分けが実務的です。

COMPILE_SCHEMAの例

compile-schema.sql
BEGIN
  DBMS_UTILITY.COMPILE_SCHEMA(
    schema      => USER,
    compile_all => FALSE
  );
END;
/

SELECT object_name, object_type
  FROM user_objects
 WHERE status = 'INVALID'
 ORDER BY object_type, object_name;

compile_all => FALSE はINVALIDオブジェクト中心、TRUE はVALIDも含めてコンパイル対象にします。通常のリリース後収束では、まずFALSEで十分です。

utlrp.sqlはいつ使うか

データベースのパッチ適用、アップグレード、コンポーネント追加の後など、DB全体のINVALIDを収束させる場面では utlrp.sql が使われます。アプリケーションの通常デプロイで毎回DB全体に対して実行するものではありません。自スキーマのデプロイなら、まず UTL_RECOMPDBMS_UTILITY.COMPILE_SCHEMA を検討します。

通常デプロイ対象スキーマのINVALID確認とスキーマ単位再コンパイルで対応します。
DBパッチ・アップグレード後DBA作業としてDB全体のINVALID収束を確認します。utlrp.sqlが候補になります。
障害対応いきなり全体再コンパイルせず、依存元とUSER_ERRORSを見て根本原因を特定します。

診断フロー:残ったINVALIDをどう直すか

再コンパイルしてもINVALIDが残る場合は、上から順に原因を切り分けます。

USER_ERRORSを見る行番号、位置、エラーメッセージを確認します。構文エラー、存在しない列、型不一致を先に直します。
依存先の状態を見る参照先ビュー、パッケージ、TYPE、シノニムがINVALIDまたは存在しない場合、先にそちらを直します。
権限を見るPL/SQLのコンパイルにはロール経由ではなく直接GRANTが必要なケースがあります。
シノニムとDBリンクを見るシノニムの参照先、リモートDBリンク、接続権限、名前解決のズレを確認します。
パッケージ状態を見る既存セッションがパッケージ状態を保持している場合、再コンパイル後にORA-04068が出ることがあります。

関連エラーとして、無効オブジェクトの PLS-00905の原因と解決方法、パッケージや関数が無効な ORA-06575の原因と解決方法、トリガー無効の ORA-04098の原因と解決方法 も確認対象です。

ORA-04068とパッケージ状態に注意する

状態を持つパッケージを再コンパイルすると、既存セッションが保持していたパッケージ状態が破棄され、ORA-04068 が発生することがあります。これはINVALIDとは別に、セッション状態とパッケージ再ロードの問題として扱います。

状態を持つパッケージの例

package-state.sql
CREATE OR REPLACE PACKAGE app_session AS
  g_current_user_id NUMBER;
  PROCEDURE set_user(p_user_id NUMBER);
END;
/

CREATE OR REPLACE PACKAGE BODY app_session AS
  PROCEDURE set_user(p_user_id NUMBER) IS
  BEGIN
    g_current_user_id := p_user_id;
  END;
END;
/

このようなパッケージをリリース中に差し替える場合、接続プールの再接続、セッション初期化、状態を持たない設計への変更を検討します。詳しくは ORA-04068完全ガイド が関連します。

デプロイ後の自動収束スクリプト

本番デプロイでは、最後にINVALID数を確認し、再コンパイルし、残ったエラーを出して失敗扱いにするところまで自動化しておくと安全です。

INVALIDが残ったら失敗させる例

deploy-invalid-gate.sql
DECLARE
  v_invalid_count NUMBER;
BEGIN
  UTL_RECOMP.RECOMP_SERIAL(schema => USER);

  SELECT COUNT(*)
    INTO v_invalid_count
    FROM user_objects
   WHERE status = 'INVALID';

  IF v_invalid_count > 0 THEN
    FOR r IN (
      SELECT object_name, object_type
        FROM user_objects
       WHERE status = 'INVALID'
       ORDER BY object_type, object_name
    ) LOOP
      DBMS_OUTPUT.PUT_LINE(
        'INVALID: ' || r.object_type || ' ' || r.object_name
      );
    END LOOP;

    RAISE_APPLICATION_ERROR(
      -20001,
      'INVALID objects remain: ' || v_invalid_count
    );
  END IF;
END;
/

この後に USER_ERRORS を出力すれば、CI/CDログから原因を追いやすくなります。デプロイ直後に代表APIを呼ぶことで、初回コンパイルやカーソルキャッシュの初期化を先に済ませることもできます。初回実行遅延やカーソル周りの観点は カーソルキャッシュとメモリ管理 が関連します。

EBRで無停止切替を検討する場面

大規模システムで、仕様変更と切替を無停止に近づけたい場合は Edition-Based Redefinition も選択肢になります。新しいエディションでオブジェクトを作成・コンパイル・検証し、セッションの接続先エディションを切り替えることで、段階的な移行ができます。

向いている場面公開API、ビュー、パッケージを段階的に切り替えたい大規模環境です。
注意点すべてのオブジェクトがエディション化できるわけではありません。テーブル変更はEditioning Viewなどで吸収します。
小規模環境まずはSPEC安定化、BODY差し替え、再コンパイル自動化で十分なことも多いです。

避けたい運用

INVALIDを放置する実行時に直る前提にすると、初回アクセスで遅延や失敗が出ます。
SPECを頻繁に変える下流依存を広く巻き込みます。公開仕様は薄く安定させ、変更はBODYや内部パッケージへ閉じます。
権限をロール任せにするDefiner RightsのPL/SQLではロールが無効になるため、直接GRANT不足でコンパイル失敗することがあります。
原因を見ずに全体再コンパイルする参照切れや権限不足が原因なら、何度再コンパイルしても直りません。

本番前チェックリスト

  • 変更対象の下流依存を USER_DEPENDENCIES で確認した
  • パッケージSPEC変更が本当に必要か確認した
  • TYPE、PACKAGE SPEC、PACKAGE BODY、VIEW、TRIGGERの順序を意識している
  • USER_OBJECTS でINVALID一覧を確認した
  • USER_ERRORS で残存エラーを確認した
  • 権限はロールではなく直接GRANTされている
  • シノニム、DBリンク、参照先ビューが有効である
  • UTL_RECOMP または DBMS_UTILITY.COMPILE_SCHEMA の実行範囲を決めている
  • 状態を持つパッケージの再コンパイルでORA-04068が出る可能性を考慮している
  • 代表処理のスモークテストをデプロイ後に実行する

まとめ

PL/SQLの依存オブジェクトとINVALID再コンパイルは、リリース作業の最後に軽く見るだけでは不十分です。変更前に依存範囲を把握し、変更後にINVALIDとUSER_ERRORSを確認し、必要に応じてUTL_RECOMPやDBMS_UTILITY.COMPILE_SCHEMAで収束させます。

特にパッケージSPEC、TYPE、ビュー、権限、シノニムの変更は影響が広くなりやすいため、公開仕様を安定させ、BODY側に変更を閉じる設計が有効です。本番では、自動再コンパイルに期待するのではなく、デプロイ手順の中でINVALIDを0に近づけ、残ったエラーを可視化し、代表処理でウォームアップするところまでを標準化しましょう。