118 DBMS_PIPE

DBMS_PIPEパッケージによって、同じインスタンスにある複数のセッションの通信を行います。Oracleパイプは、UNIXで使用するパイプと概念は似ていますが、オペレーティング・システムのパイプ・メカニズムを使用して実装されていません。

この章のトピックは、次のとおりです:

118.1 DBMS_PIPEの概要

パイプ機能には、複数の潜在的なアプリケーション(外部サービス・インタフェース、独立したトランザクション、アラート(トランザクション以外)、デバッグおよびコンセントレータ)があります。

  • 外部サービス・インタフェース: RDBMS外部にあるユーザー記述のサービスと通信できます。これは、共有サーバー・プロセスで効果的に行うことができるため、サービスの複数インスタンスが同時に実行されます。さらに、サービスは非同期で使用できます。サービスのリクエスタは、待機応答をブロックする必要がありません。リクエスタは、(タイムアウトに関係なく)後でチェックできます。このサービスは、Oracleがサポートする任意の3GL言語で記述できます。

  • 独立したトランザクション: パイプは、独立したトランザクション(トリガーで検出されたセキュリティ違反のロギングなど)内で操作を実行できる別のセッションと通信できます。

  • アラート(トランザクション以外): ポーリングするための待機プロセスを要求せずに、別のプロセスを転送できます。アプリケーションにアラートを通知するAFTER行またはAFTER文トリガーがある場合、アプリケーションは、このアラートをデータが変更された可能性を示すためのアラートとして処理します。アプリケーションはそのデータを読み込み、現行の値を取得します。これはAFTERトリガーなので、アプリケーションは、SELECT FOR UPDATEを行って正しいデータを読み込んだことを確認しようとします。

  • デバッグ: トリガーおよびストアド・プロシージャは、デバッグ情報をパイプに送信できます。別のセッションは、パイプからの読込みを続行し、その内容をスクリーンに表示したりファイルに書き込むことができます。

  • コンセントレータ: これは、少数のネットワーク接続に対して多数のユーザーがいる場合のユーザーの多重化や、複数のユーザー・トランザクションを1つのDBMSトランザクションに集中化する場合のパフォーマンス改善に役立ちます。

118.2 DBMS_PIPEのセキュリティ・モデル

セキュリティは、DBMS_PIPEパッケージのGRANT EXECUTEを使用して実現できます。これは、CREATE_PIPEファンクションのprivateパラメータを使用してパイプを作成し、特定の機能、特定のユーザーやロールへのパイプ名を表示するのみのカバー・パッケージを記述することによって行われます。

セキュリティ要件に応じて、パブリック・パイプまたはプライベート・パイプのいずれかを選択できます(「DBMS_PIPEの使用上の注意」を参照)。

118.3 DBMS_PIPEの定数

メッセージの送受信を行うための最大待機時間です。

maxwait   constant integer := 86400000; /* 1000 days */ 

118.4 DBMS_PIPEの使用上の注意

Oracleパイプを介して送信する情報は、システム・グローバル領域(SGA)にバッファリングされます。パイプ内のすべての情報は、インスタンスがシャットダウンすると失われます。

警告:

パイプはトランザクションから独立しています。トランザクション制御に影響を与える可能性がある場合は、注意してパイプを使用してください。

DBMS_PIPEの操作は、次のトピックに関して行われます。

  • パブリック・パイプ

  • パイプへの書込みおよび読込み

  • プライベート・パイプ

パブリック・パイプ

パブリック・パイプは、暗黙的または明示的に作成できます。暗黙的なパブリック・パイプの場合、パイプは最初の参照時に自動的に作成され、データがなくなると消去されます。パイプ記述子はSGAに格納されるため、空のパイプがキャッシュから削除されるまで、スペースを使用するオーバーヘッドが若干あります。

privateフラグをFALSEに設定し、CREATE_PIPEファンクションをコールして、明示的なパブリック・パイプを作成します。明示的に作成したパイプは、REMOVE_PIPEファンクションをコールして割当て解除する必要があります。

パブリック・パイプのドメインは、明示的または暗黙的にパイプが作成されたスキーマです。

パイプの読取りおよび書込み

各パブリック・パイプは非同期に動作します。スキーマ・ユーザーがDBMS_PIPEパッケージに対するEXECUTE許可を持ち、パブリック・パイプ名を知っている場合にかぎり、任意の数のスキーマ・ユーザーがパブリック・パイプへの書込みを行うことができます。ただし、バッファに入っている情報を1人のユーザーが読み込むと、その情報はバッファから消去されるため、同じパイプの他のユーザーは読み込むことができません。

送信側セッションでは、PACK_MESSAGEプロシージャに1つ以上コールを行ってメッセージを作成します。このプロシージャは、セッションのローカル・メッセージ・バッファにメッセージを追加します。このバッファ内の情報は、メッセージ送信に使用するパイプ名を指定したSEND_MESSAGEファンクションをコールして送信されます。SEND_MESSAGEをコールすると、ローカル・バッファにスタックされたすべてのメッセージが送信されます。

メッセージを受信するプロセスは、メッセージを受信するパイプ名を指定したRECEIVE_MESSAGEファンクションをコールします。次に、UNPACK_MESSAGEプロシージャをコールして、メッセージ内の各項目にアクセスします。

プライベート・パイプ

CREATE_PIPEファンクションをコールして、プライベート・パイプを明示的に作成します。プライベート・パイプは、一度作成されると、REMOVE_PIPEファンクションをコールして明示的に割当て解除するまで共有メモリーに存続します。プライベート・パイプは、データベース・インスタンスがシャットダウンしたときにも割当て解除されます。

メモリーに暗黙的なパイプが存在し、そのパイプと作成しようとするプライベート・パイプが同じ名前の場合、プライベート・パイプは作成できません。この場合は、CREATE_PIPEからエラーが戻されます。

プライベート・パイプへのアクセスは、次のように限定されます。

  • パイプ作成者と同じユーザーIDで実行中のセッション

  • パイプ作成者と同じユーザーID権限ドメインで実行中のストアド・サブプログラム

  • SYSDBAとして接続したユーザー

これ以外のユーザーがパイプ上のメッセージの送受信やパイプの削除を試みると、即時にエラーが発生します。別のユーザーが同じ名前のパイプを作成しようとしても、エラーが発生します。

パブリック・パイプと同様に、SEND_MESSAGEをコールする前にPACK_MESSAGEへのコールを使用して、最初にメッセージを作成する必要があります。同様に、RECEIVE_MESSAGEをコールしてメッセージを取り出してから、UNPACK_MESSAGEをコールしてメッセージ内の項目にアクセスする必要があります。

118.5 DBMS_PIPEの例外

DBMS_PIPEパッケージ・サブプログラムは、次の表に示すエラーを戻すことができます。

表118-1 DBMS_PIPEのエラー

エラー 説明

ORA-23321:

パイプ名はNULLにできません。このエラーは、CREATE_PIPEファンクションまたはパイプ名をパラメータとして使用しているサブプログラムによって戻されます。

ORA-23322:

パイプへのアクセス権限が不十分です。このエラーは、パラメータ・リストにあるプライベート・パイプを参照するサブプログラムによって戻されます。

118.6 DBMS_PIPEの例

次の例は、PL/SQLのデバッグ、Pro*Cのデバッグ、システム・コマンドの実行、および外部サービス・インタフェースでのDBMS_PIPEの使用例を示します。

例1: デバッグ - PL/SQL

この例は、デバッグ情報をパイプに設定するために、PL/SQLプログラムがコールできるプロシージャを示しています。

CREATE OR REPLACE PROCEDURE debug (msg VARCHAR2) AS
    status  NUMBER;
BEGIN
  DBMS_PIPE.PACK_MESSAGE(LENGTH(msg));
  DBMS_PIPE.PACK_MESSAGE(msg);
  status := DBMS_PIPE.SEND_MESSAGE('plsql_debug');
  IF status != 0 THEN
    raise_application_error(-20099, 'Debug error');
  END IF;
END debug;

例2: デバッグ - Pro*C

次のPro*Cコードでは、前述の例のPLSQL_DEBUGパイプから受信したメッセージが表示されます。Pro*Cセッションが別のウィンドウで実行されている場合、このセッションは、別のセッションで実行中のPL/SQLプログラムからデバッグ・プロシージャに送信されるメッセージの表示に使用できます。

#include <stdio.h> 
#include <string.h> 
 
EXEC SQL BEGIN DECLARE SECTION; 
   VARCHAR username[20]; 
   int     status; 
   int     msg_length;
   char    retval[2000];
EXEC SQL END DECLARE SECTION; 
 
EXEC SQL INCLUDE SQLCA; 
 
void sql_error(); 
 
main() 
{ 

-- Prepare username:
   strcpy(username.arr, "SCOTT/TIGER"); 
   username.len = strlen(username.arr); 
 
   EXEC SQL WHENEVER SQLERROR DO sql_error(); 
   EXEC SQL CONNECT :username; 
 
   printf("connected\n"); 
 
-- Start an endless loop to look for and print messages on the pipe:
   FOR (;;) 
   { 
      EXEC SQL EXECUTE 
         DECLARE 
            len INTEGER;
            typ INTEGER;
            sta INTEGER; 
            chr VARCHAR2(2000); 
         BEGIN 
            chr := ''; 
            sta := dbms_pipe.receive_message('plsql_debug'); 
            IF sta = 0 THEN 
               DBMS_PIPE.UNPACK_MESSAGE(len);
               DBMS_PIPE.UNPACK_MESSAGE(chr); 
            END IF; 
            :status := sta; 
            :retval := chr; 
            IF len IS NOT NULL THEN
               :msg_length := len;
            ELSE
               :msg_length := 2000;
            END IF;
         END; 
      END-EXEC; 
      IF (status == 0) 
         printf("\n%.*s\n", msg_length, retval);
      ELSE 
         printf("abnormal status, value is %d\n", status); 
   }
}

void sql_error() 
{ 
   char msg[1024]; 
   int rlen, len; 
   len = sizeof(msg); 
   sqlglm(msg, &len, &rlen); 
   printf("ORACLE ERROR\n"); 
   printf("%.*s\n", rlen, msg); 
   exit(1); 
} 

例3: システム・コマンドの実行

この例は、PL/SQLとPro*Cコードによって、PL/SQLストアド・プロシージャ(または無名ブロック)がPL/SQLプロシージャをコールし、コマンドをリスニングしているPro*Cプログラムにパイプを介してそのコマンドを送信する処理を示します。

Pro*Cプログラムはスリープして、名前付きパイプにメッセージが着信するのを待ちます。メッセージが着信すると、Pro*Cプログラムはこれを処理し、system()コールを使用したUNIXコマンドの実行、埋込みSQLを使用したSQLコマンドの実行など、必要なアクションを行います。

DAEMON.SQLは、PL/SQLパッケージ用のソース・コードです。このパッケージには、DBMS_PIPEパッケージを使用してPro*Cデーモンに対してメッセージを送受信するプロシージャが含まれています。完全なハンドシェイクが使用されていることに注意してください。このデーモンは、常にメッセージをパッケージに返信します(STOPコマンドの場合は除きます)。これによって、PL/SQLプロシージャはPro*Cデーモンが動作していることを確認できるため、デーモンのこの機能は重要です。

SQL*PlusまたはEnterprise Managerを使用して、無名PL/SQLブロックからDAEMONパッケージ・プロシージャをコールできます。次に例を示します。

SQLPLUS> variable rv number
SQLPLUS> execute :rv := DAEMON.EXECUTE_SYSTEM('ls -la');

これにより、UNIXシステムでは、Pro*Cデーモンによってコマンドsystem("ls -la")が実行されます。

最初にデーモンを実行する必要があることに留意してください。デーモンは、バックグラウンドで実行したり、デーモンをコールしたSQL*PlusまたはEnterprise Managerセッションの近くにある別のウィンドウで実行できます。

また、DAEMON.SQLは、DBMS_OUTPUTパッケージを使用して結果を表示します。この例を動作させるには、このパッケージに対するEXECUTE権限が必要です。

DAEMON.SQLの例。次の例は、PL/SQL DAEMONパッケージのコード例です。

CREATE OR REPLACE PACKAGE daemon AS
  FUNCTION execute_sql(command VARCHAR2, 
                       timeout NUMBER DEFAULT 10)
    RETURN NUMBER;

  FUNCTION execute_system(command VARCHAR2,
                          timeout NUMBER DEFAULT 10)
    RETURN NUMBER;

  PROCEDURE stop(timeout NUMBER DEFAULT 10);
END daemon;
/
CREATE OR REPLACE PACKAGE BODY daemon AS

  FUNCTION execute_system(command VARCHAR2,
                          timeout NUMBER DEFAULT 10)
  RETURN NUMBER IS

    status       NUMBER;
    result       VARCHAR2(20);
    command_code NUMBER;
    pipe_name    VARCHAR2(30);
  BEGIN
    pipe_name := DBMS_PIPE.UNIQUE_SESSION_NAME;

    DBMS_PIPE.PACK_MESSAGE('SYSTEM');
    DBMS_PIPE.PACK_MESSAGE(pipe_name);
    DBMS_PIPE.PACK_MESSAGE(command);
    status := DBMS_PIPE.SEND_MESSAGE('daemon', timeout);
    IF status <> 0 THEN
      RAISE_APPLICATION_ERROR(-20010,
        'Execute_system: Error while sending.  Status = ' ||
         status);
    END IF;

    status := DBMS_PIPE.RECEIVE_MESSAGE(pipe_name, timeout);
    IF status <> 0 THEN
      RAISE_APPLICATION_ERROR(-20011,
        'Execute_system: Error while receiving. 
         Status = ' || status);
    END IF;

    DBMS_PIPE.UNPACK_MESSAGE(result);
    IF result <> 'done' THEN
      RAISE_APPLICATION_ERROR(-20012,
        'Execute_system: Done not received.');
    END IF;

    DBMS_PIPE.UNPACK_MESSAGE(command_code);
    DBMS_OUTPUT.PUT_LINE('System command executed.  result = ' ||
                         command_code);
    RETURN command_code;
  END execute_system;

  FUNCTION execute_sql(command VARCHAR2,
                       timeout NUMBER DEFAULT 10)
  RETURN NUMBER IS

    status       NUMBER;
    result       VARCHAR2(20);
    command_code NUMBER;
    pipe_name    VARCHAR2(30);

  BEGIN
    pipe_name := DBMS_PIPE.UNIQUE_SESSION_NAME;

    DBMS_PIPE.PACK_MESSAGE('SQL');
    DBMS_PIPE.PACK_MESSAGE(pipe_name);
    DBMS_PIPE.PACK_MESSAGE(command);
    status := DBMS_PIPE.SEND_MESSAGE('daemon', timeout);
    IF status <> 0 THEN
      RAISE_APPLICATION_ERROR(-20020,
        'Execute_sql: Error while sending.  Status = ' || status);
    END IF;

    status := DBMS_PIPE.RECEIVE_MESSAGE(pipe_name, timeout);

    IF status <> 0 THEN
      RAISE_APPLICATION_ERROR(-20021,
        'execute_sql: Error while receiving.  
         Status = ' || status);
    END IF;

    DBMS_PIPE.UNPACK_MESSAGE(result);
    IF result <> 'done' THEN
      RAISE_APPLICATION_ERROR(-20022,
        'execute_sql: done not received.');
    END IF;

    DBMS_PIPE.UNPACK_MESSAGE(command_code);
    DBMS_OUTPUT.PUT_LINE
        ('SQL command executed.  sqlcode = ' || command_code);
    RETURN command_code;
  END execute_sql;

  PROCEDURE stop(timeout NUMBER DEFAULT 10) IS
    status NUMBER;
  BEGIN
    DBMS_PIPE.PACK_MESSAGE('STOP');
    status := DBMS_PIPE.SEND_MESSAGE('daemon', timeout);
    IF status <> 0 THEN
      RAISE_APPLICATION_ERROR(-20030,
        'stop: error while sending.  status = ' || status);
    END IF;
  END stop;
END daemon;

DAEMON.PCの例。次の例は、Pro*Cデーモンのコード例です。バージョン1.5.x以降のPro*Cプリコンパイラを使用して、このコードをプリコンパイルする必要があります。この例には埋込みPL/SQLコードが含まれているため、USERIDおよびSQLCHECKオプションも指定する必要があります。

注意:

PL/SQLブロックでVARCHAR出力ホスト変数を使用するには、そのブロックの入力前に長さの要素を初期化する必要があります。

proc iname=daemon userid=scott/tiger sqlcheck=semantics

次に、通常の方法で、Cコンパイルしてリンクします。

#include <stdio.h>
#include <string.h>

EXEC SQL INCLUDE SQLCA;

EXEC SQL BEGIN DECLARE SECTION;
  char *uid = "scott/tiger";
  int status;
  VARCHAR command[20];
  VARCHAR value[2000];
  VARCHAR return_name[30];
EXEC SQL END DECLARE SECTION;

void
connect_error()
{
  char msg_buffer[512];
  int msg_length;
  int buffer_size = 512;

  EXEC SQL WHENEVER SQLERROR CONTINUE;
  sqlglm(msg_buffer, &buffer_size, &msg_length);
  printf("Daemon error while connecting:\n");
  printf("%.*s\n", msg_length, msg_buffer);
  printf("Daemon quitting.\n");
  exit(1);
}

void
sql_error()
{
  char msg_buffer[512];
  int msg_length;
  int buffer_size = 512;

  EXEC SQL WHENEVER SQLERROR CONTINUE;
  sqlglm(msg_buffer, &buffer_size, &msg_length);
  printf("Daemon error while executing:\n");
  printf("%.*s\n", msg_length, msg_buffer);
  printf("Daemon continuing.\n");
}
main()
{
command.len = 20; /*initialize length components*/ 
value.len = 2000; 
return_name.len  = 30; 
  EXEC SQL WHENEVER SQLERROR DO connect_error();
  EXEC SQL CONNECT :uid;
  printf("Daemon connected.\n");

  EXEC SQL WHENEVER SQLERROR DO sql_error();
  printf("Daemon waiting...\n");
  while (1) {
    EXEC SQL EXECUTE
      BEGIN
        :status := DBMS_PIPE.RECEIVE_MESSAGE('daemon');
        IF :status = 0 THEN
          DBMS_PIPE.UNPACK_MESSAGE(:command);
        END IF;
      END;
    END-EXEC;
    IF (status == 0)
    {
      command.arr[command.len] = '\0';
      IF (!strcmp((char *) command.arr, "STOP"))
      {
        printf("Daemon exiting.\n");
        break;
      }

      ELSE IF (!strcmp((char *) command.arr, "SYSTEM"))
      {
        EXEC SQL EXECUTE
          BEGIN
            DBMS_PIPE.UNPACK_MESSAGE(:return_name);
            DBMS_PIPE.UNPACK_MESSAGE(:value);
          END;
        END-EXEC;
        value.arr[value.len] = '\0';
        printf("Will execute system command '%s'\n", value.arr);

        status = system(value.arr);
        EXEC SQL EXECUTE
          BEGIN
            DBMS_PIPE.PACK_MESSAGE('done');
            DBMS_PIPE.PACK_MESSAGE(:status);
            :status := DBMS_PIPE.SEND_MESSAGE(:return_name);
          END;
        END-EXEC;

        IF (status)
        {
          printf
           ("Daemon error while responding to system command.");
          printf("  status: %d\n", status);
        }
      }
      ELSE IF (!strcmp((char *) command.arr, "SQL")) {
        EXEC SQL EXECUTE
          BEGIN
            DBMS_PIPE.UNPACK_MESSAGE(:return_name);
            DBMS_PIPE.UNPACK_MESSAGE(:value);
          END;
        END-EXEC;
        value.arr[value.len] = '\0';
        printf("Will execute sql command '%s'\n", value.arr);

        EXEC SQL WHENEVER SQLERROR CONTINUE;
        EXEC SQL EXECUTE IMMEDIATE :value;
        status = sqlca.sqlcode;

        EXEC SQL WHENEVER SQLERROR DO sql_error();
        EXEC SQL EXECUTE
          BEGIN
            DBMS_PIPE.PACK_MESSAGE('done');
            DBMS_PIPE.PACK_MESSAGE(:status);
            :status := DBMS_PIPE.SEND_MESSAGE(:return_name);
          END;
        END-EXEC;

        IF (status)
        {
          printf("Daemon error while responding to sql command.");
          printf("  status: %d\n", status);
        }
      }
      ELSE
      {
        printf
          ("Daemon error: invalid command '%s' received.\n",
            command.arr);
      }
    }
    ELSE
    {
      printf("Daemon error while waiting for signal.");
      printf("  status = %d\n", status);
    }
  }
  EXEC SQL COMMIT WORK RELEASE;
  exit(0);

例4: 外部サービス・インタフェース

ユーザー作成の3GLコードを、OCIまたはプリコンパイラ・プログラムに設定します。プログラムは、データベースに接続してPL/SQLコードを実行し、パイプから要求を読み込み、結果を計算してから、PL/SQLコードを実行して、パイプ上の結果をリクエスタに返信します。

次に、株式サービス要求の例を示します。すべてのサービス要求についてパイプに渡す引数の推奨する順序を次に示します。

      protocol_version      VARCHAR2        - '1', 10 bytes or less
      returnpipe            VARCHAR2        - 30 bytes or less
      service               VARCHAR2        - 30 bytes or less
      arg1                  VARCHAR2/NUMBER/DATE
         ...
      argn                  VARCHAR2/NUMBER/DATE

結果を戻すための推奨する書式を次に示します。

      success               VARCHAR2        - 'SUCCESS' if OK,
                                              otherwise error message
      arg1                  VARCHAR2/NUMBER/DATE
         ...
      argn                  VARCHAR2/NUMBER/DATE

OCIまたはPRO*(疑似コードで)を使用して、株価要求サーバーを次のように設定します。

    <loop forever>
      BEGIN dbms_stock_server.get_request(:stocksymbol); END;
      <figure out price based on stocksymbol (probably from some radio
            signal), set error if can't find such a stock>
      BEGIN dbms_stock_server.return_price(:error, :price); END;

クライアントは次のように設定します。

    BEGIN :price := stock_request('YOURCOMPANY'); end;

前述の株価要求サーバーでコールしたストアド・プロシージャdbms_stock_serverは、次のように設定します。

    CREATE OR REPLACE PACKAGE dbms_stock_server IS
      PROCEDURE get_request(symbol OUT VARCHAR2);
      PROCEDURE return_price(errormsg IN VARCHAR2, price IN VARCHAR2);
    END;
  
    CREATE OR REPLACE PACKAGE BODY dbms_stock_server IS
      returnpipe    VARCHAR2(30);
  
      PROCEDURE returnerror(reason VARCHAR2) IS
        s INTEGER;
      BEGIN
        dbms_pipe.pack_message(reason);
        s := dbms_pipe.send_message(returnpipe);
        IF s <> 0 THEN
          raise_application_error(-20000, 'Error:' || to_char(s) ||
            ' sending on pipe');
        END IF;
      END;
  
      PROCEDURE get_request(symbol OUT VARCHAR2) IS
        protocol_version VARCHAR2(10);
        s                INTEGER;
        service          VARCHAR2(30);
      BEGIN
        s := dbms_pipe.receive_message('stock_service');
        IF s <> 0 THEN
          raise_application_error(-20000, 'Error:' || to_char(s) ||
            'reading pipe');
        END IF;
        dbms_pipe.unpack_message(protocol_version);
        IF protocol_version <> '1' THEN
          raise_application_error(-20000, 'Bad protocol: ' || 
            protocol_version);
        END IF;
        dbms_pipe.unpack_message(returnpipe);
        dbms_pipe.unpack_message(service);
        IF service != 'getprice' THEN
          returnerror('Service ' || service || ' not supported');
        END IF;
        dbms_pipe.unpack_message(symbol);
      END;
  
      PROCEDURE return_price(errormsg in VARCHAR2, price in VARCHAR2) IS
        s INTEGER;
      BEGIN
        IF errormsg is NULL THEN
          dbms_pipe.pack_message('SUCCESS');
          dbms_pipe.pack_message(price);
        ELSE
          dbms_pipe.pack_message(errormsg);
        END IF;
        s := dbms_pipe.send_message(returnpipe);
        IF s <> 0 THEN
          raise_application_error(-20000, 'Error:'||to_char(s)||
            ' sending on pipe');
        END IF;
      END;
    END;
  

クライアントがコールするプロシージャは、次のように設定します。

    CREATE OR REPLACE FUNCTION stock_request (symbol VARCHAR2) 
        RETURN VARCHAR2 IS
      s        INTEGER;
      price    VARCHAR2(20);
      errormsg VARCHAR2(512);
    BEGIN
      dbms_pipe.pack_message('1');  -- protocol version
      dbms_pipe.pack_message(dbms_pipe.unique_session_name); -- return pipe
      dbms_pipe.pack_message('getprice');
      dbms_pipe.pack_message(symbol);
      s := dbms_pipe.send_message('stock_service');
      IF s <> 0 THEN
        raise_application_error(-20000, 'Error:'||to_char(s)||
          ' sending on pipe');
      END IF;
      s := dbms_pipe.receive_message(dbms_pipe.unique_session_name);
      IF s <> 0 THEN
        raise_application_error(-20000, 'Error:'||to_char(s)||
          ' receiving on pipe');
      END IF;
      dbms_pipe.unpack_message(errormsg);
      IF errormsg <> 'SUCCESS' THEN
        raise_application_error(-20000, errormsg);
      END IF;
      dbms_pipe.unpack_message(price);
      RETURN price;
    END;

一般的に、株式サービス・アプリケーション・サーバーに対してのみDBMS_STOCK_SERVICEGRANT EXECUTEを実行し、このサービスを利用できるユーザーに対してのみstock_requestGRANT EXECUTEを実行します。

参照:

DBMS_ALERT

118.7 DBMS_PIPEサブプログラムの要約

この表は、DBMS_PIPEサブプログラムを示し、簡単に説明しています。

表118-2 DBMS_PIPEパッケージのサブプログラム

サブプログラム 説明

CREATE_PIPEファンクション

パイプを作成します(プライベート・パイプの場合は必須です)。

NEXT_ITEM_TYPEファンクション

バッファにある次の項目のデータ・タイプを戻します。

PACK_MESSAGEプロシージャ

ローカル・バッファにメッセージを作成します。

PURGEプロシージャ

名前付きパイプの内容をパージします。

RECEIVE_MESSAGEファンクション

名前付きパイプからローカル・バッファにメッセージをコピーします。

REMOVE_PIPEファンクション

名前付きパイプを削除します。

RESET_BUFFERプロシージャ

ローカル・バッファの内容をパージします。

SEND_MESSAGEファンクション

メッセージを名前付きパイプに送信します。名前付きパイプが存在しない場合は、パブリック・パイプが暗黙的に作成されます。

UNIQUE_SESSION_NAMEファンクション

一意のセッション名を戻します。

UNPACK_MESSAGEプロシージャ

バッファにある次の項目にアクセスします。

118.7.1 CREATE_PIPEファンクション

このファンクションは、パブリック・パイプまたはプライベート・パイプを明示的に作成します。privateフラグがTRUEの場合、パイプ作成者がそのプライベート・パイプの所有者として割り当てられます。

明示的に作成されたパイプは、REMOVE_PIPEをコールするか、またはインスタンスをシャットダウンすることによってのみ削除できます。

構文

DBMS_PIPE.CREATE_PIPE (
   pipename     IN VARCHAR2,
   maxpipesize  IN INTEGER DEFAULT 8192,
   private      IN BOOLEAN DEFAULT TRUE)
RETURN INTEGER;

プラグマ

pragma restrict_references(create_pipe,WNDS,RNDS); 

パラメータ

表118-3 CREATE_PIPEファンクションのパラメータ

パラメータ 説明

pipename

作成するパイプの名前。

SEND_MESSAGEおよびRECEIVE_MESSAGEをコールするときは、この名前を使用する必要があります。この名前は、インスタンス間で一意である必要があります。

注意: ORA$で始まるパイプ名を使用しないでください。これは、オラクル社が提供する製品用に予約されています。パイプ名は128バイト以下で指定し、大/小文字区別がありません。現時点では、名前にグローバリゼーション・サポート文字を含めることはできません。

maxpipesize

パイプの最大サイズ(バイト単位)。

パイプ上のすべてのメッセージの合計サイズは、この数を超えることはできません。この最大値を超えると、そのメッセージはブロックされます。デフォルトのmaxpipesizeは8192バイトです。

パイプのmaxpipesizeはパイプ特性の一部となり、パイプが存続するかぎり有効です。これより大きいパイプ・サイズでSEND_MESSAGEをコールすると、maxpipesizeの値が大きくなります。これより小さいサイズでコールした場合は、より大きい既存の値を使用します。

private

デフォルトのTRUEを使用して、プライベート・パイプを作成します。

パブリック・パイプは、SEND_MESSAGEをコールして、暗黙的に作成できます。

戻り値

表118-4 CREATE_PIPEファンクションの戻り値

戻り値 説明

0

成功。

パイプが存在し、パイプを作成するユーザーにそのパイプの使用が認可されている場合、Oracleは0(ゼロ)を戻して成功であることを示し、パイプ内のデータはそのまま残ります。

SYSDBA/SYSOPERとして接続したユーザーがパイプを再作成した場合、Oracleはステータス0(ゼロ)を戻しますが、そのパイプの所有者は変更されないままです。

ORA-23322

命名競合のために失敗。

同じ名前のパイプが存在し、別のユーザーがそのパイプを作成した場合、OracleではエラーORA-23322を通知して命名競合であることを示しています。

例外

表118-5 CREATE_PIPEファンクションの例外

例外 説明

パイプ名がNULL

アクセス権エラー: 同名のパイプが存在するため使用できません。

118.7.2 NEXT_ITEM_TYPEファンクション

このファンクションは、ローカル・メッセージ・バッファにある次の項目のタイプを判別します。

RECEIVE_MESSAGEをコールして、ローカル・バッファにパイプ情報を設定した後、NEXT_ITEM_TYPEをコールします。

構文

DBMS_PIPE.NEXT_ITEM_TYPE 
  RETURN INTEGER; 

プラグマ

pragma restrict_references(next_item_type,WNDS,RNDS);  

戻り値

表118-6 NEXT_ITEM_TYPEファンクションの戻り値

戻り値 説明

0

次の項目がありません。

6

NUMBER

9

VARCHAR2

11

ROWID

12

DATE

23

RAW

118.7.3 PACK_MESSAGEプロシージャ

このプロシージャは、ユーザーのメッセージをローカル・メッセージ・バッファに作成します。

メッセージを送信するためには、最初にPACK_MESSAGEを1回以上コールします。次に、SEND_MESSAGEをコールして、ローカル・バッファにあるメッセージを名前付きパイプに送信します。

このプロシージャは、VARCHAR2NCHARNUMBERDATERAWおよびROWIDタイプの項目を受け入れるためにオーバーロードされます。バッファ内の各項目には、データ・バイトの他に、項目のタイプを示すための1バイト、および長さを格納するための2バイトが必要です。メッセージを終了するには、さらに1バイトが必要で、VARCHAR以外のすべてのタイプのオーバーヘッドは4バイトです。

構文

DBMS_PIPE.PACK_MESSAGE (
   item  IN  VARCHAR2);

DBMS_PIPE.PACK_MESSAGE (
   item  IN  NCHAR);

DBMS_PIPE.PACK_MESSAGE (
   item  IN  NUMBER);

DBMS_PIPE.PACK_MESSAGE (
   item  IN  DATE);

DBMS_PIPE.PACK_MESSAGE_RAW (
   item  IN  RAW);

DBMS_PIPE.PACK_MESSAGE_ROWID (
   item  IN  ROWID);

プラグマ

pragma restrict_references(pack_message,WNDS,RNDS);
pragma restrict_references(pack_message_raw,WNDS,RNDS);  
pragma restrict_references(pack_message_rowid,WNDS,RNDS); 

パラメータ

表118-7 PACK_MESSAGEプロシージャのパラメータ

パラメータ 説明

item

ローカル・メッセージ・バッファにパックする項目。

使用上の注意

Oracle Databaseのバージョン8.xでは、キャラクタ・セットID(2バイト)およびキャラクタ・セット・フォーム(1バイト)が各データ項目に格納されています。そのため、Oracle Databaseのバージョン8.x使用時のオーバーヘッドは7バイトになります。

SEND_MESSAGEをコールしてメッセージを送信するときは、メッセージを送信するパイプの名前を示す必要があります。パイプがすでに存在する場合は、そのパイプにアクセスするための十分な権限が必要です。パイプが存在しない場合は、自動的に作成されます。

例外

メッセージ・バッファ(現在は4096バイト)がオーバーフローした場合は、ORA-06558が発生します。バッファ内の各項目には、実際のデータに加えて、タイプを示すために1バイト、長さを示すために2バイトが必要です。さらに、メッセージを終了するために1バイトが必要です。

118.7.4 PURGEプロシージャ

このプロシージャは、名前付きパイプの内容を空にします。

暗黙的に作成された空のパイプは、LRUアルゴリズムに従って、共有グローバル領域から削除されます。したがって、PURGEをコールすると、暗黙的に作成したパイプに関連するメモリーを空にできます。

構文

DBMS_PIPE.PURGE (
   pipename  IN  VARCHAR2);

プラグマ

pragma restrict_references(purge,WNDS,RNDS); 

パラメータ

表118-8 PURGEプロシージャのパラメータ

パラメータ 説明

pipename

すべてのメッセージを削除するパイプの名前。

メッセージが廃棄されると、ローカル・バッファがそのメッセージで上書きされる場合があります。パイプ名は128バイト以下で指定し、大/小文字は区別されません。

使用上の注意

PURGEプロシージャはRECEIVE_MESSAGEをコールするため、メッセージがパイプからパージされるとき、ローカル・バッファはそのメッセージで上書きされる場合があります。また、不十分なアクセス権でパイプをパージしようとすると、ORA-23322エラー(不十分な権限)を受け取ります。

例外

パイプを別のユーザーが所有している場合は、アクセス権エラーが発生します。

118.7.5 RECEIVE_MESSAGEファンクション

このファンクションは、メッセージをローカル・メッセージ・バッファにコピーします。

構文

DBMS_PIPE.RECEIVE_MESSAGE (
   pipename     IN VARCHAR2,
   timeout      IN INTEGER      DEFAULT maxwait)
RETURN INTEGER;

プラグマ

pragma restrict_references(receive_message,WNDS,RNDS);  

パラメータ

表118-9 RECEIVE_MESSAGEファンクションのパラメータ

パラメータ 説明

pipename

メッセージを受信するパイプ名。

ORA$で始まる名前は、オラクル社で使用するために予約されています。

timeout

メッセージを待つ時間(秒単位)。

デフォルト値は定数MAXWAITで、86400000(1000日)に定義されています。タイムアウトを0(ゼロ)に指定すると、ブロックされずに読み込むことができます。

戻り値

表118-10 RECEIVE_MESSAGEファンクションの戻り値

戻り値 説明

0

成功。

1

タイムアウトしました。暗黙的に作成されたパイプが空の場合、そのパイプは削除されます。

2

パイプにあるレコードが、バッファに対して大きすぎます。(これは起りえないことです。)

3

割込みが発生しました。

ORA-23322

パイプから読み込むための十分な権限がユーザーにありません。

使用上の注意

パイプからメッセージを受信するには、最初にRECEIVE_MESSAGEをコールします。メッセージを受信すると、メッセージはパイプから削除されるため、メッセージは1回しか受信できません。暗黙的に作成されたパイプは、最後のレコードがそのパイプから削除された後で削除されます。

RECEIVE_MESSAGEのコール時に指定したパイプがすでに存在しない場合、Oracleはパイプを暗黙的に作成してメッセージの受信を待ちます。メッセージが指定したタイムアウト時間内に着信しなかった場合、そのコールは戻され、パイプは削除されます。

メッセージの受信後、1つ以上のUNPACK_MESSAGEをコールして、メッセージ内の個別の項目にアクセスする必要があります。UNPACK_MESSAGEプロシージャは、DATENUMBERVARCHAR2タイプの項目をアンパックするためにオーバーロードされ、さらに、RAWおよびROWID項目をアンパックするための2つのプロシージャがあります。アンパックするデータのタイプが不明の場合は、NEXT_ITEM_TYPEをコールして、バッファ内にある次の項目のタイプを判別します。

例外

表118-11 RECEIVE_MESSAGEファンクションの例外

例外 説明

パイプ名がNULL

アクセス権エラー。パイプからレコードを削除するための権限が不十分です。パイプは別のユーザーが所有しています。

118.7.6 RESET_BUFFERプロシージャ

このプロシージャは、PACK_MESSAGEおよびUNPACK_MESSAGEの位置設定インジケータを0(ゼロ)にリセットします。

すべてのパイプが1つのバッファを共有しているため、新規パイプを使用する前にバッファをリセットすると効果的です。これにより、初めてメッセージをパイプに送信するとき、バッファ内に残っていた期限切れのメッセージを誤って送信することがなくなります。

構文

DBMS_PIPE.RESET_BUFFER; 

プラグマ

pragma restrict_references(reset_buffer,WNDS,RNDS);  

118.7.7 REMOVE_PIPEファンクション

このファンクションは、明示的に作成されたパイプを削除します。

SEND_MESSAGEで暗黙的に作成されたパイプは、空になると自動的に削除されます。ただし、CREATE_PIPEで明示的に作成されたパイプは、REMOVE_PIPEをコールするか、またはインスタンスをシャットダウンした場合にのみ削除されます。パイプ内の未使用のレコードは、パイプが削除される前にすべて削除されます。

これは、暗黙的に作成されたパイプでPURGEをコールするのに似ています。

構文

DBMS_PIPE.REMOVE_PIPE (
   pipename  IN  VARCHAR2)
RETURN INTEGER;

プラグマ

pragma restrict_references(remove_pipe,WNDS,RNDS); 

パラメータ

表118-12 REMOVE_PIPEファンクションのパラメータ

パラメータ 説明

pipename

削除するパイプ名。

戻り値

表118-13 REMOVE_PIPEファンクションの戻り値

戻り値 説明

0

成功。

パイプが存在しない場合、またはパイプがすでに存在し、パイプを削除しようとするユーザーに削除が認可されている場合、Oracleは0(ゼロ)を戻して成功であることを示し、パイプ内に残っているデータが削除されます。

ORA-23322

権限が不十分です。

パイプが存在しても、ユーザーにそのパイプへのアクセス権がない場合、OracleはエラーORA-23322を通知して権限が不十分であることを示します。

例外

表118-14 REMOVE_PIPEファンクションの例外

例外 説明

パイプ名がNULL

アクセス権エラー: パイプを削除する権限が不十分です。パイプは作成されていて、別のユーザーが所有しています。

118.7.8 SEND_MESSAGEファンクション

このファンクションは、メッセージを名前付きパイプに送信します。

PACK_MESSAGEへのコールで入力されたメッセージは、ローカル・メッセージ・バッファに格納されます。CREATE_PIPEを使用するとパイプを明示的に作成でき、それ以外の場合は暗黙的に作成されます。

構文

DBMS_PIPE.SEND_MESSAGE (
    pipename     IN VARCHAR2,
    timeout      IN INTEGER DEFAULT MAXWAIT,
    maxpipesize  IN INTEGER DEFAULT 8192)
  RETURN INTEGER;

プラグマ

pragma restrict_references(send_message,WNDS,RNDS);  

パラメータ

表118-15 SEND_MESSAGEファンクションのパラメータ

パラメータ 説明

pipename

メッセージを設定するパイプの名前。

明示的なパイプを使用している場合、この名前は、CREATE_PIPEをコールしたときに指定した名前です。

注意: ORA$で始まるパイプ名を使用しないでください。これは、オラクル社が提供する製品用に予約されています。パイプ名は128バイト以下で指定し、大/小文字は区別されません。現時点では、名前にグローバリゼーション・サポート文字を含めることはできません。

timeout

パイプにメッセージを設定する間の待機時間(秒単位)。

デフォルト値は定数MAXWAITで、86400000(1000日)に定義されています。

maxpipesize

パイプの最大サイズ(バイト単位)。

パイプ上のすべてのメッセージの合計サイズは、この数を超えることはできません。この最大値を超えると、そのメッセージはブロックされます。デフォルトは8192バイトです。

パイプのmaxpipesizeはパイプ特性の一部となり、パイプが存続するかぎり有効です。これより大きいパイプ・サイズでSEND_MESSAGEをコールすると、maxpipesizeの値が大きくなります。これより小さいサイズでコールした場合は、より大きい既存の値を使用します。

SEND_MESSAGEプロシージャの一部としてmaxpipesizeを指定すると、別のコールでパイプをオープンする必要がなくなります。明示的にパイプを作成した場合は、オプションのmaxpipesizeパラメータを使用して、作成したパイプのサイズを上書きできます。

戻り値

表118-16 SEND_MESSAGEファンクションの戻り値

戻り値 説明

0

成功。

パイプが存在し、パイプを作成するユーザーにそのパイプの使用が認可されている場合、Oracleは0(ゼロ)を戻して成功であることを示し、パイプ内のデータはそのまま残ります。

SYSDBS/SYSOPERとして接続したユーザーがパイプを再作成した場合、Oracleはステータス0(ゼロ)を戻しますが、そのパイプの所有者は変更されないままです。

1

タイムアウトしました。

このプロシージャは、パイプでロックが取得できないか、またはパイプがいっぱいで使用できない理由でタイムアウトできます。暗黙的に作成されたパイプが空の場合、そのパイプは削除されます。

3

割込みが発生しました。

暗黙的に作成されたパイプが空の場合、そのパイプは削除されます。

ORA-23322

権限が不十分です。

同じ名前のパイプが存在し、別のユーザーがそのパイプを作成した場合、OracleではエラーORA-23322を通知して命名競合であることを示しています。

例外

表118-17 SEND_MESSAGEファンクションの例外

例外 説明

パイプ名がNULL

アクセス権エラー。パイプに書込みを行うための権限が不十分です。パイプはプライベートで、別のユーザーが所有しています。

118.7.9 UNIQUE_SESSION_NAMEファンクション

このファンクションは、現在データベースに接続しているすべてのセッション間での一意の名前を受け取ります。

同じセッションからこのファンクションを複数回コールしても、常に同じ値が戻されます。このファンクションは、SEND_MESSAGEおよびRECEIVE_MESSAGEのコールにPIPENAMEパラメータを提供するのに役立ちます。

構文

DBMS_PIPE.UNIQUE_SESSION_NAME 
  RETURN VARCHAR2;

プラグマ

pragma restrict_references(unique_session_name,WNDS,RNDS,WNPS);

戻り値

このファンクションは、一意の名前を戻します。戻される名前は、最大30バイトです。

118.7.10 UNPACK_MESSAGEプロシージャ

このプロシージャは、バッファから項目を取り出します。

RECEIVE_MESSAGEをコールして、ローカル・バッファにパイプ情報を設定した後、UNPACK_MESSAGEをコールします。

注意:

UNPACK_MESSAGEプロシージャは、VARCHAR2NCHARNUMBERまたはDATEタイプの項目を戻すためにオーバーロードされています。さらに、RAWおよびROWID項目をアンパックするための2つのプロシージャがあります。

構文

DBMS_PIPE.UNPACK_MESSAGE (
   item  OUT VARCHAR2);

DBMS_PIPE.UNPACK_MESSAGE (
   item  OUT NCHAR);

DBMS_PIPE.UNPACK_MESSAGE (
   item  OUT NUMBER);

DBMS_PIPE.UNPACK_MESSAGE (
   item  OUT DATE);

DBMS_PIPE.UNPACK_MESSAGE_RAW (
   item  OUT RAW);

DBMS_PIPE.UNPACK_MESSAGE_ROWID (
   item  OUT ROWID);

プラグマ

pragma restrict_references(unpack_message,WNDS,RNDS);  
pragma restrict_references(unpack_message_raw,WNDS,RNDS);  
pragma restrict_references(unpack_message_rowid,WNDS,RNDS);  

パラメータ

表118-18 UNPACK_MESSAGEプロシージャのパラメータ

パラメータ 説明

item

アンパックされた次の項目をローカル・メッセージ・バッファから受け取るための引数。

例外

バッファに次の項目がない場合、または項目が要求されたタイプでない場合は、ORA-06556または06559が生成されます。