第 6 部分 - 游标抽象这一部分应该比上一部分更简短。我们只需稍作重构以便更轻松地开始实现B树(B-Tree)。我们将添加一个 Cursor 对象它代表表中的某个位置。您可以使用游标完成以下操作在表的开头创建一个游标在表的末尾创建一个游标访问游标指向的行将游标前进到下一行这些就是我们接下来要实现的功能。稍后我们还希望能够删除游标指向的行修改游标指向的行根据给定的 ID 搜索表并创建一个指向该行的游标言归正传以下是光标类型typedef struct { Table* table; uint32_t row_num; bool end_of_table; // 表示位于最后一个元素之后一个位置 } Cursor;鉴于我们当前的表数据结构要定位表中的某个位置只需知道行号即可。游标还包含对其所属表的引用因此我们的游标函数只需将游标作为参数即可。最后它有一个名为 end_of_table 的布尔变量。这样我们就可以表示表尾之后的位置这可能是我们想要插入一行的地方。table_start() 和 table_end() 会创建新的游标Cursor* table_start(Table* table) { Cursor* cursor malloc(sizeof(Cursor)); cursor-table table; cursor-row_num 0; cursor-end_of_table (table-num_rows 0); return cursor; } Cursor* table_end(Table* table) { Cursor* cursor malloc(sizeof(Cursor)); cursor-table table; cursor-row_num table-num_rows; cursor-end_of_table true; return cursor; }我们的 row_slot() 函数将改为 cursor_value()该函数返回指向游标所指位置的指针-void* row_slot(Table* table, uint32_t row_num) { void* cursor_value(Cursor* cursor) { uint32_t row_num cursor-row_num; uint32_t page_num row_num / ROWS_PER_PAGE; - void* page get_page(table-pager, page_num); void* page get_page(cursor-table-pager, page_num); uint32_t row_offset row_num % ROWS_PER_PAGE; uint32_t byte_offset row_offset * ROW_SIZE; return page byte_offset; }在当前的表结构中移动游标只需递增行号即可。但在 B树(B-Tree)中这会稍微复杂一些。void cursor_advance(Cursor* cursor) { cursor-row_num 1; if (cursor-row_num cursor-table-num_rows) { cursor-end_of_table true; } }最后我们可以修改“虚拟机”方法使其使用游标抽象。在插入一行时我们会在表尾打开一个游标向该游标位置写入数据然后关闭游标。Row* row_to_insert (statement-row_to_insert); Cursor* cursor table_end(table); - serialize_row(row_to_insert, row_slot(table, table-num_rows)); serialize_row(row_to_insert, cursor_value(cursor)); table-num_rows 1; free(cursor); return EXECUTE_SUCCESS; }在选择表中的所有行时我们会从表的开头打开一个游标打印当前行然后将游标移至下一行。重复此过程直到到达表的末尾。ExecuteResult execute_select(Statement* statement, Table* table) { Cursor* cursor table_start(table); Row row; - for (uint32_t i 0; i table-num_rows; i) { - deserialize_row(row_slot(table, i), row); while (!(cursor-end_of_table)) { deserialize_row(cursor_value(cursor), row); print_row(row); cursor_advance(cursor); } free(cursor); return EXECUTE_SUCCESS; }好就这样正如我所说这是一次较小的重构应该能帮助我们在将表数据结构重写为 B-树时更顺畅。execute_select() 和 execute_insert() 完全可以通过游标与表进行交互而无需对表的存储方式做任何假设。以下是这一部分的完整 diff -78,6 78,13 struct { } Table; typedef struct { Table* table; uint32_t row_num; bool end_of_table; // Indicates a position one past the last element } Cursor; void print_row(Row* row) { printf((%d, %s, %s)\n, row-id, row-username, row-email); } -126,12 133,38 void* get_page(Pager* pager, uint32_t page_num) { return pager-pages[page_num]; } -void* row_slot(Table* table, uint32_t row_num) { - uint32_t page_num row_num / ROWS_PER_PAGE; - void *page get_page(table-pager, page_num); - uint32_t row_offset row_num % ROWS_PER_PAGE; - uint32_t byte_offset row_offset * ROW_SIZE; - return page byte_offset; Cursor* table_start(Table* table) { Cursor* cursor malloc(sizeof(Cursor)); cursor-table table; cursor-row_num 0; cursor-end_of_table (table-num_rows 0); return cursor; } Cursor* table_end(Table* table) { Cursor* cursor malloc(sizeof(Cursor)); cursor-table table; cursor-row_num table-num_rows; cursor-end_of_table true; return cursor; } void* cursor_value(Cursor* cursor) { uint32_t row_num cursor-row_num; uint32_t page_num row_num / ROWS_PER_PAGE; void *page get_page(cursor-table-pager, page_num); uint32_t row_offset row_num % ROWS_PER_PAGE; uint32_t byte_offset row_offset * ROW_SIZE; return page byte_offset; } void cursor_advance(Cursor* cursor) { cursor-row_num 1; if (cursor-row_num cursor-table-num_rows) { cursor-end_of_table true; } } Pager* pager_open(const char* filename) { -327,19 360,28 ExecuteResult execute_insert(Statement* statement, Table* table) { } Row* row_to_insert (statement-row_to_insert); Cursor* cursor table_end(table); - serialize_row(row_to_insert, row_slot(table, table-num_rows)); serialize_row(row_to_insert, cursor_value(cursor)); table-num_rows 1; free(cursor); return EXECUTE_SUCCESS; } ExecuteResult execute_select(Statement* statement, Table* table) { Cursor* cursor table_start(table); Row row; - for (uint32_t i 0; i table-num_rows; i) { - deserialize_row(row_slot(table, i), row); while (!(cursor-end_of_table)) { deserialize_row(cursor_value(cursor), row); print_row(row); cursor_advance(cursor); } free(cursor); return EXECUTE_SUCCESS; }