Pluggable Datenbanken und “DATA_PUMP_DIR” – ORA-39087

27. November 2015 Aus Von Markus Flechtner

Vor einigen Tagen musste ich Daten mit DataPump aus einer PDB exportieren. Dazu wollte ich das Directory“DATA_PUMP_DIR“, das in jeder Datenbank vorhanden ist, verwenden. Ergebnis: „ORA-39087: directory name DATA_PUMP_DIR is invalid“. Das Verhalten ist dokumentiert („Utilities Guide“ (Chapter „Overview of DataPump“: „The default Data Pump directory object, DATA_PUMP_DIR, does not work with PDBs. You must define an explicit directory object within the PDB that you are exporting or importing.“.)  Es ist also kein Bug, sondern ein Feature 🙂   – Aber was passiert da?

Schauen wir uns mal die Directory-Objekte in unserer Multitenant-Datenbank an:

SQL> connect / as sysdba
Connected.
SQL> show pdbs

CON_ID CON_NAME OPEN MODE RESTRICTED
———- —————————— ———- ———-
2 PDB$SEED READ ONLY NO
3 CRM01 READ WRITE NO
SQL> set linesize 200
SQL> set pagesize 100
SQL> column owner format a10
SQL> column directory_name format a20
SQL> column directory_path format a60
SQL> select con_id,origin_con_id,directory_name,directory_path
2 from cdb_directories
3 where con_id in (1,3) and directory_name like ‚DATA%‘
4 order by con_id,origin_con_id,directory_name;

CON_ID ORIGIN_CON_ID DIRECTORY_NAME DIRECTORY_PATH
———- ————- ——————– ————————————-
1 1 DATA_PUMP_DIR /u00/app/oracle/admin/TVD12CDB/dpdump/
3 1 DATA_PUMP_DIR /u00/app/oracle/admin/TVD12CDB/dpdump/

Achtung: Das Directory  DATA_PUMP_DIR in Container #3 (=PDB „CRM01“) hat als „Quell-Container“ (ORIGIN_CON_ID) den Container 1 (= CDB$ROOT).

Wie sieht es denn in der PDB aus?

SQL> alter session set container=crm01;
Session altered.

SQL> select origin_con_id,directory_name,directory_path
2 from dba_directories
3 where directory_name like ‚DATA%‘;

ORIGIN_CON_ID DIRECTORY_NAME DIRECTORY_PATH
————- ——————– ———————————————-
1 DATA_PUMP_DIR /u00/app/oracle/admin/TVD12CDB/dpdump/

Jetzt probieren wir mal den Export via DataPump. Von der CDB$ROOT aus funktioniert es problemlos:

oracle@training:~/ [TVD12CDB] expdp system directory=DATA_PUMP_DIR dumpfile=root.dmp logfile=root.log full=yes
Export: Release 12.1.0.2.0 - Production on Fri Nov 27 23:19:33 2015
Copyright (c) 1982, 2014, Oracle and/or its affiliates. All rights reserved.
Password:
Connected to: Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
WARNING: Oracle Data Pump operations are not typically needed when connected to the root or seed of a container database.
Starting "SYSTEM"."SYS_EXPORT_FULL_01": system/******** directory=DATA_PUMP_DIR dumpfile=root.dmp logfile=root.log full=yes
Estimate in progress using BLOCKS method...
[..]
Dump file set for SYSTEM.SYS_EXPORT_FULL_01 is:
/u00/app/oracle/admin/TVD12CDB/dpdump/root.dmp
Job "SYSTEM"."SYS_EXPORT_FULL_01" successfully completed at Fri Nov 27 23:21:54 2015 elapsed 0 00:02:13

Die Meldung, das DataPump Exports auf Root-Ebene nicht sinnvoll sind, ignorieren wird – darum geht es in diesem Post ja nicht.

Was passiert, wenn wir den gleichen Export auf PDB-Ebene machen?

oracle@training:~/ [TVD12CDB] expdp system@crm01 directory=DATA_PUMP_DIR dumpfile=crm01.dmp logfile=crm01.log full=yes
Export: Release 12.1.0.2.0 - Production on Fri Nov 27 23:23:54 2015
Copyright (c) 1982, 2014, Oracle and/or its affiliates. All rights reserved.
Password:
Connected to: Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
ORA-39002: invalid operation
ORA-39070: Unable to open the log file.
ORA-39087: directory name DATA_PUMP_DIR is invalid

Den ORA-39087 hatte ich ja bereits in der Einleitung erwähnt.

Was passiert, wenn wir als Workaround probieren, das Verzeichnis DATA_PUMP_DIR in der PDB anzulegen:

SQL> alter session set container=crm01;
Session altered.

SQL> create or replace directory data_pump_dir as ‚/tmp‘;
create or replace directory data_pump_dir as ‚/tmp‘
*
ERROR at line 1:
ORA-65040: operation not allowed from within a pluggable database

Der Workaround funktioniert also nicht. Wir müssen also ein neues Verzeichnis mit einem neuen Namen in der PDB anlegen:

SQL> alter session set container=crm01;
SQL> create or replace directory data_pump_dir_pdb as '/u00/app/oracle/admin/TVD12CDB/dpdump/';
Directory created.

Jetzt funktioniert der Export ohne Probleme.

Anschließend legen wir in den CDB$ROOT auch ein Verzeichnis DATA_PUMP_DIR_PDB an.

SQL> connect / as sysdba
Connected.
SQL> create or replace directory data_pump_dir_pdb as '/u00/app/oracle/admin/TVD12CDB/dpdump/';
Directory created.

Wie sieht es jetzt im Data Dictionary aus?

SQL> set linesize 200
SQL> set pagesize 100
SQL> column owner format a10
SQL> column directory_name format a20
SQL> column directory_path format a60
SQL> select con_id,origin_con_id,directory_name,directory_path
2 from cdb_directories
3 where con_id in (1,3) and directory_name like 'DATA%'
4 order by con_id,origin_con_id,directory_name;
CON_ID ORIGIN_CON_ID DIRECTORY_NAME DIRECTORY_PATH
---------- ------------- -------------------- -------------------------------------
1 1 DATA_PUMP_DIR /u00/app/oracle/admin/TVD12CDB/dpdump/
1 1 DATA_PUMP_DIR_PDB /u00/app/oracle/admin/TVD12CDB/dpdump/
3 1 DATA_PUMP_DIR /u00/app/oracle/admin/TVD12CDB/dpdump/
3 3 DATA_PUMP_DIR_PDB /u00/app/oracle/admin/TVD12CDB/dpdump/

Wir haben also zwei gleichnamige Directories in CDB$ROOT und in der PDB, dabei sind CON_ID und ORIGIN_CON_ID jeweils gleich.  Es sind also“ lokale“ Directories.

Im nächsten Schritt legen wir ein weiteres Directory in der CDB$ROOT an:

SQL> alter session set "_oracle_script"=TRUE;
Session altered.

SQL> create or replace directory data_pump_dir_tst as ‚/u00/app/oracle/admin/TVD12CDB/dpdump/‘;
Directory created.

SQL> select con_id,origin_con_id,directory_name,directory_path
2 from cdb_directories
3 where con_id in (1,3) and directory_name like ‚DATA%‘
4 order by con_id,origin_con_id,directory_name;

CON_ID ORIGIN_CON_ID DIRECTORY_NAME DIRECTORY_PATH
———- ————- ——————– ——————————————————
1 1 DATA_PUMP_DIR /u00/app/oracle/admin/TVD12CDB/dpdump/
1 1 DATA_PUMP_DIR_PDB /u00/app/oracle/admin/TVD12CDB/dpdump/
1 1 DATA_PUMP_DIR_TST /u00/app/oracle/admin/TVD12CDB/dpdump/
3 1 DATA_PUMP_DIR /u00/app/oracle/admin/TVD12CDB/dpdump/
3 1 DATA_PUMP_DIR_TST /u00/app/oracle/admin/TVD12CDB/dpdump/
3 3 DATA_PUMP_DIR_PDB /u00/app/oracle/admin/TVD12CDB/dpdump/

Normalerweise schützt Oracle die Strukturen einer CDB. So kann man z.B. nicht die Seed-Datenbank PDB$SEED read-write öffnen. Über die Einstellung  „_oracle_script“= TRUE werden diese Schutzmechanismen ausgeschaltet. Und wenn man dann ein Directory anlegt, dann wird es ein „Common Directory, dass sowohl in der PDB als auch in CDB$ROOT sichtbar ist – aber die ORIGIN_CON_ID 1 hat (=CDB$ROOT).

Und für dieses Verzeichnis gilt das gleiche wie für das DATA_PUM_DIR: ein Export auf PDB-Ebene mit diesem Verzeichnis bricht mit einem „ORA-39087“ ab.

Diese Verzeichnisse (mit CON_ID ungleich der ORIGIN_CON_ID) erscheinen also in der View DBA_DIRECTORIES der PDB, können dort aber nicht genutzt werden. Meiner Ansicht nach wäre es schöner gewesen, diesen „Verzeichnistyp“ gar nicht erst zu erlauben oder in der DBA_DIRECTORIES rauszufiltern (CON_ID=ORIGIN_CON_ID).

Wenn man jetzt das DATA_PUMP_DIR in einer PDB nutzen möchte, dann kann man:

  1. Das Directory DATA_PUMP_DIR in CDB$ROOT droppen.
  2. Das Directory in CDB$ROOT neu anlegen (mit „_oracle_script“=FALSE).
  3. Das Verzeichnis DATA_PUMP_DIR in allen PDBs (lokal) neu anlegen.

Aber ich gehe davon aus, dass das kein zulässiger Workaround ist. Also sollten wir uns von der Verwendung des DATA_PUMP_DIR verabschieden und konsequent auf eigene Directory-Objekte setzen.

 


Werbung (Amazon-Partner-Link)