2.9 条件付きコンパイル
条件付きコンパイルを使用すると、ソース・テキストを削除することなく、PL/SQLアプリケーションの機能をカスタマイズできます。
たとえば、次のことが可能です。
-
最新のデータベース・リリースで新機能を使用し、古いデータベース・リリースでアプリケーションを実行する場合にそれらの新機能を無効化することができます。
-
開発環境でデバッグ文またはトレース文をアクティブ化し、本番サイトでアプリケーションを実行する場合にそれらの文を隠ぺいすることができます。
ここでのトピック
2.9.1 条件付きコンパイルの動作方法
条件付きコンパイルでは、IF文に似た選択ディレクティブを使用して、コンパイルするソース・テキストを選択します。
通常、選択ディレクティブの条件には問合せディレクティブが含まれています。エラー・ディレクティブは、ユーザー定義のエラーを呼び出します。すべての条件付きコンパイル・ディレクティブは、プリプロセッサ制御トークンとPL/SQLテキストで構成されています。
ここでのトピック
関連項目:
「静的な式」2.9.1.1 プリプロセッサ制御トークン
プリプロセッサ制御トークンによって、PL/SQLユニットがコンパイルされる前に処理されるコードが識別されます。
構文
$plsql_identifier
$とplsql_identifierの間に空白を入れることはできません。
文字$はplsql_identifier内でも使用できますが、そこでは特別な意味を持ちません。
次のプリプロセッサ制御トークンは予約されています。
-
$IF -
$THEN -
$ELSE -
$ELSIF -
$ERROR
plsql_identifierの詳細は、「識別子」を参照してください。
2.9.1.2 選択ディレクティブ
選択ディレクティブは、コンパイルするソース・テキストを選択します。
構文
$IF boolean_static_expression $THEN text [ $ELSIF boolean_static_expression $THEN text ]... [ $ELSE text $END ]
boolean_static_expressionの構文の詳細は、「静的なブール式」を参照してください。textは任意ですが、通常は文(「statement ::=」を参照)またはエラー・ディレクティブ(「エラー・ディレクティブ」を参照)です。
選択ディレクティブは、いずれかの静的なBOOLEAN式の値がTRUEであるか、またはすべての式が処理されるまで、使用されている順に式を評価します。いずれかの式の値がTRUEの場合、そのテキストがコンパイルされますが、残りの式は評価されず、そのテキストは分析されません。値がTRUEの式がなく、$ELSEが存在する場合は、そのテキストがコンパイルされますが、存在しない場合は、どのテキストもコンパイルされません。
選択ディレクティブの例は、「条件付きコンパイルの例」を参照してください。
関連項目:
選択ディレクティブと同じロジックを持つIF文の詳細は、「条件付き選択文」を参照してください
2.9.1.3 エラー・ディレクティブ
エラー・ディレクティブは、コンパイル時にユーザー定義のエラー・メッセージを生成します。
構文
$ERROR varchar2_static_expression $END
次のコンパイル時のエラー・メッセージを生成します。stringはvarchar2_static_expressionの値です。
PLS-00179: $ERROR: string
varchar2_static_expressionの構文の詳細は、「静的なVARCHAR2式」を参照してください。
エラー・ディレクティブの使用例は、例2-58を参照してください。
2.9.1.4 問合せディレクティブ
問合せディレクティブは、コンパイル環境の情報を提供します。
構文
$$name
name(引用符で囲まれていないPL/SQL識別子)の詳細は、「識別子」を参照してください。
通常、問合せディレクティブは、選択ディレクティブのboolean_static_expressionで使用しますが、その型の変数またはリテラルを使用できるすべての場所で使用できます。また、通常のPL/SQLでは(変数ではなく)リテラルのみ使用可能な場所でも使用できます(たとえば、VARCHAR2変数のサイズを指定するために使用できます)。
ここでのトピック
2.9.1.4.1 事前定義の問合せディレクティブ
事前定義の問合せディレクティブは、次のとおりです。
-
$$PLSQL_LINE現行のPL/SQLユニットでディレクティブが使用されるソース行の番号を値として持つ
PLS_INTEGERリテラル。選択ディレクティブの$$PLSQL_LINEの例を次に示します。$IF $$PLSQL_LINE = 32 $THEN ... -
$$PLSQL_UNIT現行のPL/SQLユニットの名前を持つ
VARCHAR2リテラル。現行のPL/SQLユニットが無名ブロックの場合、$$PLSQL_UNITの値はNULL値になります。 -
$$PLSQL_UNIT_OWNER現行のPL/SQLユニットの所有者を持つ
VARCHAR2リテラル。現行のPL/SQLユニットが無名ブロックの場合、$$PLSQL_UNIT_OWNERの値はNULL値になります。 -
$$PLSQL_UNIT_TYPE現行のPL/SQLユニットのタイプを持つ
VARCHAR2リテラル。ANONYMOUSBLOCK、FUNCTION、PACKAGE、PACKAGEBODY、PROCEDURE、TRIGGER、TYPEまたはTYPEBODYのいずれか。無名ブロックまたは非DMLトリガー内では、$$PLSQL_UNIT_TYPEの値はANONYMOUS BLOCKになります。 -
$$plsql_compilation_parameter名前
plsql_compilation_parameterは、PL/SQLコンパイル・パラメータ(PLSCOPE_SETTINGSなど)です。これらのパラメータの詳細は、表1-2を参照してください。
選択ディレクティブには静的なBOOLEAN式が必要なため、VARCHAR2比較で次のような$$PLSQL_UNIT、$$PLSQL_UNIT_OWNERまたは$$PLSQL_UNIT_TYPEは使用できません。
$IF $$PLSQL_UNIT = 'AWARD_BONUS' $THEN ... $IF $$PLSQL_UNIT_OWNER IS HR $THEN ... $IF $$PLSQL_UNIT_TYPE IS FUNCTION $THEN ...
ただし、前述のディレクティブをNULLと比較することはできます。たとえば:
$IF $$PLSQL_UNIT IS NULL $THEN ... $IF $$PLSQL_UNIT_OWNER IS NOT NULL $THEN ... $IF $$PLSQL_UNIT_TYPE IS NULL $THEN ...
例2-55 事前定義の問合せディレクティブ
この例は、SQL*Plusスクリプトでは、複数の事前定義の問合せディレクティブをPLS_INTEGERおよびVARCHAR2リテラルとして使用し、値がどのように代入されるかを示しています。
SQL> CREATE OR REPLACE PROCEDURE p
2 AUTHID DEFINER IS
3 i PLS_INTEGER;
4 BEGIN
5 DBMS_OUTPUT.PUT_LINE('Inside p');
6 i := $$PLSQL_LINE;
7 DBMS_OUTPUT.PUT_LINE('i = ' || i);
8 DBMS_OUTPUT.PUT_LINE('$$PLSQL_LINE = ' || $$PLSQL_LINE);
9 DBMS_OUTPUT.PUT_LINE('$$PLSQL_UNIT = ' || $$PLSQL_UNIT);
10 DBMS_OUTPUT.PUT_LINE('$$PLSQL_UNIT_OWNER = ' || $$PLSQL_UNIT_OWNER);
11 DBMS_OUTPUT.PUT_LINE('$$PLSQL_UNIT_TYPE = ' || $$PLSQL_UNIT_TYPE);
12 END;
13 /
Procedure created.
SQL> BEGIN
2 p;
3 DBMS_OUTPUT.PUT_LINE('Outside p');
4 DBMS_OUTPUT.PUT_LINE('$$PLSQL_LINE = ' || $$PLSQL_LINE);
5 DBMS_OUTPUT.PUT_LINE('$$PLSQL_UNIT = ' || $$PLSQL_UNIT);
6 DBMS_OUTPUT.PUT_LINE('$$PLSQL_UNIT_OWNER = ' || $$PLSQL_UNIT_OWNER);
7 DBMS_OUTPUT.PUT_LINE('$$PLSQL_UNIT_TYPE = ' || $$PLSQL_UNIT_TYPE);
8 END;
9 /
結果:
Inside p i = 6 $$PLSQL_LINE = 8 $$PLSQL_UNIT = P $$PLSQL_UNIT_OWNER = HR $$PLSQL_UNIT_TYPE = PROCEDURE Outside p $$PLSQL_LINE = 4 $$PLSQL_UNIT = $$PLSQL_UNIT_OWNER = $$PLSQL_UNIT_TYPE = ANONYMOUS BLOCK PL/SQL procedure successfully completed.
例2-56 PL/SQLのコンパイル・パラメータの値の表示
この例では、PL/SQLコンパイル・パラメータの現在の値を表示します。
ノート:
SQL*Plus環境では、SHOW PARAMETERSコマンドを使用して、PL/SQLコンパイル・パラメータを含む初期化パラメータの現在の値を表示できます。SHOWコマンドとPARAMETERSオプションの詳細は、『SQL*Plusユーザーズ・ガイドおよびリファレンス』を参照してください。
BEGIN
DBMS_OUTPUT.PUT_LINE('$$PLSCOPE_SETTINGS = ' || $$PLSCOPE_SETTINGS);
DBMS_OUTPUT.PUT_LINE('$$PLSQL_CCFLAGS = ' || $$PLSQL_CCFLAGS);
DBMS_OUTPUT.PUT_LINE('$$PLSQL_CODE_TYPE = ' || $$PLSQL_CODE_TYPE);
DBMS_OUTPUT.PUT_LINE('$$PLSQL_OPTIMIZE_LEVEL = ' || $$PLSQL_OPTIMIZE_LEVEL);
DBMS_OUTPUT.PUT_LINE('$$PLSQL_WARNINGS = ' || $$PLSQL_WARNINGS);
DBMS_OUTPUT.PUT_LINE('$$NLS_LENGTH_SEMANTICS = ' || $$NLS_LENGTH_SEMANTICS);
END;
/結果:
$$PLSCOPE_SETTINGS = IDENTIFIERS:NONE $$PLSQL_CCFLAGS = $$PLSQL_CODE_TYPE = INTERPRETED $$PLSQL_OPTIMIZE_LEVEL = 2 $$PLSQL_WARNINGS = ENABLE:ALL $$NLS_LENGTH_SEMANTICS = BYTE
2.9.1.4.2 問合せディレクティブへの値の代入
PLSQL_CCFLAGSコンパイル・パラメータを使用して、問合せディレクティブに値を代入できます。
たとえば:
ALTER SESSION SET PLSQL_CCFLAGS = 'name1:value1, name2:value2, ... namen:valuen'
それぞれのvalueは、BOOLEANリテラル(TRUE、FALSEまたはNULL)、あるいはPLS_INTEGERリテラルである必要があります。valueのデータ型によって、nameのデータ型が決まります。
同じデータ型または異なるデータ型の値を指定して同じnameを複数回使用できます。先に行った代入は、後で行った代入によってオーバーライドされます。たとえば、次のコマンドでは、$$flagの値は5、データ型はPLS_INTEGERに設定されます。
ALTER SESSION SET PLSQL_CCFLAGS = 'flag:TRUE, flag:5'
コンパイル・パラメータを含む事前定義の問合せディレクティブに値を代入する場合、PLSQL_CCFLAGSは使用しないことをお薦めします。コンパイル・パラメータに値を代入する場合は、ALTER SESSION文を使用することをお薦めします。
ALTER SESSION文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。
ノート:
PLSQL_CCFLAGSのコンパイル時の値は、ストアドPL/SQLユニットのメタデータとともに格納されているため、ユニットを明示的に再コンパイルするときにその値を再利用できます。詳細は、「PL/SQLユニットおよびコンパイル・パラメータ」を参照してください。
PLSQL_CCFLAGSの詳細は、『Oracle Databaseリファレンス』を参照してください。
例2-57 PLSQL_CCFLAGSによるそれ自体への値の代入
この例では、PLSQL_CCFLAGSを使用して、ユーザー定義の問合せディレクティブ$$Some_Flagに値を代入し、さらに、(推奨されてはいませんが)それ自体にも値を代入します。先に行った代入は後で行った代入によってオーバーライドされるため、$$Some_Flagの結果の値は2になり、PLSQL_CCFLAGSの結果の値はALTER SESSION文によって代入される値('Some_Flag:1, Some_Flag:2, PLSQL_CCFlags:99')ではなく、それ自体に代入される値(99)になります。
ALTER SESSION SET
PLSQL_CCFlags = 'Some_Flag:1, Some_Flag:2, PLSQL_CCFlags:99'
/
BEGIN
DBMS_OUTPUT.PUT_LINE($$Some_Flag);
DBMS_OUTPUT.PUT_LINE($$PLSQL_CCFlags);
END;
/
結果:
2 99
2.9.1.4.3 解決できない問合せディレクティブ
ソース・テキストがラップされていない場合、問合せディレクティブの値を判別できないと、PL/SQLによって警告が発行されます。
問合せディレクティブ($$name)を解決できず、ソース・テキストがラップされていない場合は、PL/SQLにより警告PLW-6003が発行され、未解決の問合せディレクティブの値としてNULLが代入されます。ソース・テキストがラップされている場合は、未解決の問合せディレクティブが示されないように、警告メッセージが無効になります。
PL/SQLソース・テキストのラップの詳細は、「PL/SQLのソース・テキストのラップ」を参照してください。
2.9.1.5 DBMS_DB_VERSIONパッケージ
DBMS_DB_VERSIONパッケージでは、Oracleバージョン番号、およびOracleのバージョンに基づいて条件付きコンパイルを簡単に選択する場合に有効なその他の情報を指定します。
DBMS_DB_VERSIONパッケージでは、次の静的定数が提供されます。
-
PLS_INTEGER定数VERSIONは、現行のOracle Databaseのバージョンを識別します。 -
PLS_INTEGER定数RELEASEは、現行のOracle Databaseのリリース番号を識別します。 -
VER_LE_v形式のBOOLEAN定数の値はそれぞれ、データベース・バージョンがv以下の場合にTRUEになり、それ以外の場合はFALSEになります。 -
VER_LE_v_r形式のBOOLEAN定数の値はそれぞれ、データベース・バージョンがv以下でリリースがr以下の場合にTRUEになり、それ以外の場合はFALSEになります。
DBMS_DB_VERSIONパッケージの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。
2.9.2 条件付きコンパイルの例
選択およびユーザー定義の問合せディレクティブを使用する条件付きコンパイルの例。
例2-58 データベース・バージョンを確認するコード
この例では、データベース・バージョンおよびリリースがOracle Database 10gリリース2より前の場合はエラー・メッセージを生成し、それ以外の場合は、バージョンおよびリリースがサポート対象であるというメッセージを表示してOracle Database 10gリリース2から使用可能になったCOMMIT文を使用します。
BEGIN $IF DBMS_DB_VERSION.VER_LE_10_1 $THEN -- selection directive begins $ERROR 'unsupported database release' $END -- error directive $ELSE DBMS_OUTPUT.PUT_LINE ( 'Release ' || DBMS_DB_VERSION.VERSION || '.' || DBMS_DB_VERSION.RELEASE || ' is supported.' ); -- This COMMIT syntax is newly supported in 10.2: COMMIT WRITE IMMEDIATE NOWAIT; $END -- selection directive ends END; /
結果:
Release 12.1 is supported.
例2-59 異なるデータベース・バージョンに対する異なるコードのコンパイル
この例では、ユーザー定義の問合せディレクティブ$$my_debugおよび$$my_tracingに値を設定し、条件付きコンパイルを使用します。
-
パッケージ
my_pkgの仕様部で、サブタイプmy_realのベース型を判断します(BINARY_DOUBLEは、Oracle Databaseバージョン10g以上でのみ使用できます)。 -
パッケージ
my_pkgの本体で、異なるデータベース・バージョンに対してmy_piおよびmy_eの値を別々に計算します -
プロシージャ
circle_areaで、問合せディレクティブ$$my_debugの値がTRUEの場合のみに一部のコードをコンパイルします。
ALTER SESSION SET PLSQL_CCFLAGS = 'my_debug:FALSE, my_tracing:FALSE'; CREATE OR REPLACE PACKAGE my_pkg AUTHID DEFINER AS SUBTYPE my_real IS $IF DBMS_DB_VERSION.VERSION < 10 $THEN NUMBER; $ELSE BINARY_DOUBLE; $END my_pi my_real; my_e my_real; END my_pkg; / CREATE OR REPLACE PACKAGE BODY my_pkg AS BEGIN $IF DBMS_DB_VERSION.VERSION < 10 $THEN my_pi := 3.14159265358979323846264338327950288420; my_e := 2.71828182845904523536028747135266249775; $ELSE my_pi := 3.14159265358979323846264338327950288420d; my_e := 2.71828182845904523536028747135266249775d; $END END my_pkg; / CREATE OR REPLACE PROCEDURE circle_area(radius my_pkg.my_real) AUTHID DEFINER IS my_area my_pkg.my_real; my_data_type VARCHAR2(30); BEGIN my_area := my_pkg.my_pi * (radius**2); DBMS_OUTPUT.PUT_LINE ('Radius: ' || TO_CHAR(radius) || ' Area: ' || TO_CHAR(my_area)); $IF $$my_debug $THEN SELECT DATA_TYPE INTO my_data_type FROM USER_ARGUMENTS WHERE OBJECT_NAME = 'CIRCLE_AREA' AND ARGUMENT_NAME = 'RADIUS'; DBMS_OUTPUT.PUT_LINE ('Data type of the RADIUS argument is: ' || my_data_type); $END END; / CALL DBMS_PREPROCESSOR.PRINT_POST_PROCESSED_SOURCE ('PACKAGE', 'HR', 'MY_PKG');
結果:
PACKAGE my_pkg AUTHID DEFINER AS SUBTYPE my_real IS BINARY_DOUBLE; my_pi my_real; my_e my_real; END my_pkg; Call completed.
2.9.3 処理後のソース・テキストの取得と出力
DBMS_PREPROCESSORパッケージでは、処理後の形式でPL/SQLのソース・テキストを取得して出力するサブプログラムが提供されます。
DBMS_PREPROCESSORパッケージの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。
例2-60 処理後のソース・テキストの表示
この例では、DBMS_PREPROCESSOR.PRINT_POST_PROCESSED_SOURCEプロシージャを起動して、「例2-59」のmy_pkgの処理後の形式を印刷します。処理後のテキストに含まれない「例2-59」のコードの行は、空白行として表示されます。
CALL DBMS_PREPROCESSOR.PRINT_POST_PROCESSED_SOURCE (
'PACKAGE', 'HR', 'MY_PKG'
);
結果:
PACKAGE my_pkg AUTHID DEFINERs AS SUBTYPE my_real IS BINARY_DOUBLE; my_pi my_real; my_e my_real; END my_pkg;
2.9.4 条件付きコンパイル・ディレクティブの制限
条件付きコンパイル・ディレクティブは、これらのセマンティクス制限の対象となります。
条件付きコンパイル・ディレクティブは、(「CREATE TYPE文」で作成した)スキーマ・レベルのユーザー定義型では使用できません。次の型仕様部で、依存する型の属性構造と依存する表の列構造を決定する、型の属性構造を指定します。
注意:
条件付きコンパイル・ディレクティブを使用して型の属性構造を変更すると、依存オブジェクトが非同期になったり、依存表がアクセス不可になる場合があります。型の属性構造は、「ALTER TYPE文」を使用してのみ変更することをお薦めします。ALTER TYPE文は、変更を依存オブジェクトまで伝播します。
スキーマ・レベルの型の仕様部で条件付きコンパイル・ディレクティブが使用されていると、コンパイラはエラー「PLS-00180: プリプロセッサ・ディレクティブは、このコンテキストでサポートされていません」を報告します。
すべての条件付きコンパイラの構造はPL/SQLプリプロセッサによって処理されるため、SQLパーサーは、ストアドPL/SQLユニットまたは無名ブロック内の最初の条件付きコンパイル・ディレクティブの場所に対して次の制限を強制します。
-
パッケージ仕様部、パッケージ本体、型本体、スキーマ・レベル・ファンクションおよびスキーマ・レベル・プロシージャには、条件コンパイル・ディレクティブが有効になる前に、ユニット名の識別子の後に少なくとも1つの空白でないPL/SQLトークンがあることが必要です。
ノート:
-
PL/SQLのコメント"--"または"/*"は、空白トークンとしてカウントされます。
-
トークンがPL/SQLで無効な場合は、PLS-00103エラーが発行されます。ただし、このルールに違反した状態で条件付きコンパイル・ディレクティブが使用されていると、ORAエラーが生成されます。
例2-61および例2-62では、最初の条件付きコンパイル・ディレクティブが、定義しているユニットの識別子に続く最初のPL/SQLトークンの後にあることを示しています。
-
-
トリガーまたは無名ブロックでは、キーワード
DECLAREまたはBEGIN(いずれか先に使用されている方)の前に最初の条件付きコンパイル・ディレクティブを配置することはできません。
SQLパーサーでは、無名ブロックでプレースホルダが使用される場合、そのプレースホルダは条件付きコンパイル・ディレクティブで使用できないという制限もあります。たとえば:
BEGIN :n := 1; -- valid use of placeholder $IF ... $THEN :n := 1; -- invalid use of placeholder $END
例2-61 パッケージ仕様の定義で使用する条件付きコンパイル・ディレクティブ
この例は、パッケージ仕様の定義で、AUTHID句の後のキーワードISの前に、最初の条件付きコンパイル・ディレクティブを配置する例を示しています。
CREATE OR REPLACE PACKAGE cc_pkg AUTHID DEFINER $IF $$XFLAG $THEN ACCESSIBLE BY(p1_pkg) $END IS i NUMBER := 10; trace CONSTANT BOOLEAN := TRUE; END cc_pkg;
結果:
Package created.
例2-62 サブプログラムの仮パラメータ・リストで使用する条件付きコンパイル・ディレクティブ
この例は、PL/SQLプロシージャ定義の仮パラメータ・リストで、左カッコの後に最初の条件付きコンパイル・ディレクティブが配置されることを示しています。
CREATE OR REPLACE PROCEDURE my_proc ( $IF $$xxx $THEN i IN PLS_INTEGER $ELSE i IN INTEGER $END ) IS BEGIN NULL; END my_proc;
結果:
Procedure created.