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);
}