暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle中不一样的更新单行数据的方式,更新单行数据(Ⅰ)


写这篇文章主要是有助于加深了解数据块的结构,行数据是怎么在块中进行存储的,通过使用bbed和非bbed的方式来修改块

首先找到这行数据存储的位置

set lines 222 col c2_hex for a50 select c2,dump(c2,16) c2_hex, dbms_rowid.rowid_relative_fno(rowid) file#, dbms_rowid.rowid_block_number(rowid) block#, dbms_rowid.rowid_row_number(rowid) row# from test_char where c2=' zhang san '; C2 C2_HEX FILE# BLOCK# ROW# -------------------- -------------------------------------------------- ---------- ---------- ---------- zhang san Typ=1 Len=11: 20,7a,68,61,6e,67,20,73,61,6e,20 2 150 0

块结构

struct kcbh, 20 bytes @0 ub1 type_kcbh @0 ub1 frmt_kcbh @1 ub2 wrp2_kcbh @2 ub4 rdba_kcbh @4 ub4 bas_kcbh @8 ub2 wrp_kcbh @12 ub1 seq_kcbh @14 ub1 flg_kcbh @15 ub2 chkval_kcbh @16 ub2 spare3_kcbh @18 struct ktbbh, 72 bytes @20 ub1 ktbbhtyp @20 union ktbbhsid, 4 bytes @24 struct ktbbhcsc, 8 bytes @28 sb2 ktbbhict @36 ub1 ktbbhflg @38 ub1 ktbbhfsl @39 ub4 ktbbhfnx @40 struct ktbbhitl[2], 48 bytes @44 struct kdbh, 14 bytes @100 ub1 kdbhflag @100 sb1 kdbhntab @101 sb2 kdbhnrow @102 sb2 kdbhfrre @104 sb2 kdbhfsbo @106 sb2 kdbhfseo @108 sb2 kdbhavsp @110 sb2 kdbhtosp @112 struct kdbt[1], 4 bytes @114 sb2 kdbtoffs @114 sb2 kdbtnrow @116 sb2 kdbr[2] @118 ub1 freespace[7999] @122 ub1 rowdata[67] @8121 ub4 tailchk @8188

修改数据

有bbed情况下修改

1.找到数据块file# 2 ,block# 150

2.找到row# 0的偏移量kdbr[0]

3.找到col1的偏移量

4.修改数据

# 设置当前数据块,2号文件150号块 BBED> set dba 2,150 DBA 0x00800096 (8388758 2,150) # 设置第0行数据位置 BBED> p *kdbr[0] rowdata[31] ----------- ub1 rowdata[31] @8152 0x2c # 查看第0行数据,可以看到col1的数据和dump出的内容一致 BBED> x /r rowdata[31] @8152 ----------- flag@8152: 0x2c (KDRHFL, KDRHFF, KDRHFH) lock@8153: 0x01 cols@8154: 2 col 0[20] @8155: 0x20 0x7a 0x68 0x61 0x6e 0x67 0x20 0x73 0x61 0x6e 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 col 1[11] @8176: 0x20 0x7a 0x68 0x61 0x6e 0x67 0x20 0x73 0x61 0x6e 0x20 # 可以格式化内容查看 col data_type for a40 select '/r'||listagg( case data_type when 'NUMBER' then 'n' when 'DATE' then 't' else 'c' end, '' ) within group (order by column_id) as data_type from dba_tab_columns where table_name='TEST_CHAR'; DATA_TYPE ---------------------------------------- /rcc BBED> x /rcc rowdata[31] @8152 ----------- flag@8152: 0x2c (KDRHFL, KDRHFF, KDRHFH) lock@8153: 0x01 cols@8154: 2 col 0[20] @8155: zhang san col 1[11] @8176: zhang san # 现在我们修改第二个字段内容,将` zhang san `,修改为`li si`,张冠李戴一下,修改的数据不要超过原有数据长度 # 设置第二个字段的数据位置 BBED> set offset 8176 OFFSET 8176 # dump查看一下数据是否正确,第一个字节为数据长度0x0b为11个字节 BBED> d /v count 12 File: /oradata/ORCL/bbed.dbf (2) Block: 150 Offsets: 8176 to 8187 Dba:0x00800096 ------------------------------------------------------- 0b207a68 616e6720 73616e20 l . zhang san <16 bytes per line> # 现在让我们获取下li si的16进制 SQL> select dump('li si',16) from dual; DUMP('LISI',16) ---------------------------- Typ=96 Len=5: 6c,69,20,73,69 # 修改字节太多报错,把它拆开修改吧 BBED> m /x 0x056c69207369 BBED-00209: invalid number (0x056c69207369) # 修改前3个字节 BBED> m /x 0x056c69 Warning: contents of previous BIFILE will be lost. Proceed? (Y/N) y File: /oradata/ORCL/bbed.dbf (2) Block: 150 Offsets: 8176 to 8186 Dba:0x00800096 ------------------------------------------------------------------------ 056c6968 616e6720 73616e <32 bytes per line> # 修改后三个字节 BBED> m /x 0x207369 offset 8179 File: /oradata/ORCL/bbed.dbf (2) Block: 150 Offsets: 8179 to 8189 Dba:0x00800096 ------------------------------------------------------------------------ 20736967 2073616e 200106 # 查看修改是否正确 BBED> d /v count 11 offset 8176 File: /oradata/ORCL/bbed.dbf (2) Block: 150 Offsets: 8176 to 8186 Dba:0x00800096 ------------------------------------------------------- 056c6920 73696720 73616e l .li sig san <16 bytes per line> # 应用修改 BBED> sum apply Check value for File 2, Block 150: current = 0xb9df, required = 0xb9df # 现在让我们在数据库查询下下修改是否正确 SQL> alter system flush buffer_cache; System altered. SQL> select * from test_char; C1 C2 -------------------- -------------------- zhang san li si 南昌 南昌

无bbed情况下修改

散修虽比不上名门正派,但也各有奇招,现在将li si重新修改为zhang san

1.找到kdbr的开始位置,开始位置为44 + ITC*24 + 8 + 14 + 4=70 + ITC*24,ITC记录在ktbbh.ktbbhict 也就是offset 36的位置,2个字节表示

2.找到有多少行数据,kdbh.kdbhnrow 位置44 + ITC*24 + 8 + 2 = 54 + ITC*24,2个字节表示

3.找到行数据存放位置,位置为kdbr 记录的位置+以下偏移量

对于ASSM:20+48+8+(ITC-1) *24 = 52 + ITC * 24

对于MSSM:20+48+ (ITC-1) *24 = 44 + ITC * 24

4.修改数据

5.修改checksum值

# 查看ITC数量,看到0x021f,怎么对不上,然后我去看了其他块,像3个ITC的显示0x0300,4个ITC的显示0x0400 # 这里应该是这个块是被复用的,所以有残留数据,这里还是2个事务槽 [oracle@cesdb3 ~]$ od -A x -t x1z -v -j $((8192 * 150 + 36)) -N 2 /oradata/ORCL/bbed.dbf 12c024 02 1f >..< # 那么经过计算kdbr的开始位置是118=70 + ITC*24,那么多少行数据呢 行数是记录在kdbh.kdbhnrow,位置44 + ITC*24 + 8 + 2 = 54 + ITC*24 行数记录在kdbh.kdbhnrow,offset 102,2个字节表示 #这里是2行数据,0x0200 [oracle@cesdb3 ~]$ od -A x -t x1z -v -j $((8192 * 150 + 102)) -N 2 /oradata/ORCL/bbed.dbf 12c066 02 00 >..< #那么数据行的存放字典我们就知道了 #从offset 118 - 121 [oracle@cesdb3 ~]$ od -A x -t x1z -v -j $((8192 * 150 + 118)) -N 4 /oradata/ORCL/bbed.dbf 12c076 74 1f 55 1f >t.U.< >....< [oracle@cesdb3 ~]$ echo "74 1f" | awk '{printf "%d\n", strtonum("0x" $2 $1)}' 8052 # 需要在这基础上+100偏移量=52 + ITC * 24 [oracle@cesdb3 ~]$ od -A x -t x1z -v -j $((8192 * 150 + 8152)) -N 40 /oradata/ORCL/bbed.dbf 12dfd8 2c 01 02 14 20 7a 68 61 6e 67 20 73 61 6e 20 20 >,... zhang san < 12dfe8 20 20 20 20 20 20 20 20 05 6c 69 20 73 69 67 20 > .li sig < 12dff8 73 61 6e 20 01 06 ca e9 >san ....< # 找到第二个字段的位置 8152 + 3 + 1 + 20 = 8176 flag lock cols collen data 1 1 1 1 [oracle@cesdb3 ~]$ od -A x -t x1z -v -j $((8192 * 150 + 8176)) -N 20 /oradata/ORCL/bbed.dbf 12dff0 05 6c 69 20 73 69 67 20 73 61 6e 20 01 06 ca e9 >.li sig san ....< 12e000 06 a2 00 00 >....< #终于找到了,现在可以更新他了 #注意,修改的数据不要超过原有数据长度,不然会覆盖后面的数据导致块异常,这里我是修改回原有的数据 [oracle@cesdb3 ~]$ echo -n " zhang san " | xxd -p 207a68616e672073616e20 #前面加上字段长度0b [oracle@cesdb3 ~]$ echo -n "0b207a68616e672073616e20" | xxd -r -p | dd of=/oradata/ORCL/bbed.dbf bs=1 seek=$((8192 * 150 + 8176)) conv=notrunc 12+0 records in 12+0 records out 12 bytes (12 B) copied, 5.883e-05 s, 204 kB/s [oracle@cesdb3 ~]$ od -A x -t x1z -v -j $((8192 * 150 + 8176)) -N 20 /oradata/ORCL/bbed.dbf 12dff0 0b 20 7a 68 61 6e 67 20 73 61 6e 20 01 06 ca e9 >. zhang san ....< 12e000 06 a2 00 00 >....< # 重新计算checksum [oracle@cesdb3 ~]$ python kcbhxoro.py /oradata/ORCL/bbed.dbf 150 checksum int = 47824 checksum hex = 0xbad0 [oracle@cesdb3 ~]$ od -A x -t x1z -v -j $((8192 * 150 + 16)) -N 2 /oradata/ORCL/bbed.dbf 12c010 df b9 >..< 12c012 [oracle@cesdb3 ~]$ echo -n "d0ba" | xxd -r -p | dd of=/oradata/ORCL/bbed.dbf bs=1 seek=$((8192 * 150 + 16)) conv=notrunc 2+0 records in 2+0 records out 2 bytes (2 B) copied, 4.857e-05 s, 41.2 kB/s [oracle@cesdb3 ~]$ od -A x -t x1z -v -j $((8192 * 150 + 16)) -N 2 /oradata/ORCL/bbed.dbf 12c010 d0 ba >..< 12c012 # 现在让我们在数据库查询下下修改是否正确 SQL> alter system flush buffer_cache; System altered. SQL> select * from test_char; C1 C2 -------------------- -------------------- zhang san zhang san 南昌 南昌

附录

kcbhxoro.py这个计算checksum的脚本网上找的忘了作者了,深感抱歉

from struct import Struct import sys, binascii ushort = Struct("H") ulong = Struct("Q") file_name = sys.argv[1] block_no = int(sys.argv[2]) block_size = 8192 dbf = open(file_name, "rb") dbf.seek(block_size*block_no) block = dbf.read(16) block += b"\x00\x00" dbf.seek(block_size*block_no + 18) block += dbf.read(block_size - 18) dbf.close() checksum_value = 0 for i in range(int(block_size/8)): checksum_value = checksum_value ^ ulong.unpack(block[i*8:i*8+8])[0] tmp = checksum_value >> 32 checksum_value = checksum_value ^ tmp tmp = checksum_value >> 16 checksum_value = checksum_value ^ tmp final_checksum = ushort.unpack(ulong.pack(checksum_value)[0:2])[0] print("checksum int = " + str(final_checksum)) print("checksum hex = " + str(hex(final_checksum)))
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论