BBED功能相当强大,因为可以用来修改block上的内容,因此很显然可以通过BBED来修改表上的数据,直接绕过通过SQL语句对表的操作。
本文尝试通过BBED来修改表中的一条记录。
建立实验环境首先建立实验环境,建立一张表test, 插入一条记录 (1, 'Frank'), 如下...
SQL> create user frank identified by frank;
User created.
SQL> grant resource, connect to frank;
Grant succeeded.
SQL> conn frank/frank
Connected.
SQL> create table test(id integer, name varchar2(10));
Table created.
SQL> insert into test values(1, 'Frank');
1 row created.
SQL> commit;
Commit complete.
SQL> select * from test;
ID NAME
---------- ----------
1 Frank
SQL> select rowid, dbms_rowid.rowid_block_number(rowid), dbms_rowid.rowid_relative_fno(rowid)
2 from test;
ROWID DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------ ------------------------------------
DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID)
------------------------------------
AAAM8RAAEAAAAGUAAA 404
4
SQL> alter system dump file 4 block 404;
alter system dump file 4 block 404
*
ERROR at line 1:
ORA-01961: Invalid dump option
SQL> alter system dump datafile 4 block 404;
alter system dump datafile 4 block 404
*
ERROR at line 1:
ORA-01031: insufficient privileges
SQL> conn / as sysdba
Connected.
SQL> grant alter system to frank;
Grant succeeded.
SQL> conn frank/frank
Connected.
SQL> alter system dump datafile 4 block 404;
System altered.
通过上面的操作,确定了我们刚才在表test中插入的一条记录位于4号数据文件上第404个block上面。
设置BBED现在把BBED 的dba 设置成 4, 404
[oracle@ora10g ~]$ bbed
Password:
BBED: Release 2.0.0.0.0 - Limited Production on Thu Feb 17 20:33:45 2011
Copyright (c) 1982, 2005, Oracle. All rights reserved.
************* !!! For Oracle Internal Use only !!! ***************
BBED> set dba 4, 404
BBED-00312: no LISTFILE specified
BBED> set listfile='/u01/app/oracle/temp/listdatafile.log'
BBED-00202: invalid parameter (=)
BBED> set listfile '/u01/app/oracle/temp/listdatafile.log'
LISTFILE /u01/app/oracle/temp/listdatafile.log
BBED> set dba 4, 404
DBA 0x01000194 (16777620 4,404)
可以通过BBED来查看我们刚才在表TEST中插入的记录,如下,(kbdr中这个数组大小为1,表示只有一条记录)
BBED> p kdbr
sb2 kdbr[0] @118 8076
BBED> p *kdbr[0]
rowdata[0]
----------
ub1 rowdata[0] @8176 0x2c
BBED> x /r
rowdata[0] @8176
----------
flag@8176: 0x2c (KDRHFL, KDRHFF, KDRHFH)
lock@8177: 0x01
cols@8178: 2
col 0[2] @8179: 0xc1 0x02
col 1[5] @8182: 0x46 0x72 0x61 0x6e 0x6b
BBED> x /rnc
rowdata[0] @8176
----------
flag@8176: 0x2c (KDRHFL, KDRHFF, KDRHFH)
lock@8177: 0x01
cols@8178: 2
col 0[2] @8179: 1
col 1[5] @8182: Frank
注意这里面用到的e(x)amine命令, 参数/r表示显示oracle表中每一行记录, /nc表示第一列为数字类型(1),第二列为字符串类型('Frank')。 从上面的输出可以看出,表中有一行记录(1, 'Frank'). 同时注意到每一行的开始有3个字节的overhead, 分别表示flag, lock, column count, 因此虽然表中第一行数据从offset 8176开始,实际的数据是从offset 8179开始。而且每个column的数据前面是有至少一个字节是用来存储改column占用的字节数,所以虽然'Frank'这个column是从offset 8182开始,实际存储的字符串'Frank'是从offset 8183开始的。
修改数据现在尝试把表中的'Frank'改成'Fraud'.
首先需要确定'Frank'这个字符串在block中的位置(offset) , 通过find命令来找到大概的位置,然后通过dump命令来进一步确认 (其实从上面的examine命令输出已经可以判断出来Frank这个字符串是从offset 8183开始的),
BBED> find /c Frank
File: /u01/app/oracle/oradata/orcl/users01.dbf (4)
Block: 404 Offsets: 8183 to 8191 Dba:0x01000194
------------------------------------------------------------------------
4672616e 6b030645 1e
<32 bytes per line>
BBED> dump /v dba 4, 404 offset 8183 count 64
File: /u01/app/oracle/oradata/orcl/users01.dbf (4)
Block: 404 Offsets: 8183 to 8191 Dba:0x01000194
-------------------------------------------------------
4672616e 6b030645 1e l Frank..E.
<16 bytes per line>
可以确定下来Frank是从offset 8183开始的。那么下面可以通过modify命令来进行数据的修改,将'Frank'修改为'Fraud'...
BBED> modify /c Fraud dba 4, 404 offset 8183
BBED-00215: editing not allowed in BROWSE mode
BBED> set mode edit
MODE Edit
BBED> modify /c Fraud dba 4, 404 offset 8183
File: /u01/app/oracle/oradata/orcl/users01.dbf (4)
Block: 404 Offsets: 8183 to 8191 Dba:0x01000194
------------------------------------------------------------------------
46726175 64030645 1e
<32 bytes per line>
BBED> dump /v dba 4, 404 offset 8183
File: /u01/app/oracle/oradata/orcl/users01.dbf (4)
Block: 404 Offsets: 8183 to 8191 Dba:0x01000194
-------------------------------------------------------
46726175 64030645 1e l Fraud..E.
<16 bytes per line>
通过dump命令可以看出block的内容却是被修改成了Fraud了。那么现在通过sql*plus来查看数据是不是改变了...
SQL> select * from test;
ID NAME
---------- ----------
1 Frank
SQL> alter system flush buffer cache;
alter system flush buffer cache
*
ERROR at line 1:
ORA-02000: missing SHARED_POOL/BUFFER_CACHE/GLOBAL CONTEXT keyword
SQL> alter system flush buffer_cache;
System altered.
SQL> select * from test;
select * from test
*
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 4, block # 404)
ORA-01110: data file 4: '/u01/app/oracle/oradata/orcl/users01.dbf'
在查询表之前,通过alter system flush buffer_cache来清空内存中的数据缓存,第一次得到的数据有可能是内存中的。flush之后再重新查询,发现报了一个错误,说block被破坏了!这是因为通过BBED修改了之后没有进行block checksum的重新计算,造成block表现为corrupted.
可以通过sum命令来更新block的checksum,
BBED> sum dba 4, 404
Check value for File 4, Block 404:
current = 0x61de, required = 0x6ec5
BBED> sum dba 4, 404 apply
Check value for File 4, Block 404:
current = 0x6ec5, required = 0x6ec5
现在再通过sql*plus来查看一下,
SQL> select * from test;
select * from test
*
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 4, block # 404)
ORA-01110: data file 4: '/u01/app/oracle/oradata/orcl/users01.dbf'
SQL> alter system flush buffer_cache;
System altered.
SQL> select * from test;
ID NAME
---------- ----------
1 Fraud
现在看到表TEST的内容被改成了Fraud. 需要注意的是,访问表之前,还需要执行下alter system flush buffer_cache命令。
数据块的恢复(revert)
可以通过revert命令来撤销刚才对数据库块的修改,使之回退到BBED启动时候的那个状态。注意,用UNDO命令会失败,因为已经进行过了block的checksum重计算了。操作如下,
BBED> undo
BBED> modify /x 4672616E6B filename '/u01/app/oracle/oradata/orcl/users01.dbf' block 404. offset 8183.
BBED-00209: invalid number (4672616E6B)
BBED> revert dba 4, 404
All changes made to this block will be rolled back. Proceed? (Y/N) Y
Reverted file '/u01/app/oracle/oradata/orcl/users01.dbf', block 404
现在通过sql*plus查询确认下...
SQL> alter system flush buffer_cache;
System altered.
SQL> select * from test;
ID NAME
---------- ----------
1 Frank
可以看到Name又从'Fraud'变成了'Frank'了。
再来一次
现在尝试往表TEST再插入一条记录(2, 'HelloFrank'),
SQL> insert into test values(2, 'HelloFrank');
1 row created.
SQL> commit;
Commit complete.
SQL> select * from test;
ID NAME
---------- ----------
1 Frank
2 HelloFrank
SQL> alter system flush buffer_cache;
System altered.
SQL> select * from test;
ID NAME
---------- ----------
1 Frank
2 HelloFrank
现在再通过print命令来查看kdbr,发现现在有两条记录了,而且第一条记录的offset还是8176,这个是显然的,因为数据是从block尾部往上插入的,第一条数据位于block的底部,offset并不会因为新数据的插入而改变。
BBED> set dba 4, 404
DBA 0x01000194 (16777620 4,404)
BBED> p kdbr
sb2 kdbr[0] @118 8076
sb2 kdbr[1] @120 8059
BBED> p kdbr[0]
sb2 kdbr[0] @118 8076
BBED> p *kdbr[0]
rowdata[17]
-----------
ub1 rowdata[17] @8176 0x2c
BBED> p /c offset 8176
rowdata[17]
-----------
ub1 rowdata[17] @8176 ,
BBED> dump /v dba 4, 404 offset 8176
File: /u01/app/oracle/oradata/orcl/users01.dbf (4)
Block: 404 Offsets: 8176 to 8191 Dba:0x01000194
-------------------------------------------------------
2c010202 c1020546 72616e6b 0206ec28 l ,...�..Frank..
<16 bytes per line>
BBED>
新插入的第二条记录是从offset 8159开始的...
BBED> dump /v dba 4, 404 offset 8159
File: /u01/app/oracle/oradata/orcl/users01.dbf (4)
Block: 404 Offsets: 8159 to 8191 Dba:0x01000194
-------------------------------------------------------
2c020202 c1030a48 656c6c6f 4672616e l ,...�..HelloFran
6b2c0102 02c10205 4672616e 6b0206ec l k,...�..Frank..
28 l (
<16 bytes per line>
BBED> dump /v dba 4, 404 offset 8166
File: /u01/app/oracle/oradata/orcl/users01.dbf (4)
Block: 404 Offsets: 8166 to 8191 Dba:0x01000194
-------------------------------------------------------
48656c6c 6f467261 6e6b2c01 0202c102 l HelloFrank,...�.
05467261 6e6b0206 ec28 l .Frank..
<16 bytes per line>
BBED>
现在尝试把'HelloFrank'改成'Fraud'试一下...
BBED> modify /c Fraud dba 4, 404 offset 8166
BBED-00215: editing not allowed in BROWSE mode
BBED> set mode edit
MODE Edit
BBED> modify /c Fraud dba 4, 404 offset 8166
Warning: contents of previous BIFILE will be lost. Proceed? (Y/N) Y
File: /u01/app/oracle/oradata/orcl/users01.dbf (4)
Block: 404 Offsets: 8166 to 8191 Dba:0x01000194
------------------------------------------------------------------------
46726175 64467261 6e6b2c01 0202c102 05467261 6e6b0206 ec28
<32 bytes per line>
BBED> dump /v dba 4, 404 offset 8166
File: /u01/app/oracle/oradata/orcl/users01.dbf (4)
Block: 404 Offsets: 8166 to 8191 Dba:0x01000194
-------------------------------------------------------
46726175 64467261 6e6b2c01 0202c102 l FraudFrank,...�.
05467261 6e6b0206 ec28 l .Frank..
<16 bytes per line>
可以看到'Fraud'不会把'HelloFrank'都替换掉,只是'Fraud'这5个字符覆盖了'Hello'这5个字符。
那如果用'I am Frank!'这个字符串替换'HelloFrank'呢? (注意到需要用引号把I am Frank!给括起来)
BBED> modify /c I am Frank! dba 4, 404 offset 8166
BBED-00207: invalid offset specifier (am)
BBED> modify /c "I am Frank!" dba 4, 404 offset 8166
File: /u01/app/oracle/oradata/orcl/users01.dbf (4)
Block: 404 Offsets: 8166 to 8191 Dba:0x01000194
------------------------------------------------------------------------
4920616d 20467261 6e6b2101 0202c102 05467261 6e6b0206 ec28
<32 bytes per line>
BBED> dump /v dba 4, 404 offset 8166
File: /u01/app/oracle/oradata/orcl/users01.dbf (4)
Block: 404 Offsets: 8166 to 8191 Dba:0x01000194
-------------------------------------------------------
4920616d 20467261 6e6b2101 0202c102 l I am Frank!...�.
05467261 6e6b0206 ec28 l .Frank..
<16 bytes per line>
BBED>
从dump结果来看,数据块被修改了,那么用SQL来访问表的结果是什么呢?
SQL> alter system flush buffer_cache;
System altered.
SQL> select * from test;
ID NAME
---------- ----------
2 I am Frank
这个结果很奇怪,第一行数据(1, ’Frank')丢失了! 而且注意Name为(I am Frank), 少了一个感叹号(!).
因为Name这一列的数据类型是varchar2(10), 最大是10个字符,而我通过BBED写入了11个字符,把下一行数据(也就是表中的第一行数据(1, 'Frank'))的开始部分的标志位的信息给覆盖掉了!!因此SQL语句不能正确读出第一行数据了,造成了数据的丢失!!
从这个案例也可以看出用BBED一定要小心再小心, 很容易就可以把一个block给破坏了!