PL/SQLパッケージは、公開インターフェースを定義する仕様(Specification)と、実装を書く本体(Body)に分かれます。この分離をうまく使うと、呼び出し側に見せるAPIを安定させたまま、内部実装だけを安全に変更できます。
この記事では、単に「SpecとBodyがある」という説明ではなく、実務で問題になりやすい依存関係、再コンパイル、デプロイ順序、権限、private実装、パッケージ状態まで含めて整理します。より広いパッケージ設計は パッケージ設計でコード管理と再利用性を極める、プロシージャ・ファンクション単体の設計は プロシージャ・ファンクション完全ガイド も参考になります。
- パッケージ仕様と本体の役割
- 公開APIとprivate実装の分け方
- 仕様変更と本体変更の依存関係・INVALID影響
ACCESSIBLE BY、AUTHID、GRANT EXECUTEの設計- package state、再コンパイル、
ORA-04068の注意点 - デプロイ順序、レビュー観点、確認SQL
パッケージ仕様と本体の役割
パッケージ仕様は、外部から見える公開APIです。呼び出し側が知るべきプロシージャ、ファンクション、型、定数、例外を宣言します。一方、パッケージ本体は、仕様で宣言したサブプログラムの実装と、外部へ公開しないprivateな処理を持ちます。
最小構成の例
まずは、公開する関数を仕様に置き、実装を本体に置く基本形です。コードブロックは仕様と本体で分けておくと、デプロイやレビューでも見やすくなります。
CREATE OR REPLACE PACKAGE math_util AS
FUNCTION add(
p_a IN NUMBER,
p_b IN NUMBER
) RETURN NUMBER;
FUNCTION subtract(
p_a IN NUMBER,
p_b IN NUMBER
) RETURN NUMBER;
END math_util;
/
CREATE OR REPLACE PACKAGE BODY math_util AS
FUNCTION add(
p_a IN NUMBER,
p_b IN NUMBER
) RETURN NUMBER
IS
BEGIN
RETURN p_a + p_b;
END add;
FUNCTION subtract(
p_a IN NUMBER,
p_b IN NUMBER
) RETURN NUMBER
IS
BEGIN
RETURN p_a - p_b;
END subtract;
END math_util;
/
仕様を見れば、外部利用者は関数名、引数、戻り値を理解できます。本体側の実装が単純加算なのか、ログを取るのか、別テーブルを参照するのかは、呼び出し側から隠せます。
公開APIとprivate実装を分ける
仕様には、外部から呼ばせたいものだけを置きます。内部検証、変換、共通チェック、SQL組み立てのような補助処理は本体のprivate関数に閉じ込めます。これにより、内部処理の名前や引数を自由に変えられます。
CREATE OR REPLACE PACKAGE customer_loader AS
PROCEDURE load_customers(
p_batch_id IN NUMBER
);
END customer_loader;
/
CREATE OR REPLACE PACKAGE BODY customer_loader AS
FUNCTION is_valid_email(
p_email IN VARCHAR2
) RETURN BOOLEAN
IS
BEGIN
RETURN p_email IS NOT NULL
AND REGEXP_LIKE(p_email, '^[^@]+@[^@]+\.[^@]+$');
END is_valid_email;
PROCEDURE load_customers(
p_batch_id IN NUMBER
) IS
BEGIN
FOR r IN (
SELECT customer_id, customer_name, email
FROM customer_stg
WHERE batch_id = p_batch_id
) LOOP
IF is_valid_email(r.email) THEN
INSERT INTO customers(customer_id, customer_name, email)
VALUES (r.customer_id, r.customer_name, r.email);
ELSE
INSERT INTO customer_load_error(batch_id, customer_id, message)
VALUES (p_batch_id, r.customer_id, 'invalid email');
END IF;
END LOOP;
END load_customers;
END customer_loader;
/
この例では、外部に公開するのは load_customers だけです。is_valid_email は本体内のprivate関数なので、あとから正規表現を変えたり別名にしたりしても、呼び出し側へ影響しません。ロギング設計は ロギングとトレーサビリティを両立する監査設計 と組み合わせると運用しやすくなります。
安全な変更と破壊的変更
パッケージ分離の価値は、変更影響を分類できることです。本体だけの変更は比較的安全ですが、仕様変更は呼び出し側のコンパイルや実行に影響します。
仕様は「契約」なので、変更するときはバージョニングや移行期間を考えます。本体変更で済むなら、仕様を変えずに済ませる設計を優先します。
依存関係を確認する
仕様に依存しているオブジェクトは USER_DEPENDENCIES で確認できます。リリース前に、どのビュー、パッケージ、プロシージャが対象パッケージを参照しているか把握しておきます。
SELECT name,
type,
referenced_owner,
referenced_name,
referenced_type
FROM user_dependencies
WHERE referenced_name = 'CUSTOMER_LOADER'
ORDER BY type, name;
SELECT owner,
name,
type,
referenced_owner,
referenced_name,
referenced_type
FROM all_dependencies
WHERE referenced_name = 'CUSTOMER_LOADER'
ORDER BY owner, type, name;
仕様変更が入る場合は、依存先の再コンパイルやテスト範囲が広がります。本体変更だけなら呼び出し側の依存影響は小さくなりますが、実行時の動作変更はもちろんテスト対象です。
INVALIDとコンパイルエラーを確認する
デプロイ後は、パッケージ本体と依存オブジェクトがVALIDか確認します。コンパイルエラーは USER_ERRORS、オブジェクト状態は USER_OBJECTS で見ます。
SELECT object_name, object_type, status
FROM user_objects
WHERE object_name IN ('CUSTOMER_LOADER')
OR status = 'INVALID'
ORDER BY object_type, object_name;
SELECT name, type, line, position, text
FROM user_errors
WHERE name = 'CUSTOMER_LOADER'
ORDER BY sequence;
コンパイルエラーの読み方や SHOW ERRORS、USER_ERRORS の使い方は PL/SQLコンパイル時エラーと警告の対処 でも詳しく扱っています。
デプロイ順序
パッケージのリリースでは、仕様、本体、依存オブジェクトの順序を意識します。仕様が変わる場合と本体だけ変わる場合で、必要な作業は変わります。
-- 仕様変更あり @customer_loader.pks @customer_loader.pkb @dependent_package.pkb -- 本体だけ変更 @customer_loader.pkb -- 最後に状態確認 SELECT object_name, object_type, status FROM user_objects WHERE status = 'INVALID';
仕様変更ありのリリースは、依存オブジェクトの再コンパイルとテストをセットで考えます。本体だけのリリースは軽くできますが、package stateを持つ場合は実行中セッションへの影響に注意します。
package stateとORA-04068
パッケージ本体にグローバル変数や初期化処理があると、セッションごとにpackage stateを持つことがあります。実行中セッションがある状態で本体を再コンパイルすると、次回参照時に既存状態が破棄され、ORA-04068 が発生することがあります。
CREATE OR REPLACE PACKAGE session_cache AS
PROCEDURE set_value(p_value IN VARCHAR2);
FUNCTION get_value RETURN VARCHAR2;
END session_cache;
/
CREATE OR REPLACE PACKAGE BODY session_cache AS
g_value VARCHAR2(100);
PROCEDURE set_value(p_value IN VARCHAR2) IS
BEGIN
g_value := p_value;
END;
FUNCTION get_value RETURN VARCHAR2 IS
BEGIN
RETURN g_value;
END;
END session_cache;
/
状態を持つパッケージは、再デプロイ時のセッション影響を考慮します。状態を持たせない設計にする、接続プールを切り替える、メンテナンス時間にリリースするなどの対策が必要です。ORA-04068 の詳細は ORA-04068完全ガイド を確認してください。
AUTHIDと権限設計
パッケージ仕様には AUTHID を指定できます。デフォルトはDefiner Rightsで、所有者権限で実行されます。呼び出し者権限で動かしたい共通部品では AUTHID CURRENT_USER を検討します。
CREATE OR REPLACE PACKAGE order_api
AUTHID DEFINER
AS
PROCEDURE create_order(
p_customer_id IN NUMBER,
p_amount IN NUMBER
);
END order_api;
/
GRANT EXECUTE ON order_api TO app_user;
GRANT EXECUTE は「仕様レベル」ではなく、パッケージオブジェクトに対して付与します。呼び出し側は仕様で公開されたサブプログラムだけを呼び出せます。AUTHIDと権限管理は AUTHIDと権限管理の設計、権限分離の実装は 権限分離とセキュリティパッケージ が参考になります。
ACCESSIBLE BYで内部パッケージを絞る
内部用パッケージを外部から呼ばせたくない場合は、利用可能なPL/SQLユニットを制限する ACCESSIBLE BY を検討できます。APIパッケージは公開し、helperパッケージはAPIからだけ呼ばせる構成にできます。
CREATE OR REPLACE PACKAGE order_internal
AUTHID DEFINER
ACCESSIBLE BY (order_api)
AS
PROCEDURE validate_order(
p_customer_id IN NUMBER,
p_amount IN NUMBER
);
END order_internal;
/
CREATE OR REPLACE PACKAGE order_api
AUTHID DEFINER
AS
PROCEDURE create_order(
p_customer_id IN NUMBER,
p_amount IN NUMBER
);
END order_api;
/
ACCESSIBLE BY は環境バージョンや利用方針を確認して使います。使えない環境でも、公開APIパッケージと内部helperパッケージを分けるだけで、レビューしやすい構成になります。
ファイル分割と命名規則
Git管理やCI/CDでは、仕様と本体を別ファイルに分けると差分が読みやすくなります。一般的には .pks を仕様、.pkb を本体として扱います。
database/
packages/
customer_loader.pks
customer_loader.pkb
order_api.pks
order_api.pkb
deploy/
001_create_specs.sql
002_create_bodies.sql
003_grants.sql
004_check_invalid_objects.sql
レビューでは、仕様ファイルの差分を特に重く見ます。仕様に変更がある場合は、呼び出し側への影響、互換性、移行期間、依存オブジェクトの再コンパイルを確認します。
運用観測性を入れる
大きなパッケージでは、どの処理が動いているかを運用から追えるようにします。DBMS_APPLICATION_INFO でモジュール名やアクション名を設定すると、V$SESSION やAWRで処理状況を追いやすくなります。
CREATE OR REPLACE PACKAGE BODY customer_loader AS
PROCEDURE load_customers(p_batch_id IN NUMBER) IS
BEGIN
DBMS_APPLICATION_INFO.SET_MODULE(
module_name => 'CUSTOMER_LOADER',
action_name => 'LOAD_CUSTOMERS'
);
-- load logic
NULL;
DBMS_APPLICATION_INFO.SET_MODULE(NULL, NULL);
EXCEPTION
WHEN OTHERS THEN
DBMS_APPLICATION_INFO.SET_MODULE(NULL, NULL);
RAISE;
END load_customers;
END customer_loader;
/
運用観測性は DBMS_APPLICATION_INFO完全ガイド や DBMS_APPLICATION_INFOで運用観測性を底上げ と組み合わせると実務的です。
レビュー観点
AUTHID と GRANT EXECUTE の方針が明確か。USER_DEPENDENCIES で影響範囲を確認したか。まとめ
PL/SQLパッケージの仕様と本体を分離する目的は、公開APIを安定させ、実装を安全に変更できるようにすることです。仕様には外部へ見せる契約だけを置き、本体にはprivate実装、内部SQL、ログ、補助関数を閉じ込めます。
実務では、仕様変更と本体変更の影響差、依存関係、INVALID確認、デプロイ順序、package state、ORA-04068、AUTHID、ACCESSIBLE BY まで含めて設計します。Specを小さく安定させ、Bodyを改善し続けられる構成にすることが、長期運用で効いてきます。

