条件付きコンパイル

条件付きコンパイルを使用すると、ソース・テキストを削除することなく、PL/SQLアプリケーションの機能をカスタマイズできます。

たとえば、次のことが可能です。

  • 最新のデータベース・リリースで新機能を使用し、古いデータベース・リリースでアプリケーションを実行する場合にそれらの新機能を無効化することができます。

  • 開発環境でデバッグ文またはトレース文をアクティブ化し、本番サイトでアプリケーションを実行する場合にそれらの文を隠ぺいすることができます。

ここでのトピック

条件付きコンパイルの動作方法

条件付きコンパイルでは、IF文に似た選択ディレクティブを使用して、コンパイルするソース・テキストを選択します。

通常、選択ディレクティブの条件には問合せディレクティブが含まれています。エラー・ディレクティブは、ユーザー定義のエラーを呼び出します。すべての条件付きコンパイル・ディレクティブは、プリプロセッサ制御トークンとPL/SQLテキストで構成されています。

ここでのトピック

関連項目:

「静的な式」

プリプロセッサ制御トークン

プリプロセッサ制御トークンによって、PL/SQLユニットがコンパイルされる前に処理されるコードが識別されます。

構文

$plsql_identifier

$plsql_identifierの間に空白を入れることはできません。

文字$plsql_identifier内でも使用できますが、そこでは特別な意味を持ちません。

次のプリプロセッサ制御トークンは予約されています。

  • $IF

  • $THEN

  • $ELSE

  • $ELSIF

  • $ERROR

plsql_identifierの詳細は、「識別子」を参照してください。

選択ディレクティブ

選択ディレクティブは、コンパイルするソース・テキストを選択します。

構文

$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文の詳細は、「条件付き選択文」を参照してください

エラー・ディレクティブ

エラー・ディレクティブは、コンパイル時にユーザー定義のエラー・メッセージを生成します。

構文

$ERROR varchar2_static_expression $END

次のコンパイル時のエラー・メッセージを生成します。stringvarchar2_static_expressionの値です。

PLS-00179: $ERROR: string

varchar2_static_expressionの構文の詳細は、「静的なVARCHAR2式」を参照してください。

エラー・ディレクティブの使用例は、例3-60を参照してください。

問合せディレクティブ

問合せディレクティブは、コンパイル環境の情報を提供します。

構文

$$name

name(引用符で囲まれていないPL/SQL識別子)の詳細は、「識別子」を参照してください。

通常、問合せディレクティブは、選択ディレクティブのboolean_static_expressionで使用しますが、その型の変数またはリテラルを使用できるすべての場所で使用できます。また、通常のPL/SQLでは(変数ではなく)リテラルのみ使用可能な場所でも使用できます(たとえば、VARCHAR2変数のサイズを指定するために使用できます)。

ここでのトピック

事前定義の問合せディレクティブ

事前定義の問合せディレクティブは、次のとおりです。

  • $$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リテラル。ANONYMOUS BLOCKFUNCTIONPACKAGEPACKAGE BODYPROCEDURETRIGGERTYPEまたはTYPE BODYのいずれか。無名ブロックまたは非DMLトリガー内では、$$PLSQL_UNIT_TYPEの値はANONYMOUS BLOCKになります。

  • $$plsql_compilation_parameter

    名前plsql_compilation_parameterは、PL/SQLコンパイル・パラメータ(PLSCOPE_SETTINGSなど)です。これらのパラメータの詳細は、表2-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 ...

例3-57 事前定義の問合せディレクティブ

この例は、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.

例3-58 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
問合せディレクティブへの値の代入

PLSQL_CCFLAGSコンパイル・パラメータを使用して、問合せディレクティブに値を代入できます。

たとえば:

ALTER SESSION SET PLSQL_CCFLAGS = 
  'name1:value1, name2:value2, ... namen:valuen'

それぞれのvalueは、BOOLEANリテラル(TRUEFALSEまたは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リファレンス』を参照してください。

例3-59 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
解決できない問合せディレクティブ

ソース・テキストがラップされていない場合、問合せディレクティブの値を判別できないと、PL/SQLによって警告が発行されます。

問合せディレクティブ($$name)を解決できず、ソース・テキストがラップされていない場合は、PL/SQLにより警告PLW-6003が発行され、未解決の問合せディレクティブの値としてNULLが代入されます。ソース・テキストがラップされている場合は、未解決の問合せディレクティブが示されないように、警告メッセージが無効になります。

PL/SQLソース・テキストのラップの詳細は、「PL/SQLのソース・テキストのラップ」を参照してください。

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パッケージおよびタイプ・リファレンス』を参照してください。

条件付きコンパイルの例

選択およびユーザー定義の問合せディレクティブを使用する条件付きコンパイルの例。

例3-60 データベース・バージョンを確認するコード

この例では、データベース・バージョンおよびリリースが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.

例3-61 異なるデータベース・バージョンに対する異なるコードのコンパイル

この例では、ユーザー定義の問合せディレクティブ$$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.

処理後のソース・テキストの取得と出力

DBMS_PREPROCESSORパッケージでは、処理後の形式でPL/SQLのソース・テキストを取得して出力するサブプログラムが提供されます。

DBMS_PREPROCESSORパッケージの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。

例3-62 処理後のソース・テキストの表示

この例では、DBMS_PREPROCESSOR.PRINT_POST_PROCESSED_SOURCEプロシージャを起動して、例3-61my_pkgの処理後の形式を出力します。処理後のテキストに含まれない例3-61のコードの行は、空白行として表示されます。

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;

条件付きコンパイル・ディレクティブの制限

条件付きコンパイル・ディレクティブは、これらのセマンティクス制限の対象となります。

条件付きコンパイル・ディレクティブは、(「CREATE TYPE文」で作成した)スキーマ・レベルのユーザー定義型では使用できません。次の型仕様部で、依存する型の属性構造と依存する表の列構造を決定する、型の属性構造を指定します。

注意:

条件付きコンパイル・ディレクティブを使用して型の属性構造を変更すると、依存オブジェクトが非同期になったり、依存表がアクセス不可になる場合があります。型の属性構造は、「ALTER TYPE文」を使用してのみ変更することをお薦めします。ALTER TYPE文は、変更を依存オブジェクトまで伝播します。

スキーマ・レベルの型の仕様部で条件付きコンパイル・ディレクティブが使用されていると、コンパイラはエラー「PLS-00180: プリプロセッサ・ディレクティブは、このコンテキストでサポートされていません」を報告します。

すべての条件付きコンパイラの構造はPL/SQLプリプロセッサによって処理されるため、SQLパーサーは、ストアドPL/SQLユニットまたは無名ブロック内の最初の条件付きコンパイル・ディレクティブの場所に対して次の制限を強制します。

  • パッケージ仕様部、パッケージ本体、型本体、スキーマ・レベル・ファンクションおよびスキーマ・レベル・プロシージャには、条件コンパイル・ディレクティブが有効になる前に、ユニット名の識別子の後に少なくとも1つの空白でないPL/SQLトークンがあることが必要です。

    ノート:

    • PL/SQLのコメント"--"または"/*"は、空白トークンとしてカウントされます。

    • トークンがPL/SQLで無効な場合は、PLS-00103エラーが発行されます。ただし、このルールに違反した状態で条件付きコンパイル・ディレクティブが使用されていると、ORAエラーが生成されます。

    例3-63および例3-64では、最初の条件付きコンパイル・ディレクティブが、定義しているユニットの識別子に続く最初のPL/SQLトークンの後にあることを示しています。

  • トリガーまたは無名ブロックでは、キーワードDECLAREまたはBEGIN(いずれか先に使用されている方)の前に最初の条件付きコンパイル・ディレクティブを配置することはできません。

SQLパーサーでは、無名ブロックでプレースホルダが使用される場合、そのプレースホルダは条件付きコンパイル・ディレクティブで使用できないという制限もあります。たとえば:

BEGIN
  :n := 1; -- valid use of placeholder
  $IF ... $THEN
    :n := 1; -- invalid use of placeholder
$END

例3-63 パッケージ仕様の定義で使用する条件付きコンパイル・ディレクティブ

この例は、パッケージ仕様の定義で、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.

例3-64 サブプログラムの仮パラメータ・リストで使用する条件付きコンパイル・ディレクティブ

この例は、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.