オーバーロードされたサブプログラム

PL/SQLでは、ネストしたサブプログラム、パッケージ・サブプログラムおよび型のメソッドをオーバーロードできます。仮パラメータの名前、数、順序またはデータ型のファミリが異なっている場合、同じ名前を複数のサブプログラムで使用できます。(データ型ファミリは、データ型とそのサブタイプです。事前定義のPL/SQLデータ型のデータ型ファミリは、「PL/SQLの事前定義のデータ型」を参照してください。ユーザー定義のPL/SQLサブタイプの詳細は、「ユーザー定義のPL/SQLサブタイプ」を参照してください。)仮パラメータの違いが名前のみである場合、名前表記法を使用して対応する実パラメータを指定する必要があります。(名前表記法の詳細は、実パラメータの位置表記法、名前表記法および混合表記法を参照してください。)

例9-26では、同じ名前(initialize)を持つ2つのサブプログラムを定義しています。プロシージャでは様々な型のコレクションを初期化します。これらのプロシージャは同じ処理を実行しているため、同じ名前を与えるのが論理的です。

この2つのinitializeプロシージャは、同じブロック、サブプログラム、パッケージまたは型本体の中に置くことができます。PL/SQLは仮パラメータをチェックして、どちらのプロシージャを起動するかを判断します。PL/SQLが使用するinitializeのバージョンは、プロシージャをdate_tab_typパラメータまたはnum_tab_typパラメータのどちらで起動するかによって異なります。

パッケージ内のオーバーロードされたプロシージャの例は、例11-9を参照してください。

ここでのトピック

例9-26 オーバーロードされたサブプログラム

DECLARE
  TYPE date_tab_typ IS TABLE OF DATE   INDEX BY PLS_INTEGER;
  TYPE num_tab_typ  IS TABLE OF NUMBER INDEX BY PLS_INTEGER;

  hiredate_tab  date_tab_typ;
  sal_tab       num_tab_typ;

  PROCEDURE initialize (tab OUT date_tab_typ, n INTEGER) IS
  BEGIN
    DBMS_OUTPUT.PUT_LINE('Invoked first version');
    FOR i IN 1..n LOOP
      tab(i) := SYSDATE;
    END LOOP;
  END initialize;

  PROCEDURE initialize (tab OUT num_tab_typ, n INTEGER) IS
  BEGIN
    DBMS_OUTPUT.PUT_LINE('Invoked second version');
    FOR i IN 1..n LOOP
      tab(i) := 0.0;
    END LOOP;
  END initialize;

BEGIN
  initialize(hiredate_tab, 50);
  initialize(sal_tab, 100);
END;
/

結果:

Invoked first version
Invoked second version

数値データ型のみが異なる仮パラメータ

サブプログラムの仮パラメータの違いが数値データ型のみの場合、それらのサブプログラムはオーバーロードできます。ファンクションの複数のバージョンが同じ名前を使用でき、それぞれが異なる数値型を受け取ることができるため、この手法は数値演算Application Programming Interface(API)を記述する場合に有効です。たとえば、BINARY_FLOATを受け取るファンクションはより高速で、BINARY_DOUBLEを受け取るファンクションは精度がより高くなる場合があります。

オーバーロードされたサブプログラムにパラメータを渡す場合、次のことに注意して、問題または予期しない結果を回避します。

  • 予期されるパラメータ・セットごとに、目的のバージョンのサブプログラムが起動されることを確認します。

    たとえば、オーバーロードされるファンクションがBINARY_FLOATおよびBINARY_DOUBLEを受け取る場合、「5.0」のようなVARCHAR2リテラルを渡すと、どちらのファンクションが起動されるかを確認します。

  • 数値リテラルを修飾したり、変換ファンクションを使用して、目的のパラメータ型を明示します。

    たとえば、5.0f(BINARY_FLOAT)、5.0d(BINARY_DOUBLE)のようなリテラル、またはTO_BINARY_FLOATTO_BINARY_DOUBLETO_NUMBERのような変換ファンクションを使用します。

PL/SQLは、次の順序で、一致する数値パラメータを検索します。

  1. PLS_INTEGER(または同じデータ型であるBINARY_INTEGER)

  2. NUMBER

  3. BINARY_FLOAT

  4. BINARY_DOUBLE

VARCHAR2値は、NUMBERBINARY_FLOATまたはBINARY_DOUBLEパラメータに一致します。

PL/SQLは、指定されたパラメータに最初に一致してオーバーロードされるサブプログラムを使用します。たとえば、SQRTファンクションは1つのパラメータを受け取ります。NUMBERBINARY_FLOATまたはBINARY_DOUBLEパラメータを受け取るオーバーロードされるファンクションがあるとします。PLS_INTEGERパラメータを渡した場合、最初に一致してオーバーロードされるファンクションは、NUMBERパラメータを持つファンクションです。

NUMBERパラメータを受け取るSQRTファンクションは、最も低速となる可能性が高くなります。より高速なバージョンを使用するには、パラメータをSQRTファンクションに渡す前に、TO_BINARY_FLOATまたはTO_BINARY_DOUBLEファンクションを使用して、そのパラメータを別のデータ型に変換します。

PL/SQLは、パラメータを別のデータ型に変換する必要がある場合、まずそのパラメータをより上位のデータ型に変換しようとします。たとえば:

  • ATAN2ファンクションは、同じ型の2つのパラメータを受け取ります。異なる型のパラメータを渡した場合(たとえば、1つがPLS_INTEGERで、1つがBINARY_FLOATの場合)、PL/SQLは、両方のパラメータでより高度な型が使用されている場合に一致するものを検索します。この例では、2つのBINARY_FLOATパラメータを受け取るバージョンのATAN2が使用され、PLS_INTEGERパラメータは、上位変換されます。

  • あるファンクションは、異なる型の2つのパラメータを受け取ります。オーバーロードされるバージョンの1つは、PLS_INTEGERパラメータおよびBINARY_FLOATパラメータを受け取ります。別のオーバーロードされるバージョンは、NUMBERパラメータおよびBINARY_DOUBLEパラメータを受け取ります。このファンクションを起動して、2つのNUMBERパラメータを渡した場合、PL/SQLは、まず2つ目のパラメータがBINARY_FLOATのオーバーロードされるバージョンを検出します。このパラメータは、他方のオーバーロードされるバージョンのBINARY_DOUBLEパラメータよりも一致度が高いため、次にPL/SQLは下位検索し、1つ目のNUMBERパラメータをPLS_INTEGERに変換します。

オーバーロードできないサブプログラム

次のサブプログラムはオーバーロードできません。

  • スタンドアロン・サブプログラム

  • 仮パラメータの違いがモードのみのサブプログラム。たとえば:

    PROCEDURE s (p IN  VARCHAR2) IS ...
    PROCEDURE s (p OUT VARCHAR2) IS ...
    
  • 仮パラメータの違いがサブタイプのみのサブプログラム。たとえば:

    PROCEDURE s (p INTEGER) IS ...
    PROCEDURE s (p REAL) IS ...
    

    INTEGERおよびREALは、NUMBERのサブタイプであるため、同じデータ型のファミリに属しています。

  • 戻り値のデータ型のみが異なるファンクション(そのデータ型のファミリが異なっている場合でも)。たとえば:

    FUNCTION f (p INTEGER) RETURN BOOLEAN IS ...
    FUNCTION f (p INTEGER) RETURN INTEGER IS ...

サブプログラムのオーバーロード・エラー

PL/SQLコンパイラは、起動されたサブプログラムを判別できないと判断するとすぐに、オーバーロード・エラーを捕捉します。複数のサブプログラムに同一のヘッダーがある場合、サブプログラム自体のコンパイル(サブプログラムがネストされている場合)、またはサブプログラムを宣言しているパッケージ仕様部のコンパイルを試行すると、コンパイラはオーバーロード・エラーを捕捉します。それ以外の場合、サブプログラムの曖昧な起動のコンパイルを試行すると、コンパイラはエラーを捕捉します。

例9-27に示す、同一のヘッダーを持つ複数のサブプログラムを宣言しているパッケージ仕様部をコンパイルしようとすると、コンパイル時エラーPLS-00305が発生します。

例9-28に示すパッケージ仕様部は、仮パラメータの違いがサブタイプのみのサブプログラムはオーバーロードできないという規則に違反していますが、エラーを生成せずにコンパイルできます。

ただし、例9-29に示すようにpkg2.sの起動をコンパイルしようとすると、コンパイル時エラーPLS-00307が発生します。

例9-30のように、オーバーロードされたサブプログラムの仮パラメータに別の名前を付けることによって、例9-28に示されているオーバーロード・エラーを修正するとします。

これによって、名前表記法を使用して実パラメータを指定する場合に、エラーを生成せずにpkg2.sの起動をコンパイルできます(例9-31を参照)。(例9-29に示されているように、位置表記法を使用して実パラメータを指定すると、コンパイル時エラーPLS-00307が発生します。)

例9-32に示すパッケージ仕様部は、オーバーロード規則に違反していないため、エラーを生成せずにコンパイルできます。ただし、オーバーロードされたプロシージャを起動すると、例9-33の2つ目の起動に示すように、コンパイル時エラーPLS-00307が発生します。

どのサブプログラムが起動されたかを特定しようとしたとき、PL/SQLコンパイラは、暗黙的にあるパラメータを一致するタイプに変換すると、暗黙的に一致するタイプに変換できる別のパラメータを検索します。複数の一致がある場合、例9-34に示すように、コンパイル時エラーPLS-00307が発生します。

初期化パラメータPLSQL_IMPLICIT_CONVERSION_BOOLは、BOOLEANおよびその他のタイプ・パラメータを含むオーバーロードされたサブプログラムの処理方法に影響します。サブプログラムがBOOLEAN型と数値型または文字型でオーバーロードされている場合、PLSQL_IMPLICIT_CONVERSION_BOOLTRUEに設定すると、コンパイル時エラーが発生する可能性があります。ただし、パラメータがFALSEに設定されている場合、サブプログラムは引数を暗黙的に代替型に変換します。

たとえば、PLSQL_IMPLICIT_CONVERSION_BOOLFALSEに設定されている場合、文字列値'1'、または文字列で表される0以外の数値は、例9-35のように、デフォルトで数値に変換されます。PLSQL_IMPLICIT_CONVERSION_BOOLTRUEに設定されている場合、'1'BOOLEANまたは数値に変換でき、例9-36のようにPLS-00307エラーが発生します。このエラーは、指定された引数をBOOLEANまたは代替オーバーロード・タイプのいずれかに変換できるときに発生します。

関連項目:

例9-27 コンパイル時エラーの原因となるオーバーロード・エラー

CREATE OR REPLACE PACKAGE pkg1 AUTHID DEFINER IS
  PROCEDURE s (p VARCHAR2);
  PROCEDURE s (p VARCHAR2);
END pkg1;
/

例9-28 正常にコンパイルが行われるオーバーロード・エラー

CREATE OR REPLACE PACKAGE pkg2 AUTHID DEFINER IS
  SUBTYPE t1 IS VARCHAR2(10);
  SUBTYPE t2 IS VARCHAR2(10);
  PROCEDURE s (p t1);
  PROCEDURE s (p t2);
END pkg2;
/

例9-29 コンパイル時エラーの原因となる例9-28のサブプログラムの起動

CREATE OR REPLACE PROCEDURE p AUTHID DEFINER IS
  a pkg2.t1 := 'a';
BEGIN
  pkg2.s(a);  -- Causes compile-time error PLS-00307
END p;
/

例9-30 例9-28のオーバーロード・エラーの修正

CREATE OR REPLACE PACKAGE pkg2 AUTHID DEFINER IS
  SUBTYPE t1 IS VARCHAR2(10);
  SUBTYPE t2 IS VARCHAR2(10);
  PROCEDURE s (p1 t1);
  PROCEDURE s (p2 t2);
END pkg2;
/

例9-31 例9-30のサブプログラムの起動

CREATE OR REPLACE PROCEDURE p AUTHID DEFINER IS
  a pkg2.t1 := 'a';
BEGIN
  pkg2.s(p1=>a);  -- Compiles without error
END p;
/

例9-32 オーバーロード・エラーが含まれていないパッケージ仕様部

CREATE OR REPLACE PACKAGE pkg3 AUTHID DEFINER IS
  PROCEDURE s (p1 VARCHAR2);
  PROCEDURE s (p1 VARCHAR2, p2 VARCHAR2 := 'p2');
END pkg3;
/

例9-33 適切にオーバーロードされたサブプログラムの不適切な起動

CREATE OR REPLACE PROCEDURE p AUTHID DEFINER IS
  a1 VARCHAR2(10) := 'a1';
  a2 VARCHAR2(10) := 'a2';
BEGIN
  pkg3.s(p1=>a1, p2=>a2);  -- Compiles without error
  pkg3.s(p1=>a1);          -- Causes compile-time error PLS-00307
END p;
/

例9-34 パラメータの暗黙的な変換によるオーバーロード・エラー

CREATE OR REPLACE PACKAGE pack1 AUTHID DEFINER AS
  PROCEDURE proc1 (a NUMBER, b VARCHAR2);
  PROCEDURE proc1 (a NUMBER, b NUMBER);
END;
/
CREATE OR REPLACE PACKAGE BODY pack1 AS
  PROCEDURE proc1 (a NUMBER, b VARCHAR2) IS BEGIN NULL; END;
  PROCEDURE proc1 (a NUMBER, b NUMBER) IS BEGIN NULL; END;
END;
/
BEGIN
  pack1.proc1(1,'2');    -- Compiles without error
  pack1.proc1(1,2);      -- Compiles without error
  pack1.proc1('1','2');  -- Causes compile-time error PLS-00307
  pack1.proc1('1',2);    -- Causes compile-time error PLS-00307
END;
/

例9-35 数値への暗黙的な変換の成功

この例の正常な実行は、FALSEに設定されている初期化パラメータPLSQL_IMPLICIT_CONVERSION_BOOLによって異なります。パラメータはデフォルトでFALSEに設定されています。

ALTER SESSION SET PLSQL_IMPLICIT_CONVERSION_BOOL = FALSE;
CREATE OR REPLACE PACKAGE pkg1 AUTHID DEFINER IS
  PROCEDURE s (p INTEGER);
  PROCEDURE s (p BOOLEAN);
END pkg1;
/
 
CREATE OR REPLACE PACKAGE BODY pkg1 IS
  PROCEDURE s (p INTEGER) AS
  BEGIN
    dbms_output.put_line ( 'Integer' );
  END;
  PROCEDURE s (p BOOLEAN) AS
  BEGIN
    dbms_output.put_line ( 'Boolean' );
  END;
END pkg1;
/

BEGIN
pkg1.s('1');  -- Compiles without error
END;
/

結果:

Integer

例9-36 BOOLEANまたは数値の暗黙的な変換によるオーバーロード・エラー

この例では、例9-35で宣言されたサブプログラムを使用します。

ALTER SESSION SET PLSQL_IMPLICIT_CONVERSION_BOOL = TRUE;

exec pkg1.s('1');  -- Causes compile-time error PLS-00307

プロシージャsINTEGERではなくVARCHAR2を受け入れ、番号1がプロシージャに指定されている場合、同じエラーが発生します。