3.12 更改字符集的内部方式
前面我们曾经提到,通过修改props$的方式更改字符集在Oracle 7之后是一种极其危险的方式,应该尽量避免。我们又知道,通过ALTER DATABASE CHARACTER SET更改字符集虽然安全可靠,但是有严格的子集和超集的约束,实际上很少能够用到这种方法。
除了前面提到的几种方法外,Oracle 还存在另外一种更改字符集的方式:通过隐含的内部命令强制修改字符集。当使用模板的种子数据库创建数据库时,在创建脚本中可以看到这样的一系列命令:
alter system enable restricted session;
alter database "eygle" open resetlogs;
alter database rename global_name to "eygle";
ALTER TABLESPACE TEMP ADD TEMPFILE 'C:\oracle\oradata\eygle\TEMP01.DBF' SIZE 20480K REUSE AUTOEXTEND ON NEXT 640KMAXSIZE UNLIMITED;
select tablespace_name from dba_tablespaces where tablespace_name='USERS';
select sid, program, serial#, username from v$session;
alter database character set INTERNAL_CONVERT ZHS16GBK;
alter database national character set INTERNAL_CONVERT AL16UTF16;
alter user sys identified by "&&sysPassword";
alter user system identified by "&&systemPassword";
alter system disable restricted session;
在这里面,可以看到这样两条重要的、Oracle非公开的命令:
alter database character set INTERNAL_CONVERT ZHS16GBK;
alter database national character set INTERNAL_CONVERT AL16UTF16;
这个命令是当选择了使用模板方式创建了种子数据库以后,Oracle 会根据选择的字符集设置,把当前种子数据库的字符集更改为期望字符集,这就是这条命令的作用。
如果检查警告日志文件,可以发现相关更改操作的痕迹:
alter database character set INTERNAL_CONVERT ZHS16GBK
Updating character set in controlfile to ZHS16GBK
SYS.SNAP$ (REL_QUERY) - CLOB representation altered
SYS.METASTYLESHEET (STYLESHEET) - CLOB representation altered
SYS.EXTERNAL_TAB$ (PARAM_CLOB) - CLOB representation altered
XDB.XDB$RESOURCE (SYS_NC00027$) - CLOB representation altered
ODM.ODM_PMML_DTD (DTD) - CLOB representation altered
OE.WAREHOUSES (SYS_NC00003$) - CLOB representation altered
PM.ONLINE_MEDIA (SYS_NC00042$) - CLOB representation altered
PM.ONLINE_MEDIA (SYS_NC00062$) - CLOB representation altered
PM.ONLINE_MEDIA (PRODUCT_text) - CLOB representation altered
PM.ONLINE_MEDIA (SYS_NC00080$) - CLOB representation altered
PM.PRINT_MEDIA (AD_SOURCEtext) - CLOB representation altered
PM.PRINT_MEDIA (AD_FINALtext) - CLOB representation altered
Completed: alter database character set INTERNAL_CONVERT ZHS1
在使用这个命令时,Oracle 会跳过所有子集及超集的检查,在任意字符集之间进行强制转换,所以,使用这个命令时你必须十分小心,并必须清楚这一操作可能带来的风险。
之前讲过的内容仍然有效,可以使用csscan扫描整个数据库,如果在转换的字符集之间确认没有严重的数据损坏,或者可以使用有效的方式更改,就可以使用这种方式进行转换。这个内部命令的用户引用格式如下:
alter database character set INTERNAL_USE ZHS16CGB231280
来看一下具体的操作过程及Oracle的内部操作:
SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> STARTUP MOUNT;
Database mounted.
SQL> ALTER SYSTEM ENABLE RESTRICTED SESSION;
System altered.
SQL> ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
System altered.
SQL> ALTER SYSTEM SET AQ_TM_PROCESSES=0;
System altered.
SQL> ALTER DATABASE OPEN;
Database altered.
SQL> alter session set events '10046 trace name contextforever,level 12';
Session altered.
SQL> alter database character set INTERNAL_USE ZHS16CGB231280
Database altered.
这时警告日志文件中的记录了以下信息:
Tue Oct 19 16:26:30 2004
Database Characterset is ZHS16GBK
replication_dependency_tracking turned off (no async multimaster replication found)
Completed: ALTER DATABASE OPEN
Tue Oct 19 16:27:07 2004
alter database character set INTERNAL_USE ZHS16CGB231280
Updating character set in controlfile to ZHS16CGB231280
Tue Oct 19 16:27:15 2004
Thread 1 advanced to log sequence 118
Current log# 2 seq# 118 mem# 0: /opt/oracle/oradata/primary/redo02.log
Tue Oct 19 16:27:15 2004
ARC0: Evaluating archive log 3 thread 1 sequence 117
ARC0: Beginning to archive log 3 thread 1 sequence 117
Creating archive destination LOG_ARCHIVE_DEST_1: '/opt/oracle/oradata/primary/archive/1_117.dbf'
ARC0: Completed archiving log 3 thread 1 sequence 117
Tue Oct 19 16:27:20 2004
Completed: alter database character set INTERNAL_USE ZHS16CGB231280
Shutting down instance: further logons disabled
Shutting down instance (immediate)
License high water mark = 1
Tue Oct 19 16:29:06 2004
ALTER DATABASE CLOSE NORMAL
……
格式化10046跟踪文件,得到以下信息(摘要):
alter session set events '10046 trace name contextforever,level 12'
alter database character set INTERNAL_USE ZHS16CGB231280
....
update col$ set charsetid = :1 where charsetform = :2
....
update argument$ set charsetid = :1 where charsetform = :2
....
update collection$ set charsetid = :1 where charsetform = :2
....
update attribute$ set charsetid = :1 where charsetform = :2
....
update parameter$ set charsetid = :1 where charsetform = :2
....
update result$ set charsetid = :1 where charsetform = :2
....
update partcol$ set spare1 = :1 where charsetform = :2
....
update subpartcol$ set spare1 = :1 where charsetform = :2
....
update props$ set value$ = :1 where name = :2
....
update "SYS"."KOTAD$" set SYS_NC_ROWINFO$ = :1 where SYS_NC_OID$ = :2
....
update seq$ set increment$=:2,minvalue=:3,maxvalue=:4,cycle#=:5,order$=:6,
cache=:7,highwater=:8,audit$=:9,flags=:10 where obj#=:1
....
update kopm$ set metadata = :1, length = :2 where name='DB_FDO'
....
ALTER DATABASE CLOSE NORMAL
可以看到这个过程和之前ALTER DATABASE CHARACTER SET操作的内部过程是完全相同的,也就是说INTERNAL_USE提供的帮助就是使Oracle数据库绕过了子集与超集的校验。
这一方法在某些方面是有用处的,比如测试;但应用于产品环境时就应该格外小心,除了你以外,没有人会为此带来的后果负责!