【故障描述】
使用网络连接的方式登录19c的PDB,然后创建oracle_loader驱动模式的外部表。创建成功后,在检索数据时,出现KUP-04040的报错,过程如下:
[oracle@sdedu ~]$ sqlplus sys/oracle@pdb1 as sysdba
SQL*Plus: Release 19.0.0.0.0 - Production on Sun Mar 7 18:12:34 2021
Version 19.3.0.0.0
Copyright (c) 1982, 2019, Oracle.  All rights reserved.
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
SQL> create directory dmp_dir as '/home/oracle/ext_table/';
Directory created.
SQL> CREATE TABLE emp_load 
  2  (
  3   first_name CHAR(15), 
  4   last_name CHAR(20), 
  5   year_of_birth CHAR(4)
  6  )
  7  ORGANIZATION EXTERNAL 
  8  (
  9    TYPE ORACLE_LOADER 
 10    DEFAULT DIRECTORY dmp_dir
 11    ACCESS PARAMETERS 
 12    (
 13     RECORDS DELIMITED BY '|'
 14     DISABLE_DIRECTORY_LINK_CHECK
 15     FIELDS TERMINATED BY ','
 16    (
 17     first_name CHAR(7),
 18     last_name CHAR(8),
 19     year_of_birth CHAR(4))
 20    )
 21     LOCATION ('info.dat')
 22  )
 23  REJECT LIMIT UNLIMITED
 24  ;
Table created.
SQL> select * from emp_load;
select * from emp_load
*
ERROR at line 1:
ORA-29913: error in executing ODCIEXTTABLEOPEN callout
ORA-29400: data cartridge error
KUP-04040: file info.dat in DMP_DIR not found
【解决过程】
经过测试发现,如果在session中切换容器到pdb1中,访问就是正常的。
SQL> alter session set container=pdb1;
Session altered.
SQL> select * from emp_load;
FIRST_NAME      LAST_NAME            YEAR
--------------- -------------------- ----
Alvin           Tolliver             1976
Kenneth         Baer                 1963
Mary            Dube                 1973
SQL> 
这个报错,和可能是由于文件和目录权限导致,因此检查一下权限:
[oracle@sdedu ~]$ ls -alt ext_table/
total 24
drwx------. 29 oracle oinstall 4096 Mar  7 21:10 ..
drwxrwxrwx   2 oracle oinstall  100 Mar  7 18:51 .
-rwxrwxrwx   1 oracle oinstall   55 Mar  7 17:59 INFO.DAT
[oracle@sdedu ~]$ 
从文件的权限没有问题,目录的权限也是没有问题。但为啥还是不行呢?
索性换一下目录来出来吧。
我们使用pdb1默认的DATA_PUMP_DIR目录来进行外部表的创建。
/u01/app/oracle/admin/SS19C/dpdump/ADB4AF11A2A07F0BE0530A0200C001E6
此时发现成功了!
[oracle@sdedu ADB4AF11A2A07F0BE0530A0200C001E6]$ sqlplus sys/oracle@pdb1 as sysdba
SQL*Plus: Release 19.0.0.0.0 - Production on Sun Mar 7 21:31:24 2021
Version 19.3.0.0.0
Copyright (c) 1982, 2019, Oracle.  All rights reserved.
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
SQL> drop table emp_load;
Table dropped.
 
SQL> CREATE TABLE emp_load
  2  (
  3   first_name CHAR(15),
  4   last_name CHAR(20), 
  5   year_of_birth CHAR(4)
  6  )
  7  ORGANIZATION EXTERNAL 
  8  (
  9    TYPE ORACLE_LOADER 
 10    DEFAULT DIRECTORY DATA_PUMP_DIR
 11    ACCESS PARAMETERS 
 12    (
 13     RECORDS DELIMITED BY '|'
 14     FIELDS TERMINATED BY ','
 15    (
 16     first_name CHAR(7),
 17     last_name CHAR(8),
 18     year_of_birth CHAR(4))
 19    )
 20     LOCATION ('INFO.DAT')
 21  )
 22  REJECT LIMIT UNLIMITED
 23  ;
Table created.
SQL> select * from emp_load;
FIRST_NAME      LAST_NAME            YEAR
--------------- -------------------- ----
Alvin           Tolliver             1976
Kenneth         Baer                 1963
Mary            Dube                 1973
SQL> 
换一个目录在测试一下,也是成功了,此时发现ORIGIN_CON_ID也为1,1代表根容器。看来要想成功,ORIGIN_CON_ID列必须为1,也就是从根容器继承过来的。
[oracle@sdedu ext_table]$ sqlplus sys/oracle@pdb1 as sysdba
SQL*Plus: Release 19.0.0.0.0 - Production on Sun Mar 7 22:46:22 2021
Version 19.3.0.0.0
Copyright (c) 1982, 2019, Oracle.  All rights reserved.
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
SQL> select * from dba_directories where directory_name='SDO_DIR_ADMIN';
  
OWNER    DIRECTORY_NAME   DIRECTORY_PATH                                   ORIGIN_CON_ID
-------- --------------- ------------------------------------------------- -------------
SYS      SDO_DIR_ADMIN   /u01/app/oracle/product/19.3.0/dbhome_1/md/admin   1
1 rows selected.
SQL> show pdbs;
    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         3 PDB1                           READ WRITE NO
SQL> !cp /home/oracle/ext_table/INFO.DAT /u01/app/oracle/product/19.3.0/dbhome_1/md/admin/
SQL> drop table emp_load;
Table dropped.
SQL> CREATE TABLE emp_load
  2  (
  3   first_name CHAR(15),
  4   last_name CHAR(20), 
  5   year_of_birth CHAR(4)
  6  )
  7  ORGANIZATION EXTERNAL 
  8  (
  9    TYPE ORACLE_LOADER 
 10    DEFAULT DIRECTORY SDO_DIR_ADMIN
 11    ACCESS PARAMETERS 
 12    (
 13     RECORDS DELIMITED BY '|'
 14     FIELDS TERMINATED BY ','
 15    (
 16     first_name CHAR(7),
 17     last_name CHAR(8),
 18     year_of_birth CHAR(4))
 19    )
 20     LOCATION ('INFO.DAT')
 21  )
 22  REJECT LIMIT UNLIMITED
 23  ;
Table created.
SQL> select * from emp_load;
FIRST_NAME      LAST_NAME            YEAR
--------------- -------------------- ----
Alvin           Tolliver             1976
Kenneth         Baer                 1963
Mary            Dube                 1973
SQL> 
在创建Directory时,是无法指定ORIGIN_CON_ID来源的。在那个容器创建,就会是那个容器的ID。这种策略很可能是BUG。
最终,由于目录权限问题导致创建外部表失败,此时我们可以使用PDB默认的DUMP目录,也就是ORIGIN_CON_ID为1的目录,来避免这个错误。
【参考资料】
ORA-29913: Error in Executing ODCIEXTTABLEOPEN Callout (Doc ID 458620.1)