e-learning、オラクル研修、LMS(学習管理システム)のiStudy

e-learning、オラクル研修、LMS(学習管理システム)のiStudy

第97回 「条件付きコンパイル(その1)」

2014.05.22

こんにちは。インストラクターの蓑島です。

前回は「ネイティブコンパイル」という機能を紹介しました。
これにより、プロシージャやファンクションなどの実行速度をある程度速くできることを確認できましたね。
今回は「条件付きコンパイル」という機能を紹介します。

条件付きコンパイルとは、条件によってコンパイルするコードを変更できる機能です。

たとえば、データベースのバージョンによって使用できる機能は異なります。バージョンに応じてコンパイルするソースコードの一部を変更したいときに利用するのが代表的な利用シーンです。
しかし、原理を理解すればこれ以外の用途にも応用できるシンプルな機能です。

早速その方法を説明します。
まず、条件を判断してコンパイルするコードを変更するということで、IF文を使用します。
しかし、普通のIF文ではなく、以下のような構文です。

1
2
3
4
5
$IF  条件1  $ THEN
     条件1のときにコンパイルするコード
$ ELSE
     条件1が該当しないときにコンパイルするコード
$ END

PL/SQLの「普通のIF文」をご存じの方は、変なIF文であることがわかりますね。

まず、IFとか、THENとか、ELSE、ENDといったキーワードの前に「$」をつけています。すなわち、「$IF」、「$ELSE」、「$THEN」、「$END」です。
このような$IF文のことを「選択ディレクティブ」といいます。「ディレクティブ」とは「指示」という意味ですが、コンパイラーに対するコンパイルするコードの指示ということになります。また、最後のキーワードが END IF; でなく、「$END」であることにも注意してください。
普通のIF文の最後は、必ず「END IF;」ですが、条件付きコンパイルのIF文の最後は「$END」になります。

次にこの選択ディレクティブの$IF文の条件式です。
以下のようなものが条件式に使用できます。

1. パッケージで定義した定数(静的なブール値 つまり、TRUEやFALSEの値)
2. データベースの初期化パラメータPLSQL_CCFLAGSで設定したフラグ

今回は、上記1のパッケージの定数を条件とする条件式を例に紹介します。上記2の部分については次回に紹介したいと思います。

まず、パッケージの定数を条件とする場合です。
先ほど、データベースのバージョンによってコンパイルするコードを変えることができると説明しましたが、DBMS_DB_VERSIONパッケージの定数でバージョンを判断することが可能です。このパッケージの定数をもちいることによって、現在のデータベースのバージョンを判断できます。
たとえば、以下のような問い合わせをご覧ください。

SQL> SHOW USER
ユーザーは"SCOTT"です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
SELECT TEXT FROM ALL_SOURCE
WHERE  NAME = 'DBMS_DB_VERSION'
AND TYPE = 'PACKAGE'
ORDER BY LINE
/
 
TEXT
-------------------------------------------------------------------------------
package dbms_db_version is
   version constant pls_integer := 11; -- RDBMS version number
   release constant pls_integer := 1;  -- RDBMS release number
 
    (途中、略)
 
   ver_le_9_1    constant boolean := FALSE ;
   ver_le_9_2    constant boolean := FALSE ;
   ver_le_9      constant boolean := FALSE ;
   ver_le_10_1   constant boolean := FALSE ;
   ver_le_10_2   constant boolean := FALSE ;
   ver_le_10     constant boolean := FALSE ;
   ver_le_11_1   constant boolean := TRUE ;
   ver_le_11     constant boolean := TRUE ;
 
end dbms_db_version;
 
41行が選択されました。

上記の問い合わせは、サンプルユーザのSCOTTユーザでも問い合わせが可能であり、DBMS_DB_VERSIONパッケージの仕様部のソースコードをそのまま問い合わせたものです。
この環境は実は11.1のバージョンなのですが、10行目のversion定数(値は11)、11行目のrelease定数(値は1)を見ればそれがわかります。また、15行目の定数 ver_le_9_1の値がFLASEになっていますが、これは現在のバージョンが 9.1以下ではないという意味です。
(定数名の leという文字は、less than equal(小なりイコール)の略で、「以下」という意味です)
このようにDBMS_DB_VERSIONパッケージを参照すれば、現在のバージョンが何か、あるいは、あるバージョンと比較してそれ以下なのかどうかをすぐに判断できます。

このようなパッケージの定数を選択ディレクティブの条件式に使えば、データベースのバージョンによってコンパイルするソースコードの一部を変更できるわけです。

では、なるべくシンプルな例で確認します。
次の例では、自分でパッケージを作り、そこに簡単なブール値の定数を格納し、その定数を条件式に使った条件付きコンパイルの例を実際に行います。

SQL> SHOW USER
ユーザーは"SCOTT"です。

1
2
3
4
5
6
7
CREATE OR REPLACE  PACKAGE PAC97
IS
    UPPER_FLAG  CONSTANT  BOOLEAN := FALSE ;
END ;
/
 
パッケージが作成されました。

ここでは、SCOTTユーザで、PAC97というパッケージ(仕様部)をつくり、そこに定数 UPPER_FLG を FALSE の値で格納しました。
ではこのパッケージ定数を使った条件付きコンパイルで簡単なファンクションを作成してみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CREATE OR REPLACE FUNCTION FNC97 ( P1 IN NUMBER)
RETURN VARCHAR2
IS
    V_NAME  VARCHAR2(100);
BEGIN
    SELECT
      $IF PAC97.UPPER_FLAG $ THEN
           UPPER (ENAME)
      $ ELSE
           LOWER (ENAME)
      $ END
    INTO V_NAME
    FROM  EMP
    WHERE EMPNO = P1;
    RETURN  V_NAME;
END FNC97;
/
 
ファンクションが作成されました。

上記の記述で、7行目から11行目の範囲が条件付きコンパイルの選択ディレクティブです。
ここでもっとも注目していただきたい点は、一つのSELECT INTO文の中の一部分だけを、条件付きコンパイルによりソースコードを変更している点です。すなわちSELECT INTO文のSELECT句の列を「UPPER(ENAME)」にしたり「LOWER(ENAME)」にしたりしているわけです。つまり、ENAME列を大文字に変換(UPPER)したものを問い合わせるか、あるいは、小文字に変換(LOWER)したものを問い合わせるか、どちらかの形でコンパイルされるわけですね。
PAC97パッケージのUPPER_FLGは定数であり、その値はFALSEと定義しました。
したがって、7行目のブール式 PAC97.UPPER_FLGの値はFALSEですから、7行目の条件は該当せず、10行目のLOWER(ENAME)がコンパイルされ、8行目のUPPER(ENAME)はコンパイルされなかったわけです。

したがって、6行目から14行目にかけて、一つのSELECT INTO文ですが、それは以下のコードでコンパイルされたことになります。
(わかり易く、改行は調整しています)

1
2
3
4
SELECT  LOWER (ENAME)
INTO  V_NAME
FROM  EMP
WHERE EMPNO = P1;

したがって、このファンクションを社員表(EMP表)の社員番号(EMPNO列)の値を指定して実行すれば、社員名(ENAME列)を小文字に変換したものが返ります。
さっそく実行してみましょう。
以下の例をご覧ください。
EMP表に対するSELECT文でこのファンクションをコールしました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SELECT EMPNO, ENAME, FNC97(EMPNO) FROM EMP;
 
      EMPNO ENAME                FNC97(EMPNO)
---------- -------------------- ---------------
       7369 SMITH                smith
       7499 ALLEN                allen
       7521 WARD                 ward
       7566 JONES                jones
       7654 MARTIN               martin
       7698 BLAKE                blake
       7782 CLARK                clark
       7788 SCOTT                scott
       7839 KING                 king
       7844 TURNER               turner
       7876 ADAMS                adams
       7900 JAMES                james
       7902 FORD                 ford
       7934 MILLER               miller

ご覧のように、FNC97(EMPNO)の値はすべて小文字に変換された社員名となっていますね。
このように条件付きコンパイルの機能を使えば、コンパイル時の条件によりコンパイルするソースコードを自由に変更できます。
ではここで、実際にコンパイルされたときに、どのようなソースコードでコンパイルされたかを調べる目的で USER_SOURCEデータディクショナリビューを問い合わせたとしましょう。
しかしそれは意味がありません。以下の例をご覧ください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
SELECT TEXT FROM USER_SOURCE
WHERE NAME = 'FNC97' AND TYPE = 'FUNCTION'
ORDER BY LINE;
 
TEXT
----------------------------------------------------------
FUNCTION FNC97 ( P1 IN NUMBER)
RETURN VARCHAR2
IS
    V_NAME  VARCHAR2(100);
BEGIN
    SELECT
      $IF PAC97.UPPER_FLAG $ THEN
           UPPER (ENAME)
      $ ELSE
           LOWER (ENAME)
      $ END
    INTO V_NAME
    FROM  EMP
    WHERE EMPNO = P1;
    RETURN  V_NAME;
END FNC97;
 
16行が選択されました。

このようにUSER_SOURCEビューを参照しても、条件付きコンパイルの選択ディレクティブを含んだコードそのもので表示されますので、実際にどのようなコードであるか調べることはできないわけです。
そこでこのような場合、以下のようにすれば実際にコンパイルされたコードを確認できます。

SQL> show user
ユーザーは"SYSTEM"です。 -- 管理者ユーザで
SQL> set serveroutput on --画面出力を有効にして

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BEGIN
   DBMS_PREPROCESSOR.PRINT_POST_PROCESSED_SOURCE   -- プロシージャコール
    ( 'FUNCTION' , 'SCOTT' , 'FNC97' );
END ;
/
   -- 以下は上記のプロシージャの画面出力結果
FUNCTION FNC97 ( P1 IN NUMBER)
RETURN VARCHAR2
IS
V_NAME  VARCHAR2(100);
BEGIN
SELECT
LOWER (ENAME)
INTO V_NAME
FROM  EMP
WHERE EMPNO = P1;
RETURN  V_NAME;
END FNC97;
 
PL/SQLプロシージャが正常に完了しました。

ご覧のように、管理者ユーザで、2行目~3行目の、DBMS_PREPROCESSOR.PRINT_POST_PROCESSED_SOURCEというパッケージプロシージャを実行することで実際にコンパイルされたソースコードが表示可能です。
3行目の引数の意味は分かりますね。
SCOTTユーザが所有するFNC97という名前のFUNCTIONを指定したということです。

そして、7~18行目にある表示が、FNC97ファンクションのソースコードそのものです。確かにSELECT句の列が LOWER(ENAME)となっていますね。

このように、パッケージの定数に依存してコンパイルされるコードが変わることが確認できました。
興味のある方は、パッケージ定数の値をFALSEからTRUEに変更してファンクションのコードと実行結果が変わることも確認してみてください。

今回の例は単純な例でしたが、パッケージ定数によって、コンパイルするコードの一部を変更できることを確認できましたね。

それではここまでにします。
次回は、冒頭で申し上げた「2.データベースの初期化パラメータPLSQL_CCFLAGSで設定したフラグ」を条件に使った条件付きコンパイルの例を紹介したいと思います。
ぜひご期待ください。

先頭へ戻る