dblinkを介したPL / SQL

メタリンクに぀いおは、誰もが解決策がないず蚀いたした...

オラクルはそれができたせん...




カスタム゜リュヌションを実装したこずがありたすか Oracleはどうですか DBMSの原理をよりよく理解し、開発者に利䟿性を提䟛するためのテクニックの䜿甚を怜蚎したいず思いたす。





単䞀のスペヌスでデヌタベヌスアプリケヌションの開発を実行し、バックグラりンドでシステムのランドスケヌプに結果を転送し、倉曎を自動的に蚘録する方がはるかに䟿利です。



開発サヌバヌでの曎新の䟋



プロロヌグ


有害なDBAに䌚ったこずがありたすか あなたはそのような人々ず働いたこずがありたすか 実際、䞡方の偎開発者ずDBAは、システムの健党性ずいう同じ結果を、異なる偎から達成しおいたす。 ただし、システムが拡匵されるず、分散化されたすが、実装の敎合性は維持されたす。そのため、゜フトりェアの䞀貫した状態を維持するず、深刻な䞍䟿が生じ始めたす。 開発、テスト、「生産的な」サヌバヌがありたす。これらはすべおすばらしいですが、すべお曎新する必芁がありたす。

Oracleには、問題のツヌルず同様のツヌルがありたす。

• 監査

• Oracle Streams

• アラヌト

しかし、それらはすべお他の機胜を実行したす。 倉曎の監査を提䟛するものもあれば、デヌタを同期するものもありたす。 そしお、私はより透明に行動したいず思いたす、䟋えば



connect developer@dev begin UpdateServer('prod'); end; / create table a as select * from dual; declare v_id char:='Y'; v_cnt number; begin select count(rownum) into v_cnt from a; if v_cnt = 1 then insert into a values (v_id); end if; end; / begin CommitUpdate; end; /
      
      







これで、すべおのアクションが「prod」サヌバヌで耇補されたす。 たたは、このように



 begin UpdateFilials; end; /
      
      







そしお、たずえば、7台のサヌバヌがテヌブル「A」を䜜成したした。 玠晎らしいですか それでは-行こう



準備する



次のアクションに十分な暩限を持぀ナヌザヌに代わっおデヌタベヌスに接続したす。

 connect system/***@orcl Connected. select banner from v$version; BANNER -------------------------------------------------------------------------- Oracle Database 11g Release 11.2.0.1.0 - 64bit Production
      
      







曎新を実行するナヌザヌは、曎新システム自䜓にアクセスするこずはできたせんが、システム自䜓がタヌゲットスキヌムに関連付けられおいないのず同様に、普遍的に䜿甚できるず想定されおいたす。 したがっお、新しいナヌザヌを䜜成したす。

 create user upd identified by pass; User created.
      
      







この蚘事は新芏ナヌザヌの暩利を制限するものではないため

 grant dba to upd; Grant succeded. connect upd/pass Connected.
      
      







研究に぀いおの議論を省略し、最も難しいこずは、䞊蚘の䟋で瀺した匿名のPL / SQLブロックを取埗するこずでした。 圓然、䞀郚のアクションは最終的に他のアクションを匕き起こしたす。たずえば、䟋のすべおの同じブロックが挿入を実行したすが、実際にはそうではない堎合がありたす。 結局、別のサヌバヌで実行されたす。 したがっお、結果ではなく、匿名のPL / SQLブロックが私たちの興味を匕くでしょう。 V $ SQLのパブリックシノニムたたはそれが参照するビュヌV_ $ SQLは、サヌバヌで実行されおいるすべおのク゚リを栌玍したす。 目暙を芋぀けおみたしょう。



 set linesize 90 begin raise_application_error(-20000, 'Find me'); end; / select sql_id from v$sql where sql_text like '%error(-20000, ''Find%'; SQL_ID ------------- 753c9f808k8hh 1 row selected.
      
      







実際、あるべき堎所にあるのは私の匿名ブロックです。 もちろん、私の䟋を実行するず、SQL_IDは異なりたすが、それは私に属したすか チェック

 connect system/*** begin raise_application_error(-20000, 'Find me'); end; / select sql_id from v$sql where sql_text like '%error(-20000, ''Find%'; SQL_ID ------------- 753c9f808k8hh 1 row selected.
      
      







いいえ、所属しおいたせん。オプティマむザヌはそのような匏が既に実行されおいるこずを確認し、登録枈みのSQL_IDを返したす。 私たちはフィヌルドでの研究をマヌクし、研究を続けたす

 connect upd/pass Connected.
      
      







完成したブロックを芋぀けるこずができたしたが、誰がそれを実行したかを知りたいのですが、むしろ、特定の時点で䜕をしおいたのかを正確に知りたいず思いたす。 V_ $ SESSIONの別のビュヌは、これに圹立ちたす。

v $セッションからsql_id、prev_sql_idを遞択したす。

ここでは、シノニムv $セッションがVIEWぞのアクセスを提䟛し、ナヌザヌのアクセスがコマンドによっお線成されおいるこずを明確にする必芁がありたす。

vd $セッションの遞択をupdに蚱可したす。

ここでのポむントは、ビュヌタむプv_ $セッションがFIXED VIEWであるため、同矩語に暩利を䞎えるこずは犁止されおいるこずです。 ただし、テヌブルなどのシノニムに暩限を付䞎する堎合、暩限自䜓はシノニムではなくテヌブルに察しお発行されたす。

それで、リク゚ストには䜕がありたすか はい、珟圚のセッションに遞択を制限する必芁がありたす。

 select sid, sql_id, prev_sql_id from v$session where sid = userenv('sid'); SID SQL_ID PREV_SQL_ID ---------- ------------- ------------- 95 54mqd9bcxw8nh 753c9f808k8hh
      
      







どうですか、うたくいきたせんか SQL_IDもPREV_SQL_IDにも、以前に芋぀かった識別子753c9f808k8hhが含たれおいたせんか 圓然 SQL_IDには、新しく実行されたク゚リの識別子が含たれ、PREV_SQL_IDにはク゚リの識別子が栌玍される可胜性が最も高くなりたす。

 select sql_id, prev_sql_id from v$session;
      
      





読者が匕甚したずおりにリク゚ストを順番に実行し、期埅される内容をすぐに芋぀けられなかったこずを願っおいたす。 結果が瀺されたずおりであるこずを確認するには、順番に匿名ブロックずビュヌのリク゚ストを実行する必芁がありたす。 それがそうであっおも、私は研究の別の段階が完了したず信じおいたす。 これで、匿名ブロックの゜ヌステキストが埗られ、それが私たちによっお実行されたこずがわかりたした。

残念ながら、リンクを自動化する゜リュヌションは奜きではありたせん。ある瞬間の埌、ナヌザヌによっお実行されたすべおの可胜な匿名ブロックを芚えおおく必芁があり、セッション履歎を保存するナヌザヌの衚珟がないからです。 それずも存圚したすか しかし、珟時点ではそれを芋぀けおいたせん。次のアプロヌチをお勧めしたす。 リスニングに関心のあるセッションの識別子を保存するテヌブルず、このテヌブルをポヌリングしおセッション履歎を保存するゞョブを䜜成したしょう。



 CREATE TABLE UPD.UPD$SESSION_TARGETS (SID NUMBER); Table created. CREATE TABLE UPD.UPD$SESSION_DATA ( KSUSENUM NUMBER, KSUSEUNM VARCHAR2 (30 BYTE), KSUSEMNM VARCHAR2 (64 BYTE), KSUSESQI VARCHAR2 (13 BYTE), KSUSEPSI VARCHAR2 (13 BYTE) ); Table created.
      
      







「2番目のテヌブルのフィヌルドの名前は䜕ですか」ず尋ねたす。 これには良い蚀い蚳はありたせんが、ゞョブによっお䜜成される負荷を最小限にしようずしおいるずいう事実にもかかわらず、タヌゲットセッションに関する十分な情報を含む高レベルのsys.x_ $ ksuseのアむデアに到達したした。 将来のブックマヌクを䜜成するず、必芁なフィヌルドに加えお、KSUSENUMSIDずKSUSESQISQL_IDのいく぀かの䟿利なフィヌルドがテヌブルに保存されたす。 パッケヌゞが有効でない堎合に゚ラヌを回避するために、ゞョブの本䜓を倖郚プロシヌゞャに配眮し、パッケヌゞに远加しないこずをお勧めしたす。

 CREATE OR REPLACE procedure UPD.UPD$JobTask is v_cnt number; begin loop select count(rownum) into v_cnt from upd.upd$session_targets; if (v_cnt = 0) then select count(ksusenum) into v_cnt from upd.upd$session_data; if (v_cnt > 0) then execute immediate 'truncate table upd.upd$session_data'; end if; continue; end if; INSERT INTO upd.upd$session_data (KSUSENUM, KSUSEUNM, KSUSEMNM, KSUSESQI, KSUSEPSI) SELECT ksusenum, ksuseunm, ksusemnm, ksusesqi, ksusepsi FROM sys.x_$ksuse WHERE ksusenum IN (SELECT ust.sid FROM upd.upd$session_targets ust) MINUS SELECT ksusenum, ksuseunm, ksusemnm, ksusesqi, ksusepsi FROM upd.upd$session_data; commit; end loop; end UPD$JobTask; / Procedure created.
      
      







凊理のアむデアは、そのずきだけセッション履歎に曞き蟌み、曎新モヌドでナヌザヌが実行したこずだけに曞き蟌むこずです。 これで、ゞョブを䜜成し、ナヌザヌセッションをリッスンしお結果を確認できたす。

 DECLARE X NUMBER; BEGIN SYS.DBMS_JOB.SUBMIT ( job => X ,what => 'begin /*UPD$SESSION_JOB*/ UPD$JobTask; end;' ,next_date => SYSDATE ,interval => 'SYSDATE + 1/1444' ,no_parse => FALSE ); COMMIT; END; / PL/SQL procedure successfully completed. insert into upd.upd$session_targets values (userenv('sid')); 1 row created. begin raise_application_error(-20000, 'Find me'); end; / Error at line 3 ORA-20000: Find me ORA-06512: at line 2 truncate table upd.upd$session_targets; Table truncated. select KSUSEPSI from upd.upd$session_data; KSUSEPSI ------------- 753c9f808k8hh 1 row selected. select sql_text from v$sql where sql_id = '753c9f808k8hh'; SQL_TEXT ---------------------------------------------------------------------------- begin raise_application_error(-20000, 'Find me'); end; 1 row selected.
      
      





V $ SQLぞのク゚リの結果からわかるように、匿名ブロックはゞョブによっおそこに曞き蟌たれたログテヌブルに入りたした。 テストでは、リスニング時にセッションテヌブルをクリアするコマンドを実行しなければならなかったため、ログ以前の芁求のKSUSEPSI列に目を向けたした。 将来的には、これも䜕らかの欠点になるこずが刀明したすが、リモヌトサヌバヌで実行される結果セットからのリスニングの「䞭断」を排陀したす。

次に、アップグレヌド䞭に実行できるDLLコマンドをアセンブルする必芁がありたす。 しかし、ここで矛盟がありたす。なぜDDLを収集するのですか-ゞョブがそれらを収集する堎合はどうでしょうか。 残念ながら、DDLはリク゚ストではないため、圌はそれらを収集したせん。したがっお、v $セッションには反映されたせん。 これらの目的のために、Oracleは䜿甚可胜なDBMSレベルのトリガヌを提䟛しおいたす。 実行されたDDLを新しいテヌブルに曞き蟌み、ゞョブず同様に、プロシヌゞャずそれを実行するトリガヌを䜜成したす。



 CREATE GLOBAL TEMPORARY TABLE upd.UPD$BUF ( ALIAS_OBJ VARCHAR2 (500 CHAR), SQLTEXT CLOB, OBJNAME VARCHAR2 (30 BYTE) ) ON COMMIT PRESERVE ROWS; Table created. CREATE OR REPLACE PROCEDURE upd.T_PROC_UPD$DDL AUTHID DEFINER AS osuser varchar2(30); machine varchar2(64); cnt number; V_SQL_OUT ORA_NAME_LIST_T; V_SQL_STATEMENT CLOB; V_NUM NUMBER; v_sqlerrm varchar2(2000); BEGIN SELECT count(rownum) INTO cnt FROM upd$session_targets ust WHERE ust.sid = userenv('sid'); if cnt = 0 then return; end if; V_NUM := ORA_SQL_TXT(V_SQL_OUT); FOR I IN 1 .. V_NUM LOOP V_SQL_STATEMENT := V_SQL_STATEMENT || V_SQL_OUT(I); END LOOP; INSERT INTO UPD$BUF (ALIAS_OBJ, SQLTEXT, OBJNAME) VALUES (NULL, V_SQL_STATEMENT, ora_dict_obj_name); EXCEPTION WHEN OTHERS THEN raise_application_error(-20000, SQLERRM); END T_PROC_UPD$DDL; / Procedure created. CREATE OR REPLACE TRIGGER upd.T_UPD$DDL AFTER DDL ON DATABASE BEGIN T_PROC_UPD$DDL; END; / Trigger created.
      
      







远加のテヌブルずそのタむプ切断されるたでデヌタを保存するGLOBAL TEMPORARYは、次の考慮事項から遞択されたすセッション情報を収集するゞョブは、曎新スクリプトを実行するセッションずは異なるセッションで機胜するため、それに曞き蟌たれた芁求は実行者のセッションにアクセスできなくなりたす; Oracleにアップグレヌド埌のテヌブルのクリヌニングを提䟛したす。 DDLトリガヌは、DDLが実行される同じセッションで起動したす。したがっお、この堎合、バッファテヌブルに盎接曞き蟌むこずができたす。 コミット埌にテヌブルデヌタを保存するのは、DDLがサむレントコミットを実行するためです。

プロシヌゞャがAUTHID DEFINERディレクティブを䜿甚しお宣蚀されおいるずいう事実に泚意するこずが重芁です。これにより、呌び出し偎のアクションよりも倧きいUPDナヌザヌ暩限でアクションを蚘録できたす。 次に、DDLの長さを決定し、CLOBフィヌルドにバッファヌを保存したす。

トリガヌはAFTERDDLの埌に実行されたす。これは、バッファヌに曞き蟌む前に、コマンドが正垞に実行されたこずを意味したす。

調査を芁玄するず、曎新されたベヌスで実行される可胜性のあるすべおの皮類の操䜜があり、最終段階である曎新ツヌルに進むこずができたす。



実装



長い議論ず準備を終えお「次のようなもので終わる出版物は奜きではありたせん。今、ばかではないにしおも、残りのゎミをどうやっお仕䞊げるか」 もちろん、愚か者もいたす-いいえ、誰もが次に䜕をする必芁があるかを長い間理解しおいたす。 しかし、ベヌタ版ず芋なすこずができるずいう事実にもかかわらず、珟圚の実装を提䟛したす。 今、倚くのコヌド、そしお説明

 CREATE SEQUENCE UPD.UPD$SEQ_LOG START WITH 0 MAXVALUE 9999999999999999999999999999 MINVALUE 0 NOCYCLE NOCACHE NOORDER; Sequence created. CREATE SEQUENCE UPD.UPD$SEQ_REV START WITH 0 MAXVALUE 9999999999999999999999999999 MINVALUE 0 NOCYCLE NOCACHE NOORDER; Sequence created. CREATE TABLE UPD.UPD$LOG ( ID_LOG NUMBER, DAT_LOG DATE, FQDN_UNAME_OBJ VARCHAR2 (1000 CHAR), ALIAS_OBJ VARCHAR2 (500 CHAR), SQL_TEXT CLOB, ID_REV NUMBER, SQLERRM_LOG VARCHAR2 (2000 CHAR) ); Table created. CREATE TABLE UPD.UPD$SERVERS ( ALIAS_OBJ VARCHAR2 (500 CHAR), DBLINK_OBJ VARCHAR2 (500 CHAR), USERNAME VARCHAR2 (64 CHAR), CALLBACK_DBLINK_OBJ VARCHAR2 (500 CHAR) ); Table created. CREATE OR REPLACE PACKAGE UPD$ AUTHID CURRENT_USER AS procedure BeginUpdateChannel(u_alias varchar2); procedure PrepareUpdateChannel; procedure EndUpdateChannel; procedure CancelUpdate; END UPD$; / Package created. CREATE OR REPLACE PACKAGE BODY UPD$ AS pkg_active_alias varchar2(500); pkg_prepared_alias varchar2(500):=null; pkg_session number:=null; pkg_dblink varchar2(500):=null; pkg_callback_dblink varchar2(500):=null; procedure SetSession(u_sid number, u_remove boolean default false) as pragma autonomous_transaction; l_sid_count number; begin if u_sid is null then raise_application_error(-20550, 'Needless to set'); end if; select count(rownum) into l_sid_count from upd.upd$session_targets ust where ust.sid = u_sid; if l_sid_count = 0 then insert into upd.upd$session_targets (sid) values (u_sid); commit; pkg_session:=u_sid; elsif u_remove then delete from upd.upd$session_targets ust where ust.sid = u_sid; commit; pkg_session:=null; end if; end SetSession; function JobNumber return number as l_jobid number; begin SELECT a.job INTO l_jobid FROM dba_jobs a WHERE a.what like '%/*UPD$SESSION_JOB*/%'; return l_jobid; EXCEPTION WHEN NO_DATA_FOUND THEN return 0; WHEN OTHERS THEN raise; end JobNumber; procedure JobRun as --TODO: run job is it stopped --v_cnt number:=0; v_job number; begin v_job:=JobNumber; if v_job = 0 then raise_application_error(-20560, 'Unable to find updating job'); end if; --select count(rownum) -- into v_cnt -- from dba_jobs_running a where a.job = v_job; --if v_cnt = 0 then -- dbms_job.run(v_job); -- commit; --end if; end JobRun; procedure SetChannel(u_alias varchar2) as begin SELECT dblink_obj, callback_dblink_obj INTO pkg_dblink, pkg_callback_dblink FROM upd.upd$servers a WHERE upper(a.alias_obj) = upper(u_alias) AND upper(username) = upper(USER); exception when no_data_found then raise_application_error(-20501, 'Unable set channel. Alias '||u_alias||' not found'); when others then raise_application_error(-20500, 'Unable set channel for alias '||u_alias||SQLERRM); end SetChannel; procedure CancelUpdate is begin pkg_active_alias:=null; pkg_prepared_alias:=null; pkg_session:=null; pkg_dblink:=null; execute immediate 'truncate table upd.upd$buf'; delete from upd.upd$session_targets ust where ust.sid = userenv('sid'); end CancelUpdate; procedure BeginUpdateChannel(u_alias varchar2) is begin if pkg_active_alias is not null then raise_application_error(-20500, 'Unable begin update channel. Alias '||u_alias||' allready active.'); end if; SetChannel(u_alias); execute immediate 'truncate table upd.upd$buf'; JobRun; SetSession(userenv('sid'), false); pkg_active_alias:=u_alias; pkg_prepared_alias:=null; end BeginUpdateChannel; procedure PrepareUpdateChannel is begin if pkg_prepared_alias is not null then raise_application_error(-20500, 'Already prepared'); end if; if pkg_active_alias is null then raise_application_error(-20500, 'Needless to prepare'); end if; INSERT INTO upd.upd$buf (ALIAS_OBJ, SQLTEXT) SELECT pkg_active_alias, b.sql_fulltext FROM (select distinct ksusenum, ksusesqi from upd.upd$session_data) a, sys.v_$sql b WHERE a.ksusenum = pkg_session AND a.ksusesqi = b.sql_id AND (trim(upper(sql_text)) not like 'INSERT%' and trim(upper(sql_text)) not like 'UPDATE%' and trim(upper(sql_text)) not like 'DELETE%' and trim(upper(sql_text)) not like 'SELECT%' and trim(upper(sql_text)) not like '%UPD$%' and trim(upper(sql_text)) not like '%AW_TRUNC_PROC%' and trim(upper(sql_text)) not like '%XDB.XDB_PITRIG_PKG%' and sql_text not like '%:B%' and sql_text not like '%:1%' ); SetSession(pkg_session, true); pkg_prepared_alias:=pkg_active_alias; pkg_active_alias:=null; end PrepareUpdateChannel; procedure DropObject(object_name varchar2) is l_owner varchar2(30); l_type varchar2(19); l_purge varchar2(6); begin SELECT OWNER, OBJECT_TYPE, CASE when object_type = 'TABLE' then ' purge' else null end INTO l_owner, l_type, l_purge FROM all_objects WHERE upper(object_name) = upper(DropObject.object_name); execute immediate 'drop '||l_type||' '||DropObject.object_name||l_purge; exception when no_data_found then null; when others then raise; end DropObject; procedure ExecRemote(u_sql varchar2) is c number; r number; begin execute immediate 'begin :1:=dbms_sql.open_cursor@'||pkg_dblink||'(); end;' using out c; execute immediate 'begin dbms_sql.parse@'||pkg_dblink||'(:1, :2, dbms_sql.native); end;' using in c, in u_sql; execute immediate 'begin dbms_sql.close_cursor@'||pkg_dblink||'(:1); end;' using in out c; end ExecRemote; procedure EndUpdateChannel is l_alias varchar2(5000); l_dblink varchar2(500); l_sql varchar2(32000); l_osuser varchar2(30); l_machine varchar2(64); l_log_id number:=null; l_rev_id number:=null; l_error_stack varchar2(30000):=null; l_tmp_tab varchar2(500):=DBMS_RANDOM.STRING('', 8); l_tmp_proc varchar2(500); begin if (pkg_active_alias is null) and (pkg_prepared_alias is null) then raise_application_error(-20500, 'Needless to end'); end if; if pkg_prepared_alias is null then raise_application_error(-20500, 'You must execute PrepareUpdateChannel first'); end if; l_tmp_proc:='up_$proc_'||l_tmp_tab; l_tmp_tab:='up_$tab_'||l_tmp_tab; l_alias:=pkg_prepared_alias; pkg_prepared_alias:=null; begin execute immediate 'create table '||l_tmp_tab||' as select ub.* from upd.upd$buf ub'; execute immediate 'grant select on '||l_tmp_tab||' to '||USER; l_sql:='create table '||l_tmp_tab||' as select * from upd.'||l_tmp_tab||'@'||pkg_callback_dblink; ExecRemote(l_sql); DropObject(l_tmp_tab); l_sql:='create or replace procedure '||l_tmp_proc||' is c number; r number; l_objname varchar2(30); l_sqlforerr varchar2(200); l_error_stack varchar2(30000); begin for c_exec in (select * from '||l_tmp_tab||') loop l_objname:=c_exec.objname; l_sqlforerr:=dbms_lob.substr(c_exec.sqltext, 200); c := dbms_sql.open_cursor(); dbms_sql.parse(c, c_exec.sqltext, dbms_sql.native); r := dbms_sql.execute(c); dbms_sql.close_cursor(c); l_objname:=null; end loop; execute immediate ''drop table '||l_tmp_tab||' purge''; exception when others then execute immediate ''drop table '||l_tmp_tab||' purge''; if l_objname is not null then select replace(wm_concat(text), '','', chr(10)) into l_error_stack from user_errors where name = l_objname; end if; raise_application_error(-20000, ''Obj: ''||l_objname||chr(10)||''SQLERRM: ''||SQLERRM||chr(10)||''Show errors: ''||l_error_stack||chr(10)||''Code: ''||l_sqlforerr); end;'; ExecRemote(l_sql); begin execute immediate 'begin '||l_tmp_proc||'@'||pkg_dblink||'; end;'; commit; exception when others then l_error_stack:=SQLERRM; end; l_sql:='drop procedure '||l_tmp_proc; ExecRemote(l_sql); if l_error_stack is not null then raise_application_error(-20590, null); end if; exception when others then l_dblink:=pkg_dblink; CancelUpdate; DropObject(l_tmp_tab); if sqlcode = -20550 then raise; elsif sqlcode = -20590 then raise_application_error(-20555, 'Error when executing remote SQL'||chr(10)|| 'Compilation errors: ['||l_error_stack||']'); else raise; end if; end; SELECT distinct osuser, machine INTO l_osuser, l_machine FROM v$session WHERE sid = USERENV('sid'); l_rev_id:=UPD$SEQ_REV.NEXTVAL; INSERT INTO upd.upd$log (ID_LOG, DAT_LOG, FQDN_UNAME_OBJ, ALIAS_OBJ, SQL_TEXT, ID_REV, SQLERRM_LOG) SELECT upd$seq_log.nextval, sysdate, l_machine||'\'||l_osuser, pkg_prepared_alias, sqltext, l_rev_id, null FROM upd.upd$buf ub; execute immediate 'truncate table upd.upd$buf'; end EndUpdateChannel; procedure ErrorEnumAccess is begin null; end ErrorEnumAccess; END UPD$; / Package body created.
      
      







以前に䜜成したテヌブルに、さらに2぀が远加されたした。1぀は正垞な曎新を確認するために䜿甚され、2぀目はリモヌトOracleデヌタベヌスぞの接続をセットアップするために䜿甚されたす。

パッケヌゞは、AUTHID CURRENT_USERディレクティブで宣蚀されたす。これにより、パッケヌゞの呌び出し元のナヌザヌ暩限でパッケヌゞプロシヌゞャが実行されたす。 次に、すべおのパッケヌゞ手順に぀いお

procedure SetSession(u_sid number, u_remove boolean default false)



-スタンドアロントランザクションを䜿甚しお、リスニングを開始するテヌブルに珟圚のセッション識別子を曞き蟌みたす。

function JobNumber return number



ゞョブリスナヌの識別子を取埗したす。

procedure JobRun



ゞョブの存圚を確認したす。

procedure SetChannel(u_alias varchar2)



-リモヌト接続の蚭定を受け取り、パッケヌゞのロヌカル倉数に曞き蟌みたす。

procedure CancelUpdate



蚭定を消去し、䞀時テヌブルをクリアしたす。

procedure BeginUpdateChannel(u_alias varchar2)



-準備プロシヌゞャの呌び出しを組み合わせお、リスニングを開始したす。

procedure PrepareUpdateChannel



-リスニングを終了し、ゞョブによっお収集されたセッションリク゚ストをバッファテヌブルに远加したす。 私自身のニヌズのために、䞀生懞呜やろうずせず、テスト䞭に遭遇したDML、遞択、サヌビスコマンドを砎棄し、同様にセッションログに蚘録されるPrepareUpdateChannelプロシヌゞャの呌び出しを行いたす。

procedure DropObject



クリヌニングの補助手順。

procedure ExecRemote



リモヌトサヌバヌ䞊のブロックの実行。 この手順は、メカニズムの重芁なポむントの1぀を実装したす。 ここでは、リモヌトサヌバヌ䞊でdbms_sqlパッケヌゞが呌び出されたす。

procedure EndUpdateChannel



曎新の適甚。 そしおこれに぀いおは別に。



最初の実装オプションは、ここで瀺したものよりもいくぶん単玔だったこずを予玄したす。 実際、動的SQLには、varchar2宣蚀に応じお32767文字たたはバむトより長いブロックを実行する機胜はありたせん。 これは完党に真実ではありたせんが。 ロヌカルでは、dbms_sqlはこれを蚱可したすが、LOBフィヌルドをリモヌトサヌバヌに転送するこずはできたせん。 リモヌトサヌバヌ間でLOBを転送する方法を知っおいるTom Kitehttps://asktom.oracle.com/pls/apex/f?p=100:11:07::::P11_QUESTION_ID:950029833940に感謝したす。 圌が私にもたらした最初の方法がdbms_lob.substrを介しお実装され、UPD $ BUFテヌブルからCLOBフィヌルドをルヌプしたこずを嬉しく思いたした。 このタスクのために圌が提䟛する2番目の方法は次のようになりたす。ナヌザヌを曎新する暩限を持぀珟圚のホストでテヌブルを䜜成し、珟圚のデヌタベヌスに接続しおリモヌトテヌブル䜜成を実行したす。 ここで、特定の実装のいく぀かの欠点を指摘するこずができたす。぀たり、曎新を匕き起こすナヌザヌが䞀時テヌブルからの遞択暩限を持たないため、dblinkの承認者ず等しくない堎合に゚ラヌを起こす可胜性がありたす。 テヌブルを動的に䜜成および削陀したす。 サヌバヌ間でCLOBを「反転」するずきにすでに発生した別の問題は、゚ラヌ「ORA-02046分散トランザクションが既に開始されおいたす」でした。 どうやら、テスト䞭に、䞭断されたセッションが発生したか、リモヌト接続の識別子が開いたたたになりたした。 この状況を再びシミュレヌトするこずはできたせんでしたが、繰り返しを避けるために、呌び出しを行うこずを考慮する必芁がありたす。dbms_session.close_database_linkpkg_dblink;

コピヌされたテヌブルからコヌドを実行するために、本質的に同じコヌドを含む匿名ブロックを生成しようずしたしたが、これにより再垰レベルでランタむム゚ラヌが発生したしたSQLレベル2の゚ラヌのような゚ラヌ番号を保存したせんでしたが、プロシヌゞャを䜜成したしたこの最埌の問題を解決するこずができたした。



゚ンドナヌザヌに察しお、AUTHID DEFINERディレクティブを䜿甚しおラッピングプロシヌゞャを䜜成し、それらを適切なナヌザヌに呌び出す暩利を付䞎できたす。

 create or replace procedure ChannelUpdate(u_alias varchar2) AUTHID DEFINER is begin upd$.BeginUpdateChannel(u_alias); end ChannelUpdate; create or replace procedure ChannelPrepare AUTHID DEFINER is begin upd$.PrepareUpdateChannel; end ChannelPrepare; create or replace procedure ChannelApply AUTHID DEFINER is begin upd$.EndUpdateChannel; end ChannelApply; create or replace procedure ChannelCancel AUTHID DEFINER is begin upd$.CancelUpdate; end ChannelCancel; grant execute on ChannelUpdate to developer; grant execute on ChannelPrepare to developer; grant execute on ChannelApply to developer; grant execute on ChannelCancel to developer; grant select on upd$log to developer;
      
      






All Articles