Seit Oracle 10gR2 ist Datapump das Werkzeug der Wahl für Export/Import.
Was liegt also näher, als sich auch einmal damit zu beschäftigen – der Gedanke entsprechende Jobs direkt in der Datenbank laufen zu lassen und damit von der Shellebene wegzugehen war zumindest sehr behaglich.
Wie immer sieht es in der Realität nicht so einfach aus.
Grundsätzlich war ich rasch in der Lage einen passenden anonymen plsql-Block aufzubauen, der genau das tut, was ich möchte.
DECLARE
job_handle NUMBER;
job_status VARCHAR2(30);
schema_name VARCHAR2(30);
job_name VARCHAR2(40);
file_name VARCHAR2(60);
BEGIN
schema_name := 'DBGUARD';
job_name := schema_name||'_EXPORT_SCHEMA';
file_name := job_name||'_'||to_char(sysdate, 'YYYYMMDD-HH24MI');
job_handle := DBMS_DATAPUMP.open(operation=>'EXPORT',job_mode=>'SCHEMA',job_name=>job_name);
DBMS_DATAPUMP.add_file(handle=>job_handle,filename=>file_name||'_%U.DMP',directory=> 'EXPORT_DIR',filetype=>1);
DBMS_DATAPUMP.add_file(handle=>job_handle,filename=>file_name||'.LOG',directory=> 'EXPORT_DIR',filetype=>3);
DBMS_DATAPUMP.metadata_filter(handle => job_handle,name=>'SCHEMA_EXPR',value=>'IN ('''||schema_name||''')');
/*
DBMS_DATAPUMP.metadata_filter(handle => job_handle,name=>'SCHEMA_EXPR',value=>'IN ('''||schema||''')');
*/
dbms_datapump.start_job (job_handle);
dbms_datapump.wait_for_job (handle => job_handle, job_state => job_status);
dbms_output.put_line ('DataPump Export - '||to_char(sysdate,'DD/MM/YYYY HH24:MI:SS')||' Status '||job_status);
dbms_datapump.detach (handle => job_handle);
END;
/
Soweit, sogut. Lasse ich das laufen, werden im EXPORT_DIR auch brav das Dumpfile und das Logfile angelegt – alles super.
Es lag nahe zu versuchen diesen Block nun in eine Stored Procedure zu verpacken.
Nach minimalen Änderungen sah das dann so aus:
CREATE OR REPLACE PROCEDURE SYSTEM.EXPORT_SCHEMA (schema in varchar2)
as
job_handle NUMBER;
job_status VARCHAR2(30);
schema_name VARCHAR2(30);
job_name VARCHAR2(40);
file_name VARCHAR2(60);
BEGIN
schema_name := 'DBGUARD';
job_name := schema_name||'_EXPORT_SCHEMA';
file_name := job_name||'_'||to_char(sysdate, 'YYYYMMDD-HH24MI');
job_handle := DBMS_DATAPUMP.open(operation=>'EXPORT',job_mode=>'SCHEMA',job_name=>job_name);
DBMS_DATAPUMP.add_file(handle=>job_handle,filename=>file_name||'_%U.DMP',directory=> 'EXPORT_DIR',filetype=>1);
DBMS_DATAPUMP.add_file(handle=>job_handle,filename=>file_name||'.LOG',directory=> 'EXPORT_DIR',filetype=>3);
DBMS_DATAPUMP.metadata_filter(handle => job_handle,name=>'SCHEMA_EXPR',value=>'IN ('''||schema_name||''')');
/*
DBMS_DATAPUMP.metadata_filter(handle => job_handle,name=>'SCHEMA_EXPR',value=>'IN ('''||schema||''')');
*/
dbms_datapump.start_job (job_handle);
dbms_datapump.wait_for_job (handle => job_handle, job_state => job_status);
dbms_output.put_line ('DataPump Export - '||to_char(sysdate,'DD/MM/YYYY HH24:MI:SS')||' Status '||job_status);
dbms_datapump.detach (handle => job_handle);
END;
/
Die Änderung beschränkt sich letztlich darauf, dass beim Aufruf der Procedure das zu exportierende Schema als Parameter mitgegeben wird.
execute SYSTEM.EXPORT_SCHEMA('DBGUARD');
Wunderbar – läuft – zumindest beim ersten Mal.
Der nächste Aufruf präsentiert mir einen wunderschönen Fehler:
ORA-31634: job already exists
ORA-06512: at "SYS.DBMS_SYS_ERROR", line 79
ORA-06512: at "SYS.DBMS_DATAPUMP", line 911
ORA-06512: at "SYS.DBMS_DATAPUMP", line 4354
ORA-06512: at "SYSTEM.EXPORT_SCHEMA", line 12
ORA-06512: at line 1
Mhm. MHM.
Gucken wir mal.
SQL> select * from dba_datapump_jobs;
OWNER_NAME JOB_NAME OPERATION JOB_MODE STATE DEGREE ATTACHED_SESSIONS DATAPUMP_SESSIONS
---------- ------------------------------ -------------------- --------------- ---------- ---------- ----------------- -----------------
SYSTEM DBGUARD_EXPORT_SCHEMA EXPORT SCHEMA DEFINING 1 1 2
Ok, wir haben meinen Job und zwar mit einer attached Session im Status „DEFINING“. Das heisst, es müßte auch die Master-Tabelle noch geben.
SQL> SELECT o.status, o.object_id, o.object_type,
o.owner||'.'||object_name "OWNER.OBJECT"
FROM dba_objects o, dba_datapump_jobs j
WHERE o.owner=j.owner_name AND o.object_name=j.job_name ; 2 3 4
STATUS OBJECT_ID OBJECT_TYP OWNER.OBJECT
--------------- ---------- ---------- ----------------------------------------
VALID 80524 TABLE SYSTEM.DBGUARD_EXPORT_SCHEMA
1 row selected.
Offenbar räumt sich dieser Job nicht mehr von allein weg.
Dann tue ich das eben:
SQL> drop table SYSTEM.DBGUARD_EXPORT_SCHEMA;
Table dropped.
Der neuerliche Job-Aufruf bringt aber den gleichen Fehler.
Ist die Session zur gedropten Mastertabelle etwa noch da?
SQL> select username,program,status,logon_time, 'alter system kill session '''||sid||','||serial#||''';' befehl from v$session where program like '%DM0%';
USERNAME PROGRAM STATUS LOGON_TIME BEFEHL
----------- ------------------------------ --------------- ------------------------- ------------------------------------
SYSTEM oracle@deerlf0vl1 (DM00) ACTIVE 07.04.2008 - 08:22:42 alter system kill session '79,3658';
Ist sie. Dann mal auch mit ihr weg.
SQL> alter system kill session '79,3658';
System altered.
Und wie sieht es nun aus?
SQL> execute SYSTEM.EXPORT_SCHEMA('DBGUARD');
PL/SQL procedure successfully completed.
Toll. Ein rascher Blick zeigt, dass diesmal auch keinerlei Reste zurückgeblieben sind: Keine Mastertabelle, keine Session. Es müßte also eigentlich auch ein weiteres Mal funktionieren.
Die Probe ergibt: Jawohl, es geht. Ein weiterer Test zeigt: Nein, es geht mit den alten Problemen nicht. wtf?
Die Erleuchtung kommt ein wenig später: Auch nachdem der Export durchgelaufen ist, scheint noch einige Zeit zu vergehen, bis die internen Aufräumarbeiten abgeschlossen sind. Ruft man in dieser Zeit die Stored Procedure noch einmal auf, verhakt sich das Ganze und muss anschließend von Hand aufgeräumt werden.
Update: Dieses Problem tritt bei 11g nicht mehr auf.