Simple Database Implementation - Part12 - Scan Multilevel B-Tree

Simple Database Implementation - Part12 - Scan Multilevel B-Tree ...
Simple Database Implementation - Part12 - Scan Multilevel B-Tree

We now support the construction of a multilevel B-tree, but this breaks the select statement.Below is a test case where he inserts 15 rows and tries to print them.

+ it 'prints all rows in a multi-level tree' do + script = [] + (1..15).each do |i| + script << "insert #{i} user#{i} person#{i}@example.com" + end + script << "select" + script << ".exit" + result = run_script(script) + + expect(result[15...result.length]).to match_array([ + "db > (1, user1, [email protected])", + "(2, user2, [email protected])", + "(3, user3, [email protected])", + "(4, user4, [email protected])", + "(5, user5, [email protected])", + "(6, user6, [email protected])", + "(7, user7, [email protected])", + "(8, user8, [email protected])", + "(9, user9, [email protected])", + "(10, user10, [email protected])", + "(11, user11, [email protected])", + "(12, user12, [email protected])", + "(13, user13, [email protected])", + "(14, user14, [email protected])", + "(15, user15, [email protected])", + "Executed.", "db > ", + ]) + end

But when we run, it actually happens:

db > select (2, user1, [email protected]) Executed.

It's strange that only one line was printed, and that line was damaged.This is because execute_select() starts at the beginning of the table, and now table_start() returns element 0 of the root node.However, the root node is now an internal node and does not contain any row data.The data we print is data left over when the root node or a leaf node is present.Execute_select() should actually return element 0 of the leftmost leaf node.

So let's delete the old part first:

-Cursor* table_start(Table* table) { - Cursor* cursor = malloc(sizeof(Cursor)); - cursor->table = table; - cursor->page_num = table->root_page_num; - cursor->cell_num = 0; - - void* root_node = get_page(table->pager, table->root_page_num); - uint32_t num_cells = *leaf_node_num_cells(root_node); - cursor->end_of_table = (num_cells == 0); - - return cursor; -}

Then add a new search to find the minimum key.If key0 does not exist, it returns the location of the smallest id.

+Cursor* table_start(Table* table) { + Cursor* cursor = table_find(table, 0); + + void* node = get_page(table->pager, cursor->page_num); + uint32_t num_cells = *leaf_node_num_cells(node); + cursor->end_of_table = (num_cells == 0); + + return cursor; +}

With the above changes, it still prints row data in only one node.

db > select (1, user1, [email protected]) (2, user2, [email protected]) (3, user3, [email protected]) (4, user4, [email protected]) (5, user5, [email protected]) (6, user6, [email protected]) (7, user7, [email protected]) Executed. db >

There are 15 elements, and our B-tree consists of an internal node and two leaf nodes, like this:

In order to scan all tables, we need to jump to the second leaf node at the end of the first leaf node.To do this, we will save a new field called next_leaf at the head of the leaf node, which will hold the page number of the sibling node of the leaf node on the right.The next_leaf value of the rightmost leaf node is 0, indicating that there are no sibling nodes.

Update Header:

const uint32_t LEAF_NODE_NUM_CELLS_SIZE = sizeof(uint32_t); const uint32_t LEAF_NODE_NUM_CELLS_OFFSET = COMMON_NODE_HEADER_SIZE; -const uint32_t LEAF_NODE_HEADER_SIZE = - COMMON_NODE_HEADER_SIZE + LEAF_NODE_NUM_CELLS_SIZE; +const uint32_t LEAF_NODE_NEXT_LEAF_SIZE = sizeof(uint32_t); +const uint32_t LEAF_NODE_NEXT_LEAF_OFFSET = + LEAF_NODE_NUM_CELLS_OFFSET + LEAF_NODE_NUM_CELLS_SIZE; +const uint32_t LEAF_NODE_HEADER_SIZE = COMMON_NODE_HEADER_SIZE + + LEAF_NODE_NUM_CELLS_SIZE + + LEAF_NODE_NEXT_LEAF_SIZE;

Add a way to access the new field:

+uint32_t* leaf_node_next_leaf(void* node) { + return node + LEAF_NODE_NEXT_LEAF_OFFSET; +}

When initializing a new leaf node, next_leaf is set to 0 by default.

@@ -322,6 +330,7 @@ void initialize_leaf_node(void* node) { set_node_type(node, NODE_LEAF); set_node_root(node, false); *leaf_node_num_cells(node) = 0; + *leaf_node_next_leaf(node) = 0; // 0 represents no sibling }

When we split a leaf node, we update the peer pointer.Brothers of old leaf nodes become brothers of new leaf nodes, and brothers of new leaf nodes become brothers of old leaf nodes.

@@ -659,6 +671,8 @@ void leaf_node_split_and_insert(Cursor* cursor, uint32_t key, Row* value) { uint32_t new_page_num = get_unused_page_num(cursor->table->pager); void* new_node = get_page(cursor->table->pager, new_page_num); initialize_leaf_node(new_node); + *leaf_node_next_leaf(new_node) = *leaf_node_next_leaf(old_node); + *leaf_node_next_leaf(old_node) = new_page_num;

Several constants need to be updated:

it 'prints constants' do script = [ ".constants", @@ -199,9 +228,9 @@ describe 'database' do "db > Constants:", "ROW_SIZE: 293", "COMMON_NODE_HEADER_SIZE: 6", - "LEAF_NODE_HEADER_SIZE: 10", + "LEAF_NODE_HEADER_SIZE: 14", "LEAF_NODE_CELL_SIZE: 297", - "LEAF_NODE_SPACE_FOR_CELLS: 4086", + "LEAF_NODE_SPACE_FOR_CELLS: 4082", "LEAF_NODE_MAX_CELLS: 13", "db > ", ])

Now, when we want to move the cursor to the end of a leaf node, we can constantly query whether the leaf node has a sibling node, if it does, it will jump back and if it does not, it will be at the end.

@@ -428,7 +432,15 @@ void cursor_advance(Cursor* cursor) { cursor->cell_num += 1; if (cursor->cell_num >= (*leaf_node_num_cells(node))) { - cursor->end_of_table = true; + /* Advance to next leaf node */ + uint32_t next_page_num = *leaf_node_next_leaf(node); + if (next_page_num == 0) { + /* This was rightmost leaf */ + cursor->end_of_table = true; + } else { + cursor->page_num = next_page_num; + cursor->cell_num = 0; + } } }

With the above changes, we can print 15 lines.

db > select (1, user1, [email protected]) (2, user2, [email protected]) (3, user3, [email protected]) (4, user4, [email protected]) (5, user5, [email protected]) (6, user6, [email protected]) (7, user7, [email protected]) (8, user8, [email protected]) (9, user9, [email protected]) (10, user10, [email protected]) (11, user11, [email protected]) (12, user12, [email protected]) (13, user13, [email protected]) (1919251317, 14, [email protected]) (15, user15, [email protected]) Executed. db >

But one doesn't look right.

(1919251317, 14, [email protected])

This is because there was an error splitting the leaf nodes:

@@ -676,7 +690,9 @@ void leaf_node_split_and_insert(Cursor* cursor, uint32_t key, Row* value) { void* destination = leaf_node_cell(destination_node, index_within_node); if (i == cursor->cell_num) { - serialize_row(value, destination); + serialize_row(value, + leaf_node_value(destination_node, index_within_node)); + *leaf_node_key(destination_node, index_within_node) = key; } else if (i > cursor->cell_num) { memcpy(destination, leaf_node_cell(old_node, i - 1), LEAF_NODE_CELL_SIZE); } else {

Remember that each cell in a leaf node contains a key first, then a value.


Now, at last, our output is as expected.

db > select (1, user1, [email protected]) (2, user2, [email protected]) (3, user3, [email protected]) (4, user4, [email protected]) (5, user5, [email protected]) (6, user6, [email protected]) (7, user7, [email protected]) (8, user8, [email protected]) (9, user9, [email protected]) (10, user10, [email protected]) (11, user11, [email protected]) (12, user12, [email protected]) (13, user13, [email protected]) (14, user14, [email protected]) (15, user15, [email protected]) Executed. db >
Radium_1209 123 original articles were published. 30 won. 30,000 visits+ Private letter follow

8 February 2020, 01:54 | Views: 3699

Add new comment

For adding a comment, please log in
or create account

0 comments