記錄頭(record head)格式: typedef struct rh0 { uint32_t recordlen; uint8_t VLD; //直接決定整個record head大小 uint8_t unknown0; uint16_t rhscnwrapper; uint32_t rhsc
記錄頭(record head)格式:
typedef struct rh0 { uint32_t recordlen; uint8_t VLD; //直接決定整個record head大小 uint8_t unknown0; uint16_t rhscnwrapper; uint32_t rhscnbase; uint16_t subscn; }Redo_rh0; typedef struct rh1 { uint8_t unknown1[10]; }Redo_rh24; //VLD = 0x01, 總大小24byte typedef struct rh5 { uint8_t unknown0[50]; uint32_t rhscntime; }Redo_rh68; //VLD = 0x05, 總大小68byteRedo_rh0
記錄頭是變長的,它的長度由VLD決定,而oracle並未明確說明它們的對應關係,幸好大多數記錄頭的VLD都是1。
記錄頭之後緊接著就是一組change項,一條記錄可能對應於我們的一個操作(比如插入),一個操作會被劃分為一組change(oracle內部的一組操作),每個change對應一個操作碼(opcode)。每個change又分為change頭,change向量表,和change數據。change頭的格式:
typedef struct oc { uint8_t layer; uint8_t code; }Redo_oc; typedef struct ch { //24 Redo_oc opcode; uint16_t CLS; //block class uint16_t AFN; //數據所在文件號 uint16_t OBJ0; uint32_t DBA; //Data block address, 前10bit文件號, 後22bit塊號 Redo_scn SCN; uint8_t SEQ; //change sequence uint8_t TYP; //數據類型 uint16_t OBJ1; }Redo_ch;Redo_ch
change頭的長度是固定的(終於有點固定的東西了),第一個元素即為本change的操作碼(opcode),opcode通常由5.2、5.1開始,5.4結束,中間出現的才是我們所做的真實操作(比如11.xx、19.1等)。
change頭中的OBJ0和OBJ1組合起來得到obj:
#define getobj(obj0, obj1) (((uint32_t)(obj1))|(((uint32_t)(obj0))<<16))
obj為本次操作的表id,可以根據obj從數據字典查到表名、用戶、欄位等信息用來構建SQL語句。
change頭之後是一個change向量表,change向量表總長度按4位元組對齊,然後其內部每2位元組一段,第1個2位元組表示向量表總長度(包括自己的2位元組)(未進行4位元組對齊之前的總長度)。總長度之後剩餘的長度(未對齊的總長度減2),劃分為每2位元組一段,每段表示一個元素的長度,這些元素會按順序寫在向量表之後,向量表中的長度為元素的真實長度,向量表之後的元素將按照4位元組對齊方式寫入,其超過真實長度的部分應被忽略。
向量表是一個變長的數組,每個元素都是一個unsigned short型數據,故應每次讀取2位元組進行解析,同時用指針從向量表之後開始,按照向量表長度獲取對應元素。獲取到的change元素,即為本次change操作的對象,例如本次change為insert,則change元素就是insert的目標欄位及內容。
獲取到change元素之後,就可以根據數據字典,將當前取得的所有信息綜合起來,構建SQL語句。
下麵詳細研究change內容。