【SimpleDB】Part 3 - An In-Memory, Append-Only, Single-Table Database

本文介绍了一个针对教学或实验目的的轻量级数据库ToyDB,它支持插入和查询操作,数据存储在内存中,仅限单表且硬编码。文章详细讲解了如何解析SQL语句、存储结构设计(如serialize_row与deserialize_row),以及如何执行insert和select操作。特别强调了页面管理和行存储在内存页中的处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概述

论这个DB为啥是个toyDB

有以下限制:

  1. 只支持两个操作:插入一行/打印全表
  2. 表只放在内存中
  3. 支持单个、硬编码的表
    在这里插入图片描述
    支持的语法:

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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值