Data Pumpと文字コードの変換
Oracle Databaseは文字列型のエンコードをデータベース単位に決定します。
Oracle Database Clientはデータベースのエンコードとクライアントのエンコードを自動的に変換することでアプリケーションから文字コードの違いを隠蔽しています。
Data PumpはOracle Databaseに付属するツールで、論理バックアップやデータの移行に使われています。ここではData Pumpを使って、日本語EUC(JA16EUC)を使ったデータベースからUTF-8(AL32UTF8)を使ったデータベースへ移行する際に文字コード変換エラーを検知する方法について検証します。
JA16EUCと外字
Oracle Databaseの日本語EUC文字コードであるJA16EUCには、Shift_JISにMicrosoftやIBMが追加した外字に相当するエンコードが含まれません。Shift_JISでは0x8740、日本語EUCでは0xADA1に定義された「①」はJA16EUCには含まれません。Oracle Databaseに付属するLocale Builderを使うと、エンコードに含まれる文字のコードを確認することができます。
JA16EUCには①等のコードは含まれませんが、Oracle Databaseはクライアントとサーバー間の文字コードが同一の場合、文字コードチェックを行わないため、本来では格納すべきではない文字も格納できる場合があります。
$ printenv NLS_LANG
Japanese_Japan.JA16EUC
$
$ sqlplus / as sysdba
SQL> SELECT value FROM V$NLS_PARAMETERS WHERE name='NLS_CHARACTERSET' ;
VALUE
----------------------------------------------------------------
JA16EUC
SQL> INSERT INTO SCOTT.DATA1 VALUES ('①') ; -- 0xADA1を格納
1行が作成されました。
SQL> COMMIT ;
コミットが完了しました。
SQL> SELECT DUMP(c1, 16) FROM SCOTT.DATA1 ;
DUMP(C1,16)
--------------------------------------------------------------------------------
Typ=1 Len=2: ad,a1
Data Pumpを使ったデータ移行
expdpコマンドとimpdpコマンドを使ってデータベースのデータを移行します。
-- 移行元データベース(JA16EUCJP)
$ expdp SYSTEM/pass SCHEMAS=SCOTT DIRECTORY=dir1 DUMPFILE=expdat.dmp
Export: Release 11.2.0.4.0 - Production on 日 11月 19 12:16:46 2017
Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved.
...
-- 移行先データベース(AL32UTF8)
$ impdp SYSTEM/pass SCHEMAS=SCOTT DIRECTORY=dir1 DUMPFILE=expdat.dmp
Import: Release 12.2.0.1.0 - Production on Mon Nov 20 12:30:32 2017
Copyright (c) 1982, 2017, Oracle and/or its affiliates. All rights reserved.
Connected to: Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
Master table "SYSTEM"."SYS_IMPORT_SCHEMA_01" successfully loaded/unloaded
import done in AL32UTF8 character set and AL16UTF16 NCHAR character set
export done in JA16EUC character set and AL16UTF16 NCHAR character set
Warning: possible data loss in character set conversions
...
0xADA1の文字コードは変換できなかったはずですが、エラーは発生していません。移行先データを確認するとREPLACEMENT CHARACTER (0xEFBFBD)に変換されていることがわかります。
-- 移行先データベース(AL32UTF8)
SQL> SELECT c1, DUMP(c1, 16) DUMP FROM SCOTT.DATA1 ;
C1 DUMP
----- ------------------------------
? Typ=1 Len=3: ef,bf,bd
データ移行時に変換エラーを拒否
impdpコマンドに data_options=REJECT_ROWS_WITH_REPL_CHARを指定すると、文字コード変換エラーが発生したレコードは拒否され、変換エラーの情報がログに出力されるようになります。
-- 移行先データベース(AL32UTF8)
$ impdp SYSTEM/pass SCHEMAS=SCOTT DIRECTORY=dir1 DUMPFILE=expdat.dmp data_options=REJECT_ROWS_WITH_REPL_CHAR
Import: Release 12.2.0.1.0 - Production on Mon Nov 20 12:26:14 2017
Copyright (c) 1982, 2017, Oracle and/or its affiliates. All rights reserved.
Connected to: Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
Master table "SYSTEM"."SYS_IMPORT_SCHEMA_01" successfully loaded/unloaded
import done in AL32UTF8 character set and AL16UTF16 NCHAR character set
export done in JA16EUC character set and AL16UTF16 NCHAR character set
Warning: possible data loss in character set conversions
Starting "SYSTEM"."SYS_IMPORT_SCHEMA_01": SYSTEM/******** SCHEMAS=SCOTT DIRECTORY=dir1 DUMPFILE=expdat.dmp data_options=REJECT_ROWS_WITH_REPL_CHAR
Processing object type SCHEMA_EXPORT/USER
Processing object type SCHEMA_EXPORT/SYSTEM_GRANT
Processing object type SCHEMA_EXPORT/DEFAULT_ROLE
Processing object type SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMA
Processing object type SCHEMA_EXPORT/SEQUENCE/SEQUENCE
Processing object type SCHEMA_EXPORT/TABLE/TABLE
Processing object type SCHEMA_EXPORT/TABLE/TABLE_DATA
ORA-02374: conversion error loading table "SCOTT"."DATA1"
ORA-39347: Character data loss was detected during conversion, the row was rejected.
ORA-02372: data for row: C1 : 0X'ADA1'
. . imported "SCOTT"."DATA1" 5.015 KB 1 out of 1 rows
Processing object type SCHEMA_EXPORT/VIEW/VIEW
Job "SYSTEM"."SYS_IMPORT_SCHEMA_01" successfully completed at Mon Nov 20 12:26:21 2017 elapsed 0 00:00:06