ヘッダーをスキップ
Oracle Streamsレプリケーション管理者ガイド
11g リリース1(11.1)
E05776-02
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

11 論理変更レコード(LCR)の管理

この章では、Oracle Streamsレプリケーション環境で論理変更レコード(LCR)を管理する方法について説明します。

この章の内容は次のとおりです。


参照:

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

LCRを管理するための要件

この項では、論理変更レコード(LCR)を作成または変更するための要件について説明します。LCR型のコンストラクタを使用してLCRを作成し、そのLCRをANYDATAキューの永続キュー部分にエンキューできます。このようなLCRは、永続LCRです。

また、適用ハンドラまたはルールベースの変換を使用して、LCRを変更できます。取得LCRまたは永続LCRを変更できます。

LCRを管理するときには、次の要件を満たしていることを確認してください。


参照:


LCRの構成とエンキュー

次のLCRコンストラクタを使用してLCRを作成します。

次の例では、Oracle Database内のキューと、そのキューに関連付けられた適用プロセスを作成します。次に、渡された情報に基づいて行LCRを構成し、それをキューにエンキューするPL/SQLプロシージャを作成します。この例では、Oracle Streams管理者strmadminを構成して、この管理者にDBAロールを付与していると想定しています。

この操作の手順は次のとおりです。

  1. SQL*Plusで、管理ユーザーとしてデータベースに接続します。

    SQL*Plusでデータベースに接続する手順については、『Oracle Database管理者ガイド』を参照してください。

  2. Oracle Streams管理者にDBMS_STREAMS_MESSAGINGパッケージに対するEXECUTE権限を付与します。次に例を示します。

    GRANT EXECUTE ON DBMS_STREAMS_MESSAGING TO strmadmin;
    

    手順9のPL/SQLプロシージャ内でこのパッケージのプロシージャをコールするため、このパッケージに対する明示的なEXECUTE権限が必要です。この場合、ロールを介して権限を付与するのみでは不十分です。

  3. SQL*Plusで、Oracle Streams管理者としてデータベースに接続します。

  4. Oracle DatabaseにANYDATAキューを作成します。

    BEGIN
      DBMS_STREAMS_ADM.SET_UP_QUEUE(
        queue_table          =>  'strm04_queue_table',
        storage_clause       =>  NULL,
        queue_name           =>  'strm04_queue');
    END;
    /
    
  5. キュー内のメッセージを受信するOracle Databaseで、適用プロセスを作成します。適用プロセスでは、取得LCRではなく永続LCRが適用されるため、この適用プロセスの作成時には、apply_capturedパラメータをFALSEに設定してください。また、hr.regions表に変更が適用され、適用ユーザーにはこの表にDML変更を加える権限が必要であるため、apply_userパラメータがhrに設定されていることを確認してください。

    BEGIN
      DBMS_APPLY_ADM.CREATE_APPLY(
         queue_name      => 'strm04_queue',
         apply_name      => 'strm04_apply',
         apply_captured  => FALSE,
         apply_user      => 'hr');
    END;
    /
    
  6. 適用プロセスのポジティブ・ルール・セットを作成し、DML変更をdbs1.netソース・データベースに作成されたhr.regions表に適用するルールを追加します。

    BEGIN
      DBMS_STREAMS_ADM.ADD_TABLE_RULES(
        table_name          =>  'hr.regions',
        streams_type        =>  'apply',
        streams_name        =>  'strm04_apply',
        queue_name          =>  'strm04_queue',
        include_dml         =>  TRUE,
        include_ddl         =>  FALSE,
        include_tagged_lcr  =>  FALSE,
        source_database     =>  'dbs1.net',
        inclusion_rule      =>  TRUE);
    END;
    /
    
  7. 適用プロセスのdisable_on_errorパラメータをnに設定します。

    BEGIN
      DBMS_APPLY_ADM.SET_PARAMETER(
        apply_name  => 'strm04_apply',
        parameter   => 'disable_on_error',
        value       => 'N');
    END;
    /
    
  8. 適用プロセスを起動します。

    EXEC DBMS_APPLY_ADM.START_APPLY('strm04_apply');
    
  9. 行LCRを構成して手順4で作成したキューにエンキューするプロシージャconstruct_row_lcrを作成します。

    CREATE OR REPLACE PROCEDURE construct_row_lcr(
                     source_dbname  VARCHAR2,
                     cmd_type       VARCHAR2,
                     obj_owner      VARCHAR2,
                     obj_name       VARCHAR2,
                     old_vals       SYS.LCR$_ROW_LIST,
                     new_vals       SYS.LCR$_ROW_LIST) AS
      row_lcr        SYS.LCR$_ROW_RECORD;
    BEGIN
      -- Construct the LCR based on information passed to procedure
      row_lcr := SYS.LCR$_ROW_RECORD.CONSTRUCT(
        source_database_name  =>  source_dbname,
        command_type          =>  cmd_type,
        object_owner          =>  obj_owner,
        object_name           =>  obj_name,
        old_values            =>  old_vals,
        new_values            =>  new_vals);
      -- Enqueue the created row LCR
      DBMS_STREAMS_MESSAGING.ENQUEUE(
        queue_name         =>  'strm04_queue',
        payload            =>  ANYDATA.ConvertObject(row_lcr));
    END construct_row_lcr;
    /
    

    注意:

    トランザクション識別子やSCNは適用プロセスで生成されてメモリーに格納されるため、これらの値をLCRの作成時にアプリケーションで指定する必要はありません。LCRでトランザクション識別子またはSCNを指定しても、適用プロセスでは無視され、新規の値が割り当てられます。


    参照:

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

  10. 手順5で作成したconstruct_row_lcrプロシージャを使用してLCRを作成し、エンキューします。

    1. SQL*Plusで、Oracle Streams管理者としてデータベースに接続します。

    2. hr.regions表に1行を挿入する行LCRを作成します。

      DECLARE
        newunit1  SYS.LCR$_ROW_UNIT;
        newunit2  SYS.LCR$_ROW_UNIT;
        newvals   SYS.LCR$_ROW_LIST;
      BEGIN
        newunit1 := SYS.LCR$_ROW_UNIT(
          'region_id',
          ANYDATA.ConvertNumber(5),
          DBMS_LCR.NOT_A_LOB,
          NULL,
          NULL);
        newunit2 := SYS.LCR$_ROW_UNIT(
          'region_name',
          ANYDATA.ConvertVarchar2('Moon'),
          DBMS_LCR.NOT_A_LOB,
          NULL,
          NULL);
        newvals := SYS.LCR$_ROW_LIST(newunit1,newunit2);
      construct_row_lcr(
        source_dbname  =>  'dbs1.net',
        cmd_type       =>  'INSERT',
        obj_owner      =>  'hr',
        obj_name       =>  'regions',
        old_vals       =>  NULL,
        new_vals       =>  newvals);
      END;
      /
      COMMIT;
      
    3. SQL*Plusで、hrユーザーとしてデータベースに接続します。

    4. hr.regions表を問い合せて、適用済の行の変更を表示します。region_id5である行は、region_nameMoonとなっているはずです。

      SELECT * FROM hr.regions;
      
    5. SQL*Plusで、Oracle Streams管理者としてデータベースに接続します。

    6. hr.regions表から1行を更新する行LCRを作成します。

      DECLARE
        oldunit1  SYS.LCR$_ROW_UNIT;
        oldunit2  SYS.LCR$_ROW_UNIT;
        oldvals   SYS.LCR$_ROW_LIST;
        newunit1  SYS.LCR$_ROW_UNIT;
        newvals   SYS.LCR$_ROW_LIST;
      BEGIN
        oldunit1 := SYS.LCR$_ROW_UNIT(
          'region_id',
          ANYDATA.ConvertNumber(5),
          DBMS_LCR.NOT_A_LOB,
          NULL,
          NULL);
        oldunit2 := SYS.LCR$_ROW_UNIT(
          'region_name',
          ANYDATA.ConvertVarchar2('Moon'),
          DBMS_LCR.NOT_A_LOB,
          NULL,
          NULL);
        oldvals := SYS.LCR$_ROW_LIST(oldunit1,oldunit2);
        newunit1 := SYS.LCR$_ROW_UNIT(
          'region_name',
          ANYDATA.ConvertVarchar2('Mars'),
          DBMS_LCR.NOT_A_LOB,
          NULL,
          NULL);
        newvals := SYS.LCR$_ROW_LIST(newunit1);
      construct_row_lcr(
        source_dbname  =>  'dbs1.net',
        cmd_type       =>  'UPDATE',
        obj_owner      =>  'hr',
        obj_name       =>  'regions',
        old_vals       =>  oldvals,
        new_vals       =>  newvals);
      END;
      /
      COMMIT;
      
    7. SQL*Plusで、hrユーザーとしてデータベースに接続します。

    8. hr.regions表を問い合せて、適用済の行の変更を表示します。region_id5である行は、region_nameMarsとなっているはずです。

      SELECT * FROM hr.regions;
      
    9. hr.regions表から1行を削除する行LCRを作成します。

      DECLARE
        oldunit1  SYS.LCR$_ROW_UNIT;
        oldunit2  SYS.LCR$_ROW_UNIT;
        oldvals   SYS.LCR$_ROW_LIST;
      BEGIN
        oldunit1 := SYS.LCR$_ROW_UNIT(
          'region_id',
          ANYDATA.ConvertNumber(5),
          DBMS_LCR.NOT_A_LOB,
          NULL,
          NULL);
        oldunit2 := SYS.LCR$_ROW_UNIT(
          'region_name',
          ANYDATA.ConvertVarchar2('Mars'),
          DBMS_LCR.NOT_A_LOB,
          NULL,
          NULL);
        oldvals := SYS.LCR$_ROW_LIST(oldunit1,oldunit2);
      construct_row_lcr(
        source_dbname  =>  'dbs1.net',
        cmd_type       =>  'DELETE',
        obj_owner      =>  'hr',
        obj_name       =>  'regions',
        old_vals       =>  oldvals,
        new_vals       =>  NULL);
      END;
      /
      COMMIT;
      
    10. SQL*Plusで、hrユーザーとしてデータベースに接続します。

    11. hr.regions表を問い合せて、適用済の行の変更を表示します。region_id5である行は、削除されているはずです。

      SELECT * FROM hr.regions;
      

LCRの実行

行LCRおよびDDL LCR用に、個別のEXECUTEメンバー・プロシージャがあります。これらのメンバー・プロシージャを使用すると、現行ユーザーのセキュリティ・ドメインでLCRが実行されます。LCRが正常に実行されると、LCRに記録された変更がローカル・データベースに対して行われます。ここでは、行LCRおよびDDL LCRの実行について説明します。

行LCRの実行

行LCR用のEXECUTEメンバー・プロシージャは、LCR$_ROW_RECORD型のサブプログラムです。行LCRにEXECUTEメンバー・プロシージャを実行すると、行LCRが実行されます。行LCRが適用プロセスによって実行される場合、LCRに対して実行される適用プロセスのハンドラは実行されません。

次のいずれかの条件が満たされる場合は、行LCRにEXECUTEメンバー・プロシージャを実行できます。

  • LCRが適用ハンドラによって処理されている。

  • LCRがキューに存在し、このLCRが適用プロセス、アプリケーションまたはユーザーによって最後にエンキューされたLCRである。

  • LCRがLCR$_ROW_RECORDコンストラクタ・ファンクションを使用して構成されているが、エンキューされていない。

  • LCRがエラー・キューに存在する。

行LCRにEXECUTEメンバー・プロシージャを実行する場合、競合解消を実行するかどうかをconflict_resolutionパラメータで制御します。具体的には、conflict_resolutionパラメータをTRUEに設定した場合、変更対象の表に定義されている競合解消を使用して、LCRの実行によって発生する競合が解消されます。conflict_resolutionパラメータをFALSEに設定した場合、競合解消は使用されません。conflict_resolutionパラメータを設定しない場合、またはNULLに設定した場合は、エラーが発生します。


注意:

カスタム・ルールベースの変換では、行LCRにEXECUTEメンバー・プロシージャを実行しないでください。実行すると、トランザクション・コンテキストの外部で行LCRが実行される場合があります。


参照:

  • 「LCRの適用プロセスのオプション」

  • 行LCRにEXECUTEメンバー・プロシージャを実行するDMLハンドラの例については、「DMLハンドラの管理」を参照してください。

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


行LCRの構成および実行の例

この項の例では、PL/SQLプロシージャを作成し、行LCRを構成および実行することによってhr.jobs表の行の挿入、更新および削除を実行します。行LCRは、適用プロセスによってエンキューまたは処理されずに実行されます。この例では、Oracle Streams管理者strmadminを構成して、この管理者にDBAロールを付与していると想定しています。

この操作の手順は次のとおりです。

  1. SQL*Plusで、Oracle Streams管理者としてデータベースに接続します。

    SQL*Plusでデータベースに接続する手順については、『Oracle Database管理者ガイド』を参照してください。

  2. 行LCRを実行するPL/SQLプロシージャexecute_row_lcrを作成します。

    CREATE OR REPLACE PROCEDURE execute_row_lcr(
                     source_dbname  VARCHAR2,
                     cmd_type       VARCHAR2,
                     obj_owner      VARCHAR2,
                     obj_name       VARCHAR2,
                     old_vals       SYS.LCR$_ROW_LIST,
                     new_vals       SYS.LCR$_ROW_LIST) as
      xrow_lcr  SYS.LCR$_ROW_RECORD;
    BEGIN
      -- Construct the row LCR based on information passed to procedure
      xrow_lcr := SYS.LCR$_ROW_RECORD.CONSTRUCT(
        source_database_name => source_dbname,
        command_type         => cmd_type,
        object_owner         => obj_owner,
        object_name          => obj_name,
        old_values           => old_vals,
        new_values           => new_vals);
      -- Execute the row LCR
      xrow_lcr.EXECUTE(FALSE);
    END execute_row_lcr;
    /
    
  3. hr.jobs表に行を挿入する行LCRを実行するPL/SQLプロシージャinsert_job_lcrを作成します。

    CREATE OR REPLACE PROCEDURE insert_job_lcr(
                     j_id     VARCHAR2,
                     j_title  VARCHAR2,
                     min_sal  NUMBER,
                     max_sal  NUMBER) AS
      xrow_lcr   SYS.LCR$_ROW_RECORD;
      col1_unit  SYS.LCR$_ROW_UNIT;
      col2_unit  SYS.LCR$_ROW_UNIT;
      col3_unit  SYS.LCR$_ROW_UNIT;
      col4_unit  SYS.LCR$_ROW_UNIT;
      newvals    SYS.LCR$_ROW_LIST;
    BEGIN
      col1_unit := SYS.LCR$_ROW_UNIT(
        'job_id',
        ANYDATA.ConvertVarchar2(j_id),
        DBMS_LCR.NOT_A_LOB,
        NULL,
        NULL);
      col2_unit := SYS.LCR$_ROW_UNIT(
        'job_title',
        ANYDATA.ConvertVarchar2(j_title),
        DBMS_LCR.NOT_A_LOB,
        NULL,
        NULL);
      col3_unit := SYS.LCR$_ROW_UNIT(
        'min_salary',
        ANYDATA.ConvertNumber(min_sal),
        DBMS_LCR.NOT_A_LOB,
        NULL,
        NULL);
      col4_unit := SYS.LCR$_ROW_UNIT(
        'max_salary',
        ANYDATA.ConvertNumber(max_sal),
        DBMS_LCR.NOT_A_LOB,
        NULL,
        NULL);
      newvals := SYS.LCR$_ROW_LIST(col1_unit,col2_unit,col3_unit,col4_unit);
      -- Execute the row LCR
      execute_row_lcr(
        source_dbname => 'DB1.NET',
        cmd_type      => 'INSERT',
        obj_owner     => 'HR',
        obj_name      => 'JOBS',
        old_vals      => NULL,
        new_vals      => newvals);
    END insert_job_lcr;
    /
    
  4. hr.jobs表の行のmax_salary値を更新する行LCRを実行するPL/SQLプロシージャupdate_max_salary_lcrを作成します。

    CREATE OR REPLACE PROCEDURE update_max_salary_lcr(
                     j_id         VARCHAR2,
                     old_max_sal NUMBER,
                     new_max_sal NUMBER) AS
      xrow_lcr      SYS.LCR$_ROW_RECORD;
      oldcol1_unit  SYS.LCR$_ROW_UNIT;
      oldcol2_unit  SYS.LCR$_ROW_UNIT;
      newcol1_unit  SYS.LCR$_ROW_UNIT;
      oldvals       SYS.LCR$_ROW_LIST;
      newvals       SYS.LCR$_ROW_LIST;
    BEGIN
      oldcol1_unit := SYS.LCR$_ROW_UNIT(
        'job_id',
        ANYDATA.ConvertVarchar2(j_id),
        DBMS_LCR.NOT_A_LOB,
        NULL,
        NULL);
      oldcol2_unit := SYS.LCR$_ROW_UNIT(
        'max_salary',
        ANYDATA.ConvertNumber(old_max_sal),
        DBMS_LCR.NOT_A_LOB,
        NULL,
        NULL);
      oldvals := SYS.LCR$_ROW_LIST(oldcol1_unit,oldcol2_unit);
      newcol1_unit := SYS.LCR$_ROW_UNIT(
        'max_salary',
        ANYDATA.ConvertNumber(new_max_sal),
        DBMS_LCR.NOT_A_LOB,
        NULL,
        NULL);
      newvals := SYS.LCR$_ROW_LIST(newcol1_unit);
      -- Execute the row LCR
      execute_row_lcr(
        source_dbname => 'DB1.NET',
        cmd_type      => 'UPDATE',
        obj_owner     => 'HR',
        obj_name      => 'JOBS',
        old_vals      => oldvals,
        new_vals      => newvals);
    END update_max_salary_lcr;
    /
    
  5. hr.jobs表から行を削除する行LCRを実行するPL/SQLプロシージャdelete_job_lcrを作成します。

    CREATE OR REPLACE PROCEDURE delete_job_lcr(j_id VARCHAR2) AS
      xrow_lcr   SYS.LCR$_ROW_RECORD;
      col1_unit  SYS.LCR$_ROW_UNIT;
      oldvals    SYS.LCR$_ROW_LIST;
    BEGIN
      col1_unit := SYS.LCR$_ROW_UNIT(
        'job_id',
        ANYDATA.ConvertVarchar2(j_id),
        DBMS_LCR.NOT_A_LOB,
        NULL,
        NULL);
      oldvals := SYS.LCR$_ROW_LIST(col1_unit);
      -- Execute the row LCR
      execute_row_lcr(
        source_dbname => 'DB1.NET',
        cmd_type      => 'DELETE',
        obj_owner     => 'HR',
        obj_name      => 'JOBS',
        old_vals      => oldvals,
        new_vals      => NULL);
    END delete_job_lcr;
    /
    
  6. insert_job_lcrプロシージャを使用して、hr.jobs表に行を挿入します。

    EXEC insert_job_lcr('BN_CNTR','BEAN COUNTER',5000,10000);
    
  7. hr.jobs表に挿入された行を問い合せます。

    SELECT * FROM hr.jobs WHERE job_id = 'BN_CNTR';
    
    JOB_ID     JOB_TITLE                           MIN_SALARY MAX_SALARY
    ---------- ----------------------------------- ---------- ----------
    BN_CNTR    BEAN COUNTER                              5000      10000
    
  8. update_max_salary_lcrプロシージャを使用して、手順6hr.jobs表に挿入した行のmax_salary値を更新します。

    EXEC update_max_salary_lcr('BN_CNTR',10000,12000);
    
  9. hr.jobs表で更新された行を問い合せます。

    SELECT * FROM hr.jobs WHERE job_id = 'BN_CNTR';
    
    JOB_ID     JOB_TITLE                           MIN_SALARY MAX_SALARY
    ---------- ----------------------------------- ---------- ----------
    BN_CNTR    BEAN COUNTER                              5000      12000
    
  10. delete_job_lcrプロシージャを使用して、手順6hr.jobs表に挿入した行を削除します。

    EXEC delete_job_lcr('BN_CNTR');
    
  11. hr.jobs表から削除された行を問い合せます。

    SELECT * FROM hr.jobs WHERE job_id = 'BN_CNTR';
    
    no rows selected
    

DDL LCRの実行

DDL LCR用のEXECUTEメンバー・プロシージャは、LCR$_DDL_RECORD型のサブプログラムです。DDL LCRにEXECUTEメンバー・プロシージャを実行すると、LCRが実行されます。LCRに対して実行される適用プロセスのハンドラは実行されません。DDL LCR用のEXECUTEメンバー・プロシージャは、適用プロセスの適用ハンドラのみがコールできます。

適用されたDDL LCRは、すべて自動的にコミットされます。したがって、DDLハンドラがDDL LCRのEXECUTEメンバー・プロシージャをコールすると、自動的にコミットが実行されます。


参照:

  • 「LCRの適用プロセスのオプション」

  • DDL LCRにEXECUTEメンバー・プロシージャを実行するDDLハンドラの例については、「DDLハンドラの管理」を参照してください。

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


LOB列を含むLCRの管理

LOBデータ型は、取得プロセスによって取得される行LCRに存在できますが、他のデータ型として表されます。LOBデータ型は、同期取得によって取得される行LCRには存在できません。特定のLOBデータ型は、ユーザーが構成する行LCRには存在できません。表11-1に、これらのデータ型のLCR表現、および行LCRに存在できるかどうかを示します。

表11-1 行LCR内のLOBデータ型の表現

データ型 行LCR表現 取得プロセスによって取得される行LCRに存在できるかどうか 同期取得によって取得される行LCRに存在できるかどうか ユーザーが構成する行LCRに存在できるかどうか

固定幅CLOB

VARCHAR2

できる

できない

できる

可変幅CLOB

AL16UTF16キャラクタ・セットのRAW

できる

できない

できない

NCLOB

AL16UTF16キャラクタ・セットのRAW

できる

できない

できない

BLOB

RAW

できる

できない

できる

CLOBとして格納されたXMLType

RAW

できる

できない

できない


Oracle Streams環境でのLOBデータ型に関係する行変更の一般的な考慮事項は、次のとおりです。

ここでは、LOB列を構成または処理する場合に満たす必要のある要件、LOB列を含むLCRに対する適用プロセスの動作、およびLOBアセンブリについて説明します。また、LOB列を含むLCRを構成してエンキューする例も示します。

この項の内容は、次のとおりです。


参照:

LOBの詳細は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください。

LOBを含むLCRの直接適用に対する適用プロセスの動作

LOB列を含むLCRを(適用ハンドラを使用せずに)直接適用すると、適用プロセスは次のように動作します。

  • コマンド・タイプがINSERTまたはUPDATEのLCRにデータを含む新規のLOBがあり、lob_informationDBMS_LCR.LOB_CHUNKまたはDBMS_LCR.LAST_LOB_CHUNKでない場合は、そのデータが適用されます。

  • コマンド・タイプがINSERTまたはUPDATEのLCRにデータを含まない新規のLOBがあり、lob_informationDBMS_LCR.EMPTY_LOBの場合は、空のLOBとして適用されます。

  • コマンド・タイプがINSERTまたはUPDATEのLCRにデータを含まない新規のLOBがあり、lob_informationDBMS_LCR.NULL_LOBまたはDBMS_LCR.INLINE_LOBの場合は、NULLとして適用されます。

  • コマンド・タイプがINSERTまたはUPDATEのLCRに新規のLOBがあり、lob_informationDBMS_LCR.LOB_CHUNKまたはDBMS_LCR.LAST_LOB_CHUNKの場合は、すべてのLOB値が無視されます。コマンド・タイプがINSERTの場合は、LOBチャンクが続くものとみなされ、列に空のLOBが挿入されます。コマンド・タイプがUPDATEの場合は、LOBチャンクが続くものとみなされ、列の値が無視されます。

  • コマンド・タイプがUPDATEのLCR内の新規列がすべてLOBで、そのlob_informationDBMS_LCR.LOB_CHUNKまたはDBMS_LCR.LAST_LOB_CHUNKの場合は、LOBチャンクが続くものとみなされ、更新がスキップされます。

  • コマンド・タイプがUPDATEまたはDELETEのLCRの場合は、古いLOB値が無視されます。

LOBアセンブリとLOB列を含むLCRのカスタム適用

LOB列を含まない表の行に対して変更を行うと、1つの行LCRが生成されますが、1つ以上のLOB列を含む行に対して変更を行うと、複数の行LCRが生成される場合があります。適用プロセスは、LOB列を含む行LCRを適用ハンドラに送信しない場合、その行LCRを直接適用できます。ただし、Oracle Database 10g リリース2(10.2)より前のリリースでは、1つの行変更に対して複数のLCRが適切に処理されるように適用ハンドラを構成する必要があったため、LOB列を含む行LCRのカスタム処理は複雑でした。

Oracle Database 10g リリース2(10.2)以上では、LOBアセンブリによって、取得プロセスで取得されたLOB列を含む行LCRのカスタム処理が簡略化されています。LOBアセンブリを使用すると、LOB列を含む行に対する変更によって生成される複数の取得された行LCRが、1つの行LCRに自動的に結合されます。LOBアセンブリを有効にすると、適用プロセスがこの1つの行LCRをDMLハンドラまたはエラー・ハンドラに渡します。また、LOBアセンブリの実行後は、LOB列値がVARCHAR2またはRAWデータ型値ではなくLOBロケータとして表されます。DMLハンドラまたはエラー・ハンドラにLOBアセンブリを有効化するには、DBMS_APPLY_ADM.SET_DML_HANDLERプロシージャでassemble_lobsパラメータをTRUEに設定します。

DMLハンドラまたはエラー・ハンドラに対してassemble_lobsパラメータをFALSEに設定した場合、LOBアセンブリは無効化され、LOB列を含む1行の変更に対して複数の行LCRがハンドラに渡されます。表11-2に、LOBアセンブリを無効化した場合のOracle Streamsの動作を示します。具体的には、LOB列を含む1行に対する変更によって生成され、DMLハンドラまたはエラー・ハンドラに渡されるLCRを示します。

表11-2 LOBアセンブリを無効化した場合のOracle Streamsの動作

元の行変更 最初のLCRのセット 2番目のLCRのセット 3番目のLCRのセット 最後のLCRのセット

INSERT

1つのINSERT LCR

1つ以上のLOB WRITE LCR

1つ以上のLOB TRIM LCR

UPDATE

UPDATE

1つのUPDATE LCR

1つ以上のLOB WRITE LCR

1つ以上のLOB TRIM LCR

UPDATE

DELETE

1つのDELETE LCR

なし

なし

なし

DBMS_LOB.WRITE

1つ以上のLOB WRITE LCR

なし

なし

なし

DBMS_LOB.TRIM

1つのLOB TRIM LCR

なし

なし

なし

DBMS_LOB.ERASE

1つのLOB ERASE LCR

なし

なし

なし


表11-3に、LOBアセンブリを有効化した場合のOracle Streamsの動作を示します。具体的には、LOB列を含む1行に対する変更によって生成され、DMLハンドラまたはエラー・ハンドラに渡される行LCRを示します。

表11-3 LOBアセンブリを有効化した場合のOracle Streamsの動作

元の行変更 1つのLCR

INSERT

INSERT

UPDATE

UPDATE

DELETE

DELETE

DBMS_LOB.WRITE

LOB WRITE

DBMS_LOB.TRIM

LOB TRIM

DBMS_LOB.ERASE

LOB ERASE


LOBアセンブリを有効化すると、DMLハンドラまたはエラー・ハンドラによって、行LCRのLOB列が変更されます。DMLハンドラまたはエラー・ハンドラとして指定したPL/SQLプロシージャ内でLOBに対する操作を実行するには、DBMS_LOBパッケージのサブプログラムを使用することをお薦めします。行LCRにNULLのLOB列が含まれている場合、新しいLOBロケータでNULLを置き換える必要があります。EXECUTEメンバー・プロシージャを使用して行LCRを適用する場合は、行LCR用のADD_COLUMNSET_VALUEおよびSET_VALUESメンバー・プロシージャを使用してLOBに対する変更を行います。

LOBアセンブリを有効化すると、LOBアセンブリによって、永続LCR内の非NULLのLOB列がLOBロケータに変換されます。ただし、LOBアセンブリでは、複数の永続行LCRが1つの行LCRに結合されません。たとえば、永続行LCRの場合、LOBアセンブリでは、INSERT行LCRの後に続く複数のLOB WRITE行LCRは1つのINSERT行LCRに結合されません。


参照:

  • 適用ハンドラの詳細は、「LCRの適用プロセスのオプション」を参照してください。

  • DBMS_LOBパッケージを使用する方法の詳細は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』および『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

  • 行LCR用のADD_COLUMNSET_VALUEおよびSET_VALUESメンバー・プロシージャの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。


LOBアセンブリに関する考慮事項

LOBアセンブリを使用する場合の考慮事項は次のとおりです。

  • DMLハンドラまたはエラー・ハンドラを使用して、アセンブルされたLOBを複数の接続先データベースで処理するには、LOBアセンブリによって各接続先データベースで個別にLOBをアセンブルする必要があります。

  • Oracle Database 10g リリース2(10.2)より前のリリースのOracleが実行されているデータベースで取得された行LCRは、LOBアセンブリでアセンブルできません。

  • 互換レベルが10.2.0より低いOracle Database 10g リリース2(10.2)以上が実行されているデータベースで取得された行LCRは、LOBアセンブリでアセンブルできません。

  • 適用ハンドラにLOBアセンブリを指定するには、適用ハンドラが実行されているデータベースの互換レベルが10.2.0以上である必要があります。

  • LONGまたはLONG RAW列を含む表からの行LCRは、LOBアセンブリでアセンブルできません。

  • DBMS_APPLY_ADMパッケージのSET_ENQUEUE_DESTINATIONおよびSET_EXECUTEプロシージャは、常に元のアセンブルされていない行LCRに実行されます。したがって、LOB列を含む行LCRの場合、行LCRが接続先データベースで適用ハンドラ用に個別にアセンブルされていても、元のアセンブルされていない行LCRがエンキューまたは実行されます。

  • 取得、伝播または適用中に、LOB列を含む行LCRにルールベースの変換が実行された場合、適用ハンドラは変換済の行LCRに実行されます。ソース・データベースにLONGまたはLONG RAW列が存在し、ルールベースの変換で行LCR用のCONVERT_LONG_TO_LOB_CHUNKメンバー・ファンクションを使用してそれらの列がLOBに変換される場合、これらの行LCRに実行される適用ハンドラに対してLOBアセンブリを有効化できます。

  • 行LCRに1つ以上のXMLType列が含まれている場合、DMLハンドラまたはエラー・ハンドラに対してassemble_lobsパラメータがFALSEに設定されていても、行LCRのすべてのXMLType列およびLOB列が常にアセンブルされます。


参照:

  • データベースの互換レベルの詳細は、『Oracle Databaseリファレンス』および『Oracle Databaseアップグレード・ガイド』を参照してください。

  • DBMS_APPLY_ADMパッケージのサブプログラムの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。


LOBアセンブリの例

この項では、DMLハンドラとともにLOBアセンブリを使用する例を示します。この例では、複数のデータベースでoe.production_information表を共有しているが、一部のデータベースのみを会社のオンラインWorld Wide Webカタログに使用している会社について考えます。この会社は各製品の写真をカタログ・データベースに格納する予定ですが、領域を節約するために、カタログ・データベース以外のデータベースには写真を格納しません。

この目標を達成するには、接続先カタログ・データベースのDMLハンドラで、データ型BLOBの列photoを、ソース・データベースのproduct_information表に対して実行される各INSERTおよびUPDATEに追加します。ソース・データベースの表にはphoto列が含まれていません。DMLハンドラは、接続先データベースの既存の写真を使用して更新および挿入を実行するように構成されます。また、この会社には、すべてのデータベースのoe.product_information表にproduct_long_descを追加する目標もあります。この表には、簡単な説明を含むproduct_description列がすでに存在します。product_long_desc列のデータ型はCLOBで、詳細な説明が含まれています。詳細な説明は英語で記述されていますが、会社の1つのデータベースを使用して、会社のカタログがスペイン語で表示されます。したがって、適切な言語で詳細な説明が表示されるように、DMLハンドラによってproduct_long_desc列が更新されます。

次の手順を実行して、LOBアセンブリを使用して前述の目標を達成するようにDMLハンドラを構成します。


手順1: product_information表へのphoto列の追加

次の文を実行すると、photo列が接続先データベースのproduct_information表に追加されます。

ALTER TABLE oe.product_information ADD(photo BLOB);

手順2: product_information表へのproduct_long_desc列の追加

次の文を実行すると、product_long_desc列が、環境内のすべてのデータベースのproduct_information表に追加されます。

ALTER TABLE oe.product_information ADD(product_long_desc CLOB);

手順3: DMLハンドラ用のPL/SQLプロシージャの作成

この例では、convert_product_informationプロシージャを作成します。このプロシージャは、DMLハンドラ用に使用されます。このプロシージャでは、ユーザーが作成した次のPL/SQLサブプログラムが存在すると想定されています。

  • get_photoプロシージャ。product_idに基づいてURLまたは表からBLOB形式の写真を取得し、引数として渡されたBLOBロケータを更新します。

  • get_product_long_descプロシージャ。product_idIN引数およびproduct_long_descIN OUT引数を持ち、product_long_descをスペイン語に翻訳するか、または置換用のスペイン語の説明を取得して、product_long_descを更新します。

次のコードを実行すると、convert_product_informationプロシージャが作成されます。

CREATE OR REPLACE PROCEDURE convert_product_information(in_any IN ANYDATA)
IS
  lcr                      SYS.LCR$_ROW_RECORD;
  rc                       PLS_INTEGER;
  product_id_anydata       ANYDATA;
  photo_anydata            ANYDATA;
  long_desc_anydata        ANYDATA;
  tmp_photo                BLOB;
  tmp_product_id           NUMBER;
  tmp_prod_long_desc       CLOB;
  tmp_prod_long_desc_src   CLOB;
  tmp_prod_long_desc_dest  CLOB;
  t                        PLS_INTEGER;
BEGIN
  -- Access LCR
  rc := in_any.GETOBJECT(lcr);
  product_id_anydata := lcr.GET_VALUE('OLD', 'PRODUCT_ID');
  t := product_id_anydata.GETNUMBER(tmp_product_id);
  IF ((lcr.GET_COMMAND_TYPE = 'INSERT') or (lcr.GET_COMMAND_TYPE = 'UPDATE')) THEN
    -- If there is no photo column in the lcr then it must be added
    photo_anydata := lcr.GET_VALUE('NEW', 'PHOTO');
    -- Check if photo has been sent and if so whether it is NULL
    IF (photo_anydata is NULL) THEN
      tmp_photo := NULL;
      ELSE
      t := photo_anydata.GETBLOB(tmp_photo);
    END IF;
    -- If tmp_photo is NULL then a new temporary LOB must be created and
    -- updated with the photo if it exists
    IF (tmp_photo is NULL) THEN
      DBMS_LOB.CREATETEMPORARY(tmp_photo, TRUE);
      get_photo(tmp_product_id, tmp_photo);
    END IF;
    -- If photo column did not exist then it must be added
    IF (photo_anydata is NULL) THEN
      lcr.ADD_COLUMN('NEW', 'PHOTO', ANYDATA.CONVERTBLOB(tmp_photo));
      -- Else the existing photo column must be set to the new photo
      ELSE
        lcr.SET_VALUE('NEW', 'PHOTO', ANYDATA.CONVERTBLOB(tmp_photo));
    END IF;
    long_desc_anydata := lcr.GET_VALUE('NEW', 'PRODUCT_LONG_DESC');
    IF (long_desc_anydata is NULL) THEN
      tmp_prod_long_desc_src := NULL;
      ELSE
      t := long_desc_anydata.GETCLOB(tmp_prod_long_desc_src);
    END IF;
    IF (tmp_prod_long_desc_src IS NOT NULL) THEN
      get_product_long_desc(tmp_product_id, tmp_prod_long_desc);
    END IF;
    -- If tmp_prod_long_desc IS NOT NULL, then use it to update the LCR
    IF (tmp_prod_long_desc IS NOT NULL) THEN
      lcr.SET_VALUE('NEW', 'PRODUCT_LONG_DESC',
                    ANYDATA.CONVERTCLOB(tmp_prod_long_desc_dest));
    END IF;
  END IF;
  -- DBMS_LOB operations also are executed
  -- Inserts and updates invoke all changes
  lcr.EXECUTE(TRUE);
END;
/

手順4: 適用プロセスのDMLハンドラの設定

この手順では、接続先データベースでのINSERTUPDATEおよびLOB_UPDATE操作用のDMLハンドラとしてconvert_product_informationプロシージャを設定します。SET_DML_HANDLERプロシージャを実行するたびに、assemble_lobsパラメータがTRUEに設定されていることに注意してください。

BEGIN
  DBMS_APPLY_ADM.SET_DML_HANDLER(
    object_name         => 'oe.product_information',
    object_type         => 'TABLE',
    operation_name      => 'INSERT',
    error_handler       => FALSE,
    user_procedure      => 'strmadmin.convert_product_information',
    apply_database_link => NULL,
    assemble_lobs       => TRUE);
  DBMS_APPLY_ADM.SET_DML_HANDLER(
    object_name         => 'oe.product_information',
    object_type         => 'TABLE',
    operation_name      => 'UPDATE',
    error_handler       => FALSE,
    user_procedure      => 'strmadmin.convert_product_information',
    apply_database_link => NULL,
    assemble_lobs       => TRUE);
  DBMS_APPLY_ADM.SET_DML_HANDLER(
    object_name         => 'oe.product_information',
    object_type         => 'TABLE',
    operation_name      => 'LOB_UPDATE',
    error_handler       => FALSE,
    user_procedure      => 'strmadmin.convert_product_information',
    apply_database_link => NULL,
    assemble_lobs       => TRUE);
END;
/

手順5: DBA_APPLY_DML_HANDLERSビューの問合せ

oe.product_information表に対してDMLハンドラが適切に設定されていることを確認するには、次の問合せを実行します。

COLUMN OBJECT_OWNER HEADING 'Table|Owner' FORMAT A5
COLUMN OBJECT_NAME HEADING 'Table Name' FORMAT A20
COLUMN OPERATION_NAME HEADING 'Operation' FORMAT A10
COLUMN USER_PROCEDURE HEADING 'Handler Procedure' FORMAT A25
COLUMN ASSEMBLE_LOBS HEADING 'LOB Assembly?' FORMAT A15

SELECT OBJECT_OWNER,
       OBJECT_NAME,
       OPERATION_NAME,
       USER_PROCEDURE,
       ASSEMBLE_LOBS
  FROM DBA_APPLY_DML_HANDLERS;

出力は次のようになります。

Table
Owner Table Name           Operation  Handler Procedure         LOB Assembly?
----- -------------------- ---------- ------------------------- ---------------
OE    PRODUCT_INFORMATION  INSERT     "STRMADMIN"."CONVERT_PROD Y
                                      UCT_INFORMATION"

OE    PRODUCT_INFORMATION  UPDATE     "STRMADMIN"."CONVERT_PROD Y
                                      UCT_INFORMATION"

OE    PRODUCT_INFORMATION  LOB_UPDATE "STRMADMIN"."CONVERT_PROD Y
                                      UCT_INFORMATION"

正しいプロシージャconvert_product_informationが、表に対する各操作に使用されていることに注意してください。また、各ハンドラでLOBアセンブリが使用されていることにも注意してください。

LOB列を含むLCRを構成および処理するための要件

環境でLOB列を含む行LCRを生成する場合は、そのLCRを構成または処理するときに、次の項に示す要件を満たす必要があります。

LOBアセンブリを使用せずにLCRを構成および処理するための要件

LOB列を含むLCRを構成する場合、およびLOBアセンブリが無効化されているDMLハンドラまたはエラー・ハンドラでLOB列を処理する場合、次の要件を満たす必要があります。

  • LOBアセンブリが無効化されているDMLハンドラまたはエラー・ハンドラで、行LCR内のLOB列データを変更しないでください。ただし、DMLハンドラまたはエラー・ハンドラで、行LCR内のLOB以外の列を変更することはできます。

  • 特定の操作にのみ起動される適用ハンドラで、LOB列を含む表からのLCRを処理しないでください。たとえば、INSERT操作にのみ起動される適用ハンドラによって、1つ以上のLOB列を含む表からのLCRを処理しないでください。

  • LCRのLOB列のデータ部分が、VARCHAR2型またはRAW型である必要があります。VARCHAR2CLOBRAWBLOBとして解析されます。

  • ユーザーが構成する行LCRのLOB列は、BLOBまたは固定幅CLOBのいずれかである必要があります。NCLOB型または可変幅CLOB型のLOB列を含む行LCRは構成できません。

  • 表外のLOBに有効なコマンド・タイプは、LOB WRITELOB ERASEおよびLOB TRIMのみです。

  • LOB WRITELOB ERASEおよびLOB TRIMのLCRの場合は、old_valuesコレクションを空またはNULLnew_valuesを空以外にする必要があります。

  • LOB WRITE LCRとLOB ERASE LCRの場合は、lob_offsetを有効な値にする必要があります。他のすべてのコマンド・タイプの場合は、その列のLOBチャンクが続くものとみなして、lob_offsetNULLにする必要があります。

  • LOB ERASE LCRとLOB TRIM LCRの場合は、lob_operation_sizeを有効な値にする必要があります。他のすべてのコマンド・タイプの場合は、lob_operation_sizeNULLにする必要があります。

  • LOB TRIMおよびLOB ERASEは、LCRに含まれているLOB列のlob_informationLAST_LOB_CHUNKに設定されている場合にのみ有効なコマンド・タイプです。

  • LOB WRITEは、LCRに含まれているLOB列のlob_informationLAST_LOB_CHUNKまたはLOB_CHUNKに設定されている場合にのみ有効なコマンド・タイプです。

  • lob_informationNULL_LOBに設定されているLOBの場合は、列のデータ部分をVARCHAR2型のNULLCLOBの場合)またはRAW型のNULLBLOBの場合)にする必要があります。それ以外の場合は、非NULLのインラインLOB列として解析されます。

  • LOB WRITE LCR、LOB ERASE LCRおよびLOB TRIM LCRに許容されるのは、1つの新規チャンクを持つ1つのLOB列参照のみです。

  • LOB ERASE LCRおよびLOB TRIM LCR用の新規LOBチャンクは、ANYDATAにカプセル化されたNULL値にする必要があります。

前述の要件の妥当性チェックは、すべて適用プロセスによって実行されます。前述の要件が満たされない場合、LOB列を含む行LCRは適用プロセスでは適用できず、適用ハンドラでは処理できません。この場合、LCRは同じトランザクション内の他のLCRとともにエラー・キューに移動します。


参照:


LOBアセンブリを使用してLCRを適用ハンドラで処理するための要件

LOBアセンブリが有効化されているDMLハンドラまたはエラー・ハンドラでLOB列を処理する場合、次の要件を満たす必要があります。

  • アセンブルされたLOBを含む行LCR内のLOB列には、次の行LCRのメンバー・プロシージャを使用しないでください。

    • SET_LOB_INFORMATION

    • SET_LOB_OFFSET

    • SET_LOB_OPERATION_SIZE

    これらのいずれかのプロシージャを行LCR内のLOB列に使用すると、エラーが発生します。

  • LOBアセンブリで構成した行LCRは、DMLハンドラまたはエラー・ハンドラではエンキューできません。ただし、接続先データベースの1つ以上のハンドラにLOBアセンブリが有効化されている場合でも、DBMS_APPLY_ADMパッケージのSET_ENQUEUE_DESTINATIONプロシージャを使用して、LOB列を含む元のアセンブルされていない行LCRをエンキューできます。

前述の要件の妥当性チェックは、すべて適用プロセスによって実行されます。前述の要件が満たされない場合、LOB列を含む行LCRは適用プロセスでは適用できず、適用ハンドラでは処理できません。この場合、LCRは同じトランザクション内の他のLCRとともにエラー・キューに移動します。LOB列を含む行LCRの場合、元のアセンブルされていない行LCRがエラー・キューに置かれます。


参照:

  • 適用ハンドラの詳細は、「LCRの適用プロセスのオプション」を参照してください。

  • 行LCR用のメンバー・プロシージャおよびSET_ENQUEUE_DESTINATIONプロシージャの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。


LOBを含むLCRをルールベースの変換で処理するための要件

ルールベースの変換でLOB列を含む行LCRを処理する場合、次の要件を満たす必要があります。

  • カスタム・ルールベースの変換で、行LCR内のLOB列データを変更しないでください。ただし、カスタム・ルールベースの変換で、LOB列を含む行LCR内のLOB以外の列を変更することはできます。

  • カスタム・ルールベースの変換で行LCRを処理する場合、次の行LCRのメンバー・プロシージャをLOB列に使用することはできません。

    • ADD_COLUMN

    • SET_LOB_INFORMATION

    • SET_LOB_OFFSET

    • SET_LOB_OPERATION_SIZE

    • SET_VALUE

    • SET_VALUES

  • DBMS_STREAMS_ADMパッケージのADD_COLUMNプロシージャで作成した宣言ルールベースの変換では、LOB列を行LCRに追加できません。

  • 行変更に対応するすべての行LCRが同じ方法で変換されるように、LOB列を含む行LCRで実行されるルールベースの変換ファンクションは決定的である必要があります。

  • 特定の操作にのみ起動されるカスタム・ルールベースの変換で、LOB列を含む表からのLCRを処理しないでください。たとえば、INSERT操作にのみ実行されるカスタム・ルールベースの変換によって、1つ以上のLOB列を含む表からのLCRを処理しないでください。


注意:

行LCRにLOB列が含まれる場合、ルールベースの変換は、常に元のアセンブルされていない行LCRに実行されます。


参照:

  • 「LCRの構成とエンキュー」

  • 適用ハンドラの詳細は、「LCRの適用プロセスのオプション」を参照してください。

  • ルールベースの変換の詳細は、『Oracle Streams概要および管理』を参照してください。

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

  • 決定的なファンクションの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。


LOBを含むLCRを構成およびエンキューするスクリプトの例

この項の例に、LOBを含むLCRを構成およびエンキューするPL/SQLプロシージャの作成手順を示します。この例は、『Oracle Streams概要および管理』で説明した必要な操作を完了し、Oracle Streams用にデータベースの準備を完了したものと想定しています。

  1. 出力およびスプール結果の表示

  2. Oracle Streams管理者へのDBMS_STREAMS_MESSAGINGに対するEXECUTE権限の付与

  3. Oracle Streams管理者としての接続

  4. ANYDATAキューの作成

  5. 適用プロセスの作成および起動

  6. LOB列を含む表が存在するスキーマの作成

  7. Oracle Streams管理者への表に対する必要な権限の付与

  8. LOBを含むLCRをエンキューするPL/SQLプロシージャの作成

  9. CLOBデータをエンキューするdo_enq_clobファンクションの作成

  10. do_enq_clobファンクションを使用したCLOBデータのエンキュー

  11. スプール結果のチェック


注意:

このマニュアルをオンラインで参照している場合、この注意の後にある「BEGINNING OF SCRIPT」の行から次の「END OF SCRIPT」の行までのテキストをテキスト・エディタにコピーし、テキストを編集してご使用の環境用のスクリプトを作成できます。環境内のすべてのデータベースに接続可能なコンピュータで、SQL*Plusを使用してスクリプトを実行します。

/************************* BEGINNING OF SCRIPT ******************************

手順1: 出力およびスプール結果の表示

SET ECHO ONを実行し、スクリプトのスプール・ファイルを指定します。このスクリプトの実行後に、スプール・ファイルにエラーがないかをチェックします。

*/

SET ECHO ON
SPOOL lob_construct.out

/*

手順2: Oracle Streams管理者へのDBMS_STREAMS_MESSAGINGに対するEXECUTE権限の付与

手順8のPL/SQLプロシージャ内でこのパッケージのプロシージャをコールするため、このパッケージに対する明示的なEXECUTE権限が必要です。

*/

CONNECT / AS SYSDBA;

GRANT EXECUTE ON DBMS_STREAMS_MESSAGING TO strmadmin;

/*

手順3: Oracle Streams管理者としての接続

*/
SET ECHO ON
SET FEEDBACK 1
SET NUMWIDTH 10
SET LINESIZE 80
SET TRIMSPOOL ON
SET TAB OFF
SET PAGESIZE 100
SET SERVEROUTPUT ON SIZE 100000

CONNECT strmadmin

/*

手順4: ANYDATAキューの作成

*/
BEGIN
  DBMS_STREAMS_ADM.SET_UP_QUEUE(
    queue_table => 'lobex_queue_table',
    queue_name  => 'lobex_queue');
END;
/

/*

手順5: 適用プロセスの作成および起動

*/
BEGIN
  DBMS_APPLY_ADM.CREATE_APPLY(
    queue_name      => 'strmadmin.lobex_queue',
    apply_name      => 'apply_lob',
    apply_captured  => FALSE);
END;
/

BEGIN
  DBMS_APPLY_ADM.SET_PARAMETER(
    apply_name => 'apply_lob',
    parameter  => 'disable_on_error',
    value      => 'N');
END;
/

BEGIN
  DBMS_APPLY_ADM.START_APPLY(
    'apply_lob');
END;
/

/*

手順6: LOB列を含む表が存在するスキーマの作成

*/
CONNECT system

CREATE TABLESPACE lob_user_tbs DATAFILE 'lob_user_tbs.dbf'
  SIZE 5M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;

ACCEPT password PROMPT 'Enter password for user: ' HIDE

CREATE USER lob_user
IDENTIFIED BY &password
  DEFAULT TABLESPACE lob_user_tbs
  QUOTA UNLIMITED ON lob_user_tbs;

GRANT ALTER SESSION, CREATE CLUSTER, CREATE DATABASE LINK, CREATE SEQUENCE,
  CREATE SESSION, CREATE SYNONYM, CREATE TABLE, CREATE VIEW, CREATE INDEXTYPE,
  CREATE OPERATOR, CREATE PROCEDURE, CREATE TRIGGER, CREATE TYPE
TO lob_user;

CONNECT lob_user/lob_user_pw

CREATE TABLE with_clob (a  NUMBER PRIMARY KEY,
                        c1 CLOB,
                        c2 CLOB,
                        c3 CLOB);

CREATE TABLE with_blob (a NUMBER PRIMARY KEY,
                        b BLOB);

/*

手順7: Oracle Streams管理者への表に対する必要な権限の付与

次の権限を付与すると、Oracle Streams管理者は、LOBオフセット長を取得し、表にDML操作を実行できるようになります。

*/

GRANT ALL ON with_clob TO strmadmin;
GRANT ALL ON with_blob TO strmadmin;
COMMIT;

/*

手順8: LOBを含むLCRをエンキューするPL/SQLプロシージャの作成

*/
CONNECT strmadmin

CREATE OR REPLACE PROCEDURE enq_row_lcr(source_dbname  VARCHAR2,
                                            cmd_type       VARCHAR2,
                                            obj_owner      VARCHAR2,
                                            obj_name       VARCHAR2,
                                            old_vals       SYS.LCR$_ROW_LIST,
                                            new_vals       SYS.LCR$_ROW_LIST) AS
  xr_lcr         SYS.LCR$_ROW_RECORD;
BEGIN
  xr_lcr := SYS.LCR$_ROW_RECORD.CONSTRUCT(
              source_database_name => source_dbname,
              command_type         => cmd_type,
              object_owner         => obj_owner,
              object_name          => obj_name,
              old_values           => old_vals,
              new_values           => new_vals);
  -- Enqueue a row lcr
  DBMS_STREAMS_MESSAGING.ENQUEUE(
        queue_name         => 'lobex_queue',
        payload            => ANYDATA.ConvertObject(xr_lcr));
END enq_row_lcr;
/
SHOW ERRORS

/*

手順9: CLOBデータをエンキューするdo_enq_clobファンクションの作成

*/
-- Description of each variable:
-- src_dbname  : Source database name
-- tab_owner   : Table owner
-- tab_name    : Table name
-- col_name    : Name of the CLOB column
-- new_vals    : SYS.LCR$_ROW_LIST containing primary key and supplementally
--               logged colums
-- clob_data   : CLOB that contains data to be sent
-- offset      : Offset from which data should be sent, default is 1
-- lsize       : Size of data to be sent, default is 0
-- chunk_size  : Size used for creating LOB chunks, default is 2048

CREATE OR REPLACE FUNCTION do_enq_clob(src_dbname     VARCHAR2,
                                       tab_owner      VARCHAR2,
                                       tab_name       VARCHAR2,
                                       col_name       VARCHAR2,
                                       new_vals       SYS.LCR$_ROW_LIST,
                                       clob_data      CLOB,
                                       offset         NUMBER default 1,
                                       lsize          NUMBER default 0,
                                       chunk_size     NUMBER default 2048)
RETURN NUMBER IS
  lob_offset NUMBER; -- maintain lob offset
  newunit    SYS.LCR$_ROW_UNIT;
  tnewvals   SYS.LCR$_ROW_LIST;
  lob_flag   NUMBER;
  lob_data   VARCHAR2(32767);
  lob_size   NUMBER;
  unit_pos   NUMBER;
  final_size NUMBER;
  exit_flg   BOOLEAN;
  c_size     NUMBER;
  i          NUMBER;
BEGIN
  lob_size := DBMS_LOB.GETLENGTH(clob_data);
  unit_pos := new_vals.count + 1;
  tnewvals := new_vals;
  c_size   := chunk_size;
  i := 0;
  -- validate parameters
  IF (unit_pos <= 1) THEN
    DBMS_OUTPUT.PUT_LINE('Invalid new_vals list');
    RETURN 1;
  END IF;

  IF (c_size < 1) THEN
    DBMS_OUTPUT.PUT_LINE('Invalid LOB chunk size');
    RETURN 1;
  END IF;

  IF (lsize < 0 OR lsize > lob_size) THEN
    DBMS_OUTPUT.PUT_LINE('Invalid LOB size');
    RETURN 1;
  END IF;

  IF (offset < 1 OR offset >= lob_size) THEN
    DBMS_OUTPUT.PUT_LINE('Invalid lob offset');
    RETURN 1;
  ELSE
    lob_offset := offset;
  END IF;

  -- calculate final size
  IF (lsize = 0) THEN
    final_size := lob_size;
  ELSE
    final_size := lob_offset + lsize;
  END IF;

  --  The following output lines are for debugging purposes only.
  -- DBMS_OUTPUT.PUT_LINE('Final size: ' || final_size);
  -- DBMS_OUTPUT.PUT_LINE('Lob size: ' || lob_size);

  IF (final_size < 1 OR final_size > lob_size) THEN
    DBMS_OUTPUT.PUT_LINE('Invalid lob size');
    RETURN 1;
  END IF;

  -- expand new_vals list for LOB column
  tnewvals.extend();

  exit_flg := FALSE;

  -- Enqueue all LOB chunks
  LOOP
    --  The following output line is for debugging purposes only.
    DBMS_OUTPUT.PUT_LINE('About to write chunk#' || i);
    i := i + 1;

    -- check if last LOB chunk
    IF ((lob_offset + c_size) < final_size) THEN
      lob_flag := DBMS_LCR.LOB_CHUNK;
    ELSE
      lob_flag := DBMS_LCR.LAST_LOB_CHUNK;
      exit_flg := TRUE;
      --  The following output line is for debugging purposes only.
      DBMS_OUTPUT.PUT_LINE('Last LOB chunk');
    END IF;

    --  The following output lines are for debugging purposes only.
    DBMS_OUTPUT.PUT_LINE('lob offset: ' || lob_offset);
    DBMS_OUTPUT.PUT_LINE('Chunk size: ' || to_char(c_size));

    lob_data := DBMS_LOB.SUBSTR(clob_data, c_size, lob_offset);

    -- create row unit for clob
    newunit := SYS.LCR$_ROW_UNIT(col_name,
                                 ANYDATA.ConvertVarChar2(lob_data),
                                 lob_flag,
                                 lob_offset,
                                 NULL);

    -- insert new LCR$_ROW_UNIT
    tnewvals(unit_pos) := newunit;

    -- enqueue lcr
    enq_row_lcr(
          source_dbname => src_dbname,
          cmd_type      => 'LOB WRITE',
          obj_owner     => tab_owner,
          obj_name      => tab_name,
          old_vals      => NULL,
          new_vals      => tnewvals);

    -- calculate next chunk size
    lob_offset := lob_offset + c_size;

    IF ((final_size - lob_offset) < c_size) THEN
      c_size := final_size - lob_offset + 1;
    END IF;

    --  The following output line is for debugging purposes only.
    DBMS_OUTPUT.PUT_LINE('Next chunk size : ' || TO_CHAR(c_size));

    IF (c_size < 1) THEN
      exit_flg := TRUE;
    END IF;

    EXIT WHEN exit_flg;

  END LOOP;

  RETURN 0;
END do_enq_clob;
/

SHOW ERRORS

/*

手順10: do_enq_clobファンクションを使用したCLOBデータのエンキュー

次の例に示すDBMS_OUTPUT行は、必要に応じて、デバッグのために使用できます。この行が不要な場合は、コメント・アウトまたは削除できます。

*/

SET SERVEROUTPUT ON SIZE 100000
DECLARE
  c1_data CLOB;
  c2_data CLOB;
  c3_data CLOB;
  newunit1 SYS.LCR$_ROW_UNIT;
  newunit2 SYS.LCR$_ROW_UNIT;
  newunit3 SYS.LCR$_ROW_UNIT;
  newunit4 SYS.LCR$_ROW_UNIT;
  newvals  SYS.LCR$_ROW_LIST;
  big_data VARCHAR(22000);
  n        NUMBER;
BEGIN
  -- Create primary key for LCR$_ROW_UNIT
  newunit1 := SYS.LCR$_ROW_UNIT('A',
                                ANYDATA.ConvertNumber(3),
                                NULL,
                                NULL,
                                NULL);
  -- Create empty CLOBs
  newunit2 := sys.lcr$_row_unit('C1',
                                ANYDATA.ConvertVarChar2(NULL),
                                DBMS_LCR.EMPTY_LOB,
                                NULL,
                                NULL);
  newunit3 := SYS.LCR$_ROW_UNIT('C2',
                                ANYDATA.ConvertVarChar2(NULL),
                                DBMS_LCR.EMPTY_LOB,
                                NULL,
                                NULL);
  newunit4 := SYS.LCR$_ROW_UNIT('C3',
                                ANYDATA.ConvertVarChar2(NULL),
                                DBMS_LCR.EMPTY_LOB,
                                NULL,
                                NULL);
  newvals := SYS.LCR$_ROW_LIST(newunit1,newunit2,newunit3,newunit4);

  -- Perform an insert
  enq_row_lcr(
    source_dbname => 'MYDB.NET',
    cmd_type      => 'INSERT',
    obj_owner     => 'LOB_USER',
    obj_name      => 'WITH_CLOB',
    old_vals      => NULL,
    new_vals      => newvals);

  -- construct clobs
  big_data := RPAD('Hello World', 1000, '_');
  big_data := big_data || '#';
  big_data := big_data || big_data || big_data || big_data || big_data;
  DBMS_LOB.CREATETEMPORARY(
    lob_loc => c1_data,
    cache   => TRUE);
  DBMS_LOB.WRITEAPPEND(
    lob_loc => c1_data,
    amount  => length(big_data),
    buffer  => big_data);

  big_data := RPAD('1234567890#', 1000, '_');
  big_data := big_data || big_data || big_data || big_data;
  DBMS_LOB.CREATETEMPORARY(
    lob_loc => c2_data,
    cache   => TRUE);
  DBMS_LOB.WRITEAPPEND(
    lob_loc => c2_data,
    amount  => length(big_data),
    buffer  => big_data);

  big_data := RPAD('ASDFGHJKLQW', 2000, '_');
  big_data := big_data || '#';
  big_data := big_data || big_data || big_data || big_data || big_data;
  DBMS_LOB.CREATETEMPORARY(
    lob_loc => c3_data,
    cache   => TRUE);
  DBMS_LOB.WRITEAPPEND(
    lob_loc => c3_data,
    amount  => length(big_data),
    buffer  => big_data);

  -- pk info
  newunit1 := SYS.LCR$_ROW_UNIT('A',
                                ANYDATA.ConvertNumber(3),
                                NULL,
                                NULL,
                                NULL);
  newvals  := SYS.LCR$_ROW_LIST(newunit1);

  -- write c1 clob
  n := do_enq_clob(
         src_dbname => 'MYDB.NET',
         tab_owner  => 'LOB_USER',
         tab_name   => 'WITH_CLOB',
         col_name   => 'C1',
         new_vals   => newvals,
         clob_data  => c1_data,
         offset     => 1,
         chunk_size => 1024);
  DBMS_OUTPUT.PUT_LINE('n=' || n);

  -- write c2 clob
  newvals  := SYS.LCR$_ROW_LIST(newunit1);
  n := do_enq_clob(
         src_dbname => 'MYDB.NET',
         tab_owner  => 'LOB_USER',
         tab_name   => 'WITH_CLOB',
         col_name   => 'C2',
         new_vals   => newvals,
         clob_data  => c2_data,
         offset     => 1,
         chunk_size => 2000);
  DBMS_OUTPUT.PUT_LINE('n=' || n);

  -- write c3 clob
  newvals  := SYS.LCR$_ROW_LIST(newunit1);
  n := do_enq_clob(src_dbname=>'MYDB.NET',
         tab_owner  => 'LOB_USER',
         tab_name   => 'WITH_CLOB',
         col_name   => 'C3',
         new_vals   => newvals,
         clob_data  => c3_data,
         offset     => 1,
         chunk_size => 500);
  DBMS_OUTPUT.PUT_LINE('n=' || n);

  COMMIT;

END;
/

/*

手順11: スプール結果のチェック

lob_construct.outスプール・ファイルをチェックして、このスクリプトの完了後にすべてのアクションが正常に終了していることを確認します。

*/

SET ECHO OFF
SPOOL OFF

/*************************** END OF SCRIPT ******************************/

スクリプトの実行後に、lob_user.with_clob表を問い合せて、適用プロセスによって適用された行を表示できます。DBMS_LOCK.SLEEP文を使用して、エンキューされた行を適用プロセスが適用するための時間のロックを指定します。

CONNECT lob_user/lob_user_pw

EXECUTE DBMS_LOCK.SLEEP(10);

SELECT a, c1, c2, c3 FROM with_clob ORDER BY a;

SELECT a, LENGTH(c1), LENGTH(c2), LENGTH(c3) FROM with_clob ORDER BY a;

LONGまたはLONG RAW列を含むLCRの管理

LONGおよびLONG RAWデータ型は、いずれも取得プロセスによって取得される行LCRに存在できますが、これらのデータ型は行LCR内では次のデータ型として表されます。

LONGまたはLONG RAW列に関係する行変更は、複数のLCRとして取得、伝播および適用される場合があります。環境でLONGまたはLONG RAW列を含むLCRを使用する場合は、LCRのLONGまたはLONG RAW列のデータ部分が、VARCHAR2型またはRAW型である必要があります。VARCHAR2LONGRAWLONG RAWとして解析されます。

Oracle StreamsでLONGまたはLONG RAW列のデータを含む行LCRを処理する場合は、次の要件を満たす必要があります。


注意:

LONGおよびLONG RAWデータ型は、同期取得によって取得される行LCRまたはユーザーが構成する行LCRには存在できません。


参照:

  • 適用ハンドラの詳細は、「LCRの適用プロセスのオプション」を参照してください。

  • ルールベースの変換の詳細は、『Oracle Streams概要および管理』を参照してください。

  • 決定的なファンクションの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。