この章では、Oracle Streamsレプリケーション環境で論理変更レコード(LCR)を管理する方法について説明します。
この章の内容は次のとおりです。
|
参照: LCRの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』および『Oracle Streams概要および管理』を参照してください。 |
この項では、論理変更レコード(LCR)を作成または変更するための要件について説明します。LCR型のコンストラクタを使用してLCRを作成し、そのLCRをANYDATAキューの永続キュー部分にエンキューできます。このようなLCRは、永続LCRです。
また、適用ハンドラまたはルールベースの変換を使用して、LCRを変更できます。取得LCRまたは永続LCRを変更できます。
LCRを管理するときには、次の要件を満たしていることを確認してください。
行LCRを作成または変更する場合は、command_type属性に古い列値の有無および新しい列値の有無との一貫性があることを確認してください。
DDL LCRを作成または変更する場合は、ddl_textがbase_table_name、base_table_owner、object_type、object_owner、object_nameおよびcommand_type属性と一貫していることを確認してください。
ユーザーが構成する行LCRの列に許可されるデータ型は、次のとおりです。
CHAR
VARCHAR2
NCHAR
NVARCHAR2
NUMBER
DATE
BINARY_FLOAT
BINARY_DOUBLE
RAW
TIMESTAMP
TIMESTAMP WITH TIME ZONE
TIMESTAMP WITH LOCAL TIME ZONE
INTERVAL YEAR TO MONTH
INTERVAL DAY TO SECOND
ユーザーが構成する行LCRの列に許可されるデータ型は、これらのデータ型のみです。ただし、特定の方法を使用すると、LOB情報を含むLCRを構成できます。また、取得プロセスによって取得されるLCRでは、サポートされるデータ型がより多くなっていますが、同期取得によって取得されるLCRでは、サポートされるデータ型がより少なくなっています。
|
参照:
|
次のLCRコンストラクタを使用してLCRを作成します。
データ操作言語(DML)文によって生じた行に対する変更を含む行LCRを作成するには、SYS.LCR$_ROW_RECORDコンストラクタを使用します。
データ定義言語(DDL)変更を含むDDL LCRを作成するには、SYS.LCR$_DDL_RECORDコンストラクタを使用します。各DDL LCRのddl_text属性に指定するDDLテキストが、Oracle SQL構文に準拠していることを確認してください。
次の例では、Oracle Database内のキューと、そのキューに関連付けられた適用プロセスを作成します。次に、渡された情報に基づいて行LCRを構成し、それをキューにエンキューするPL/SQLプロシージャを作成します。この例では、Oracle Streams管理者strmadminを構成して、この管理者にDBAロールを付与していると想定しています。
この操作の手順は次のとおりです。
SQL*Plusで、管理ユーザーとしてデータベースに接続します。
SQL*Plusでデータベースに接続する手順については、『Oracle Database管理者ガイド』を参照してください。
Oracle Streams管理者にDBMS_STREAMS_MESSAGINGパッケージに対するEXECUTE権限を付与します。次に例を示します。
GRANT EXECUTE ON DBMS_STREAMS_MESSAGING TO strmadmin;
手順9のPL/SQLプロシージャ内でこのパッケージのプロシージャをコールするため、このパッケージに対する明示的なEXECUTE権限が必要です。この場合、ロールを介して権限を付与するのみでは不十分です。
SQL*Plusで、Oracle Streams管理者としてデータベースに接続します。
Oracle DatabaseにANYDATAキューを作成します。
BEGIN
DBMS_STREAMS_ADM.SET_UP_QUEUE(
queue_table => 'strm04_queue_table',
storage_clause => NULL,
queue_name => 'strm04_queue');
END;
/
キュー内のメッセージを受信する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;
/
適用プロセスのポジティブ・ルール・セットを作成し、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;
/
適用プロセスのdisable_on_errorパラメータをnに設定します。
BEGIN
DBMS_APPLY_ADM.SET_PARAMETER(
apply_name => 'strm04_apply',
parameter => 'disable_on_error',
value => 'N');
END;
/
適用プロセスを起動します。
EXEC DBMS_APPLY_ADM.START_APPLY('strm04_apply');
行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パッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。 |
手順5で作成したconstruct_row_lcrプロシージャを使用してLCRを作成し、エンキューします。
SQL*Plusで、Oracle Streams管理者としてデータベースに接続します。
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;
SQL*Plusで、hrユーザーとしてデータベースに接続します。
hr.regions表を問い合せて、適用済の行の変更を表示します。region_idが5である行は、region_nameがMoonとなっているはずです。
SELECT * FROM hr.regions;
SQL*Plusで、Oracle Streams管理者としてデータベースに接続します。
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;
SQL*Plusで、hrユーザーとしてデータベースに接続します。
hr.regions表を問い合せて、適用済の行の変更を表示します。region_idが5である行は、region_nameがMarsとなっているはずです。
SELECT * FROM hr.regions;
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;
SQL*Plusで、hrユーザーとしてデータベースに接続します。
hr.regions表を問い合せて、適用済の行の変更を表示します。region_idが5である行は、削除されているはずです。
SELECT * FROM hr.regions;
行LCRおよびDDL LCR用に、個別のEXECUTEメンバー・プロシージャがあります。これらのメンバー・プロシージャを使用すると、現行ユーザーのセキュリティ・ドメインでLCRが実行されます。LCRが正常に実行されると、LCRに記録された変更がローカル・データベースに対して行われます。ここでは、行LCRおよびDDL 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が実行される場合があります。 |
|
参照:
|
この項の例では、PL/SQLプロシージャを作成し、行LCRを構成および実行することによってhr.jobs表の行の挿入、更新および削除を実行します。行LCRは、適用プロセスによってエンキューまたは処理されずに実行されます。この例では、Oracle Streams管理者strmadminを構成して、この管理者にDBAロールを付与していると想定しています。
この操作の手順は次のとおりです。
SQL*Plusで、Oracle Streams管理者としてデータベースに接続します。
SQL*Plusでデータベースに接続する手順については、『Oracle Database管理者ガイド』を参照してください。
行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;
/
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;
/
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;
/
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;
/
insert_job_lcrプロシージャを使用して、hr.jobs表に行を挿入します。
EXEC insert_job_lcr('BN_CNTR','BEAN COUNTER',5000,10000);
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
update_max_salary_lcrプロシージャを使用して、手順6でhr.jobs表に挿入した行のmax_salary値を更新します。
EXEC update_max_salary_lcr('BN_CNTR',10000,12000);
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
delete_job_lcrプロシージャを使用して、手順6でhr.jobs表に挿入した行を削除します。
EXEC delete_job_lcr('BN_CNTR');
hr.jobs表から削除された行を問い合せます。
SELECT * FROM hr.jobs WHERE job_id = 'BN_CNTR';
no rows selected
DDL LCR用のEXECUTEメンバー・プロシージャは、LCR$_DDL_RECORD型のサブプログラムです。DDL LCRにEXECUTEメンバー・プロシージャを実行すると、LCRが実行されます。LCRに対して実行される適用プロセスのハンドラは実行されません。DDL LCR用のEXECUTEメンバー・プロシージャは、適用プロセスの適用ハンドラのみがコールできます。
適用されたDDL LCRは、すべて自動的にコミットされます。したがって、DDLハンドラがDDL LCRのEXECUTEメンバー・プロシージャをコールすると、自動的にコミットが実行されます。
|
参照:
|
LOBデータ型は、取得プロセスによって取得される行LCRに存在できますが、他のデータ型として表されます。LOBデータ型は、同期取得によって取得される行LCRには存在できません。特定のLOBデータ型は、ユーザーが構成する行LCRには存在できません。表11-1に、これらのデータ型のLCR表現、および行LCRに存在できるかどうかを示します。
表11-1 行LCR内のLOBデータ型の表現
| データ型 | 行LCR表現 | 取得プロセスによって取得される行LCRに存在できるかどうか | 同期取得によって取得される行LCRに存在できるかどうか | ユーザーが構成する行LCRに存在できるかどうか |
|---|---|---|---|---|
|
固定幅 |
|
できる |
できない |
できる |
|
可変幅 |
AL16UTF16キャラクタ・セットの |
できる |
できない |
できない |
|
|
AL16UTF16キャラクタ・セットの |
できる |
できない |
できない |
|
|
|
できる |
できない |
できる |
|
|
|
できる |
できない |
できない |
Oracle Streams環境でのLOBデータ型に関係する行変更の一般的な考慮事項は、次のとおりです。
LOB列に関係する行変更は、複数の行LCRとして取得、伝播および適用される場合があります。
これらの行LCRの評価に使用されるルールは、ルール・セット内のルールが行変更に対応するすべての行LCRをTRUEと評価するか、またはどの行LCRもTRUEと評価しないように、決定的である必要があります。
ここでは、LOB列を構成または処理する場合に満たす必要のある要件、LOB列を含むLCRに対する適用プロセスの動作、およびLOBアセンブリについて説明します。また、LOB列を含むLCRを構成してエンキューする例も示します。
この項の内容は、次のとおりです。
|
参照: LOBの詳細は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください。 |
LOB列を含むLCRを(適用ハンドラを使用せずに)直接適用すると、適用プロセスは次のように動作します。
コマンド・タイプがINSERTまたはUPDATEのLCRにデータを含む新規のLOBがあり、lob_informationがDBMS_LCR.LOB_CHUNKまたはDBMS_LCR.LAST_LOB_CHUNKでない場合は、そのデータが適用されます。
コマンド・タイプがINSERTまたはUPDATEのLCRにデータを含まない新規のLOBがあり、lob_informationがDBMS_LCR.EMPTY_LOBの場合は、空のLOBとして適用されます。
コマンド・タイプがINSERTまたはUPDATEのLCRにデータを含まない新規のLOBがあり、lob_informationがDBMS_LCR.NULL_LOBまたはDBMS_LCR.INLINE_LOBの場合は、NULLとして適用されます。
コマンド・タイプがINSERTまたはUPDATEのLCRに新規のLOBがあり、lob_informationがDBMS_LCR.LOB_CHUNKまたはDBMS_LCR.LAST_LOB_CHUNKの場合は、すべてのLOB値が無視されます。コマンド・タイプがINSERTの場合は、LOBチャンクが続くものとみなされ、列に空のLOBが挿入されます。コマンド・タイプがUPDATEの場合は、LOBチャンクが続くものとみなされ、列の値が無視されます。
コマンド・タイプがUPDATEのLCR内の新規列がすべてLOBで、そのlob_informationがDBMS_LCR.LOB_CHUNKまたはDBMS_LCR.LAST_LOB_CHUNKの場合は、LOBチャンクが続くものとみなされ、更新がスキップされます。
コマンド・タイプがUPDATEまたはDELETEのLCRの場合は、古いLOB値が無視されます。
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のセット |
|---|---|---|---|---|
|
|
1つの |
1つ以上の |
1つ以上の |
|
|
|
1つの |
1つ以上の |
1つ以上の |
|
|
|
1つの |
なし |
なし |
なし |
|
|
1つ以上の |
なし |
なし |
なし |
|
|
1つの |
なし |
なし |
なし |
|
|
1つの |
なし |
なし |
なし |
表11-3に、LOBアセンブリを有効化した場合のOracle Streamsの動作を示します。具体的には、LOB列を含む1行に対する変更によって生成され、DMLハンドラまたはエラー・ハンドラに渡される行LCRを示します。
表11-3 LOBアセンブリを有効化した場合のOracle Streamsの動作
| 元の行変更 | 1つのLCR |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOBアセンブリを有効化すると、DMLハンドラまたはエラー・ハンドラによって、行LCRのLOB列が変更されます。DMLハンドラまたはエラー・ハンドラとして指定したPL/SQLプロシージャ内でLOBに対する操作を実行するには、DBMS_LOBパッケージのサブプログラムを使用することをお薦めします。行LCRにNULLのLOB列が含まれている場合、新しいLOBロケータでNULLを置き換える必要があります。EXECUTEメンバー・プロシージャを使用して行LCRを適用する場合は、行LCR用のADD_COLUMN、SET_VALUEおよびSET_VALUESメンバー・プロシージャを使用してLOBに対する変更を行います。
LOBアセンブリを有効化すると、LOBアセンブリによって、永続LCR内の非NULLのLOB列がLOBロケータに変換されます。ただし、LOBアセンブリでは、複数の永続行LCRが1つの行LCRに結合されません。たとえば、永続行LCRの場合、LOBアセンブリでは、INSERT行LCRの後に続く複数のLOB WRITE行LCRは1つのINSERT行LCRに結合されません。
|
参照:
|
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列が常にアセンブルされます。
|
参照:
|
この項では、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_idのIN引数およびproduct_long_descのIN 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ハンドラの設定
この手順では、接続先データベースでのINSERT、UPDATEおよび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を生成する場合は、そのLCRを構成または処理するときに、次の項に示す要件を満たす必要があります。
LOB列を含むLCRを構成する場合、およびLOBアセンブリが無効化されているDMLハンドラまたはエラー・ハンドラでLOB列を処理する場合、次の要件を満たす必要があります。
LOBアセンブリが無効化されているDMLハンドラまたはエラー・ハンドラで、行LCR内のLOB列データを変更しないでください。ただし、DMLハンドラまたはエラー・ハンドラで、行LCR内のLOB以外の列を変更することはできます。
特定の操作にのみ起動される適用ハンドラで、LOB列を含む表からのLCRを処理しないでください。たとえば、INSERT操作にのみ起動される適用ハンドラによって、1つ以上のLOB列を含む表からのLCRを処理しないでください。
LCRのLOB列のデータ部分が、VARCHAR2型またはRAW型である必要があります。VARCHAR2はCLOB、RAWはBLOBとして解析されます。
ユーザーが構成する行LCRのLOB列は、BLOBまたは固定幅CLOBのいずれかである必要があります。NCLOB型または可変幅CLOB型のLOB列を含む行LCRは構成できません。
表外のLOBに有効なコマンド・タイプは、LOB WRITE、LOB ERASEおよびLOB TRIMのみです。
LOB WRITE、LOB ERASEおよびLOB TRIMのLCRの場合は、old_valuesコレクションを空またはNULL、new_valuesを空以外にする必要があります。
LOB WRITE LCRとLOB ERASE LCRの場合は、lob_offsetを有効な値にする必要があります。他のすべてのコマンド・タイプの場合は、その列のLOBチャンクが続くものとみなして、lob_offsetをNULLにする必要があります。
LOB ERASE LCRとLOB TRIM LCRの場合は、lob_operation_sizeを有効な値にする必要があります。他のすべてのコマンド・タイプの場合は、lob_operation_sizeをNULLにする必要があります。
LOB TRIMおよびLOB ERASEは、LCRに含まれているLOB列のlob_informationがLAST_LOB_CHUNKに設定されている場合にのみ有効なコマンド・タイプです。
LOB WRITEは、LCRに含まれているLOB列のlob_informationがLAST_LOB_CHUNKまたはLOB_CHUNKに設定されている場合にのみ有効なコマンド・タイプです。
lob_informationがNULL_LOBに設定されているLOBの場合は、列のデータ部分をVARCHAR2型のNULL(CLOBの場合)またはRAW型のNULL(BLOBの場合)にする必要があります。それ以外の場合は、非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アセンブリが有効化されている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がエラー・キューに置かれます。
|
参照:
|
ルールベースの変換で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に実行されます。 |
|
参照:
|
この項の例に、LOBを含むLCRを構成およびエンキューするPL/SQLプロシージャの作成手順を示します。この例は、『Oracle Streams概要および管理』で説明した必要な操作を完了し、Oracle Streams用にデータベースの準備を完了したものと想定しています。
|
注意: このマニュアルをオンラインで参照している場合、この注意の後にある「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に存在できますが、これらのデータ型は行LCR内では次のデータ型として表されます。
LONGデータ型は、行LCR内ではVARCHAR2データ型として表されます。
LONG RAWデータ型は、行LCR内ではRAWデータ型として表されます。
LONGまたはLONG RAW列に関係する行変更は、複数のLCRとして取得、伝播および適用される場合があります。環境でLONGまたはLONG RAW列を含むLCRを使用する場合は、LCRのLONGまたはLONG RAW列のデータ部分が、VARCHAR2型またはRAW型である必要があります。VARCHAR2はLONG、RAWはLONG RAWとして解析されます。
Oracle StreamsでLONGまたはLONG RAW列のデータを含む行LCRを処理する場合は、次の要件を満たす必要があります。
カスタム・ルールベースの変換を使用して、LCR内のLONGまたはLONG RAW列のデータを変更しないでください。ただし、ルールベースの変換を使用して、LONGまたはLONG RAW列のデータを含む行LCR内のLONGおよびLONG RAW以外の列を変更することはできます。
LONGまたはLONG RAWデータを含む行LCRを処理するカスタム・ルールベースの変換では、行LCRのSET_VALUEまたはSET_VALUESメンバー・プロシージャは使用しないでください。これらのメンバー・プロシージャを使用すると、ORA-26679エラーが発生します。
行変更に対応するすべてのLCRが同じ方法で変換されるように、LONGまたはLONG RAW列を含むLCRで実行されるルールベースの変換ファンクションは決定的である必要があります。
DBMS_STREAMS_ADMパッケージのADD_COLUMNプロシージャで作成した宣言ルールベースの変換では、LONGまたはLONG RAW列を行LCRに追加できません。
DMLハンドラまたはエラー・ハンドラを使用して、LONGまたはLONG RAW列のデータを含む行LCRを処理することはできません。
LONGまたはLONG RAW列を含むLCRの評価に使用されるルールは、ルール・セット内のルールが行変更に対応するすべてのLCRをTRUEと評価するか、またはどのLCRもTRUEと評価しないように、決定的である必要があります。
適用プロセスを使用して、LONGまたはLONG RAW列のデータを含むLCRを宛先キューにエンキューすることはできません。DBMS_APPLY_ADMパッケージのSET_DESTINATION_QUEUEプロシージャを実行すると、指定した適用プロセスのルールを満たすLCRの宛先キューが設定されます。
|
注意: LONGおよびLONG RAWデータ型は、同期取得によって取得される行LCRまたはユーザーが構成する行LCRには存在できません。 |
|
参照:
|