一、概述
论这个DB为啥是个toyDB
有以下限制:
- 只支持两个操作:插入一行/打印全表
- 表只放在内存中
- 支持单个、硬编码的表
支持的语法:
insert 1 cstack foo@bar.com
insert 字段1 字段2 字段3
因此要对我们的“ttttoy compiler”补充一些内容:
对于输入的statement,进行进一步的解析(phrase)
sscanf读取格式化输入:
int sscanf(const char *str, const char *format, ...)
str为读取输入的源,format为格式化输入串。
返回值:如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回 EOF。
//读取输入,解析后存储到statement中
PrepareResult prepare_statement(InputBuffer* inputBuffer,Statement* statement){
if(strncmp(inputBuffer->buffer,"insert",6)==0){
statement->type=STATEMENT_INSERT;
//input %d need sing & !!!(so easy to ignore)
int args_num= sscanf(inputBuffer->buffer,"insert %d %s %s",&(statement->row.id),statement->row.email,statement->row.username);
if(args_num<3){
return PREPARE_SYNTAX_ERROR;
}
return PREPARE_SUCCESS;
}
if(strcmp(inputBuffer->buffer,"select")==0){
statement->type=STATEMENT_SELECT;
return PREPARE_SUCCESS;
}
return PREPARE_UNRECOGNIZED_STATEMENT;
}
读取insert的数据后,要将数据存到磁盘上了。
SQLlite用的是B树存储,可以快速查找和删除。一页存储了多个数据。这个菜鸟DB就简化一下,一页用一个数组代替。
- Store rows in blocks of memory called pages
- Each page stores as many rows as it can fit
也就是说我们将rows存在pages中,而rows包含了设计好的三个字段。因此Rows的存储结构需要设计偏移量
将rows数据按行存储的过程称为:serialize_row,
逆过程为deserialize_row
设计以下两个函数:
void serialize_row(Row source, void destination)**
void deserialize_row(void source, Row destination) **
设计Table属性,来展示Table信息
- num_rows 表示的是Table中记录的数量
- pages数组 数组中的每一个元素为一页。
PAGE_SIZE定义为4kb,和通常数据库的页面大小一样
const uint32_t PAGE_SIZE = 4096;
#define TABLE_MAX_PAGES 100
const uint32_t ROWS_PER_PAGE = PAGE_SIZE / ROW_SIZE;
const uint32_t TABLE_MAX_ROWS = ROWS_PER_PAGE * TABLE_MAX_PAGES;
typedef struct {
uint32_t num_rows;
void* pages[TABLE_MAX_PAGES];
} Table;
在这个菜鸟DB中,设定我们能保存的最大页数为100,但是在实际使用中,虽然在内存中有保存的页数的限制,但数据库的大小仅由文件大小限定。
另外,行是不能超出页面边界的。因为每个页的存储并不是连续的,超出边界会让读取速度减慢(因为又要去找下一个页)
从table中读取row:
void* row_slot(Table* table, uint32_t row_num)
table中存了row的数量和page本身。读取的是row应该存放的地址。 一通指针直接操作内存猛如虎~
void* read_slot(Table* table,uint32_t row_num){
int page_id=row_num/ROW_NUM_PER_PAGE;
void* page=table->page[page_id];
if(page==NULL){
//insert
page=malloc(PAGE_SIZE);
table->page[page_id]=page;
}
uint32_t row_offset=row_num%ROW_NUM_PER_PAGE;
uint32_t offset=row_offset*ROW_SIZE;
return offset+page;
}
然后修改执行器execute和main
执行器注意封装:
ExecuteResult execute_insert(Statement* statement,Table* table){
if(table->row_num>=TABLE_MAX_ROWS){
return EXECUTE_TABLE_FULL;
}
Row *row=&(statement->row);
serialize_row(row, read_slot(table,table->row_num));
table->row_num++;
return EXECUTE_SUCCESS;
}
void print_row(Row* row){
printf("(%d %s %s)\n",row->id,row->username,row->email);
}
ExecuteResult execute_select_all(Statement* statement,Table* table){
uint32_t all_num=table->row_num;
if(all_num<0) return EXECUTE_ERROR;
Row row;
for(uint32_t i=0;i<all_num;i++){
deserialize_row(read_slot(table,i),&row);
print_row(&row);
}
return EXECUTE_SUCCESS;
}
ExecuteResult execute_statement(Statement* statement,Table* table){
switch (statement->type) {
case(STATEMENT_INSERT):
printf("insert successfully!\n");
return execute_insert(statement,table);
case (STATEMENT_SELECT):
printf("select successfully!\n");
return execute_select_all(statement,table);
case(STATEMENT_DELETE):
printf("do delete\n");
break;
case(STATEMENT_UPDATE):
printf("do update\n");
break;
}
}