简单数据库实现——Part6 - 游标抽象
这部分只是做了一些重构,使更容易实现B树。
我们将要新增一个表示表中某个位置的游标(cursor)对象。你可以对它进行如下操作:
- 在表开头创建一个游标
- 在表末尾创建一个游标
- 访问游标指向的行
- 游标移动到下一行
之后我们还要实现的:
- 删除游标指向的行
- 修改游标指向的行
- 搜索给点ID,创建一个指向该ID的游标
下面是游标结构体:
+typedef struct {
+ Table* table;
+ uint32_t row_num;
+ bool end_of_table; // Indicates a position one past the last element
+} Cursor;
我们目前的表(table)数据结构,只需要知道行号就可以确定位置,所以游标数据结构中含有行号(row_num
)来确定位置。
游标还有一个指向所属表的指针table
。
最后还有一个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树中要更复杂)。
+void cursor_advance(Cursor* cursor) {
+ cursor->row_num += 1;
+ if (cursor->row_num >= cursor->table->num_rows) {
+ cursor->end_of_table = true;
+ }
+}
最后我们可以更改虚拟机(virtual machine)方法来使用游标。当插入一行时,我们在表的末尾打开一个游标,写入游标位置,然后关闭游标。
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;}
在选择(select)所有行时,我们在表的开始处打开一个游标,打印该行,然后将游标移动到下一行。重复直至表的尽头。
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树做准备。
修改的地方
@@ -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;}