【PL/SQL】パッケージ間依存を可視化するメタデータ解析(ALL_DEPENDENCIES活用)

【PL/SQL】パッケージ間依存を可視化するメタデータ解析(ALL_DEPENDENCIES活用) PL/SQL

PL/SQLのパッケージを保守していると、「このパッケージを直すと、どこまで影響するのか」「INVALIDになったオブジェクトをどの順に直すべきか」を確認したくなります。Oracleでは ALL_DEPENDENCIES を使うと、パッケージ、プロシージャ、ファンクション、ビュー、トリガーなどの静的な依存関係を確認できます。

この記事では、ALL_DEPENDENCIES の基本だけでなく、依存方向の読み方、影響範囲の調べ方、循環依存の検出、Graphviz用の出力、INVALID再コンパイルへつなげる判断まで、実務で使う形に寄せて整理します。

この記事でわかること

  • ALL_DEPENDENCIES で「依存元」と「参照先」を正しく読む方法
  • あるパッケージが参照しているオブジェクトを階層表示するSQL
  • 変更したオブジェクトの影響を受けるPL/SQLを探すSQL
  • 外部スキーマ参照、循環依存、INVALIDを確認する観点
  • 再コンパイル順序を誤らないための考え方

INVALIDの確認や再コンパイルそのものは 依存オブジェクトとINVALID再コンパイルの制御、権限まわりは AUTHIDと権限管理の設計 と合わせて読むと整理しやすくなります。

スポンサーリンク

ALL_DEPENDENCIESで見るべき列

ALL_DEPENDENCIES は、「あるオブジェクトが、どのオブジェクトを参照しているか」を持つビューです。まずは列の意味をそろえます。

  • OWNER / NAME / TYPE: 依存している側。パッケージやビューなど、変更影響を受ける可能性があるオブジェクトです。
  • REFERENCED_OWNER / REFERENCED_NAME / REFERENCED_TYPE: 参照されている側。テーブル、ビュー、パッケージ、シノニムなどです。
  • DEPENDENCY_TYPE: 依存の種別です。通常は HARD として扱う場面が多いです。

矢印で書くなら、OWNER.NAME -> REFERENCED_OWNER.REFERENCED_NAME です。左が依存元、右が参照先です。この向きを間違えると、影響調査と再コンパイル順序を逆に考えてしまいます。

list-direct-dependencies.sql
SELECT
  owner,
  name,
  type,
  referenced_owner,
  referenced_name,
  referenced_type,
  dependency_type
FROM all_dependencies
WHERE owner = UPPER(:owner)
  AND name  = UPPER(:object_name)
ORDER BY type, referenced_owner, referenced_type, referenced_name;

まずはこのSQLで、対象オブジェクトが直接参照している相手を確認します。USER_DEPENDENCIES でも見られますが、複数スキーマをまたぐ保守では ALL_DEPENDENCIES を使い、所有者を明示したほうが誤読しにくくなります。

依存関係を階層でたどる

Oracleで依存を階層表示するなら、WITH RECURSIVE ではなく、CONNECT BY NOCYCLE またはOracleの再帰的サブクエリファクタリングを使います。ここではバージョン差の少ない CONNECT BY NOCYCLE で示します。

dependency-tree.sql
WITH dep AS (
  SELECT
    owner,
    name,
    type,
    referenced_owner,
    referenced_name,
    referenced_type
  FROM all_dependencies
  WHERE owner = UPPER(:owner)
    AND referenced_owner IS NOT NULL
)
SELECT
  LEVEL AS depth,
  LPAD(' ', (LEVEL - 1) * 2) || owner || '.' || name AS from_object,
  referenced_owner || '.' || referenced_name AS to_object,
  referenced_type,
  SYS_CONNECT_BY_PATH(owner || '.' || name, ' -> ') AS path_text
FROM dep
START WITH owner = UPPER(:owner)
       AND name  = UPPER(:object_name)
CONNECT BY NOCYCLE
       PRIOR referenced_owner = owner
   AND PRIOR referenced_name  = name
   AND PRIOR referenced_type  = type
ORDER SIBLINGS BY referenced_owner, referenced_type, referenced_name;

このSQLは「対象パッケージが何を参照しているか」を下流へたどるためのものです。たとえば PKG_ORDERPKG_CUSTOMER を呼び、さらに PKG_CUSTOMER が別のビューを参照している場合、その連鎖を追えます。

注意

ALL_DEPENDENCIES は静的な依存関係を中心に扱います。動的SQLで組み立てたテーブル名、文字列で呼び出すプロシージャ名、アプリケーション側からのSQL実行までは完全には拾えません。重要な変更では、ソース検索や実行ログも併用します。

変更影響を受けるオブジェクトを探す

テーブル、ビュー、パッケージ仕様などを変更したときに知りたいのは、「それを参照している側」です。つまり先ほどとは逆に、REFERENCED_OWNER / REFERENCED_NAME を起点にします。

find-affected-objects.sql
WITH dep AS (
  SELECT
    owner,
    name,
    type,
    referenced_owner,
    referenced_name,
    referenced_type
  FROM all_dependencies
  WHERE referenced_owner IS NOT NULL
)
SELECT
  LEVEL AS depth,
  owner || '.' || name AS affected_object,
  type AS affected_type,
  referenced_owner || '.' || referenced_name AS depends_on,
  SYS_CONNECT_BY_PATH(owner || '.' || name, ' <- ') AS impact_path
FROM dep
START WITH referenced_owner = UPPER(:changed_owner)
       AND referenced_name  = UPPER(:changed_object)
CONNECT BY NOCYCLE
       PRIOR owner = referenced_owner
   AND PRIOR name  = referenced_name
   AND PRIOR type  = referenced_type
ORDER BY depth, affected_type, affected_object;

この結果の depth=1 は直接影響、depth=2 以降は間接影響です。修正対象を洗い出すときは、まず直接影響を確認し、その後に間接影響をどこまでテスト対象に含めるかを判断します。

再コンパイル順序は参照される側から整える

依存関係の再コンパイルでは、基本的に「参照される側」を先に整えます。たとえば PKG_APKG_B を呼んでいるなら、先に PKG_B の仕様や本体を有効にし、その後で PKG_A を確認します。

参照先を先に直すテーブル、ビュー、パッケージ仕様など、呼ばれる側のINVALIDを先に解消します。
参照元を後で見る呼ぶ側のパッケージ本体やプロシージャは、参照先が整ってから再コンパイルします。
一括再コンパイルを過信しないUTL_RECOMP は便利ですが、根本原因を隠したまま再実行を繰り返すと原因調査が遅れます。
check-invalid-related-objects.sql
SELECT
  owner,
  object_type,
  object_name,
  status,
  last_ddl_time
FROM all_objects
WHERE status = 'INVALID'
  AND owner = UPPER(:owner)
  AND object_name IN (
    SELECT name
    FROM all_dependencies
    WHERE owner = UPPER(:owner)
       OR referenced_owner = UPPER(:owner)
  )
ORDER BY object_type, object_name;

INVALIDの解消で迷ったら、依存関係の上流にあるオブジェクトから確認します。詳細な再コンパイル手順は USER_DEPENDENCIES・UTL_RECOMPの記事 で扱っています。

外部スキーマ参照を確認する

外部スキーマ参照の判定は、オブジェクト名にドットが含まれるかではなく、OWNERREFERENCED_OWNER の差で見ます。シノニムを経由する場合もあるため、所有者列を使うほうが正確です。

find-cross-schema-dependencies.sql
SELECT
  owner || '.' || name AS object_name,
  type,
  referenced_owner || '.' || referenced_name AS referenced_object,
  referenced_type
FROM all_dependencies
WHERE owner = UPPER(:owner)
  AND referenced_owner IS NOT NULL
  AND referenced_owner <> owner
ORDER BY referenced_owner, referenced_type, referenced_name;

本番環境だけでINVALIDになる場合は、外部スキーマへの直接GRANT不足、シノニムの向き違い、Definer Rights / Invoker Rightsの違いが原因になることがあります。権限設計は AUTHIDと権限管理の記事 も参考になります。

循環依存を見つける

パッケージ間で相互参照が増えると、変更影響が読みづらくなります。CONNECT_BY_ISCYCLE を使うと、循環の疑いがある経路を拾えます。

find-cyclic-dependencies.sql
WITH dep AS (
  SELECT
    owner,
    name,
    type,
    referenced_owner,
    referenced_name,
    referenced_type
  FROM all_dependencies
  WHERE owner = UPPER(:owner)
    AND referenced_owner IS NOT NULL
)
SELECT
  owner || '.' || name AS object_name,
  referenced_owner || '.' || referenced_name AS referenced_object,
  SYS_CONNECT_BY_PATH(owner || '.' || name, ' -> ') AS path_text
FROM dep
WHERE CONNECT_BY_ISCYCLE = 1
CONNECT BY NOCYCLE
       PRIOR referenced_owner = owner
   AND PRIOR referenced_name  = name
   AND PRIOR referenced_type  = type
START WITH owner = UPPER(:owner);

循環依存が見つかったからといって、すぐに不具合とは限りません。ただし、仕様パッケージに共通型やインターフェースを寄せる、実装依存を分離するなど、依存方向を単純にできないか検討する価値があります。

Graphvizで依存関係を可視化する

依存関係が多い場合は、SQL結果を眺めるより図にしたほうが早いです。次のSQLはGraphvizのDOT形式に近い行を出力します。

export-dependencies-dot.sql
SELECT 'digraph plsql_dependencies {' AS dot_line FROM dual
UNION ALL
SELECT '  "' || owner || '.' || name || '" -> "' ||
       referenced_owner || '.' || referenced_name || '";' AS dot_line
FROM all_dependencies
WHERE owner = UPPER(:owner)
  AND referenced_owner IS NOT NULL
  AND type IN ('PACKAGE', 'PACKAGE BODY', 'PROCEDURE', 'FUNCTION', 'VIEW', 'TRIGGER')
UNION ALL
SELECT '}' AS dot_line FROM dual;

出力した行を .dot ファイルに保存すれば、依存元から参照先への矢印として可視化できます。影響調査では、「変更対象へ向かっている矢印」と「変更対象から伸びている矢印」を分けて見ると混乱しにくくなります。

調査時のチェックリスト

  • 依存の向きは OWNER.NAME -> REFERENCED_OWNER.REFERENCED_NAME と読めているか
  • 変更したいオブジェクトを参照しているPL/SQLを洗い出したか
  • 外部スキーマ参照を REFERENCED_OWNER で確認したか
  • 動的SQLやアプリケーション側SQLなど、ビューに出ない依存を別途確認したか
  • INVALIDは参照される側から順に解消しているか
  • 循環依存や過密な共通パッケージが変更リスクを高めていないか

まとめ

ALL_DEPENDENCIES は、PL/SQLの依存関係を読むための基本ビューです。重要なのは、単に一覧を出すことではなく、依存元と参照先の向きを正しく読み、変更影響と再コンパイル順序に落とし込むことです。

まず直接依存を確認し、必要に応じて階層表示、影響範囲、外部スキーマ参照、循環依存を確認します。そのうえでINVALIDや権限不足を切り分ければ、パッケージ改修時の調査漏れを減らせます。