sqlite_masterの中身を読み込む(1)
うまくいくときと、いかないときがあるのですが、sqlite_masterに書かれた内容を標準出力するプログラムを書きました(また直ったら更新すると思います)。現在のところは,リーフページのみ読み込めるのみです。まず読み込むためのsqlite3を使って読み込むファイルを作ります。
$ sqlite3 test.db
SQLite version 3.7.7.1 2011-06-28 17:39:05
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> CREATE TABLE Products(name text, price integer, locale text);
sqlite> CREATE TABLE People(name text, age integer);
sqlite> CREATE TABLE Tbl(one text, two text, three integer, four integer);
sqlite> .exit
次にプログラムを動かします。"./a.out <データベースファイル> <インデックス>"で0からはじまるインデックスに定義されたレコードをtype, name, tbl_name, rootpage, sqlの順に表示します。
./a.out test.db 1
table|People|People|3|CREATE TABLE People(name text, age integer)
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdlib.h> #include <sys/mman.h> #include <stdio.h> #include <fcntl.h> #include <string.h> #include <assert.h> #include <stdint.h> #include <stdlib.h> #define get2byte(x) ((x)[0] << 8 |(x)[1]) #define get4byte(x) (int)(((x)[3] << 24) | ((x)[2] << 16) | ((x)[1] << 8) | (x)[0]) #define findCell(buf, mask, cellOffset, idx) (buf + (mask & get2byte(&buf[cellOffset]+idx*2))) struct MasterRecord { char type[80]; char name[80]; char table[80]; uint32_t rootpage; char sql[160]; }; void rec_init(struct MasterRecord *rec) { memset(rec->type, 0, 80); memset(rec->table, 0, 80); memset(rec->table, 0, 80); memset(rec->sql, 0, 160); } unsigned getVarint(uint8_t *pCell,uint64_t *v) { *v = *pCell++; if(*pCell < (uint8_t)0x80) return 1; *v <<= 7; if(*pCell&0x80) *v &= 0x7f; int i = 1; for(; (i < 8) && (*pCell&0x80) != 0; i++) { *v |= (*pCell++ & 0x7f); *v <<= 7; } if(i > 1) { if(i == 8) *v <<= 1; *v |= *pCell; i++; } return i; } uint32_t getfieldsize(uint32_t serial_type) { if(serial_type >= 12) { return (serial_type-12)/2; } else { static size[] = {0,1,2,3,4,6,8,8,0,0,0,0}; return size[serial_type]; } } int main(int argc, char* argv[]) { int fd = open(argv[1], O_RDONLY); if(fd < 0) { fprintf(stderr, "open failed\n"); exit(1); } struct stat st; if(fstat(fd, &st) < 0) { fprintf(stderr, "stat failed\n"); exit(1); } unsigned char* buf; buf = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); close(fd); unsigned hdroffset = 100; unsigned cellOffset = hdroffset + 8; unsigned int pageSize = (unsigned int)(buf[16]<< 8| buf[17] << 16); unsigned mask = pageSize-1; struct MasterRecord rec; rec_init(&rec); uint8_t* pCell = findCell(buf, mask, cellOffset, atoi(argv[2])); uint64_t nPayload, nKey; unsigned n; n = getVarint(pCell, &nPayload); n += getVarint(&pCell[n], &nKey); assert(nPayload < 160); uint8_t Payload[160]; memset(Payload, 0, 160*sizeof(uint8_t)); memcpy(Payload, &pCell[n], nPayload); uint32_t serial_type; int serial_space = 0; uint32_t offset; uint32_t fsize = 0; serial_space += getVarint(Payload,(uint64_t*)&serial_type); offset = serial_type; serial_space += getVarint(&Payload[serial_space],(uint64_t*)&serial_type); fsize = getfieldsize(serial_type); memcpy(rec.type, Payload + offset, fsize); offset += fsize; serial_space += getVarint(&Payload[serial_space],(uint64_t*)&serial_type); fsize = getfieldsize(serial_type); memcpy(rec.name, Payload + offset, fsize); offset += fsize; serial_space += getVarint(&Payload[serial_space],(uint64_t*)&serial_type); fsize = getfieldsize(serial_type); memcpy(rec.table, Payload + offset, fsize); offset += fsize; rec.rootpage = Payload[offset]; serial_space += getVarint(&Payload[serial_space],(uint64_t*)&serial_type); offset += serial_type; serial_space += getVarint(&Payload[serial_space],(uint64_t*)&serial_type); fsize = getfieldsize(serial_type); memcpy(rec.sql, Payload + offset, fsize); offset += fsize; assert(nPayload == offset); printf("%s|%s|%s|%u|%s\n", rec.type, rec.name, rec.table, rec.rootpage, rec.sql); munmap(buf, st.st_size); exit(0); }