diff --git a/main.c b/main.c index ec61f95..b800f64 100644 --- a/main.c +++ b/main.c @@ -13,12 +13,11 @@ #include // Macros -#define COLUMN_USERNAME_SIZE 32 +#define COLUMN_USERNAME_SIZE 255 #define COLUMN_EMAIL_SIZE 255 - +#define TABLE_MAX_PAGES 100 #define size_of_attribute(Struct, Attribute) sizeof(((Struct *)0)->Attribute) -#define TABLE_MAX_PAGES 100 typedef enum { EXECUTE_SUCCESS, EXECUTE_DUPLICATE_KEY, @@ -119,11 +118,87 @@ const uint32_t LEAF_NODE_KEY_OFFSET = 0; const uint32_t LEAF_NODE_VALUE_SIZE = ROW_SIZE; const uint32_t LEAF_NODE_VALUE_OFFSET = LEAF_NODE_KEY_OFFSET + LEAF_NODE_KEY_SIZE; -const uint32_t LEAF_NODE_CELL_SIZE = LEAF_NODE_KEY_SIZE + LEAF_NODE_HEADER_SIZE; +const uint32_t LEAF_NODE_CELL_SIZE = LEAF_NODE_KEY_SIZE + LEAF_NODE_VALUE_SIZE; const uint32_t LEAF_NODE_SPACE_FOR_CELLS = PAGE_SIZE - LEAF_NODE_HEADER_SIZE; const uint32_t LEAF_NODE_MAX_CELLS = LEAF_NODE_SPACE_FOR_CELLS / LEAF_NODE_CELL_SIZE; +const uint32_t LEAF_NODE_RIGHT_SPLIT_COUNT = (LEAF_NODE_MAX_CELLS + 1) / 2; +const uint32_t LEAF_NODE_LEFT_SPLIT_COUNT = + (LEAF_NODE_MAX_CELLS + 1) - LEAF_NODE_RIGHT_SPLIT_COUNT; + +/* + * Internal Node Header Layout + */ +const uint32_t INTERNAL_NODE_NUM_KEYS_SIZE = sizeof(uint32_t); +const uint32_t INTERNAL_NODE_NUM_KEYS_OFFSET = COMMON_NODE_HEADER_SIZE; +const uint32_t INTERNAL_NODE_RIGHT_CHILD_SIZE = sizeof(uint32_t); +const uint32_t INTERNAL_NODE_RIGHT_CHILD_OFFSET = + INTERNAL_NODE_NUM_KEYS_OFFSET + INTERNAL_NODE_NUM_KEYS_SIZE; +const uint32_t INTERNAL_NODE_HEADER_SIZE = COMMON_NODE_HEADER_SIZE + + INTERNAL_NODE_NUM_KEYS_SIZE + + INTERNAL_NODE_RIGHT_CHILD_SIZE; + +/* + * Internal Node Body Layout + */ +const uint32_t INTERNAL_NODE_KEY_SIZE = sizeof(uint32_t); +const uint32_t INTERNAL_NODE_CHILD_SIZE = sizeof(uint32_t); +const uint32_t INTERNAL_NODE_CELL_SIZE = + INTERNAL_NODE_CHILD_SIZE + INTERNAL_NODE_KEY_SIZE; + +// Prototypes + +Cursor *leaf_node_find(Table *, uint32_t, uint32_t); +Cursor *table_find(Table *, uint32_t); +Cursor *table_start(Table *); +ExecuteResult execute_insert(Statement *, Table *); +ExecuteResult execute_select(Statement *, Table *); +ExecuteResult execute_statement(Statement *, Table *); +InputBuffer *new_input_buffer(); +MetaCommandResult do_meta_command(InputBuffer *, Table *); +NodeType get_node_type(void *); +Pager *pager_open(const char *); +PrepareResult prepare_insert(InputBuffer *, Statement *); +PrepareResult prepare_statement(InputBuffer *, Statement *); +Table *db_open(const char *); +bool is_node_root(void *); +uint32_t *internal_node_child(void *, uint32_t); +uint32_t *internal_node_key(void *, uint32_t); +uint32_t *internal_node_num_keys(void *); +uint32_t *internal_node_right_child(void *); +uint32_t *leaf_node_key(void *, uint32_t); +uint32_t *leaf_node_num_cells(void *); +uint32_t get_node_max_key(void *); +uint32_t get_unused_page_num(Pager *); +void *cursor_value(Cursor *); +void *get_page(Pager *, uint32_t); +void *get_page(Pager *, uint32_t); +void *leaf_node_cell(void *, uint32_t); +void *leaf_node_value(void *, uint32_t); +void close_input_buffer(InputBuffer *); +void create_new_root(Table *, uint32_t); +void cursor_advance(Cursor *); +void db_close(Table *); +void deserialize_row(void *, Row *); +void indent(uint32_t); +void initialize_internal_node(void *); +void initialize_internal_node(void *); +void initialize_leaf_node(void *); +void leaf_node_insert(Cursor *, uint32_t, Row *); +void leaf_node_split_and_insert(Cursor *, uint32_t, Row *); +void pager_flush(Pager *, uint32_t); +void print_row(Row *); +void print_tree(Pager *, uint32_t, uint32_t); +void print_tree(Pager *, uint32_t, uint32_t); +void read_input(InputBuffer *); +void serialize_row(Row *, void *); +void set_node_root(void *, bool); +void set_node_type(void *, NodeType); +void print_prompt(); + +void print_prompt() { printf("db > "); } + void print_constants() { printf("ROW_SIZE: %d\n", ROW_SIZE); printf("COMMON_NODE_HEADER_SIZE: %d\n", COMMON_NODE_HEADER_SIZE); @@ -133,6 +208,63 @@ void print_constants() { printf("LEAF_NODE_MAX_CELLS: %d\n", LEAF_NODE_MAX_CELLS); } +void print_row(Row *row) { + printf("(%d, %s, %s)\n", row->id, row->username, row->email); +} + +void print_tree(Pager* pager, uint32_t page_num, uint32_t indentation_level) { + void* node = get_page(pager, page_num); + uint32_t num_keys, child; + + switch (get_node_type(node)) { + case (NODE_LEAF): + num_keys = *leaf_node_num_cells(node); + indent(indentation_level); + printf("- leaf (size %d)\n", num_keys); + for (uint32_t i = 0; i < num_keys; i++) { + indent(indentation_level + 1); + printf("- %d\n", *leaf_node_key(node, i)); + } + break; + case (NODE_INTERNAL): + num_keys = *internal_node_num_keys(node); + indent(indentation_level); + printf("- internal (size %d)\n", num_keys); + for (uint32_t i = 0; i < num_keys; i++) { + child = *internal_node_child(node, i); + print_tree(pager, child, indentation_level + 1); + + indent(indentation_level + 1); + printf("- key %d\n", *internal_node_key(node, i)); + } + child = *internal_node_right_child(node); + print_tree(pager, child, indentation_level + 1); + break; + } +} + +void indent(uint32_t level) { + for (uint32_t i = 0; i < level; i++) { + printf(" "); + } +} +/* + * Setter and Getter + */ +void set_node_type(void *node, NodeType type) { + uint8_t value = type; + *((uint8_t *)(node + NODE_TYPE_OFFSET)) = value; +} + +NodeType get_node_type(void *node) { + uint8_t value = *((uint8_t *)(node + NODE_TYPE_OFFSET)); + return (NodeType)value; +} + +void set_node_root(void *node, bool is_root) { + uint8_t value = is_root; + *((uint8_t *)(node + IS_ROOT_OFFSET)) = value; +} /* * Takes a Node * Returns the number of cells found in the node @@ -169,33 +301,199 @@ void *leaf_node_value(void *node, uint32_t cell_num) { return leaf_node_cell(node, cell_num) + LEAF_NODE_KEY_SIZE; } -NodeType get_node_type(void* node) { - uint8_t value = *((uint8_t*)(node + NODE_TYPE_OFFSET)); - return (NodeType)value; -} - -void set_node_type(void* node, NodeType type) { - uint8_t value = type; - *((uint8_t*)(node + NODE_TYPE_OFFSET)) = value; -} /* * Takes a Node * Sets the number of cell for a node to 0 * */ -void initialize_leaf_node(void* node) { +void initialize_leaf_node(void *node) { set_node_type(node, NODE_LEAF); + set_node_root(node, false); *leaf_node_num_cells(node) = 0; } +/* + * Takes a Cursor, Key and Value + * Gets the node from the cursor. Checks if the page can input new data. + * If our cursor cell number is less that the available space shift memory back + * one and add data to the cursor cell number. + * */ + +void leaf_node_insert(Cursor *cursor, uint32_t key, Row *value) { + void *node = get_page(cursor->table->pager, cursor->page_num); -void print_leaf_node(void *node) { uint32_t num_cells = *leaf_node_num_cells(node); - printf("leaf (size %d)\n", num_cells); - for (uint32_t i = 0; i < num_cells; i++) { - uint32_t key = *leaf_node_key(node, i); - printf(" - %d : %d\n", i, key); + if (num_cells >= LEAF_NODE_MAX_CELLS) { + // Node is Full. + leaf_node_split_and_insert(cursor, key, value); + return; + } + + if (cursor->cell_num < num_cells) { + // Make room for new cell + for (uint32_t i = num_cells; i > cursor->cell_num; i--) { + memcpy(leaf_node_cell(node, i), leaf_node_cell(node, i - 1), + LEAF_NODE_CELL_SIZE); + } } + + *(leaf_node_num_cells(node)) += 1; + *(leaf_node_key(node, cursor->cell_num)) = key; + serialize_row(value, leaf_node_value(node, cursor->cell_num)); } +void leaf_node_split_and_insert(Cursor *cursor, uint32_t key, Row *value) { + /* + * Create new node and move half the cells over. + * Insert new value in one of the two nodes. + * Update the parent or create new parent. + */ + + void *old_node = get_page(cursor->table->pager, cursor->page_num); + 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); + + /* + * All existing key plus new key should be divided + * between old(left) and new(right) nodes. + * Starting from the right, move each key to correct position. + */ + + for (int32_t i = LEAF_NODE_MAX_CELLS; i >= 0; i--) { + void *destination_node; + if (i >= LEAF_NODE_LEFT_SPLIT_COUNT) { + destination_node = new_node; + } else { + destination_node = old_node; + } + + uint32_t index_within_node = i % LEAF_NODE_LEFT_SPLIT_COUNT; + void *destination = leaf_node_cell(destination_node, index_within_node); + + if (i == cursor->cell_num) { + serialize_row(value, destination); + } else if (i > cursor->cell_num) { + memcpy(destination, leaf_node_cell(old_node, i - 1), LEAF_NODE_CELL_SIZE); + } else { + memcpy(destination, leaf_node_cell(old_node, i), LEAF_NODE_CELL_SIZE); + } + } + + *(leaf_node_num_cells(old_node)) = LEAF_NODE_LEFT_SPLIT_COUNT; + *(leaf_node_num_cells(new_node)) = LEAF_NODE_RIGHT_SPLIT_COUNT; + + if (is_node_root(old_node)) { + return create_new_root(cursor->table, new_page_num); + } else { + printf("Need to implement updating parent after split"); + exit(EXIT_FAILURE); + } +} +Cursor *leaf_node_find(Table *table, uint32_t page_num, uint32_t key) { + void *node = get_page(table->pager, page_num); + uint32_t num_cells = *leaf_node_num_cells(node); + + Cursor *cursor = malloc(sizeof(Cursor)); + cursor->table = table; + cursor->page_num = page_num; + + // Binary search + uint32_t min_index = 0; + uint32_t one_past_max_index = num_cells; + while (one_past_max_index != min_index) { + uint32_t index = (min_index + one_past_max_index) / 2; + uint32_t key_at_index = *leaf_node_key(node, index); + if (key == key_at_index) { + cursor->cell_num = index; + return cursor; + } + if (key < key_at_index) { + one_past_max_index = index; + } else { + min_index = index + 1; + } + } + + cursor->cell_num = min_index; + return cursor; +} + +uint32_t *internal_node_num_keys(void *node) { + return node + INTERNAL_NODE_NUM_KEYS_OFFSET; +} + +uint32_t *internal_node_right_child(void *node) { + return node + INTERNAL_NODE_RIGHT_CHILD_OFFSET; +} + +uint32_t *internal_node_cell(void *node, uint32_t cell_num) { + return node + INTERNAL_NODE_HEADER_SIZE + cell_num * INTERNAL_NODE_CELL_SIZE; +} + +uint32_t *internal_node_child(void *node, uint32_t child_num) { + uint32_t num_keys = *internal_node_num_keys(node); + if (child_num > num_keys) { + printf("Tried to accesss child num %d > num keys %d\n", child_num, + num_keys); + exit(EXIT_FAILURE); + } else if (child_num == num_keys) { + return internal_node_right_child(node); + } else { + return internal_node_cell(node, child_num); + } +} + +uint32_t *internal_node_key(void *node, uint32_t key_num) { + return internal_node_cell(node, key_num) + INTERNAL_NODE_CHILD_SIZE; +} + +void initialize_internal_node(void *node) { + set_node_type(node, NODE_INTERNAL); + set_node_root(node, false); + *internal_node_num_keys(node) = 0; +} + +uint32_t get_node_max_key(void *node) { + switch (get_node_type(node)) { + case NODE_INTERNAL: + return *internal_node_key(node, *internal_node_num_keys(node) - 1); + case NODE_LEAF: + return *leaf_node_key(node, *leaf_node_num_cells(node) - 1); + } +} + +bool is_node_root(void *node) { + uint8_t value = *((uint8_t *)(node + IS_ROOT_OFFSET)); + return (bool)value; +} + +void create_new_root(Table *table, uint32_t right_child_page_num) { + /* + * Handling Splitting the root. + * Old root copid to new page and becomes the left child. + * Address of right child passed in. + * Re-initialized root page to containt he new root node. + * New root node point to two children. + */ + + void *root = get_page(table->pager, table->root_page_num); + void *right_child = get_page(table->pager, right_child_page_num); + uint32_t left_child_page_num = get_unused_page_num(table->pager); + void *left_child = get_page(table->pager, left_child_page_num); + + memcpy(left_child, root, PAGE_SIZE); + set_node_root(left_child, false); + + /* Root node is new internal node with one key and two children */ + initialize_internal_node(root); + set_node_root(root, true); + *internal_node_num_keys(root) = 1; + *internal_node_child(root, 0) = left_child_page_num; + uint32_t left_child_max_key = get_node_max_key(left_child); + *internal_node_key(root, 0) = left_child_max_key; + *internal_node_right_child(root) = right_child_page_num; +} + + /* * It take the Row as a source and void as destination as memory. * Copys the data from the row to the page memory found at the destincation @@ -203,8 +501,8 @@ void print_leaf_node(void *node) { * */ void serialize_row(Row *source, void *destination) { memcpy(destination + ID_OFFSET, &(source->id), ID_SIZE); - strncpy(destination + USERNAME_OFFSET, source->username, USERNAME_SIZE); - strncpy(destination + EMAIL_OFFSET, source->email, EMAIL_SIZE); + memcpy(destination + USERNAME_OFFSET, &(source->username), USERNAME_SIZE); + memcpy(destination + EMAIL_OFFSET, &(source->email), EMAIL_SIZE); } /* @@ -307,48 +605,6 @@ void pager_flush(Pager *pager, uint32_t page_num) { } } -/* - * Takes pager and page num - * Checks if the page exists and grabs it. If not out of memory creates a new - * page. Retuns the wanted page. - * */ -void *get_page(Pager *pager, uint32_t page_num) { - if (page_num > TABLE_MAX_PAGES) { - printf("Tried to fetch page number out of bounds. %d > %d\n", page_num, - TABLE_MAX_PAGES); - exit(EXIT_FAILURE); - } - - if (pager->pages[page_num] == NULL) { - // Cach miss. - // Nedd to allocate memrory and load from file. - void *page = malloc(PAGE_SIZE); - uint32_t num_pages = pager->file_length / PAGE_SIZE; - - // Savind partial page at the end of a file. - if (pager->file_length % PAGE_SIZE) { - num_pages += 1; - } - - if (page_num <= num_pages) { - lseek(pager->file_descriptor, page_num * PAGE_SIZE, SEEK_SET); - ssize_t bytes_read = read(pager->file_descriptor, page, PAGE_SIZE); - if (bytes_read == -1) { - printf("Error reading file: %d\n", errno); - exit(EXIT_FAILURE); - } - } - - pager->pages[page_num] = page; - - if (page_num >= pager->num_pages) { - pager->num_pages = page_num + 1; - } - } - - return pager->pages[page_num]; -} - /* * Create a new table by allocating the porper size for a table * It also initialized all the page to NULL @@ -365,39 +621,11 @@ Table *db_open(const char *filename) { // New DB file. initialize page 0 as leaf node. void *root_node = get_page(pager, 0); initialize_leaf_node(root_node); + set_node_root(root_node, true); } return table; } -/* - * Takes a Cursor, Key and Value - * Gets the node from the cursor. Checks if the page can input new data. - * If our cursor cell number is less that the available space shift memory back - * one and add data to the cursor cell number. - * */ -void leaf_node_insert(Cursor *cursor, uint32_t key, Row *value) { - void *node = get_page(cursor->table->pager, cursor->page_num); - - uint32_t num_cells = *leaf_node_num_cells(node); - if (num_cells >= LEAF_NODE_MAX_CELLS) { - // Node is Full. - printf("Need to implement splitting a leaf node.\n"); - exit(EXIT_FAILURE); - } - - if (cursor->cell_num < num_cells) { - // Make room for new cell - for (uint32_t i = num_cells; i > cursor->cell_num; i--) { - memcpy(leaf_node_cell(node, i), leaf_node_cell(node, i - 1), - LEAF_NODE_CELL_SIZE); - } - } - - *(leaf_node_num_cells(node)) += 1; - *(leaf_node_key(node, cursor->cell_num)) = key; - serialize_row(value, leaf_node_value(node, cursor->cell_num)); -} - /* * Takes Table * Frees the memory allocated to Table and Pager. @@ -434,6 +662,53 @@ void db_close(Table *table) { free(table); } +/* + * Until we start recycling free pages newpages go to the end of the file. + */ +uint32_t get_unused_page_num(Pager *pager) { return pager->num_pages; } + +/* + * Takes pager and page num + * Checks if the page exists and grabs it. If not out of memory creates a new + * page. Retuns the wanted page. + * */ +void *get_page(Pager *pager, uint32_t page_num) { + if (page_num > TABLE_MAX_PAGES) { + printf("Tried to fetch page number out of bounds. %d > %d\n", page_num, + TABLE_MAX_PAGES); + exit(EXIT_FAILURE); + } + + if (pager->pages[page_num] == NULL) { + // Cach miss. + // Nedd to allocate memrory and load from file. + void *page = malloc(PAGE_SIZE); + uint32_t num_pages = pager->file_length / PAGE_SIZE; + + // Savind partial page at the end of a file. + if (pager->file_length % PAGE_SIZE) { + num_pages += 1; + } + + if (page_num <= num_pages) { + lseek(pager->file_descriptor, page_num * PAGE_SIZE, SEEK_SET); + ssize_t bytes_read = read(pager->file_descriptor, page, PAGE_SIZE); + if (bytes_read == -1) { + printf("Error reading file: %d\n", errno); + exit(EXIT_FAILURE); + } + } + + pager->pages[page_num] = page; + + if (page_num >= pager->num_pages) { + pager->num_pages = page_num + 1; + } + } + + return pager->pages[page_num]; +} + /* * Accepts a table and free all memory allocated to pages as well as the table * */ @@ -446,8 +721,6 @@ void free_table(Table* table){ } */ -void print_prompt() { printf("db > "); } - /* * Takes an InputBuffer struct and inserts the input data. * Using the getline method it also save the input lenght. @@ -486,46 +759,17 @@ Cursor *table_start(Table *table) { return cursor; } -Cursor* leaf_node_find(Table* table, uint32_t page_num, uint32_t key) { - void* node = get_page(table->pager, page_num); - uint32_t num_cells = *leaf_node_num_cells(node); - - Cursor* cursor = malloc(sizeof(Cursor)); - cursor->table = table; - cursor->page_num = page_num; - - // Binary search - uint32_t min_index = 0; - uint32_t one_past_max_index = num_cells; - while (one_past_max_index != min_index) { - uint32_t index = (min_index + one_past_max_index) / 2; - uint32_t key_at_index = *leaf_node_key(node, index); - if (key == key_at_index) { - cursor->cell_num = index; - return cursor; - } - if (key < key_at_index) { - one_past_max_index = index; - } else { - min_index = index + 1; - } - } - - cursor->cell_num = min_index; - return cursor; -} - /* Return the position of the given key. If the key is not present, return the position where it should be inserted */ -Cursor* table_find(Table* table, uint32_t key) { +Cursor *table_find(Table *table, uint32_t key) { uint32_t root_page_num = table->root_page_num; - void* root_node = get_page(table->pager, root_page_num); + void *root_node = get_page(table->pager, root_page_num); if (get_node_type(root_node) == NODE_LEAF) { - return leaf_node_find(table, root_page_num, key); + return leaf_node_find(table, root_page_num, key); } else { printf("Need to implement searching an internal node\n"); exit(EXIT_FAILURE); @@ -548,6 +792,7 @@ void *cursor_value(Cursor *cursor) { * Takes a Cursor * Advances the cursor to the next row and check if it is the end of table. * */ + void cursor_advance(Cursor *cursor) { uint32_t page_num = cursor->page_num; void *node = get_page(cursor->table->pager, page_num); @@ -558,10 +803,6 @@ void cursor_advance(Cursor *cursor) { } } -void print_row(Row *row) { - printf("(%d, %s, %s)\n", row->id, row->username, row->email); -} - /* * It takes an inputbuffer and does all the MetaCommands passed. * */ @@ -576,7 +817,7 @@ MetaCommandResult do_meta_command(InputBuffer *input_buffer, Table *table) { return META_COMMAND_SUCCESS; } else if (strcmp(input_buffer->buffer, ".btree") == 0) { printf("Tree:\n"); - print_leaf_node(get_page(table->pager, 0)); + print_tree(table->pager, 0, 0); return META_COMMAND_SUCCESS; } else { return META_COMMAND_UNRECOGNIZED_COMMAND; @@ -648,20 +889,17 @@ PrepareResult prepare_statement(InputBuffer *input_buffer, ExecuteResult execute_insert(Statement *statement, Table *table) { void *node = get_page(table->pager, table->root_page_num); uint32_t num_cells = (*leaf_node_num_cells(node)); - if (num_cells >= LEAF_NODE_MAX_CELLS) { - return EXECUTE_TABLE_FULL; - } - Row* row_to_insert = &(statement->row_to_insert); + Row *row_to_insert = &(statement->row_to_insert); uint32_t key_to_insert = row_to_insert->id; - Cursor* cursor = table_find(table, key_to_insert); + Cursor *cursor = table_find(table, key_to_insert); if (cursor->cell_num < num_cells) { uint32_t key_at_index = *leaf_node_key(node, cursor->cell_num); if (key_at_index == key_to_insert) { return EXECUTE_DUPLICATE_KEY; } - } + } leaf_node_insert(cursor, row_to_insert->id, row_to_insert); @@ -699,10 +937,8 @@ ExecuteResult execute_statement(Statement *statement, Table *table) { switch (statement->type) { case (STATEMENT_INSERT): return execute_insert(statement, table); - break; case (STATEMENT_SELECT): return execute_select(statement, table); - break; } } @@ -748,7 +984,7 @@ int main(int argc, char *argv[]) { printf("Syntax Error. Could not parse statment."); continue; case (PREPARE_UNRECOGNIZED_STATEMENT): - printf("Uncrecognized keyword at start '%s'.\n", input_buffer->buffer); + printf("Unrecognized keyword at start '%s'.\n", input_buffer->buffer); continue; } diff --git a/spec/main_spec.rb b/spec/main_spec.rb index 90824f1..087011f 100644 --- a/spec/main_spec.rb +++ b/spec/main_spec.rb @@ -45,12 +45,26 @@ def run_script(commands) it 'inserts and retrieves a row' do result = run_script([ "insert 1 user1 person1@example.com", + "insert 2 user1 person1@example.com", + "insert 3 user1 person1@example.com", + "insert 4 user1 person1@example.com", + "insert 5 user1 person1@example.com", + "insert 6 user1 person1@example.com", + "insert 7 user1 person1@example.com", + "insert 8 user1 person1@example.com", "select", ".exit", ]) expect(result).to match_array([ "db > Executed.", "db > (1, user1, person1@example.com)", + "db > (2, user1, person1@example.com)", + "db > (3, user1, person1@example.com)", + "db > (4, user1, person1@example.com)", + "db > (5, user1, person1@example.com)", + "db > (6, user1, person1@example.com)", + "db > (7, user1, person1@example.com)", + "db > (8, user1, person1@example.com)", "Executed.", "db > ", ]) @@ -145,14 +159,47 @@ def run_script(commands) "db > Executed.", "db > Executed.", "db > Tree:", - "leaf (size 3)", - " - 0 : 1", - " - 1 : 2", - " - 2 : 3", + "- leaf (size 3)", + " - 1", + " - 2", + " - 3", "db > " ]) end + it 'allows printing out the structure of a 3-leaf-node btree' do + script = (1..14).map do |i| + "insert #{i} user#{i} person#{i}@example.com" + end + script << ".btree" + script << "insert 15 user15 person15@example.com" + script << ".exit" + result = run_script(script) + + expect(result[14...(result.length)]).to match_array([ + "db > Tree:", + "- internal (size 1)", + " - leaf (size 7)", + " - 1", + " - 2", + " - 3", + " - 4", + " - 5", + " - 6", + " - 7", + " - key 7", + " - leaf (size 7)", + " - 8", + " - 9", + " - 10", + " - 11", + " - 12", + " - 13", + " - 14", + "db > Need to implement searching an internal node", + ]) + end + it 'prints an error message if there is a duplicate id' do script = [ "insert 1 user1 person1@example.com",